From 5987fb7459e42ee970b22a9e7d896fc415321743 Mon Sep 17 00:00:00 2001 From: squalus Date: Tue, 4 Oct 2022 00:47:43 -0700 Subject: [PATCH 001/910] Add fsync-store-paths option - Add recursiveSync function to flush a directory tree to disk - Add AutoCloseFD::startFsync to initiate an asynchronous fsync without waiting for the result - Initiate an asynchronous fsync while extracting NAR files - Implement the fsync-store-paths option in LocalStore --- src/libstore/globals.hh | 7 ++++++ src/libstore/local-store.cc | 23 +++++++++++++++---- src/libutil/archive.cc | 11 ++++++++-- src/libutil/archive.hh | 2 +- src/libutil/filesystem.cc | 44 +++++++++++++++++++++++++++++++++++++ src/libutil/util.cc | 14 +++++++++++- src/libutil/util.hh | 11 ++++++++-- 7 files changed, 102 insertions(+), 10 deletions(-) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 274a15dd7..fd5cce7ad 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -234,6 +234,13 @@ public: default is `true`. )"}; + Setting fsyncStorePaths{this, false, "fsync-store-paths", + R"( + "Whether to call `fsync()` on store paths before registering them, to + flush them to disk. This improves robustness in case of system crashes, + but reduces performance. The default is `false`. + )"}; + Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", "Whether SQLite should use WAL mode."}; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index b67668e52..4bbeebc3a 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1299,7 +1299,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, TeeSource wrapperSource { source, hashSink }; - restorePath(realPath, wrapperSource); + restorePath(realPath, wrapperSource, settings.fsyncStorePaths); auto hashResult = hashSink.finish(); @@ -1342,6 +1342,11 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, optimisePath(realPath, repair); // FIXME: combine with hashPath() + if (settings.fsyncStorePaths) { + recursiveSync(realPath); + syncParent(realPath); + } + registerValidPath(info); } @@ -1402,7 +1407,7 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name tempPath = tempDir + "/x"; if (method == FileIngestionMethod::Recursive) - restorePath(tempPath, bothSource); + restorePath(tempPath, bothSource, settings.fsyncStorePaths); else writeFile(tempPath, bothSource); @@ -1434,7 +1439,7 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name StringSource dumpSource { dump }; /* Restore from the NAR in memory. */ if (method == FileIngestionMethod::Recursive) - restorePath(realPath, dumpSource); + restorePath(realPath, dumpSource, settings.fsyncStorePaths); else writeFile(realPath, dumpSource); } else { @@ -1459,6 +1464,12 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name info.narSize = narHash.second; info.references = references; info.ca = FixedOutputHash { .method = method, .hash = hash }; + + if (settings.fsyncStorePaths) { + recursiveSync(realPath); + syncParent(realPath); + } + registerValidPath(info); } @@ -1491,7 +1502,7 @@ StorePath LocalStore::addTextToStore( autoGC(); - writeFile(realPath, s); + writeFile(realPath, s, 0666, settings.fsyncStorePaths); canonicalisePathMetaData(realPath, {}); @@ -1505,6 +1516,10 @@ StorePath LocalStore::addTextToStore( info.narSize = sink.s.size(); info.references = references; info.ca = TextHash { .hash = hash }; + + if (settings.fsyncStorePaths) + syncParent(realPath); + registerValidPath(info); } diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 0e2b9d12c..e85fe3d3f 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -306,6 +306,9 @@ struct RestoreSink : ParseSink { Path dstPath; AutoCloseFD fd; + bool startFsync; + + explicit RestoreSink(bool startFsync) : startFsync{startFsync} {} void createDirectory(const Path & path) override { @@ -323,6 +326,10 @@ struct RestoreSink : ParseSink void closeRegularFile() override { + /* Initiate an fsync operation without waiting for the result. The real fsync should be run before registering + a store path, but this is a performance optimization to allow the disk write to start early. */ + if (startFsync) + fd.startFsync(); /* Call close explicitly to make sure the error is checked */ fd.close(); } @@ -367,9 +374,9 @@ struct RestoreSink : ParseSink }; -void restorePath(const Path & path, Source & source) +void restorePath(const Path & path, Source & source, bool startFsync) { - RestoreSink sink; + RestoreSink sink { startFsync }; sink.dstPath = path; parseDump(sink, source); } diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index e42dea540..64b3501b6 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -95,7 +95,7 @@ struct RetrieveRegularNARSink : ParseSink void parseDump(ParseSink & sink, Source & source); -void restorePath(const Path & path, Source & source); +void restorePath(const Path & path, Source & source, bool startFsync = false); /* Read a NAR from 'source' and write it to 'sink'. */ void copyNAR(Source & source, Sink & sink); diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc index 3a732cff8..5666fc809 100644 --- a/src/libutil/filesystem.cc +++ b/src/libutil/filesystem.cc @@ -1,6 +1,7 @@ #include #include #include +#include #include "finally.hh" #include "util.hh" @@ -170,4 +171,47 @@ void moveFile(const Path & oldName, const Path & newName) } } +void recursiveSync(const Path & path) +{ + /* If it's a file, just fsync and return */ + auto st = lstat(path); + if (S_ISREG(st.st_mode)) { + AutoCloseFD fd = open(path.c_str(), O_RDONLY, 0); + if (!fd) + throw SysError("opening file '%1%'", path); + fd.fsync(); + return; + } + + /* Otherwise, perform a depth-first traversal of the directory and fsync all the files */ + std::deque dirsToEnumerate; + dirsToEnumerate.push_back(path); + std::vector dirsToFsync; + while (!dirsToEnumerate.empty()) { + auto currentDir = dirsToEnumerate.back(); + dirsToEnumerate.pop_back(); + const auto dirEntries = readDirectory(currentDir); + for (const auto& dirEntry : dirEntries) { + auto entryPath = currentDir + "/" + dirEntry.name; + if (dirEntry.type == DT_DIR) { + dirsToEnumerate.emplace_back(std::move(entryPath)); + } else if (dirEntry.type == DT_REG) { + AutoCloseFD fd = open(entryPath.c_str(), O_RDONLY, 0); + if (!fd) + throw SysError("opening file '%1%'", entryPath); + fd.fsync(); + } + } + dirsToFsync.emplace_back(std::move(currentDir)); + } + + /* fsync all the directories */ + for (auto dir = dirsToFsync.rbegin(); dir != dirsToFsync.rend(); ++dir) { + AutoCloseFD fd = open(dir->c_str(), O_RDONLY, 0); + if (!fd) + throw SysError("opening directory '%1%'", *dir); + fd.fsync(); + } +} + } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 993dc1cb6..383288667 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -838,7 +839,7 @@ void AutoCloseFD::close() } } -void AutoCloseFD::fsync() +void AutoCloseFD::fsync() const { if (fd != -1) { int result; @@ -853,6 +854,17 @@ void AutoCloseFD::fsync() } +void AutoCloseFD::startFsync() const +{ +#if __linux__ + if (fd != -1) { + /* Ignore failure, since fsync must be run later anyway. This is just a performance optimization. */ + ::sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE); + } +#endif +} + + AutoCloseFD::operator bool() const { return fd != -1; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 9b149de80..ea83351a7 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -119,9 +119,12 @@ void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool s void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false); -/* Flush a file's parent directory to disk */ +/* Flush a path's parent directory to disk */ void syncParent(const Path & path); +/* Flush a file or entire directory tree to disk */ +void recursiveSync(const Path & path); + /* Read a line from a file descriptor. */ std::string readLine(int fd); @@ -234,7 +237,11 @@ public: explicit operator bool() const; int release(); void close(); - void fsync(); + /* Perform a blocking fsync operation */ + void fsync() const; + /* Asynchronously flush to disk without blocking, if available on the platform. This is just a performance + * optimization, and fsync must be run later even if this is called. */ + void startFsync() const; }; From 04023360edbeb3477286783b3101a5b53f78021b Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Tue, 3 Aug 2021 15:25:15 -0500 Subject: [PATCH 002/910] Evaluate nix-shell -i args relative to script When writing a shebang script, you expect your path to be relative to the script, not the cwd. We previously handled this correctly for relative file paths, but not for expressions. This handles both -p & -E args. My understanding is this should be what we want in any cases I can think of - people run scripts from many different working directories. @edolstra is there any reason to handle -p args differently in this case? Fixes #4232 --- src/nix-build/nix-build.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 75ce12a8c..4120ca3cf 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -298,7 +298,9 @@ static void main_nix_build(int argc, char * * argv) else for (auto i : left) { if (fromArgs) - exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath(CanonPath::fromCwd()))); + exprs.push_back(state->parseExprFromString( + std::move(i), + state->rootPath(CanonPath::fromCwd(inShebang ? dirOf(script) : ".")))); else { auto absolute = i; try { @@ -311,7 +313,7 @@ static void main_nix_build(int argc, char * * argv) /* If we're in a #! script, interpret filenames relative to the script. */ exprs.push_back(state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, - inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i))))); + inShebang ? absPath(i, absPath(dirOf(script))) : i))))); } } From 9a4641146f79d631eb0825c7313dd335715de0d6 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sat, 25 Nov 2023 19:06:45 -0500 Subject: [PATCH 003/910] tests: ensure nix-shell uses relative paths for expressions --- tests/functional/nix-shell.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 13403fadb..702d3a6b5 100644 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -59,6 +59,16 @@ chmod a+rx $TEST_ROOT/shell.shebang.sh output=$($TEST_ROOT/shell.shebang.sh abc def) [ "$output" = "foo bar abc def" ] +# Test nix-shell shebang mode with an alternate working directory +sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.expr > $TEST_ROOT/shell.shebang.expr +chmod a+rx $TEST_ROOT/shell.shebang.expr +# Should fail due to expressions using relative path +! $TEST_ROOT/shell.shebang.expr bar +cp shell.nix config.nix $TEST_ROOT +# Should succeed +output=$($TEST_ROOT/shell.shebang.expr bar) +[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/shell.shebang.expr bar' ] + # Test nix-shell shebang mode again with metacharacters in the filename. # First word of filename is chosen to not match any file in the test root. sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh From f66f498bd43efaa6883f12ca5988a282eef09697 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sat, 25 Nov 2023 19:07:29 -0500 Subject: [PATCH 004/910] notes: document change in nix-shell behavior --- doc/manual/rl-next/shebang-relative.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 doc/manual/rl-next/shebang-relative.md diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md new file mode 100644 index 000000000..dbda0db4c --- /dev/null +++ b/doc/manual/rl-next/shebang-relative.md @@ -0,0 +1,8 @@ +synopsis: ensure nix-shell shebang uses relative path +prs: #5088 +description: { + +`nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. +The new behavior is that evalutations are performed relative to the script. + +} From 5d0bdb1d3f60d446a0d810e097d1e27edaa203a4 Mon Sep 17 00:00:00 2001 From: wh0 Date: Sun, 10 Dec 2023 19:14:51 -0800 Subject: [PATCH 005/910] nix-profile: fix both profile links detection --- scripts/nix-profile-daemon.sh.in | 7 +++---- scripts/nix-profile.sh.in | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/nix-profile-daemon.sh.in b/scripts/nix-profile-daemon.sh.in index d256b24ed..6a7318912 100644 --- a/scripts/nix-profile-daemon.sh.in +++ b/scripts/nix-profile-daemon.sh.in @@ -9,11 +9,9 @@ else NIX_LINK_NEW=$HOME/.local/state/nix/profile fi if [ -e "$NIX_LINK_NEW" ]; then - NIX_LINK="$NIX_LINK_NEW" -else - if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then + if [ -t 2 ] && [ -e "$NIX_LINK" ]; then warning="\033[1;35mwarning:\033[0m" - printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 + printf "$warning Both %s and legacy %s exist; using the former.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then printf " Since the profiles match, you can safely delete either of them.\n" 1>&2 else @@ -26,6 +24,7 @@ else printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2 fi fi + NIX_LINK="$NIX_LINK_NEW" fi export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK" diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in index 44bc96e89..7fa0f4a7f 100644 --- a/scripts/nix-profile.sh.in +++ b/scripts/nix-profile.sh.in @@ -9,11 +9,9 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then NIX_LINK_NEW="$HOME/.local/state/nix/profile" fi if [ -e "$NIX_LINK_NEW" ]; then - NIX_LINK="$NIX_LINK_NEW" - else - if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then + if [ -t 2 ] && [ -e "$NIX_LINK" ]; then warning="\033[1;35mwarning:\033[0m" - printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 + printf "$warning Both %s and legacy %s exist; using the former.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then printf " Since the profiles match, you can safely delete either of them.\n" 1>&2 else @@ -26,6 +24,7 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2 fi fi + NIX_LINK="$NIX_LINK_NEW" fi # Set up environment. From 29eb4d354ab0ef07a2099f7ecb17a14585e059c3 Mon Sep 17 00:00:00 2001 From: w Date: Fri, 29 Dec 2023 07:14:53 +0000 Subject: [PATCH 006/910] nix-profile: add cross reference to installer test --- scripts/nix-profile-daemon.sh.in | 1 + scripts/nix-profile.sh.in | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/nix-profile-daemon.sh.in b/scripts/nix-profile-daemon.sh.in index 6a7318912..f0e396da0 100644 --- a/scripts/nix-profile-daemon.sh.in +++ b/scripts/nix-profile-daemon.sh.in @@ -1,4 +1,5 @@ # Only execute this file once per shell. +# This file is tested by tests/installer/default.nix. if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi __ETC_PROFILE_NIX_SOURCED=1 diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in index 7fa0f4a7f..e868399b1 100644 --- a/scripts/nix-profile.sh.in +++ b/scripts/nix-profile.sh.in @@ -1,3 +1,4 @@ +# This file is tested by tests/installer/default.nix. if [ -n "$HOME" ] && [ -n "$USER" ]; then # Set up the per-user profile. From 4e3dc5f925b8e84b85a1d9b52b57c0e70b45f664 Mon Sep 17 00:00:00 2001 From: w Date: Sat, 30 Dec 2023 06:24:06 +0000 Subject: [PATCH 007/910] tests: test with conflicting profile links --- tests/installer/default.nix | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/installer/default.nix b/tests/installer/default.nix index 238c6ac8e..4aed6eae4 100644 --- a/tests/installer/default.nix +++ b/tests/installer/default.nix @@ -13,6 +13,17 @@ let ''; }; + install-both-profile-links = { + script = '' + tar -xf ./nix.tar.xz + mv ./nix-* nix + ln -s $HOME/.local/state/nix/profiles/a-profile $HOME/.nix-profile + mkdir -p $HOME/.local/state/nix + ln -s $HOME/.local/state/nix/profiles/b-profile $HOME/.local/state/nix/profile + ./nix/install --no-channel-add + ''; + }; + install-force-no-daemon = { script = '' tar -xf ./nix.tar.xz From 8594f3cd5ad34838b2b7997af4909160e5887d73 Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Wed, 31 Jan 2024 19:41:17 +0800 Subject: [PATCH 008/910] libutil/url: fix git+file:./ parse error Previously, the "file:./" prefix was not correctly recognized in fixGitURL; instead, it was mistaken as a file path, which resulted in a parsed url of the form "file://file:./". This commit fixes the issue by properly detecting the "file:" prefix. Note, however, that unlike "file://", the "file:./" URI is _not_ standardized, but has been widely used to referred to relative file paths. In particular, the "git+file:./" did work for nix<=2.18, and was broken since nix 2.19.0. Finally, this commit fixes the issue completely for the 2.19 series, but is still inadequate for the 2.20 series due to new behaviors from the switch to libgit2. However, it does improve the correctness of parsing even though it is not yet a complete solution. --- src/libutil/url.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libutil/url.cc b/src/libutil/url.cc index c6561441d..f4178f87f 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -171,16 +171,16 @@ std::string fixGitURL(const std::string & url) std::regex scpRegex("([^/]*)@(.*):(.*)"); if (!hasPrefix(url, "/") && std::regex_match(url, scpRegex)) return std::regex_replace(url, scpRegex, "ssh://$1@$2/$3"); - else { - if (url.find("://") == std::string::npos) { - return (ParsedURL { - .scheme = "file", - .authority = "", - .path = url - }).to_string(); - } else - return url; + if (hasPrefix(url, "file:")) + return url; + if (url.find("://") == std::string::npos) { + return (ParsedURL { + .scheme = "file", + .authority = "", + .path = url + }).to_string(); } + return url; } // https://www.rfc-editor.org/rfc/rfc3986#section-3.1 From 358c26fd13a902d9a4032a00e6683571be07a384 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 17 Feb 2024 19:36:32 +0700 Subject: [PATCH 009/910] fetchTree: shallow git fetching by default Motivation: make git fetching more efficient for most repos by default --- .../shallow-git-fetching-by-default.md | 12 +++++ src/libexpr/primops/fetchTree.cc | 27 ++++++++--- .../test-cases/fetchTree-shallow/default.nix | 45 +++++++++++++++++++ 3 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 doc/manual/rl-next/shallow-git-fetching-by-default.md create mode 100644 tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix diff --git a/doc/manual/rl-next/shallow-git-fetching-by-default.md b/doc/manual/rl-next/shallow-git-fetching-by-default.md new file mode 100644 index 000000000..4d044f881 --- /dev/null +++ b/doc/manual/rl-next/shallow-git-fetching-by-default.md @@ -0,0 +1,12 @@ +--- +synopsis: "`fetchTree` now fetches git repositories shallowly by default" +prs: 10028 +--- + +`builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. + +Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. + +From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. + +The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 1997d5513..2926e3161 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -138,6 +138,11 @@ static void fetchTree( attrs.emplace("exportIgnore", Explicit{true}); } + // fetchTree should fetch git repos with shallow = true by default + if (type == "git" && !params.isFetchGit && !attrs.contains("shallow")) { + attrs.emplace("shallow", Explicit{true}); + } + if (!params.allowNameArgument) if (auto nameIter = attrs.find("name"); nameIter != attrs.end()) state.error( @@ -321,6 +326,8 @@ static RegisterPrimOp primop_fetchTree({ - `ref` (String, optional) + By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled. + A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name. Default: `"HEAD"` @@ -334,8 +341,9 @@ static RegisterPrimOp primop_fetchTree({ - `shallow` (Bool, optional) Make a shallow clone when fetching the Git tree. + When this is enabled, the options `ref` and `allRefs` have no effect anymore. - Default: `false` + Default: `true` - `submodules` (Bool, optional) @@ -345,8 +353,11 @@ static RegisterPrimOp primop_fetchTree({ - `allRefs` (Bool, optional) - If set to `true`, always fetch the entire repository, even if the latest commit is still in the cache. - Otherwise, only the latest commit is fetched if it is not already cached. + By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled. + + Whether to fetch all references (eg. branches and tags) of the repository. + With this argument being true, it's possible to load a `rev` from *any* `ref`. + (Without setting this option, only `rev`s from the specified `ref` are supported). Default: `false` @@ -600,6 +611,8 @@ static RegisterPrimOp primop_fetchGit({ [Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References + This option has no effect once `shallow` cloning is enabled. + 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/`. @@ -617,13 +630,15 @@ static RegisterPrimOp primop_fetchGit({ - `shallow` (default: `false`) Make a shallow clone when fetching the Git tree. - + When this is enabled, the options `ref` and `allRefs` have no effect anymore. - `allRefs` - Whether to fetch all references of the repository. - With this argument being true, it's possible to load a `rev` from *any* `ref` + Whether to fetch all references (eg. branches and tags) 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). + This option has no effect once `shallow` cloning is enabled. + - `verifyCommit` (default: `true` if `publicKey` or `publicKeys` are provided, otherwise `false`) Whether to check `rev` for a signature matching `publicKey` or `publicKeys`. diff --git a/tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix b/tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix new file mode 100644 index 000000000..f635df1f8 --- /dev/null +++ b/tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix @@ -0,0 +1,45 @@ +{ + description = "fetchTree fetches git repos shallowly by default"; + script = '' + # purge nix git cache to make sure we start with a clean slate + client.succeed("rm -rf ~/.cache/nix") + + # add two commits to the repo: + # - one with a large file (2M) + # - another one making the file small again + client.succeed(f""" + dd if=/dev/urandom of={repo.path}/thailand bs=1M count=2 \ + && {repo.git} add thailand \ + && {repo.git} commit -m 'commit1' \ + && echo 'ThaigerSprint' > {repo.path}/thailand \ + && {repo.git} add thailand \ + && {repo.git} commit -m 'commit2' \ + && {repo.git} push origin main + """) + + # memoize the revision + commit2_rev = client.succeed(f""" + {repo.git} rev-parse HEAD + """).strip() + + # construct the fetcher call + fetchGit_expr = f""" + builtins.fetchTree {{ + type = "git"; + url = "{repo.remote}"; + rev = "{commit2_rev}"; + }} + """ + + # fetch the repo via nix + fetched1 = client.succeed(f""" + nix eval --impure --raw --expr '({fetchGit_expr}).outPath' + """) + + # check that the size of ~/.cache/nix is less than 1M + cache_size = client.succeed(""" + du -s ~/.cache/nix + """).strip().split()[0] + assert int(cache_size) < 1024, f"cache size is {cache_size}K which is larger than 1M" + ''; +} From eff90af49811ff186b94c009d34bbcda518acd6a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 13 Apr 2024 12:10:12 -0400 Subject: [PATCH 010/910] Slight refactors in preparation for #10480 Code operating on store objects (including creating them) should, in general, use `ContentAddressMethod` rather than `FileIngestionMethod`. See also dfc876531f269950a4e183a4f77a813c421d7d64 which included some similar refactors. --- src/libexpr/primops.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8cec93001..d50dad5ef 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2248,7 +2248,7 @@ static void addPath( std::string_view name, SourcePath path, Value * filterFun, - FileIngestionMethod method, + ContentAddressMethod method, const std::optional expectedHash, Value & v, const NixStringContext & context) @@ -2280,11 +2280,10 @@ static void addPath( std::optional expectedStorePath; if (expectedHash) - expectedStorePath = state.store->makeFixedOutputPath(name, FixedOutputInfo { - .method = method, - .hash = *expectedHash, - .references = {}, - }); + expectedStorePath = state.store->makeFixedOutputPathFromCA(name, ContentAddressWithReferences::fromParts( + method, + *expectedHash, + {})); if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { auto dstPath = fetchToStore( @@ -2380,7 +2379,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value std::optional path; std::string name; Value * filterFun = nullptr; - auto method = FileIngestionMethod::Recursive; + ContentAddressMethod method = FileIngestionMethod::Recursive; std::optional expectedHash; NixStringContext context; @@ -2395,7 +2394,9 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value else if (n == "filter") state.forceFunction(*(filterFun = attr.value), attr.pos, "while evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") - method = FileIngestionMethod { state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") }; + method = state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") + ? FileIngestionMethod::Recursive + : FileIngestionMethod::Flat; else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256); else From d084c1cb410679ba2f80998e2263c60108aa0115 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 10 Apr 2024 12:46:21 +0200 Subject: [PATCH 011/910] Remove the "locked" flag from the fetcher cache This also reworks the Mercurial fetcher (which was still using the old cache interface) to have two distinct cache mappings: * A ref-to-rev mapping, which is store-independent. * A rev-to-store-path mapping. --- src/libfetchers/cache.cc | 9 ++-- src/libfetchers/cache.hh | 3 +- src/libfetchers/fetch-to-store.cc | 3 +- src/libfetchers/tarball.cc | 3 +- src/libfetchers/unix/mercurial.cc | 84 ++++++++++++++---------------- tests/functional/fetchMercurial.sh | 1 + 6 files changed, 45 insertions(+), 58 deletions(-) diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index e071b4717..83962856c 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -14,7 +14,7 @@ create table if not exists Cache ( input text not null, info text not null, path text not null, - immutable integer not null, + immutable integer not null, /* obsolete */ timestamp integer not null, primary key (input) ); @@ -45,7 +45,7 @@ struct CacheImpl : Cache state->db.exec(schema); state->add.create(state->db, - "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, ?, ?)"); + "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, false, ?)"); state->lookup.create(state->db, "select info, path, immutable, timestamp from Cache where input = ?"); @@ -59,7 +59,6 @@ struct CacheImpl : Cache (attrsToJSON(inAttrs).dump()) (attrsToJSON(infoAttrs).dump()) ("") // no path - (false) (time(0)).exec(); } @@ -109,14 +108,12 @@ struct CacheImpl : Cache Store & store, const Attrs & inAttrs, const Attrs & infoAttrs, - const StorePath & storePath, - bool locked) override + const StorePath & storePath) override { _state.lock()->add.use() (attrsToJSON(inAttrs).dump()) (attrsToJSON(infoAttrs).dump()) (store.printStorePath(storePath)) - (locked) (time(0)).exec(); } diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 791d77025..5e05d7af8 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -53,8 +53,7 @@ struct Cache Store & store, const Attrs & inAttrs, const Attrs & infoAttrs, - const StorePath & storePath, - bool locked) = 0; + const StorePath & storePath) = 0; virtual std::optional> lookup( Store & store, diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 398286065..4156302c4 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -47,10 +47,9 @@ StorePath fetchToStore( name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) - fetchers::getCache()->add(store, *cacheKey, {}, storePath, true); + fetchers::getCache()->add(store, *cacheKey, {}, storePath); return storePath; } - } diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index a1f934c35..fd59c1132 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -99,8 +99,7 @@ DownloadFileResult downloadFile( *store, inAttrs, infoAttrs, - *storePath, - false); + *storePath); } return { diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index a2702338f..783e338bf 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -224,22 +224,17 @@ struct MercurialInputScheme : InputScheme if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); - auto checkHashAlgorithm = [&](const std::optional & hash) + auto revInfoCacheKey = [&](const Hash & rev) { - if (hash.has_value() && hash->algo != HashAlgorithm::SHA1) - throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(HashFormat::Base16, true)); - }; + if (rev.algo != HashAlgorithm::SHA1) + throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", rev.to_string(HashFormat::Base16, true)); - - auto getLockedAttrs = [&]() - { - checkHashAlgorithm(input.getRev()); - - return Attrs({ - {"type", "hg"}, + return Attrs{ + {"_what", "hgRev"}, + {"store", store->storeDir}, {"name", name}, - {"rev", input.getRev()->gitRev()}, - }); + {"rev", input.getRev()->gitRev()} + }; }; auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath @@ -250,26 +245,22 @@ struct MercurialInputScheme : InputScheme return storePath; }; - if (input.getRev()) { - if (auto res = getCache()->lookup(*store, getLockedAttrs())) - return makeResult(res->first, std::move(res->second)); + /* Check the cache for the most recent rev for this URL/ref. */ + Attrs refToRevCacheKey{ + {"_what", "hgRefToRev"}, + {"url", actualUrl}, + {"ref", *input.getRef()} + }; + + if (!input.getRev()) { + if (auto res = getCache()->lookupWithTTL(refToRevCacheKey)) + input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev()); } - auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef(); - - Attrs unlockedAttrs({ - {"type", "hg"}, - {"name", name}, - {"url", actualUrl}, - {"ref", *input.getRef()}, - }); - - if (auto res = getCache()->lookup(*store, unlockedAttrs)) { - auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), HashAlgorithm::SHA1); - if (!input.getRev() || input.getRev() == rev2) { - input.attrs.insert_or_assign("rev", rev2.gitRev()); - return makeResult(res->first, std::move(res->second)); - } + /* If we have a rev, check if we have a cached store path. */ + if (auto rev = input.getRev()) { + if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(*rev))) + return makeResult(res->infoAttrs, res->storePath); } Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false)); @@ -302,21 +293,29 @@ struct MercurialInputScheme : InputScheme } } + /* Fetch the remote rev or ref. */ auto tokens = tokenizeString>( - runHg({ "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" })); + runHg({ + "log", "-R", cacheDir, + "-r", input.getRev() ? input.getRev()->gitRev() : *input.getRef(), + "--template", "{node} {rev} {branch}" + })); assert(tokens.size() == 3); - input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], HashAlgorithm::SHA1).gitRev()); + auto rev = Hash::parseAny(tokens[0], HashAlgorithm::SHA1); + input.attrs.insert_or_assign("rev", rev.gitRev()); auto revCount = std::stoull(tokens[1]); input.attrs.insert_or_assign("ref", tokens[2]); - if (auto res = getCache()->lookup(*store, getLockedAttrs())) - return makeResult(res->first, std::move(res->second)); + /* Now that we have the rev, check the cache again for a + cached store path. */ + if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(rev))) + return makeResult(res->infoAttrs, res->storePath); Path tmpDir = createTempDir(); AutoDelete delTmpDir(tmpDir, true); - runHg({ "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir }); + runHg({ "archive", "-R", cacheDir, "-r", rev.gitRev(), tmpDir }); deletePath(tmpDir + "/.hg_archival.txt"); @@ -324,24 +323,17 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore(name, accessor, CanonPath { tmpDir }); Attrs infoAttrs({ - {"rev", input.getRev()->gitRev()}, {"revCount", (uint64_t) revCount}, }); if (!origRev) - getCache()->add( - *store, - unlockedAttrs, - infoAttrs, - storePath, - false); + getCache()->upsert(refToRevCacheKey, {{"rev", rev.gitRev()}}); getCache()->add( *store, - getLockedAttrs(), + revInfoCacheKey(rev), infoAttrs, - storePath, - true); + storePath); return makeResult(infoAttrs, std::move(storePath)); } diff --git a/tests/functional/fetchMercurial.sh b/tests/functional/fetchMercurial.sh index e133df1f8..9f7cef7b2 100644 --- a/tests/functional/fetchMercurial.sh +++ b/tests/functional/fetchMercurial.sh @@ -101,6 +101,7 @@ path4=$(nix eval --impure --refresh --raw --expr "(builtins.fetchMercurial file: [[ $path2 = $path4 ]] echo paris > $repo/hello + # Passing a `name` argument should be reflected in the output path path5=$(nix eval -vvvvv --impure --refresh --raw --expr "(builtins.fetchMercurial { url = \"file://$repo\"; name = \"foo\"; } ).outPath") [[ $path5 =~ -foo$ ]] From aad11f44962aa49a61cf73155d61204ad48e42e3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 10 Apr 2024 20:59:18 +0200 Subject: [PATCH 012/910] Simplify the fetcher cache --- src/libfetchers/cache.cc | 186 +++++++++++++++--------------- src/libfetchers/cache.hh | 61 ++++++---- src/libfetchers/fetch-to-store.cc | 9 +- src/libfetchers/git-utils.cc | 7 +- src/libfetchers/github.cc | 14 ++- src/libfetchers/tarball.cc | 43 ++++--- src/libfetchers/unix/git.cc | 14 ++- src/libfetchers/unix/mercurial.cc | 26 ++--- 8 files changed, 187 insertions(+), 173 deletions(-) diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 83962856c..34eb6f32c 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -11,12 +11,11 @@ namespace nix::fetchers { static const char * schema = R"sql( create table if not exists Cache ( - input text not null, - info text not null, - path text not null, - immutable integer not null, /* obsolete */ + domain text not null, + key text not null, + value text not null, timestamp integer not null, - primary key (input) + primary key (domain, key) ); )sql"; @@ -28,7 +27,7 @@ struct CacheImpl : Cache struct State { SQLite db; - SQLiteStmt add, lookup; + SQLiteStmt upsert, lookup; }; Sync _state; @@ -37,133 +36,134 @@ struct CacheImpl : Cache { auto state(_state.lock()); - auto dbPath = getCacheDir() + "/nix/fetcher-cache-v1.sqlite"; + auto dbPath = getCacheDir() + "/nix/fetcher-cache-v2.sqlite"; createDirs(dirOf(dbPath)); state->db = SQLite(dbPath); state->db.isCache(); state->db.exec(schema); - state->add.create(state->db, - "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, false, ?)"); + state->upsert.create(state->db, + "insert or replace into Cache(domain, key, value, timestamp) values (?, ?, ?, ?)"); state->lookup.create(state->db, - "select info, path, immutable, timestamp from Cache where input = ?"); + "select value, timestamp from Cache where domain = ? and key = ?"); } void upsert( - const Attrs & inAttrs, - const Attrs & infoAttrs) override + std::string_view domain, + const Attrs & key, + const Attrs & value) override { - _state.lock()->add.use() - (attrsToJSON(inAttrs).dump()) - (attrsToJSON(infoAttrs).dump()) - ("") // no path + _state.lock()->upsert.use() + (domain) + (attrsToJSON(key).dump()) + (attrsToJSON(value).dump()) (time(0)).exec(); } - std::optional lookup(const Attrs & inAttrs) override + std::optional lookup( + std::string_view domain, + const Attrs & key) override { - if (auto res = lookupExpired(inAttrs)) - return std::move(res->infoAttrs); + if (auto res = lookupExpired(domain, key)) + return std::move(res->value); return {}; } - std::optional lookupWithTTL(const Attrs & inAttrs) override + std::optional lookupWithTTL( + std::string_view domain, + const Attrs & key) override { - if (auto res = lookupExpired(inAttrs)) { + if (auto res = lookupExpired(domain, key)) { if (!res->expired) - return std::move(res->infoAttrs); - debug("ignoring expired cache entry '%s'", - attrsToJSON(inAttrs).dump()); - } - return {}; - } - - std::optional lookupExpired(const Attrs & inAttrs) override - { - auto state(_state.lock()); - - auto inAttrsJSON = attrsToJSON(inAttrs).dump(); - - auto stmt(state->lookup.use()(inAttrsJSON)); - if (!stmt.next()) { - debug("did not find cache entry for '%s'", inAttrsJSON); - return {}; - } - - auto infoJSON = stmt.getStr(0); - auto locked = stmt.getInt(2) != 0; - auto timestamp = stmt.getInt(3); - - debug("using cache entry '%s' -> '%s'", inAttrsJSON, infoJSON); - - return Result2 { - .expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)), - .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)), - }; - } - - void add( - Store & store, - const Attrs & inAttrs, - const Attrs & infoAttrs, - const StorePath & storePath) override - { - _state.lock()->add.use() - (attrsToJSON(inAttrs).dump()) - (attrsToJSON(infoAttrs).dump()) - (store.printStorePath(storePath)) - (time(0)).exec(); - } - - std::optional> lookup( - Store & store, - const Attrs & inAttrs) override - { - if (auto res = lookupExpired(store, inAttrs)) { - if (!res->expired) - return std::make_pair(std::move(res->infoAttrs), std::move(res->storePath)); - debug("ignoring expired cache entry '%s'", - attrsToJSON(inAttrs).dump()); + return std::move(res->value); + debug("ignoring expired cache entry '%s:%s'", + domain, attrsToJSON(key).dump()); } return {}; } std::optional lookupExpired( - Store & store, - const Attrs & inAttrs) override + std::string_view domain, + const Attrs & key) override { auto state(_state.lock()); - auto inAttrsJSON = attrsToJSON(inAttrs).dump(); + auto keyJSON = attrsToJSON(key).dump(); - auto stmt(state->lookup.use()(inAttrsJSON)); + auto stmt(state->lookup.use()(domain)(keyJSON)); if (!stmt.next()) { - debug("did not find cache entry for '%s'", inAttrsJSON); + debug("did not find cache entry for '%s:%s'", domain, keyJSON); return {}; } - auto infoJSON = stmt.getStr(0); - auto storePath = store.parseStorePath(stmt.getStr(1)); - auto locked = stmt.getInt(2) != 0; - auto timestamp = stmt.getInt(3); + auto valueJSON = stmt.getStr(0); + auto timestamp = stmt.getInt(1); - store.addTempRoot(storePath); - if (!store.isValidPath(storePath)) { + debug("using cache entry '%s:%s' -> '%s'", domain, keyJSON, valueJSON); + + return Result { + .expired = settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0), + .value = jsonToAttrs(nlohmann::json::parse(valueJSON)), + }; + } + + void upsert( + std::string_view domain, + Attrs key, + Store & store, + Attrs value, + const StorePath & storePath) + { + /* Add the store prefix to the cache key to handle multiple + store prefixes. */ + key.insert_or_assign("store", store.storeDir); + + value.insert_or_assign("storePath", (std::string) storePath.to_string()); + + upsert(domain, key, value); + } + + std::optional lookupStorePath( + std::string_view domain, + Attrs key, + Store & store) override + { + key.insert_or_assign("store", store.storeDir); + + auto res = lookupExpired(domain, key); + if (!res) return std::nullopt; + + auto storePathS = getStrAttr(res->value, "storePath"); + res->value.erase("storePath"); + + ResultWithStorePath res2(*res, StorePath(storePathS)); + + store.addTempRoot(res2.storePath); + if (!store.isValidPath(res2.storePath)) { // FIXME: we could try to substitute 'storePath'. - debug("ignoring disappeared cache entry '%s'", inAttrsJSON); - return {}; + debug("ignoring disappeared cache entry '%s' -> '%s'", + attrsToJSON(key).dump(), + store.printStorePath(res2.storePath)); + return std::nullopt; } debug("using cache entry '%s' -> '%s', '%s'", - inAttrsJSON, infoJSON, store.printStorePath(storePath)); + attrsToJSON(key).dump(), + attrsToJSON(res2.value).dump(), + store.printStorePath(res2.storePath)); - return Result { - .expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)), - .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)), - .storePath = std::move(storePath) - }; + return res2; + } + + std::optional lookupStorePathWithTTL( + std::string_view domain, + Attrs key, + Store & store) override + { + auto res = lookupStorePath(domain, std::move(key), store); + return res && !res->expired ? res : std::nullopt; } }; diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 5e05d7af8..3295b56bc 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -19,56 +19,73 @@ struct Cache * Attrs to Attrs. */ virtual void upsert( - const Attrs & inAttrs, - const Attrs & infoAttrs) = 0; + std::string_view domain, + const Attrs & key, + const Attrs & value) = 0; /** * Look up a key with infinite TTL. */ virtual std::optional lookup( - const Attrs & inAttrs) = 0; + std::string_view domain, + const Attrs & key) = 0; /** * Look up a key. Return nothing if its TTL has exceeded * `settings.tarballTTL`. */ virtual std::optional lookupWithTTL( - const Attrs & inAttrs) = 0; + std::string_view domain, + const Attrs & key) = 0; - struct Result2 + struct Result { bool expired = false; - Attrs infoAttrs; + Attrs value; }; /** * Look up a key. Return a bool denoting whether its TTL has * exceeded `settings.tarballTTL`. */ - virtual std::optional lookupExpired( - const Attrs & inAttrs) = 0; + virtual std::optional lookupExpired( + std::string_view domain, + const Attrs & key) = 0; - /* Old cache for things that have a store path. */ - virtual void add( + /** + * Insert a cache entry that has a store path associated with + * it. Such cache entries are always considered stale if the + * associated store path is invalid. + */ + virtual void upsert( + std::string_view domain, + Attrs key, Store & store, - const Attrs & inAttrs, - const Attrs & infoAttrs, + Attrs value, const StorePath & storePath) = 0; - virtual std::optional> lookup( - Store & store, - const Attrs & inAttrs) = 0; - - struct Result + struct ResultWithStorePath : Result { - bool expired = false; - Attrs infoAttrs; StorePath storePath; }; - virtual std::optional lookupExpired( - Store & store, - const Attrs & inAttrs) = 0; + /** + * Look up a store path in the cache. The returned store path will + * be valid, but it may be expired. + */ + virtual std::optional lookupStorePath( + std::string_view domain, + Attrs key, + Store & store) = 0; + + /** + * Look up a store path in the cache. Return nothing if its TTL + * has exceeded `settings.tarballTTL`. + */ + virtual std::optional lookupStorePathWithTTL( + std::string_view domain, + Attrs key, + Store & store) = 0; }; ref getCache(); diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 4156302c4..2116906ad 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -16,20 +16,19 @@ StorePath fetchToStore( // FIXME: add an optimisation for the case where the accessor is // an FSInputAccessor pointing to a store path. + auto domain = "fetchToStore"; std::optional cacheKey; if (!filter && path.accessor->fingerprint) { cacheKey = fetchers::Attrs{ - {"_what", "fetchToStore"}, - {"store", store.storeDir}, {"name", std::string{name}}, {"fingerprint", *path.accessor->fingerprint}, {"method", std::string{method.render()}}, {"path", path.path.abs()} }; - if (auto res = fetchers::getCache()->lookup(store, *cacheKey)) { + if (auto res = fetchers::getCache()->lookupStorePath(domain, *cacheKey, store)) { debug("store path cache hit for '%s'", path); - return res->second; + return res->storePath; } } else debug("source path '%s' is uncacheable", path); @@ -47,7 +46,7 @@ StorePath fetchToStore( name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) - fetchers::getCache()->add(store, *cacheKey, {}, storePath); + fetchers::getCache()->upsert(domain, *cacheKey, store, {}, storePath); return storePath; } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5ecd825b7..ae4facc67 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -456,14 +456,15 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto accessor = getAccessor(treeHash, false); - fetchers::Attrs cacheKey({{"_what", "treeHashToNarHash"}, {"treeHash", treeHash.gitRev()}}); + auto domain = "treeHashToNarHash"; + fetchers::Attrs cacheKey({{"treeHash", treeHash.gitRev()}}); - if (auto res = fetchers::getCache()->lookup(cacheKey)) + if (auto res = fetchers::getCache()->lookup(domain, cacheKey)) return Hash::parseAny(fetchers::getStrAttr(*res, "narHash"), HashAlgorithm::SHA256); auto narHash = accessor->hashPath(CanonPath::root); - fetchers::getCache()->upsert(cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); + fetchers::getCache()->upsert(domain, cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); return narHash; } diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 985f2e479..487144925 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -225,11 +225,13 @@ struct GitArchiveInputScheme : InputScheme auto cache = getCache(); - Attrs treeHashKey{{"_what", "gitRevToTreeHash"}, {"rev", rev->gitRev()}}; - Attrs lastModifiedKey{{"_what", "gitRevToLastModified"}, {"rev", rev->gitRev()}}; + auto treeHashDomain = "gitRevToTreeHash"; + Attrs treeHashKey{{"rev", rev->gitRev()}}; + auto lastModifiedDomain = "gitRevToLastModified"; + Attrs lastModifiedKey{{"rev", rev->gitRev()}}; - if (auto treeHashAttrs = cache->lookup(treeHashKey)) { - if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) { + if (auto treeHashAttrs = cache->lookup(treeHashDomain, treeHashKey)) { + if (auto lastModifiedAttrs = cache->lookup(lastModifiedDomain, lastModifiedKey)) { auto treeHash = getRevAttr(*treeHashAttrs, "treeHash"); auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified"); if (getTarballCache()->hasObject(treeHash)) @@ -257,8 +259,8 @@ struct GitArchiveInputScheme : InputScheme .lastModified = lastModified }; - cache->upsert(treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); - cache->upsert(lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); + cache->upsert(treeHashDomain, treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); + cache->upsert(lastModifiedDomain, lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); #if 0 if (upstreamTreeHash != tarballInfo.treeHash) diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index fd59c1132..285e2803c 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -23,21 +23,22 @@ DownloadFileResult downloadFile( { // FIXME: check store - Attrs inAttrs({ - {"type", "file"}, + auto domain = "file"; + + Attrs key({ {"url", url}, {"name", name}, }); - auto cached = getCache()->lookupExpired(*store, inAttrs); + auto cached = getCache()->lookupStorePath(domain, key, *store); auto useCached = [&]() -> DownloadFileResult { return { .storePath = std::move(cached->storePath), - .etag = getStrAttr(cached->infoAttrs, "etag"), - .effectiveUrl = getStrAttr(cached->infoAttrs, "url"), - .immutableUrl = maybeGetStrAttr(cached->infoAttrs, "immutableUrl"), + .etag = getStrAttr(cached->value, "etag"), + .effectiveUrl = getStrAttr(cached->value, "url"), + .immutableUrl = maybeGetStrAttr(cached->value, "immutableUrl"), }; }; @@ -47,7 +48,7 @@ DownloadFileResult downloadFile( FileTransferRequest request(url); request.headers = headers; if (cached) - request.expectedETag = getStrAttr(cached->infoAttrs, "etag"); + request.expectedETag = getStrAttr(cached->value, "etag"); FileTransferResult res; try { res = getFileTransfer()->download(request); @@ -93,13 +94,9 @@ DownloadFileResult downloadFile( /* Cache metadata for all URLs in the redirect chain. */ for (auto & url : res.urls) { - inAttrs.insert_or_assign("url", url); + key.insert_or_assign("url", url); infoAttrs.insert_or_assign("url", *res.urls.rbegin()); - getCache()->add( - *store, - inAttrs, - infoAttrs, - *storePath); + getCache()->upsert(domain, key, *store, infoAttrs, *storePath); } return { @@ -114,12 +111,12 @@ DownloadTarballResult downloadTarball( const std::string & url, const Headers & headers) { - Attrs inAttrs({ - {"_what", "tarballCache"}, + auto domain = "tarball"; + Attrs cacheKey{ {"url", url}, - }); + }; - auto cached = getCache()->lookupExpired(inAttrs); + auto cached = getCache()->lookupExpired(domain, cacheKey); auto attrsToResult = [&](const Attrs & infoAttrs) { @@ -132,19 +129,19 @@ DownloadTarballResult downloadTarball( }; }; - if (cached && !getTarballCache()->hasObject(getRevAttr(cached->infoAttrs, "treeHash"))) + if (cached && !getTarballCache()->hasObject(getRevAttr(cached->value, "treeHash"))) cached.reset(); if (cached && !cached->expired) /* We previously downloaded this tarball and it's younger than `tarballTtl`, so no need to check the server. */ - return attrsToResult(cached->infoAttrs); + return attrsToResult(cached->value); auto _res = std::make_shared>(); auto source = sinkToSource([&](Sink & sink) { FileTransferRequest req(url); - req.expectedETag = cached ? getStrAttr(cached->infoAttrs, "etag") : ""; + req.expectedETag = cached ? getStrAttr(cached->value, "etag") : ""; getFileTransfer()->download(std::move(req), sink, [_res](FileTransferResult r) { @@ -167,7 +164,7 @@ DownloadTarballResult downloadTarball( if (res->cached) { /* The server says that the previously downloaded version is still current. */ - infoAttrs = cached->infoAttrs; + infoAttrs = cached->value; } else { infoAttrs.insert_or_assign("etag", res->etag); infoAttrs.insert_or_assign("treeHash", parseSink->sync().gitRev()); @@ -178,8 +175,8 @@ DownloadTarballResult downloadTarball( /* Insert a cache entry for every URL in the redirect chain. */ for (auto & url : res->urls) { - inAttrs.insert_or_assign("url", url); - getCache()->upsert(inAttrs, infoAttrs); + cacheKey.insert_or_assign("url", url); + getCache()->upsert(domain, cacheKey, infoAttrs); } // FIXME: add a cache entry for immutableUrl? That could allow diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 45e62ebe1..bb1bff7ab 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -427,34 +427,36 @@ struct GitInputScheme : InputScheme uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - Attrs key{{"_what", "gitLastModified"}, {"rev", rev.gitRev()}}; + auto domain = "gitLastModified"; + Attrs key{{"rev", rev.gitRev()}}; auto cache = getCache(); - if (auto res = cache->lookup(key)) + if (auto res = cache->lookup(domain, key)) return getIntAttr(*res, "lastModified"); auto lastModified = GitRepo::openRepo(repoDir)->getLastModified(rev); - cache->upsert(key, Attrs{{"lastModified", lastModified}}); + cache->upsert(domain, key, {{"lastModified", lastModified}}); return lastModified; } uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - Attrs key{{"_what", "gitRevCount"}, {"rev", rev.gitRev()}}; + auto domain = "gitRevCount"; + Attrs key{{"rev", rev.gitRev()}}; auto cache = getCache(); - if (auto revCountAttrs = cache->lookup(key)) + if (auto revCountAttrs = cache->lookup(domain, key)) return getIntAttr(*revCountAttrs, "revCount"); Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url)); auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev); - cache->upsert(key, Attrs{{"revCount", revCount}}); + cache->upsert(domain, key, Attrs{{"revCount", revCount}}); return revCount; } diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 783e338bf..42757f2db 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -224,13 +224,13 @@ struct MercurialInputScheme : InputScheme if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); - auto revInfoCacheKey = [&](const Hash & rev) + auto revInfoDomain = "hgRev"; + auto revInfoKey = [&](const Hash & rev) { if (rev.algo != HashAlgorithm::SHA1) throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", rev.to_string(HashFormat::Base16, true)); return Attrs{ - {"_what", "hgRev"}, {"store", store->storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()} @@ -246,21 +246,21 @@ struct MercurialInputScheme : InputScheme }; /* Check the cache for the most recent rev for this URL/ref. */ - Attrs refToRevCacheKey{ - {"_what", "hgRefToRev"}, + auto refToRevDomain = "hgRefToRev"; + Attrs refToRevKey{ {"url", actualUrl}, {"ref", *input.getRef()} }; if (!input.getRev()) { - if (auto res = getCache()->lookupWithTTL(refToRevCacheKey)) + if (auto res = getCache()->lookupWithTTL(refToRevDomain, refToRevKey)) input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev()); } /* If we have a rev, check if we have a cached store path. */ if (auto rev = input.getRev()) { - if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(*rev))) - return makeResult(res->infoAttrs, res->storePath); + if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(*rev), *store)) + return makeResult(res->value, res->storePath); } Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false)); @@ -309,8 +309,8 @@ struct MercurialInputScheme : InputScheme /* Now that we have the rev, check the cache again for a cached store path. */ - if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(rev))) - return makeResult(res->infoAttrs, res->storePath); + if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(rev), *store)) + return makeResult(res->value, res->storePath); Path tmpDir = createTempDir(); AutoDelete delTmpDir(tmpDir, true); @@ -327,13 +327,9 @@ struct MercurialInputScheme : InputScheme }); if (!origRev) - getCache()->upsert(refToRevCacheKey, {{"rev", rev.gitRev()}}); + getCache()->upsert(refToRevDomain, refToRevKey, {{"rev", rev.gitRev()}}); - getCache()->add( - *store, - revInfoCacheKey(rev), - infoAttrs, - storePath); + getCache()->upsert(revInfoDomain, revInfoKey(rev), *store, infoAttrs, storePath); return makeResult(infoAttrs, std::move(storePath)); } From cceae30aafad32e7ba8301980aabee511e5c05de Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 10 Apr 2024 21:39:40 +0200 Subject: [PATCH 013/910] Combine the domain and key arguments into a single value for convenience --- src/libfetchers/cache.cc | 59 ++++++++++++++----------------- src/libfetchers/cache.hh | 35 +++++++++--------- src/libfetchers/fetch-to-store.cc | 11 +++--- src/libfetchers/git-utils.cc | 7 ++-- src/libfetchers/github.cc | 14 ++++---- src/libfetchers/tarball.cc | 23 +++++------- src/libfetchers/unix/git.cc | 14 ++++---- src/libfetchers/unix/mercurial.cc | 20 +++++------ 8 files changed, 84 insertions(+), 99 deletions(-) diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 34eb6f32c..87a8e4702 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -51,57 +51,53 @@ struct CacheImpl : Cache } void upsert( - std::string_view domain, - const Attrs & key, + const Key & key, const Attrs & value) override { _state.lock()->upsert.use() - (domain) - (attrsToJSON(key).dump()) + (key.first) + (attrsToJSON(key.second).dump()) (attrsToJSON(value).dump()) (time(0)).exec(); } std::optional lookup( - std::string_view domain, - const Attrs & key) override + const Key & key) override { - if (auto res = lookupExpired(domain, key)) + if (auto res = lookupExpired(key)) return std::move(res->value); return {}; } std::optional lookupWithTTL( - std::string_view domain, - const Attrs & key) override + const Key & key) override { - if (auto res = lookupExpired(domain, key)) { + if (auto res = lookupExpired(key)) { if (!res->expired) return std::move(res->value); debug("ignoring expired cache entry '%s:%s'", - domain, attrsToJSON(key).dump()); + key.first, attrsToJSON(key.second).dump()); } return {}; } std::optional lookupExpired( - std::string_view domain, - const Attrs & key) override + const Key & key) override { auto state(_state.lock()); - auto keyJSON = attrsToJSON(key).dump(); + auto keyJSON = attrsToJSON(key.second).dump(); - auto stmt(state->lookup.use()(domain)(keyJSON)); + auto stmt(state->lookup.use()(key.first)(keyJSON)); if (!stmt.next()) { - debug("did not find cache entry for '%s:%s'", domain, keyJSON); + debug("did not find cache entry for '%s:%s'", key.first, keyJSON); return {}; } auto valueJSON = stmt.getStr(0); auto timestamp = stmt.getInt(1); - debug("using cache entry '%s:%s' -> '%s'", domain, keyJSON, valueJSON); + debug("using cache entry '%s:%s' -> '%s'", key.first, keyJSON, valueJSON); return Result { .expired = settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0), @@ -110,29 +106,27 @@ struct CacheImpl : Cache } void upsert( - std::string_view domain, - Attrs key, + Key key, Store & store, Attrs value, const StorePath & storePath) { /* Add the store prefix to the cache key to handle multiple store prefixes. */ - key.insert_or_assign("store", store.storeDir); + key.second.insert_or_assign("store", store.storeDir); value.insert_or_assign("storePath", (std::string) storePath.to_string()); - upsert(domain, key, value); + upsert(key, value); } std::optional lookupStorePath( - std::string_view domain, - Attrs key, + Key key, Store & store) override { - key.insert_or_assign("store", store.storeDir); + key.second.insert_or_assign("store", store.storeDir); - auto res = lookupExpired(domain, key); + auto res = lookupExpired(key); if (!res) return std::nullopt; auto storePathS = getStrAttr(res->value, "storePath"); @@ -143,14 +137,16 @@ struct CacheImpl : Cache store.addTempRoot(res2.storePath); if (!store.isValidPath(res2.storePath)) { // FIXME: we could try to substitute 'storePath'. - debug("ignoring disappeared cache entry '%s' -> '%s'", - attrsToJSON(key).dump(), + debug("ignoring disappeared cache entry '%s:%s' -> '%s'", + key.first, + attrsToJSON(key.second).dump(), store.printStorePath(res2.storePath)); return std::nullopt; } - debug("using cache entry '%s' -> '%s', '%s'", - attrsToJSON(key).dump(), + debug("using cache entry '%s:%s' -> '%s', '%s'", + key.first, + attrsToJSON(key.second).dump(), attrsToJSON(res2.value).dump(), store.printStorePath(res2.storePath)); @@ -158,11 +154,10 @@ struct CacheImpl : Cache } std::optional lookupStorePathWithTTL( - std::string_view domain, - Attrs key, + Key key, Store & store) override { - auto res = lookupStorePath(domain, std::move(key), store); + auto res = lookupStorePath(std::move(key), store); return res && !res->expired ? res : std::nullopt; } }; diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 3295b56bc..1a72162d7 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -15,28 +15,35 @@ struct Cache virtual ~Cache() { } /** - * Add a value to the cache. The cache is an arbitrary mapping of - * Attrs to Attrs. + * A domain is a partition of the key/value cache for a particular + * purpose, e.g. "Git revision to revcount". + */ + using Domain = std::string_view; + + /** + * A cache key is a domain and an arbitrary set of attributes. + */ + using Key = std::pair; + + /** + * Add a key/value pair to the cache. */ virtual void upsert( - std::string_view domain, - const Attrs & key, + const Key & key, const Attrs & value) = 0; /** * Look up a key with infinite TTL. */ virtual std::optional lookup( - std::string_view domain, - const Attrs & key) = 0; + const Key & key) = 0; /** * Look up a key. Return nothing if its TTL has exceeded * `settings.tarballTTL`. */ virtual std::optional lookupWithTTL( - std::string_view domain, - const Attrs & key) = 0; + const Key & key) = 0; struct Result { @@ -49,8 +56,7 @@ struct Cache * exceeded `settings.tarballTTL`. */ virtual std::optional lookupExpired( - std::string_view domain, - const Attrs & key) = 0; + const Key & key) = 0; /** * Insert a cache entry that has a store path associated with @@ -58,8 +64,7 @@ struct Cache * associated store path is invalid. */ virtual void upsert( - std::string_view domain, - Attrs key, + Key key, Store & store, Attrs value, const StorePath & storePath) = 0; @@ -74,8 +79,7 @@ struct Cache * be valid, but it may be expired. */ virtual std::optional lookupStorePath( - std::string_view domain, - Attrs key, + Key key, Store & store) = 0; /** @@ -83,8 +87,7 @@ struct Cache * has exceeded `settings.tarballTTL`. */ virtual std::optional lookupStorePathWithTTL( - std::string_view domain, - Attrs key, + Key key, Store & store) = 0; }; diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 2116906ad..96743cb52 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -16,17 +16,16 @@ StorePath fetchToStore( // FIXME: add an optimisation for the case where the accessor is // an FSInputAccessor pointing to a store path. - auto domain = "fetchToStore"; - std::optional cacheKey; + std::optional cacheKey; if (!filter && path.accessor->fingerprint) { - cacheKey = fetchers::Attrs{ + cacheKey = fetchers::Cache::Key{"fetchToStore", { {"name", std::string{name}}, {"fingerprint", *path.accessor->fingerprint}, {"method", std::string{method.render()}}, {"path", path.path.abs()} - }; - if (auto res = fetchers::getCache()->lookupStorePath(domain, *cacheKey, store)) { + }}; + if (auto res = fetchers::getCache()->lookupStorePath(*cacheKey, store)) { debug("store path cache hit for '%s'", path); return res->storePath; } @@ -46,7 +45,7 @@ StorePath fetchToStore( name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) - fetchers::getCache()->upsert(domain, *cacheKey, store, {}, storePath); + fetchers::getCache()->upsert(*cacheKey, store, {}, storePath); return storePath; } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index ae4facc67..d4ba1a91d 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -456,15 +456,14 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto accessor = getAccessor(treeHash, false); - auto domain = "treeHashToNarHash"; - fetchers::Attrs cacheKey({{"treeHash", treeHash.gitRev()}}); + fetchers::Cache::Key cacheKey{"treeHashToNarHash", {{"treeHash", treeHash.gitRev()}}}; - if (auto res = fetchers::getCache()->lookup(domain, cacheKey)) + if (auto res = fetchers::getCache()->lookup(cacheKey)) return Hash::parseAny(fetchers::getStrAttr(*res, "narHash"), HashAlgorithm::SHA256); auto narHash = accessor->hashPath(CanonPath::root); - fetchers::getCache()->upsert(domain, cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); + fetchers::getCache()->upsert(cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); return narHash; } diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 487144925..7aa857dfe 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -225,13 +225,11 @@ struct GitArchiveInputScheme : InputScheme auto cache = getCache(); - auto treeHashDomain = "gitRevToTreeHash"; - Attrs treeHashKey{{"rev", rev->gitRev()}}; - auto lastModifiedDomain = "gitRevToLastModified"; - Attrs lastModifiedKey{{"rev", rev->gitRev()}}; + Cache::Key treeHashKey{"gitRevToTreeHash", {{"rev", rev->gitRev()}}}; + Cache::Key lastModifiedKey{"gitRevToLastModified", {{"rev", rev->gitRev()}}}; - if (auto treeHashAttrs = cache->lookup(treeHashDomain, treeHashKey)) { - if (auto lastModifiedAttrs = cache->lookup(lastModifiedDomain, lastModifiedKey)) { + if (auto treeHashAttrs = cache->lookup(treeHashKey)) { + if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) { auto treeHash = getRevAttr(*treeHashAttrs, "treeHash"); auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified"); if (getTarballCache()->hasObject(treeHash)) @@ -259,8 +257,8 @@ struct GitArchiveInputScheme : InputScheme .lastModified = lastModified }; - cache->upsert(treeHashDomain, treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); - cache->upsert(lastModifiedDomain, lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); + cache->upsert(treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); + cache->upsert(lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); #if 0 if (upstreamTreeHash != tarballInfo.treeHash) diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 285e2803c..89ef31c7e 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -23,14 +23,12 @@ DownloadFileResult downloadFile( { // FIXME: check store - auto domain = "file"; - - Attrs key({ + Cache::Key key{"file", {{ {"url", url}, {"name", name}, - }); + }}}; - auto cached = getCache()->lookupStorePath(domain, key, *store); + auto cached = getCache()->lookupStorePath(key, *store); auto useCached = [&]() -> DownloadFileResult { @@ -94,9 +92,9 @@ DownloadFileResult downloadFile( /* Cache metadata for all URLs in the redirect chain. */ for (auto & url : res.urls) { - key.insert_or_assign("url", url); + key.second.insert_or_assign("url", url); infoAttrs.insert_or_assign("url", *res.urls.rbegin()); - getCache()->upsert(domain, key, *store, infoAttrs, *storePath); + getCache()->upsert(key, *store, infoAttrs, *storePath); } return { @@ -111,12 +109,9 @@ DownloadTarballResult downloadTarball( const std::string & url, const Headers & headers) { - auto domain = "tarball"; - Attrs cacheKey{ - {"url", url}, - }; + Cache::Key cacheKey{"tarball", {{"url", url}}}; - auto cached = getCache()->lookupExpired(domain, cacheKey); + auto cached = getCache()->lookupExpired(cacheKey); auto attrsToResult = [&](const Attrs & infoAttrs) { @@ -175,8 +170,8 @@ DownloadTarballResult downloadTarball( /* Insert a cache entry for every URL in the redirect chain. */ for (auto & url : res->urls) { - cacheKey.insert_or_assign("url", url); - getCache()->upsert(domain, cacheKey, infoAttrs); + cacheKey.second.insert_or_assign("url", url); + getCache()->upsert(cacheKey, infoAttrs); } // FIXME: add a cache entry for immutableUrl? That could allow diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index bb1bff7ab..1d7c719a6 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -427,36 +427,34 @@ struct GitInputScheme : InputScheme uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - auto domain = "gitLastModified"; - Attrs key{{"rev", rev.gitRev()}}; + Cache::Key key{"gitLastModified", {{"rev", rev.gitRev()}}}; auto cache = getCache(); - if (auto res = cache->lookup(domain, key)) + if (auto res = cache->lookup(key)) return getIntAttr(*res, "lastModified"); auto lastModified = GitRepo::openRepo(repoDir)->getLastModified(rev); - cache->upsert(domain, key, {{"lastModified", lastModified}}); + cache->upsert(key, {{"lastModified", lastModified}}); return lastModified; } uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - auto domain = "gitRevCount"; - Attrs key{{"rev", rev.gitRev()}}; + Cache::Key key{"gitRevCount", {{"rev", rev.gitRev()}}}; auto cache = getCache(); - if (auto revCountAttrs = cache->lookup(domain, key)) + if (auto revCountAttrs = cache->lookup(key)) return getIntAttr(*revCountAttrs, "revCount"); Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url)); auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev); - cache->upsert(domain, key, Attrs{{"revCount", revCount}}); + cache->upsert(key, Attrs{{"revCount", revCount}}); return revCount; } diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 42757f2db..0dd1acf45 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -224,17 +224,16 @@ struct MercurialInputScheme : InputScheme if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); - auto revInfoDomain = "hgRev"; auto revInfoKey = [&](const Hash & rev) { if (rev.algo != HashAlgorithm::SHA1) throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", rev.to_string(HashFormat::Base16, true)); - return Attrs{ + return Cache::Key{"hgRev", { {"store", store->storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()} - }; + }}; }; auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath @@ -246,20 +245,19 @@ struct MercurialInputScheme : InputScheme }; /* Check the cache for the most recent rev for this URL/ref. */ - auto refToRevDomain = "hgRefToRev"; - Attrs refToRevKey{ + Cache::Key refToRevKey{"hgRefToRev", { {"url", actualUrl}, {"ref", *input.getRef()} - }; + }}; if (!input.getRev()) { - if (auto res = getCache()->lookupWithTTL(refToRevDomain, refToRevKey)) + if (auto res = getCache()->lookupWithTTL(refToRevKey)) input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev()); } /* If we have a rev, check if we have a cached store path. */ if (auto rev = input.getRev()) { - if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(*rev), *store)) + if (auto res = getCache()->lookupStorePath(revInfoKey(*rev), *store)) return makeResult(res->value, res->storePath); } @@ -309,7 +307,7 @@ struct MercurialInputScheme : InputScheme /* Now that we have the rev, check the cache again for a cached store path. */ - if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(rev), *store)) + if (auto res = getCache()->lookupStorePath(revInfoKey(rev), *store)) return makeResult(res->value, res->storePath); Path tmpDir = createTempDir(); @@ -327,9 +325,9 @@ struct MercurialInputScheme : InputScheme }); if (!origRev) - getCache()->upsert(refToRevDomain, refToRevKey, {{"rev", rev.gitRev()}}); + getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}}); - getCache()->upsert(revInfoDomain, revInfoKey(rev), *store, infoAttrs, storePath); + getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath); return makeResult(infoAttrs, std::move(storePath)); } From dd19cce9c423192b79ef26b3f57418f7678bbff9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 17:17:59 +0200 Subject: [PATCH 014/910] doc/external-api/local.mk: Rebuild when headers change --- doc/external-api/local.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/external-api/local.mk b/doc/external-api/local.mk index c739bdaf0..ae2b44db8 100644 --- a/doc/external-api/local.mk +++ b/doc/external-api/local.mk @@ -1,4 +1,4 @@ -$(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg +$(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg src/lib*-c/*.h mkdir -p $(docdir)/external-api { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/external-api" ; } | doxygen - From 0fade05e96229358192cd2b0c54895655d5bf70a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 17:28:30 +0200 Subject: [PATCH 015/910] doc/internal-api/local.mk: Rebuild when headers change --- doc/internal-api/local.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/internal-api/local.mk b/doc/internal-api/local.mk index bf2c4dede..be9b7bb55 100644 --- a/doc/internal-api/local.mk +++ b/doc/internal-api/local.mk @@ -1,4 +1,4 @@ -$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg +$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg src/**/*.hh mkdir -p $(docdir)/internal-api { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/internal-api" ; } | doxygen - From 3a3c205fa7b25dee3e6794cc40873faed63b6fce Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 18 Apr 2024 16:56:42 -0400 Subject: [PATCH 016/910] Use `rand` not `random` for creating GC root indirect links I don't think fewer bits matters for this, and `rand` but not `random` is available on Windows. --- src/libmain/shared.cc | 3 ++- src/libstore/unix/gc.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index a43a00f16..e09aed2c6 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -173,12 +173,13 @@ void initNix() everybody. */ umask(0022); -#ifndef _WIN32 /* Initialise the PRNG. */ struct timeval tv; gettimeofday(&tv, 0); +#ifndef _WIN32 srandom(tv.tv_usec); #endif + srand(tv.tv_usec); } diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index 9b2e6d525..68d049485 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -41,7 +41,7 @@ static void makeSymlink(const Path & link, const Path & target) createDirs(dirOf(link)); /* Create the new symlink. */ - Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), random()); + Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand()); createSymlink(target, tempLink); /* Atomically replace the old one. */ From b973cd494feb03488260580be862d1232f158f0f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 18 Apr 2024 16:49:52 -0400 Subject: [PATCH 017/910] Enable the `unix://` store on Windows Windows now has some basic Unix Domain Socket support, see https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ Building `nix daemon` on Windows I've left for later, because the daemon currently forks per connection but this is not an option on Windows. But we can get the client part working right away. --- src/libstore/indirect-root-store.cc | 44 ++++++++++ src/libstore/indirect-root-store.hh | 3 + src/libstore/local.mk | 3 + src/libstore/{unix => }/uds-remote-store.cc | 16 ++-- src/libstore/{unix => }/uds-remote-store.hh | 0 src/libstore/{unix => }/uds-remote-store.md | 0 src/libstore/unix/gc.cc | 40 ---------- src/libutil/{unix => }/unix-domain-socket.cc | 30 ++++--- src/libutil/unix-domain-socket.hh | 84 ++++++++++++++++++++ src/libutil/unix/unix-domain-socket.hh | 31 -------- 10 files changed, 164 insertions(+), 87 deletions(-) create mode 100644 src/libstore/indirect-root-store.cc rename src/libstore/{unix => }/uds-remote-store.cc (86%) rename src/libstore/{unix => }/uds-remote-store.hh (100%) rename src/libstore/{unix => }/uds-remote-store.md (100%) rename src/libutil/{unix => }/unix-domain-socket.cc (84%) create mode 100644 src/libutil/unix-domain-socket.hh delete mode 100644 src/libutil/unix/unix-domain-socket.hh diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc new file mode 100644 index 000000000..b92279928 --- /dev/null +++ b/src/libstore/indirect-root-store.cc @@ -0,0 +1,44 @@ +#include "indirect-root-store.hh" + +namespace nix { + +void IndirectRootStore::makeSymlink(const Path & link, const Path & target) +{ + /* Create directories up to `gcRoot'. */ + createDirs(dirOf(link)); + + /* Create the new symlink. */ + Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand()); + createSymlink(target, tempLink); + + /* Atomically replace the old one. */ + renameFile(tempLink, link); +} + + +Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) +{ + Path gcRoot(canonPath(_gcRoot)); + + if (isInStore(gcRoot)) + throw Error( + "creating a garbage collector root (%1%) in the Nix store is forbidden " + "(are you running nix-build inside the store?)", gcRoot); + + /* Register this root with the garbage collector, if it's + running. This should be superfluous since the caller should + have registered this root yet, but let's be on the safe + side. */ + addTempRoot(storePath); + + /* Don't clobber the link if it already exists and doesn't + point to the Nix store. */ + if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) + throw Error("cannot create symlink '%1%'; already exists", gcRoot); + makeSymlink(gcRoot, printStorePath(storePath)); + addIndirectRoot(gcRoot); + + return gcRoot; +} + +} diff --git a/src/libstore/indirect-root-store.hh b/src/libstore/indirect-root-store.hh index c11679fe8..b74ebc1ee 100644 --- a/src/libstore/indirect-root-store.hh +++ b/src/libstore/indirect-root-store.hh @@ -67,6 +67,9 @@ struct IndirectRootStore : public virtual LocalFSStore * The form this weak-reference takes is implementation-specific. */ virtual void addIndirectRoot(const Path & path) = 0; + +protected: + void makeSymlink(const Path & link, const Path & target); }; } diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 2e118f6cb..590a230e5 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -21,6 +21,9 @@ libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) $(THREAD_LDFLAGS) ifdef HOST_LINUX libstore_LDFLAGS += -ldl endif +ifdef HOST_WINDOWS + libstore_LDFLAGS += -lws2_32 +endif $(foreach file,$(libstore_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/sandbox))) diff --git a/src/libstore/unix/uds-remote-store.cc b/src/libstore/uds-remote-store.cc similarity index 86% rename from src/libstore/unix/uds-remote-store.cc rename to src/libstore/uds-remote-store.cc index 226cdf717..649644146 100644 --- a/src/libstore/unix/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -2,16 +2,20 @@ #include "unix-domain-socket.hh" #include "worker-protocol.hh" +#include #include #include -#include -#include #include #include #include -#include - +#ifdef _WIN32 +# include +# include +#else +# include +# include +#endif namespace nix { @@ -57,7 +61,7 @@ std::string UDSRemoteStore::getUri() void UDSRemoteStore::Connection::closeWrite() { - shutdown(fd.get(), SHUT_WR); + shutdown(toSocket(fd.get()), SHUT_WR); } @@ -68,7 +72,7 @@ ref UDSRemoteStore::openConnection() /* Connect to a daemon that does the privileged work for us. */ conn->fd = createUnixDomainSocket(); - nix::connect(conn->fd.get(), path ? *path : settings.nixDaemonSocketFile); + nix::connect(toSocket(conn->fd.get()), path ? *path : settings.nixDaemonSocketFile); conn->from.fd = conn->fd.get(); conn->to.fd = conn->fd.get(); diff --git a/src/libstore/unix/uds-remote-store.hh b/src/libstore/uds-remote-store.hh similarity index 100% rename from src/libstore/unix/uds-remote-store.hh rename to src/libstore/uds-remote-store.hh diff --git a/src/libstore/unix/uds-remote-store.md b/src/libstore/uds-remote-store.md similarity index 100% rename from src/libstore/unix/uds-remote-store.md rename to src/libstore/uds-remote-store.md diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index 68d049485..be5794395 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -35,20 +35,6 @@ static std::string gcSocketPath = "/gc-socket/socket"; static std::string gcRootsDir = "gcroots"; -static void makeSymlink(const Path & link, const Path & target) -{ - /* Create directories up to `gcRoot'. */ - createDirs(dirOf(link)); - - /* Create the new symlink. */ - Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand()); - createSymlink(target, tempLink); - - /* Atomically replace the old one. */ - renameFile(tempLink, link); -} - - void LocalStore::addIndirectRoot(const Path & path) { std::string hash = hashString(HashAlgorithm::SHA1, path).to_string(HashFormat::Nix32, false); @@ -57,32 +43,6 @@ void LocalStore::addIndirectRoot(const Path & path) } -Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) -{ - Path gcRoot(canonPath(_gcRoot)); - - if (isInStore(gcRoot)) - throw Error( - "creating a garbage collector root (%1%) in the Nix store is forbidden " - "(are you running nix-build inside the store?)", gcRoot); - - /* Register this root with the garbage collector, if it's - running. This should be superfluous since the caller should - have registered this root yet, but let's be on the safe - side. */ - addTempRoot(storePath); - - /* Don't clobber the link if it already exists and doesn't - point to the Nix store. */ - if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) - throw Error("cannot create symlink '%1%'; already exists", gcRoot); - makeSymlink(gcRoot, printStorePath(storePath)); - addIndirectRoot(gcRoot); - - return gcRoot; -} - - void LocalStore::createTempRootsFile() { auto fdTempRoots(_fdTempRoots.lock()); diff --git a/src/libutil/unix/unix-domain-socket.cc b/src/libutil/unix-domain-socket.cc similarity index 84% rename from src/libutil/unix/unix-domain-socket.cc rename to src/libutil/unix-domain-socket.cc index 0bcf9040d..87914bb83 100644 --- a/src/libutil/unix/unix-domain-socket.cc +++ b/src/libutil/unix-domain-socket.cc @@ -1,24 +1,31 @@ #include "file-system.hh" -#include "processes.hh" #include "unix-domain-socket.hh" #include "util.hh" -#include -#include +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include "processes.hh" +#endif #include namespace nix { AutoCloseFD createUnixDomainSocket() { - AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM + AutoCloseFD fdSocket = toDescriptor(socket(PF_UNIX, SOCK_STREAM #ifdef SOCK_CLOEXEC | SOCK_CLOEXEC #endif - , 0); + , 0)); if (!fdSocket) throw SysError("cannot create Unix domain socket"); +#ifndef _WIN32 closeOnExec(fdSocket.get()); +#endif return fdSocket; } @@ -32,16 +39,15 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode) if (chmod(path.c_str(), mode) == -1) throw SysError("changing permissions on '%1%'", path); - if (listen(fdSocket.get(), 100) == -1) + if (listen(toSocket(fdSocket.get()), 100) == -1) throw SysError("cannot listen on socket '%1%'", path); return fdSocket; } - static void bindConnectProcHelper( std::string_view operationName, auto && operation, - int fd, const std::string & path) + Socket fd, const std::string & path) { struct sockaddr_un addr; addr.sun_family = AF_UNIX; @@ -54,6 +60,9 @@ static void bindConnectProcHelper( auto * psaddr = reinterpret_cast(&addr); if (path.size() + 1 >= sizeof(addr.sun_path)) { +#ifdef _WIN32 + throw Error("cannot %s to socket at '%s': path is too long", operationName, path); +#else Pipe pipe; pipe.create(); Pid pid = startProcess([&] { @@ -83,6 +92,7 @@ static void bindConnectProcHelper( errno = *errNo; throw SysError("cannot %s to socket at '%s'", operationName, path); } +#endif } else { memcpy(addr.sun_path, path.c_str(), path.size() + 1); if (operation(fd, psaddr, sizeof(addr)) == -1) @@ -91,7 +101,7 @@ static void bindConnectProcHelper( } -void bind(int fd, const std::string & path) +void bind(Socket fd, const std::string & path) { unlink(path.c_str()); @@ -99,7 +109,7 @@ void bind(int fd, const std::string & path) } -void connect(int fd, const std::string & path) +void connect(Socket fd, const std::string & path) { bindConnectProcHelper("connect", ::connect, fd, path); } diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix-domain-socket.hh new file mode 100644 index 000000000..9aba9f628 --- /dev/null +++ b/src/libutil/unix-domain-socket.hh @@ -0,0 +1,84 @@ +#pragma once +///@file + +#include "types.hh" +#include "file-descriptor.hh" + +#ifdef _WIN32 +# include +#endif +#include + +namespace nix { + +/** + * Create a Unix domain socket. + */ +AutoCloseFD createUnixDomainSocket(); + +/** + * Create a Unix domain socket in listen mode. + */ +AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); + + +/** + * Often we want to use `Descriptor`, but Windows makes a slightly + * stronger file descriptor vs socket distinction, at least at the level + * of C types. + */ +using Socket = +#ifdef _WIN32 + SOCKET +#else + int +#endif + ; + +#ifdef _WIN32 +/** + * Windows gives this a different name + */ +# define SHUT_WR SD_SEND +#endif + +/** + * Convert a `Socket` to a `Descriptor` + * + * This is a no-op except on Windows. + */ +static inline Socket toSocket(Descriptor fd) +{ +#ifdef _WIN32 + return reinterpret_cast(fd); +#else + return fd; +#endif +} + +/** + * Convert a `Socket` to a `Descriptor` + * + * This is a no-op except on Windows. + */ +static inline Descriptor fromSocket(Socket fd) +{ +#ifdef _WIN32 + return reinterpret_cast(fd); +#else + return fd; +#endif +} + + +/** + * Bind a Unix domain socket to a path. + */ +void bind(Socket fd, const std::string & path); + +/** + * Connect to a Unix domain socket. + */ +void connect(Socket fd, const std::string & path); + +} diff --git a/src/libutil/unix/unix-domain-socket.hh b/src/libutil/unix/unix-domain-socket.hh deleted file mode 100644 index b78feb454..000000000 --- a/src/libutil/unix/unix-domain-socket.hh +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -///@file - -#include "types.hh" -#include "file-descriptor.hh" - -#include - -namespace nix { - -/** - * Create a Unix domain socket. - */ -AutoCloseFD createUnixDomainSocket(); - -/** - * Create a Unix domain socket in listen mode. - */ -AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); - -/** - * Bind a Unix domain socket to a path. - */ -void bind(int fd, const std::string & path); - -/** - * Connect to a Unix domain socket. - */ -void connect(int fd, const std::string & path); - -} From 5cc4af5231608d0d85d120c20bd9bbc9da9338fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 18 Apr 2024 20:05:59 +0200 Subject: [PATCH 018/910] Add isInitialized to nix::Value Add a method to check if a value has been initialized. This helps avoid segfaults when calling `type()`. Useful in the context of the new C API. Closes #10524 --- src/libexpr/value.hh | 9 ++++++++- tests/unit/libexpr/value/value.cc | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libexpr/value/value.cc diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 7ed3fa5a9..a1003e16b 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -23,6 +23,7 @@ class BindingsBuilder; typedef enum { + tUnset, tInt = 1, tBool, tString, @@ -166,7 +167,7 @@ public: struct Value { private: - InternalType internalType; + InternalType internalType = tUnset; friend std::string showType(const Value & v); @@ -270,6 +271,7 @@ public: inline ValueType type(bool invalidIsThunk = false) const { switch (internalType) { + case tUnset: break; case tInt: return nInt; case tBool: return nBool; case tString: return nString; @@ -294,6 +296,11 @@ public: internalType = newType; } + inline bool isInitialized() + { + return internalType != tUnset; + } + inline void mkInt(NixInt n) { finishValue(tInt, { .integer = n }); diff --git a/tests/unit/libexpr/value/value.cc b/tests/unit/libexpr/value/value.cc new file mode 100644 index 000000000..49a896c87 --- /dev/null +++ b/tests/unit/libexpr/value/value.cc @@ -0,0 +1,25 @@ +#include "value.hh" + +#include "tests/libstore.hh" + +namespace nix { + +class ValueTest : public LibStoreTest +{}; + +TEST_F(ValueTest, unsetValue) +{ + Value unsetValue; + ASSERT_EQ(false, unsetValue.isInitialized()); + ASSERT_EQ(nThunk, unsetValue.type(true)); + ASSERT_DEATH(unsetValue.type(), ""); +} + +TEST_F(ValueTest, vInt) +{ + Value vInt; + vInt.mkInt(42); + ASSERT_EQ(true, vInt.isInitialized()); +} + +} // namespace nix From 9d7dee4a8f279ffd220d41d05a651761ed10d3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sat, 20 Apr 2024 14:15:05 +0200 Subject: [PATCH 019/910] nix::Value: Use more descriptive names --- src/libexpr/value.hh | 15 ++++++++++----- tests/unit/libexpr/value/value.cc | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index a1003e16b..5795f04cf 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -23,7 +23,7 @@ class BindingsBuilder; typedef enum { - tUnset, + tUninitialized = 0, tInt = 1, tBool, tString, @@ -167,7 +167,7 @@ public: struct Value { private: - InternalType internalType = tUnset; + InternalType internalType = tUninitialized; friend std::string showType(const Value & v); @@ -271,7 +271,7 @@ public: inline ValueType type(bool invalidIsThunk = false) const { switch (internalType) { - case tUnset: break; + case tUninitialized: break; case tInt: return nInt; case tBool: return nBool; case tString: return nString; @@ -296,9 +296,14 @@ public: internalType = newType; } - inline bool isInitialized() + /** + * A value becomes valid when it is initialized. We don't use this + * in the evaluator; only in the bindings, where the slight extra + * cost is warranted because of inexperienced callers. + */ + inline bool isValid() const { - return internalType != tUnset; + return internalType != tUninitialized; } inline void mkInt(NixInt n) diff --git a/tests/unit/libexpr/value/value.cc b/tests/unit/libexpr/value/value.cc index 49a896c87..5762d5891 100644 --- a/tests/unit/libexpr/value/value.cc +++ b/tests/unit/libexpr/value/value.cc @@ -10,7 +10,7 @@ class ValueTest : public LibStoreTest TEST_F(ValueTest, unsetValue) { Value unsetValue; - ASSERT_EQ(false, unsetValue.isInitialized()); + ASSERT_EQ(false, unsetValue.isValid()); ASSERT_EQ(nThunk, unsetValue.type(true)); ASSERT_DEATH(unsetValue.type(), ""); } @@ -19,7 +19,7 @@ TEST_F(ValueTest, vInt) { Value vInt; vInt.mkInt(42); - ASSERT_EQ(true, vInt.isInitialized()); + ASSERT_EQ(true, vInt.isValid()); } } // namespace nix From ccad6e94e2d79241c0eab0d9f708bcd30155e840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sat, 20 Apr 2024 22:09:32 +0200 Subject: [PATCH 020/910] C API: add (un)initialized value checks --- src/libexpr-c/nix_api_value.cc | 46 ++++++++- tests/unit/libexpr/nix_api_value.cc | 93 ++++++++++++++----- .../libutil-support/tests/nix_api_util.hh | 13 ++- 3 files changed, 128 insertions(+), 24 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 2550e975a..a3686022d 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -20,7 +20,7 @@ # include "gc_cpp.h" #endif -// Helper function to throw an exception if value is null +// Helper function to throw an exception if value is null or in an invalid state static const nix::Value & check_value_not_null(const Value * value) { if (!value) { @@ -37,6 +37,20 @@ static nix::Value & check_value_not_null(Value * value) return *((nix::Value *) value); } +static void check_value_initialized(const nix::Value & value) +{ + if (!value.isValid()) { + throw std::runtime_error("Uninitialized Value"); + } +} + +static void check_value_uninitialized(const nix::Value & value) +{ + if (value.isValid()) { + throw std::runtime_error("Value already initialized. Variables are immutable"); + } +} + /** * Helper function to convert calls from nix into C API. * @@ -112,6 +126,7 @@ ValueType nix_get_type(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); using namespace nix; switch (v.type()) { case nThunk: @@ -148,6 +163,7 @@ const char * nix_get_typename(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); auto s = nix::showType(v); return strdup(s.c_str()); } @@ -160,6 +176,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nBool); return v.boolean(); } @@ -172,6 +189,7 @@ nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_str context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nString); call_nix_get_string_callback(v.c_str(), callback, user_data); } @@ -184,6 +202,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nPath); // NOTE (from @yorickvP) // v._path.path should work but may not be how Eelco intended it. @@ -203,6 +222,7 @@ unsigned int nix_get_list_size(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nList); return v.listSize(); } @@ -215,6 +235,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nAttrs); return v.attrs()->size(); } @@ -227,6 +248,7 @@ double nix_get_float(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nFloat); return v.fpoint(); } @@ -239,6 +261,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nInt); return v.integer(); } @@ -251,6 +274,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nExternal); return (ExternalValue *) v.external(); } @@ -263,6 +287,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nList); auto * p = v.listElems()[ix]; nix_gc_incref(nullptr, p); @@ -279,6 +304,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -299,6 +325,7 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -316,6 +343,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); const nix::Attr & a = (*v.attrs())[i]; *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); nix_gc_incref(nullptr, a.value); @@ -331,6 +359,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); const nix::Attr & a = (*v.attrs())[i]; return ((const std::string &) (state->state.symbols[a.name])).c_str(); } @@ -343,6 +372,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkBool(b); } NIXC_CATCH_ERRS @@ -355,6 +385,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkString(std::string_view(str)); } NIXC_CATCH_ERRS @@ -366,6 +397,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkPath(s->state.rootPath(nix::CanonPath(str))); } NIXC_CATCH_ERRS @@ -377,6 +409,7 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkFloat(d); } NIXC_CATCH_ERRS @@ -388,6 +421,7 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkInt(i); } NIXC_CATCH_ERRS @@ -399,6 +433,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkNull(); } NIXC_CATCH_ERRS @@ -423,6 +458,7 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); auto r = (nix::ExternalValueBase *) val; v.mkExternal(r); } @@ -450,6 +486,7 @@ nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_buil context->last_err_code = NIX_OK; try { auto & e = check_value_not_null(value); + check_value_initialized(e); list_builder->builder[index] = &e; } NIXC_CATCH_ERRS @@ -470,6 +507,7 @@ nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkList(list_builder->builder); } NIXC_CATCH_ERRS @@ -481,6 +519,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkPrimOp((nix::PrimOp *) p); } NIXC_CATCH_ERRS @@ -492,7 +531,9 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); auto & s = check_value_not_null(source); + check_value_initialized(s); v = s; } NIXC_CATCH_ERRS @@ -504,6 +545,7 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkAttrs(b->builder); } NIXC_CATCH_ERRS @@ -530,6 +572,7 @@ nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * b context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); nix::Symbol s = bb->builder.state.symbols.create(name); bb->builder.insert(s, &v); } @@ -551,6 +594,7 @@ nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * st context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); nix::NixStringContext stringContext; auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned(); nix::StorePathSet storePaths; diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index ac0cdb9c4..9e922f8c7 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -14,11 +14,16 @@ namespace nixC { -TEST_F(nix_api_expr_test, nix_value_set_get_int) +TEST_F(nix_api_expr_test, nix_value_get_int_invalid) { ASSERT_EQ(0, nix_get_int(ctx, nullptr)); - ASSERT_DEATH(nix_get_int(ctx, value), ""); + assert_ctx_err(); + ASSERT_EQ(0, nix_get_int(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_int) +{ int myInt = 1; nix_init_int(ctx, value, myInt); @@ -27,11 +32,16 @@ TEST_F(nix_api_expr_test, nix_value_set_get_int) ASSERT_EQ(NIX_TYPE_INT, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_float) +TEST_F(nix_api_expr_test, nix_value_set_get_float_invalid) { ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, nullptr)); - ASSERT_DEATH(nix_get_float(ctx, value), ""); + assert_ctx_err(); + ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_float) +{ float myDouble = 1.0; nix_init_float(ctx, value, myDouble); @@ -40,11 +50,16 @@ TEST_F(nix_api_expr_test, nix_value_set_get_float) ASSERT_EQ(NIX_TYPE_FLOAT, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_bool) +TEST_F(nix_api_expr_test, nix_value_set_get_bool_invalid) { ASSERT_EQ(false, nix_get_bool(ctx, nullptr)); - ASSERT_DEATH(nix_get_bool(ctx, value), ""); + assert_ctx_err(); + ASSERT_EQ(false, nix_get_bool(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_bool) +{ bool myBool = true; nix_init_bool(ctx, value, myBool); @@ -53,12 +68,18 @@ TEST_F(nix_api_expr_test, nix_value_set_get_bool) ASSERT_EQ(NIX_TYPE_BOOL, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_string) +TEST_F(nix_api_expr_test, nix_value_set_get_string_invalid) { std::string string_value; ASSERT_EQ(NIX_ERR_UNKNOWN, nix_get_string(ctx, nullptr, OBSERVE_STRING(string_value))); - ASSERT_DEATH(nix_get_string(ctx, value, OBSERVE_STRING(string_value)), ""); + assert_ctx_err(); + ASSERT_EQ(NIX_ERR_UNKNOWN, nix_get_string(ctx, value, OBSERVE_STRING(string_value))); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_string) +{ + std::string string_value; const char * myString = "some string"; nix_init_string(ctx, value, myString); @@ -68,21 +89,29 @@ TEST_F(nix_api_expr_test, nix_value_set_get_string) ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(ctx, value)); } +TEST_F(nix_api_expr_test, nix_value_set_get_null_invalid) +{ + ASSERT_EQ(NULL, nix_get_typename(ctx, value)); + assert_ctx_err(); +} + TEST_F(nix_api_expr_test, nix_value_set_get_null) { - ASSERT_DEATH(nix_get_typename(ctx, value), ""); - nix_init_null(ctx, value); ASSERT_STREQ("null", nix_get_typename(ctx, value)); ASSERT_EQ(NIX_TYPE_NULL, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_path) +TEST_F(nix_api_expr_test, nix_value_set_get_path_invalid) { ASSERT_EQ(nullptr, nix_get_path_string(ctx, nullptr)); - ASSERT_DEATH(nix_get_path_string(ctx, value), ""); - + assert_ctx_err(); + ASSERT_EQ(nullptr, nix_get_path_string(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_path) +{ const char * p = "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"; nix_init_path_string(ctx, state, value, p); @@ -91,14 +120,21 @@ TEST_F(nix_api_expr_test, nix_value_set_get_path) ASSERT_EQ(NIX_TYPE_PATH, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_build_and_init_list) +TEST_F(nix_api_expr_test, nix_build_and_init_list_invalid) { ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, nullptr, state, 0)); + assert_ctx_err(); ASSERT_EQ(0, nix_get_list_size(ctx, nullptr)); + assert_ctx_err(); - ASSERT_DEATH(nix_get_list_byidx(ctx, value, state, 0), ""); - ASSERT_DEATH(nix_get_list_size(ctx, value), ""); + ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 0)); + assert_ctx_err(); + ASSERT_EQ(0, nix_get_list_size(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_build_and_init_list) +{ int size = 10; ListBuilder * builder = nix_make_list_builder(ctx, state, size); @@ -119,20 +155,33 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) nix_gc_decref(ctx, intValue); } -TEST_F(nix_api_expr_test, nix_build_and_init_attr) +TEST_F(nix_api_expr_test, nix_build_and_init_attr_invalid) { ASSERT_EQ(nullptr, nix_get_attr_byname(ctx, nullptr, state, 0)); + assert_ctx_err(); ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, nullptr, state, 0, nullptr)); + assert_ctx_err(); ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, nullptr, state, 0)); + assert_ctx_err(); ASSERT_EQ(0, nix_get_attrs_size(ctx, nullptr)); + assert_ctx_err(); ASSERT_EQ(false, nix_has_attr_byname(ctx, nullptr, state, "no-value")); + assert_ctx_err(); - ASSERT_DEATH(nix_get_attr_byname(ctx, value, state, 0), ""); - ASSERT_DEATH(nix_get_attr_byidx(ctx, value, state, 0, nullptr), ""); - ASSERT_DEATH(nix_get_attr_name_byidx(ctx, value, state, 0), ""); - ASSERT_DEATH(nix_get_attrs_size(ctx, value), ""); - ASSERT_DEATH(nix_has_attr_byname(ctx, value, state, "no-value"), ""); + ASSERT_EQ(nullptr, nix_get_attr_byname(ctx, value, state, 0)); + assert_ctx_err(); + ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, value, state, 0, nullptr)); + assert_ctx_err(); + ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, value, state, 0)); + assert_ctx_err(); + ASSERT_EQ(0, nix_get_attrs_size(ctx, value)); + assert_ctx_err(); + ASSERT_EQ(false, nix_has_attr_byname(ctx, value, state, "no-value")); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_build_and_init_attr) +{ int size = 10; const char ** out_name = (const char **) malloc(sizeof(char *)); diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh index 75d302bd6..efd200116 100644 --- a/tests/unit/libutil-support/tests/nix_api_util.hh +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -24,7 +24,9 @@ protected: nix_c_context * ctx; - inline void assert_ctx_ok() { + inline void assert_ctx_ok() + { + if (nix_err_code(ctx) == NIX_OK) { return; } @@ -33,5 +35,14 @@ protected: std::string msg(p, n); FAIL() << "nix_err_code(ctx) != NIX_OK, message: " << msg; } + + inline void assert_ctx_err() + { + if (nix_err_code(ctx) != NIX_OK) { + return; + } + FAIL() << "Got NIX_OK, but expected an error!"; + } }; + } From ff76dd2211d0acddc82a35bb0b1a884323c2a3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 21 Apr 2024 20:24:56 +0200 Subject: [PATCH 021/910] C API: fix test, nix float is a double internally --- tests/unit/libexpr/nix_api_value.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 9e922f8c7..7c8e82c50 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -34,18 +34,18 @@ TEST_F(nix_api_expr_test, nix_value_set_get_int) TEST_F(nix_api_expr_test, nix_value_set_get_float_invalid) { - ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, nullptr)); + ASSERT_DOUBLE_EQ(0.0, nix_get_float(ctx, nullptr)); assert_ctx_err(); - ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, value)); + ASSERT_DOUBLE_EQ(0.0, nix_get_float(ctx, value)); assert_ctx_err(); } TEST_F(nix_api_expr_test, nix_value_set_get_float) { - float myDouble = 1.0; + double myDouble = 1.0; nix_init_float(ctx, value, myDouble); - ASSERT_FLOAT_EQ(myDouble, nix_get_float(ctx, value)); + ASSERT_DOUBLE_EQ(myDouble, nix_get_float(ctx, value)); ASSERT_STREQ("a float", nix_get_typename(ctx, value)); ASSERT_EQ(NIX_TYPE_FLOAT, nix_get_type(ctx, value)); } From 8d70db3251f68597f332de1bffcc3f716ff30450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 21 Apr 2024 22:13:26 +0200 Subject: [PATCH 022/910] C API: add check_value_[in,out] helper functions --- src/libexpr-c/nix_api_value.cc | 109 ++++++++++++---------------- tests/unit/libexpr/nix_api_value.cc | 9 ++- 2 files changed, 54 insertions(+), 64 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index a3686022d..0d61292c4 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -20,7 +20,7 @@ # include "gc_cpp.h" #endif -// Helper function to throw an exception if value is null or in an invalid state +// Internal helper functions to check [in] and [out] `Value *` parameters static const nix::Value & check_value_not_null(const Value * value) { if (!value) { @@ -37,18 +37,31 @@ static nix::Value & check_value_not_null(Value * value) return *((nix::Value *) value); } -static void check_value_initialized(const nix::Value & value) +static const nix::Value & check_value_in(const Value * value) { - if (!value.isValid()) { + auto & v = check_value_not_null(value); + if (!v.isValid()) { throw std::runtime_error("Uninitialized Value"); } + return v; } -static void check_value_uninitialized(const nix::Value & value) +static nix::Value & check_value_in(Value * value) { - if (value.isValid()) { + auto & v = check_value_not_null(value); + if (!v.isValid()) { + throw std::runtime_error("Uninitialized Value"); + } + return v; +} + +static nix::Value & check_value_out(Value * value) +{ + auto & v = check_value_not_null(value); + if (v.isValid()) { throw std::runtime_error("Value already initialized. Variables are immutable"); } + return v; } /** @@ -125,8 +138,7 @@ ValueType nix_get_type(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); using namespace nix; switch (v.type()) { case nThunk: @@ -162,8 +174,7 @@ const char * nix_get_typename(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); auto s = nix::showType(v); return strdup(s.c_str()); } @@ -175,8 +186,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nBool); return v.boolean(); } @@ -188,8 +198,7 @@ nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_str if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nString); call_nix_get_string_callback(v.c_str(), callback, user_data); } @@ -201,8 +210,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nPath); // NOTE (from @yorickvP) // v._path.path should work but may not be how Eelco intended it. @@ -221,8 +229,7 @@ unsigned int nix_get_list_size(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nList); return v.listSize(); } @@ -234,8 +241,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nAttrs); return v.attrs()->size(); } @@ -247,8 +253,7 @@ double nix_get_float(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nFloat); return v.fpoint(); } @@ -260,8 +265,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nInt); return v.integer(); } @@ -273,8 +277,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_out(value); assert(v.type() == nix::nExternal); return (ExternalValue *) v.external(); } @@ -286,8 +289,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nList); auto * p = v.listElems()[ix]; nix_gc_incref(nullptr, p); @@ -303,8 +305,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -324,8 +325,7 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -342,8 +342,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); nix_gc_incref(nullptr, a.value); @@ -358,8 +357,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; return ((const std::string &) (state->state.symbols[a.name])).c_str(); } @@ -371,8 +369,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkBool(b); } NIXC_CATCH_ERRS @@ -384,8 +381,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkString(std::string_view(str)); } NIXC_CATCH_ERRS @@ -396,8 +392,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkPath(s->state.rootPath(nix::CanonPath(str))); } NIXC_CATCH_ERRS @@ -408,8 +403,7 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkFloat(d); } NIXC_CATCH_ERRS @@ -420,8 +414,7 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkInt(i); } NIXC_CATCH_ERRS @@ -432,8 +425,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkNull(); } NIXC_CATCH_ERRS @@ -457,8 +449,7 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); auto r = (nix::ExternalValueBase *) val; v.mkExternal(r); } @@ -486,7 +477,6 @@ nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_buil context->last_err_code = NIX_OK; try { auto & e = check_value_not_null(value); - check_value_initialized(e); list_builder->builder[index] = &e; } NIXC_CATCH_ERRS @@ -506,8 +496,7 @@ nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkList(list_builder->builder); } NIXC_CATCH_ERRS @@ -518,8 +507,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkPrimOp((nix::PrimOp *) p); } NIXC_CATCH_ERRS @@ -530,10 +518,8 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); - auto & s = check_value_not_null(source); - check_value_initialized(s); + auto & v = check_value_out(value); + auto & s = check_value_in(source); v = s; } NIXC_CATCH_ERRS @@ -544,8 +530,7 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkAttrs(b->builder); } NIXC_CATCH_ERRS @@ -572,7 +557,6 @@ nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * b context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); - check_value_initialized(v); nix::Symbol s = bb->builder.state.symbols.create(name); bb->builder.insert(s, &v); } @@ -593,8 +577,7 @@ nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * st if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); nix::NixStringContext stringContext; auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned(); nix::StorePathSet storePaths; diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 7c8e82c50..6e209695e 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -139,13 +139,20 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) ListBuilder * builder = nix_make_list_builder(ctx, state, size); Value * intValue = nix_alloc_value(ctx, state); + Value * intValue2 = nix_alloc_value(ctx, state); + + // `init` and `insert` can be called in any order nix_init_int(ctx, intValue, 42); nix_list_builder_insert(ctx, builder, 0, intValue); + nix_list_builder_insert(ctx, builder, 1, intValue2); + nix_init_int(ctx, intValue2, 43); + nix_make_list(ctx, builder, value); nix_list_builder_free(builder); ASSERT_EQ(42, nix_get_int(ctx, nix_get_list_byidx(ctx, value, state, 0))); - ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 1)); + ASSERT_EQ(43, nix_get_int(ctx, nix_get_list_byidx(ctx, value, state, 1))); + ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 2)); ASSERT_EQ(10, nix_get_list_size(ctx, value)); ASSERT_STREQ("a list", nix_get_typename(ctx, value)); From 6acf02b32a12c9db052c9ca24a238882f12f10a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 21 Apr 2024 22:15:12 +0200 Subject: [PATCH 023/910] C API: source argument to nix_copy_value should be const --- src/libexpr-c/nix_api_value.cc | 2 +- src/libexpr-c/nix_api_value.h | 2 +- tests/unit/libexpr/nix_api_value.cc | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 0d61292c4..0366e5020 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -513,7 +513,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) NIXC_CATCH_ERRS } -nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source) +nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index d8bd77c33..b2b3439ef 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -422,7 +422,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * op); * @param[in] source value to copy from * @return error code, NIX_OK on success. */ -nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source); +nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source); /**@}*/ /** @brief Create a bindings builder diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 6e209695e..6e1131e10 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -367,4 +367,17 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) nix_gc_decref(ctx, e); } +TEST_F(nix_api_expr_test, nix_copy_value) +{ + Value * source = nix_alloc_value(ctx, state); + + nix_init_int(ctx, source, 42); + nix_copy_value(ctx, value, source); + + ASSERT_EQ(42, nix_get_int(ctx, value)); + + // Clean up + nix_gc_decref(ctx, source); +} + } From a60a1f09b2f667d10ff0873b9fd5a70bad3febfd Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 19 Apr 2024 16:21:51 +0200 Subject: [PATCH 024/910] Reuse eval caches and related values when possible --- src/libcmd/installables.cc | 22 +++++++++++++++------- src/libexpr/eval.hh | 8 ++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index ed6772377..5683e1afa 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -444,12 +444,10 @@ ref openEvalCache( std::shared_ptr lockedFlake) { auto fingerprint = lockedFlake->getFingerprint(state.store); - return make_ref( - evalSettings.useEvalCache && evalSettings.pureEval - ? fingerprint - : std::nullopt, - state, - [&state, lockedFlake]() + auto hash = evalSettings.useEvalCache && evalSettings.pureEval + ? fingerprint + : std::nullopt; + auto rootLoader = [&state, lockedFlake]() { /* For testing whether the evaluation cache is complete. */ @@ -465,7 +463,17 @@ ref openEvalCache( assert(aOutputs); return aOutputs->value; - }); + }; + + if (hash) { + auto search = state.evalCaches.find(hash.value()); + if (search == state.evalCaches.end()) { + search = state.evalCaches.emplace(hash.value(), make_ref(hash, state, rootLoader)).first; + } + return search->second; + } else { + return make_ref(hash, state, rootLoader); + } } Installables SourceExprCommand::parseInstallables( diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 3477f6c46..508361301 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -34,6 +34,9 @@ class StorePath; struct SingleDerivedPath; enum RepairFlag : bool; struct MemoryInputAccessor; +namespace eval_cache { + class EvalCache; +} /** @@ -282,6 +285,11 @@ public: return *new EvalErrorBuilder(*this, args...); } + /** + * A cache for evaluation caches, so as to reuse the same root value if possible + */ + std::map> evalCaches; + private: /* Cache for calls to addToStore(); maps source paths to the store From 1c4e392c64939ca961458cfb15a9da1e4d883b22 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 25 Apr 2024 00:44:47 +0200 Subject: [PATCH 025/910] Compute fingerprint only if needed As per Eelco's review comments Co-authored-by: Eelco Dolstra --- src/libcmd/installables.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5683e1afa..e5c981629 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -443,9 +443,8 @@ ref openEvalCache( EvalState & state, std::shared_ptr lockedFlake) { - auto fingerprint = lockedFlake->getFingerprint(state.store); - auto hash = evalSettings.useEvalCache && evalSettings.pureEval - ? fingerprint + auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval + ? lockedFlake->getFingerprint(state.store) : std::nullopt; auto rootLoader = [&state, lockedFlake]() { @@ -472,7 +471,7 @@ ref openEvalCache( } return search->second; } else { - return make_ref(hash, state, rootLoader); + return make_ref(std::nullopt, state, rootLoader); } } From 19cc50dcbf58dd52a82308ab1733d2d7764db9d1 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 25 Apr 2024 00:47:46 +0200 Subject: [PATCH 026/910] fixup: Compute fingerprint only if needed --- src/libcmd/installables.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index e5c981629..b93c7f7e8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -464,10 +464,10 @@ ref openEvalCache( return aOutputs->value; }; - if (hash) { - auto search = state.evalCaches.find(hash.value()); + if (fingerprint) { + auto search = state.evalCaches.find(fingerprint.value()); if (search == state.evalCaches.end()) { - search = state.evalCaches.emplace(hash.value(), make_ref(hash, state, rootLoader)).first; + search = state.evalCaches.emplace(fingerprint.value(), make_ref(fingerprint, state, rootLoader)).first; } return search->second; } else { From e5f509ef0b5c364544c904faa0bfda57dba03611 Mon Sep 17 00:00:00 2001 From: Sarah Brofeldt Date: Sun, 28 Apr 2024 16:32:58 +0200 Subject: [PATCH 027/910] nix repl: hide progress bar during :edit --- src/libcmd/repl.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a045e83d2..bade1b538 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -506,6 +506,10 @@ ProcessLineResult NixRepl::processLine(std::string line) auto editor = args.front(); args.pop_front(); + // avoid garbling the editor with the progress bar + logger->pause(); + Finally resume([&]() { logger->resume(); }); + // runProgram redirects stdout to a StringSink, // using runProgram2 to allow editors to display their UI runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args }); From 4d99d07bc97bab9ba3e399ac3ca6e78a8ccf4aab Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 15:34:35 +0200 Subject: [PATCH 028/910] Whitespace --- src/libfetchers/path.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 0af1bad73..67a9fc2f2 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -54,6 +54,7 @@ struct PathInputScheme : InputScheme "narHash", }; } + std::optional inputFromAttrs(const Attrs & attrs) const override { getStrAttr(attrs, "path"); From 458441c637e7bac1f579c8448aa5a7210bd27fcf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 15:34:38 +0200 Subject: [PATCH 029/910] Test dirOf behaviour on the root of a flake --- tests/functional/flakes/common.sh | 2 ++ tests/functional/flakes/flakes.sh | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh index fc45cf7bf..65c12a9c3 100644 --- a/tests/functional/flakes/common.sh +++ b/tests/functional/flakes/common.sh @@ -21,6 +21,8 @@ writeSimpleFlake() { # To test "nix flake init". legacyPackages.$system.hello = import ./simple.nix; + + parent = builtins.dirOf ./.; }; } EOF diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 4f41cae0a..5beb1e0f5 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -231,6 +231,9 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"$flake1Dir\").pack # 'getFlake' on a locked flakeref should succeed even in pure mode. nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1Dir?rev=$hash2\").packages.$system.default" +# Regression test for dirOf on the root of the flake. +[[ $(nix eval --json flake1#parent) = \""$NIX_STORE_DIR"\" ]] + # Building a flake with an unlocked dependency should fail in pure mode. (! nix build -o "$TEST_ROOT/result" flake2#bar --no-registries) (! nix build -o "$TEST_ROOT/result" flake2#bar --no-use-registries) From 503be57bbd88d5cfc2246b900b6dcb4e388f5066 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 15:43:33 +0200 Subject: [PATCH 030/910] Test baseNameOf behaviour on the root of a flake --- tests/functional/flakes/common.sh | 2 ++ tests/functional/flakes/flakes.sh | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh index 65c12a9c3..e0776d5ed 100644 --- a/tests/functional/flakes/common.sh +++ b/tests/functional/flakes/common.sh @@ -23,6 +23,8 @@ writeSimpleFlake() { legacyPackages.$system.hello = import ./simple.nix; parent = builtins.dirOf ./.; + + baseName = builtins.baseNameOf ./.; }; } EOF diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 5beb1e0f5..b2bea0711 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -234,6 +234,9 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1 # Regression test for dirOf on the root of the flake. [[ $(nix eval --json flake1#parent) = \""$NIX_STORE_DIR"\" ]] +# Regression test for baseNameOf on the root of the flake. +[[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]*-source$ ]] + # Building a flake with an unlocked dependency should fail in pure mode. (! nix build -o "$TEST_ROOT/result" flake2#bar --no-registries) (! nix build -o "$TEST_ROOT/result" flake2#bar --no-use-registries) From 724132468afc6d2bf760209a3b9160fd207ed5d8 Mon Sep 17 00:00:00 2001 From: Christian Albertsen Date: Tue, 30 Apr 2024 17:08:04 +0200 Subject: [PATCH 031/910] Update distibuted-builds.md not to use nix-store info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When trying the „nix-store info“ commands on this page I received the error "error: 'info' is not a recognised command". According to https://github.com/NixOS/nix/issues/9349 info seems to have been an alias for ping. So why not just replace info with ping? --- doc/manual/src/advanced-topics/distributed-builds.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/advanced-topics/distributed-builds.md b/doc/manual/src/advanced-topics/distributed-builds.md index 52acd039c..ddabaeb4d 100644 --- a/doc/manual/src/advanced-topics/distributed-builds.md +++ b/doc/manual/src/advanced-topics/distributed-builds.md @@ -12,14 +12,14 @@ machine is accessible via SSH and that it has Nix installed. You can test whether connecting to the remote Nix instance works, e.g. ```console -$ nix store info --store ssh://mac +$ nix store ping --store ssh://mac ``` will try to connect to the machine named `mac`. It is possible to specify an SSH identity file as part of the remote store URI, e.g. ```console -$ nix store info --store ssh://mac?ssh-key=/home/alice/my-key +$ nix store ping --store ssh://mac?ssh-key=/home/alice/my-key ``` Since builds should be non-interactive, the key should not have a From f29a220b7092954644884d193aea13531f77b69b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 16:46:36 +0200 Subject: [PATCH 032/910] Test that the root of a tree produces /nix/store/--source --- tests/functional/flakes/common.sh | 2 ++ tests/functional/flakes/flakes.sh | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh index e0776d5ed..f83a02aba 100644 --- a/tests/functional/flakes/common.sh +++ b/tests/functional/flakes/common.sh @@ -25,6 +25,8 @@ writeSimpleFlake() { parent = builtins.dirOf ./.; baseName = builtins.baseNameOf ./.; + + root = ./.; }; } EOF diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index b2bea0711..a74d56a5b 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -235,7 +235,10 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1 [[ $(nix eval --json flake1#parent) = \""$NIX_STORE_DIR"\" ]] # Regression test for baseNameOf on the root of the flake. -[[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]*-source$ ]] +[[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]+-source$ ]] + +# Test that the root of a tree returns a path named /nix/store/--source (#10627). +[[ $(nix eval --raw flake1#root) =~ ^.*/[a-z0-9]+-[a-z0-9]+-source$ ]] # Building a flake with an unlocked dependency should fail in pure mode. (! nix build -o "$TEST_ROOT/result" flake2#bar --no-registries) From 1f4168221757f46d57f951692158138dbb4059b0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 18:10:16 +0200 Subject: [PATCH 033/910] Update tests/functional/flakes/flakes.sh Co-authored-by: John Ericson --- tests/functional/flakes/flakes.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index a74d56a5b..35b0c5d84 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -237,7 +237,9 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1 # Regression test for baseNameOf on the root of the flake. [[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]+-source$ ]] -# Test that the root of a tree returns a path named /nix/store/--source (#10627). +# Test that the root of a tree returns a path named /nix/store/--source. +# This behavior is *not* desired, but has existed for a while. +# Issue #10627 what to do about it. [[ $(nix eval --raw flake1#root) =~ ^.*/[a-z0-9]+-[a-z0-9]+-source$ ]] # Building a flake with an unlocked dependency should fail in pure mode. From f34b52b52163ec0db60dca6907f309acc45e2205 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 1 May 2024 18:47:19 +0200 Subject: [PATCH 034/910] libexpr: Add missing GC root for baseEnv This missing GC root wasn't much of a problem before, because the heap would end up with a reference to the `baseEnv` pretty soon, but when unit testing, the construction of `EvalState` doesn't necessarily happen well before GC runs for the first time. Found while unit testing the Rust bindings that currently reside at https://github.com/nixops4/nixops4/tree/main/rust --- src/libexpr/eval.cc | 8 +++++++- src/libexpr/eval.hh | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 35ccca79a..aa058b04f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -50,6 +50,7 @@ #include #include +#include #include #include @@ -342,6 +343,8 @@ void initGC() gcInitialised = true; } +static constexpr size_t BASE_ENV_SIZE = 128; + EvalState::EvalState( const LookupPath & _lookupPath, ref store, @@ -424,8 +427,11 @@ EvalState::EvalState( #if HAVE_BOEHMGC , valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr)) , env1AllocCache(std::allocate_shared(traceable_allocator(), nullptr)) + , baseEnvP(std::allocate_shared(traceable_allocator(), &allocEnv(BASE_ENV_SIZE))) + , baseEnv(**baseEnvP) +#else + , baseEnv(allocEnv(BASE_ENV_SIZE)) #endif - , baseEnv(allocEnv(128)) , staticBaseEnv{std::make_shared(nullptr, nullptr)} { corepkgsFS->setPathDisplay(""); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 508361301..ae8b9dd04 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -547,6 +547,11 @@ public: */ SingleDerivedPath coerceToSingleDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx); +#if HAVE_BOEHMGC + /** A GC root for the baseEnv reference. */ + std::shared_ptr baseEnvP; +#endif + public: /** From 037c8d771d56095617b4c2cbfd07ef1199905685 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 2 May 2024 21:41:24 -0400 Subject: [PATCH 035/910] Fix format errors Fix formatting violations, update blacklist to reflect moved files. PR #10556 passed CI before the new formating rules were added, and our CI has the race condition of allowing old results, resulting in master getting broken. --- maintainers/flake-module.nix | 6 +++--- src/libstore/indirect-root-store.cc | 6 +++--- src/libutil/unix-domain-socket.hh | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 2ea94e9d9..d35b03148 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -252,8 +252,8 @@ ''^src/libstore/unix/pathlocks\.cc$'' ''^src/libstore/unix/posix-fs-canonicalise\.cc$'' ''^src/libstore/unix/posix-fs-canonicalise\.hh$'' - ''^src/libstore/unix/uds-remote-store\.cc$'' - ''^src/libstore/unix/uds-remote-store\.hh$'' + ''^src/libstore/uds-remote-store\.cc$'' + ''^src/libstore/uds-remote-store\.hh$'' ''^src/libstore/windows/build\.cc$'' ''^src/libstore/worker-protocol-impl\.hh$'' ''^src/libstore/worker-protocol\.cc$'' @@ -350,7 +350,7 @@ ''^src/libutil/unix/processes\.cc$'' ''^src/libutil/unix/signals-impl\.hh$'' ''^src/libutil/unix/signals\.cc$'' - ''^src/libutil/unix/unix-domain-socket\.cc$'' + ''^src/libutil/unix-domain-socket\.cc$'' ''^src/libutil/unix/users\.cc$'' ''^src/libutil/url-parts\.hh$'' ''^src/libutil/url\.cc$'' diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc index b92279928..9da05778d 100644 --- a/src/libstore/indirect-root-store.cc +++ b/src/libstore/indirect-root-store.cc @@ -15,15 +15,15 @@ void IndirectRootStore::makeSymlink(const Path & link, const Path & target) renameFile(tempLink, link); } - Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) { Path gcRoot(canonPath(_gcRoot)); if (isInStore(gcRoot)) throw Error( - "creating a garbage collector root (%1%) in the Nix store is forbidden " - "(are you running nix-build inside the store?)", gcRoot); + "creating a garbage collector root (%1%) in the Nix store is forbidden " + "(are you running nix-build inside the store?)", + gcRoot); /* Register this root with the garbage collector, if it's running. This should be superfluous since the caller should diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix-domain-socket.hh index 9aba9f628..a8bbc4b89 100644 --- a/src/libutil/unix-domain-socket.hh +++ b/src/libutil/unix-domain-socket.hh @@ -5,7 +5,7 @@ #include "file-descriptor.hh" #ifdef _WIN32 -# include +# include #endif #include @@ -21,7 +21,6 @@ AutoCloseFD createUnixDomainSocket(); */ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); - /** * Often we want to use `Descriptor`, but Windows makes a slightly * stronger file descriptor vs socket distinction, at least at the level @@ -39,7 +38,7 @@ using Socket = /** * Windows gives this a different name */ -# define SHUT_WR SD_SEND +# define SHUT_WR SD_SEND #endif /** @@ -70,7 +69,6 @@ static inline Descriptor fromSocket(Socket fd) #endif } - /** * Bind a Unix domain socket to a path. */ From ba5929c7becfb8e3668cdabddc4e1ea0b0330189 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 3 May 2024 12:14:01 +0200 Subject: [PATCH 036/910] Merge InputAccessor into SourceAccessor After the removal of the InputAccessor::fetchToStore() method, the only remaining functionality in InputAccessor was `fingerprint` and `getLastModified()`, and there is no reason to keep those in a separate class. --- src/libexpr/eval.cc | 12 ++++----- src/libexpr/eval.hh | 10 +++---- src/libexpr/nixexpr.hh | 4 +-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 8 +++--- src/libexpr/primops.cc | 8 +++--- src/libexpr/value.hh | 5 ++-- src/libfetchers/fetchers.cc | 5 ++-- src/libfetchers/fetchers.hh | 10 +++---- src/libfetchers/filtering-input-accessor.cc | 8 +++--- src/libfetchers/filtering-input-accessor.hh | 7 +++-- src/libfetchers/fs-input-accessor.cc | 15 ++++------- src/libfetchers/fs-input-accessor.hh | 7 +++-- src/libfetchers/git-utils.cc | 21 +++++++-------- src/libfetchers/git-utils.hh | 5 ++-- src/libfetchers/github.cc | 2 +- src/libfetchers/indirect.cc | 2 +- src/libfetchers/memory-input-accessor.cc | 29 --------------------- src/libfetchers/memory-input-accessor.hh | 18 ------------- src/libfetchers/mounted-input-accessor.cc | 10 +++---- src/libfetchers/mounted-input-accessor.hh | 4 +-- src/libfetchers/path.cc | 2 +- src/libfetchers/tarball.cc | 4 +-- src/libfetchers/tarball.hh | 4 +-- src/libfetchers/unix/git.cc | 12 ++++----- src/libfetchers/unix/mercurial.cc | 2 +- src/libutil/input-accessor.hh | 27 ------------------- src/libutil/memory-source-accessor.cc | 10 +++++-- src/libutil/memory-source-accessor.hh | 4 +-- src/libutil/source-accessor.hh | 26 +++++++++++++++++- src/libutil/source-path.cc | 6 ++--- src/libutil/source-path.hh | 12 ++++----- src/nix-env/nix-env.cc | 14 +++++----- src/nix/main.cc | 2 +- tests/unit/libexpr/primops.cc | 1 - 35 files changed, 130 insertions(+), 188 deletions(-) delete mode 100644 src/libfetchers/memory-input-accessor.cc delete mode 100644 src/libfetchers/memory-input-accessor.hh delete mode 100644 src/libutil/input-accessor.hh diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index aa058b04f..fbd846d14 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -17,7 +17,7 @@ #include "print.hh" #include "fs-input-accessor.hh" #include "filtering-input-accessor.hh" -#include "memory-input-accessor.hh" +#include "memory-source-accessor.hh" #include "signals.hh" #include "gc-small-vector.hh" #include "url.hh" @@ -400,7 +400,7 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListInputAccessor::create(makeFSInputAccessor(), {}, + ? ref(AllowListInputAccessor::create(makeFSInputAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" @@ -408,8 +408,8 @@ EvalState::EvalState( throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); })) : makeFSInputAccessor()) - , corepkgsFS(makeMemoryInputAccessor()) - , internalFS(makeMemoryInputAccessor()) + , corepkgsFS(make_ref()) + , internalFS(make_ref()) , derivationInternal{corepkgsFS->addFile( CanonPath("derivation-internal.nix"), #include "primops/derivation.nix.gen.hh" @@ -2766,12 +2766,12 @@ SourcePath resolveExprPath(SourcePath path) if (++followCount >= maxFollow) throw Error("too many symbolic links encountered while traversing the path '%s'", path); auto p = path.parent().resolveSymlinks() / path.baseName(); - if (p.lstat().type != InputAccessor::tSymlink) break; + if (p.lstat().type != SourceAccessor::tSymlink) break; path = {path.accessor, CanonPath(p.readLink(), path.path.parent().value_or(CanonPath::root))}; } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == InputAccessor::tDirectory) + if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ae8b9dd04..7ca2d6227 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -9,7 +9,7 @@ #include "symbol-table.hh" #include "config.hh" #include "experimental-features.hh" -#include "input-accessor.hh" +#include "source-accessor.hh" #include "search-path.hh" #include "repl-exit-status.hh" @@ -33,7 +33,7 @@ class EvalState; class StorePath; struct SingleDerivedPath; enum RepairFlag : bool; -struct MemoryInputAccessor; +struct MemorySourceAccessor; namespace eval_cache { class EvalCache; } @@ -229,18 +229,18 @@ public: /** * The accessor for the root filesystem. */ - const ref rootFS; + const ref rootFS; /** * The in-memory filesystem for paths. */ - const ref corepkgsFS; + const ref corepkgsFS; /** * In-memory filesystem for internal, non-user-callable Nix * expressions like call-flake.nix. */ - const ref internalFS; + const ref internalFS; const SourcePath derivationInternal; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index e3cae8385..e37e3bdd1 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -92,10 +92,10 @@ struct ExprString : Expr struct ExprPath : Expr { - ref accessor; + ref accessor; std::string s; Value v; - ExprPath(ref accessor, std::string s) : accessor(accessor), s(std::move(s)) + ExprPath(ref accessor, std::string s) : accessor(accessor), s(std::move(s)) { v.mkPath(&*accessor, this->s.c_str()); } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 024e79c43..5a928e9aa 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -44,7 +44,7 @@ struct ParserState Expr * result; SourcePath basePath; PosTable::Origin origin; - const ref rootFS; + const ref rootFS; const Expr::AstSymbols & s; void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index bff066170..00300449f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -41,7 +41,7 @@ Expr * parseExprFromBuf( const SourcePath & basePath, SymbolTable & symbols, PosTable & positions, - const ref rootFS, + const ref rootFS, const Expr::AstSymbols & astSymbols); } @@ -291,7 +291,7 @@ path_start /* add back in the trailing '/' to the first segment */ if ($1.p[$1.l-1] == '/' && $1.l > 1) path += "/"; - $$ = new ExprPath(ref(state->rootFS), std::move(path)); + $$ = new ExprPath(ref(state->rootFS), std::move(path)); } | HPATH { if (evalSettings.pureEval) { @@ -301,7 +301,7 @@ path_start ); } Path path(getHome() + std::string($1.p + 1, $1.l - 1)); - $$ = new ExprPath(ref(state->rootFS), std::move(path)); + $$ = new ExprPath(ref(state->rootFS), std::move(path)); } ; @@ -430,7 +430,7 @@ Expr * parseExprFromBuf( const SourcePath & basePath, SymbolTable & symbols, PosTable & positions, - const ref rootFS, + const ref rootFS, const Expr::AstSymbols & astSymbols) { yyscan_t scanner; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index df274caed..a3ccc9771 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1828,12 +1828,12 @@ static RegisterPrimOp primop_hashFile({ .fun = prim_hashFile, }); -static Value * fileTypeToString(EvalState & state, InputAccessor::Type type) +static Value * fileTypeToString(EvalState & state, SourceAccessor::Type type) { return - type == InputAccessor::Type::tRegular ? &state.vStringRegular : - type == InputAccessor::Type::tDirectory ? &state.vStringDirectory : - type == InputAccessor::Type::tSymlink ? &state.vStringSymlink : + type == SourceAccessor::Type::tRegular ? &state.vStringRegular : + type == SourceAccessor::Type::tDirectory ? &state.vStringDirectory : + type == SourceAccessor::Type::tSymlink ? &state.vStringSymlink : &state.vStringUnknown; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 5795f04cf..61cf2d310 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -7,7 +7,6 @@ #include "symbol-table.hh" #include "value/context.hh" -#include "input-accessor.hh" #include "source-path.hh" #include "print-options.hh" @@ -217,7 +216,7 @@ public: }; struct Path { - InputAccessor * accessor; + SourceAccessor * accessor; const char * path; }; @@ -335,7 +334,7 @@ public: void mkPath(const SourcePath & path); void mkPath(std::string_view path); - inline void mkPath(InputAccessor * accessor, const char * path) + inline void mkPath(SourceAccessor * accessor, const char * path) { finishValue(tPath, { .path = { .accessor = accessor, .path = path } }); } diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 0577b8d9d..73923907c 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -1,6 +1,5 @@ #include "fetchers.hh" #include "store-api.hh" -#include "input-accessor.hh" #include "source-path.hh" #include "fetch-to-store.hh" #include "json-utils.hh" @@ -238,7 +237,7 @@ void InputScheme::checkLocks(const Input & specified, const Input & final) const } } -std::pair, Input> Input::getAccessor(ref store) const +std::pair, Input> Input::getAccessor(ref store) const { try { auto [accessor, final] = getAccessorUnchecked(store); @@ -252,7 +251,7 @@ std::pair, Input> Input::getAccessor(ref store) const } } -std::pair, Input> Input::getAccessorUnchecked(ref store) const +std::pair, Input> Input::getAccessorUnchecked(ref store) const { // FIXME: cache the accessor diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index bb21c68cc..42b184393 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -11,7 +11,7 @@ #include #include -namespace nix { class Store; class StorePath; struct InputAccessor; } +namespace nix { class Store; class StorePath; struct SourceAccessor; } namespace nix::fetchers { @@ -84,15 +84,15 @@ public: std::pair fetchToStore(ref store) const; /** - * Return an InputAccessor that allows access to files in the + * Return a `SourceAccessor` that allows access to files in the * input without copying it to the store. Also return a possibly * unlocked input. */ - std::pair, Input> getAccessor(ref store) const; + std::pair, Input> getAccessor(ref store) const; private: - std::pair, Input> getAccessorUnchecked(ref store) const; + std::pair, Input> getAccessorUnchecked(ref store) const; public: @@ -185,7 +185,7 @@ struct InputScheme std::string_view contents, std::optional commitMsg) const; - virtual std::pair, Input> getAccessor(ref store, const Input & input) const = 0; + virtual std::pair, Input> getAccessor(ref store, const Input & input) const = 0; /** * Is this `InputScheme` part of an experimental feature? diff --git a/src/libfetchers/filtering-input-accessor.cc b/src/libfetchers/filtering-input-accessor.cc index e0cbfd905..d2b47b5e5 100644 --- a/src/libfetchers/filtering-input-accessor.cc +++ b/src/libfetchers/filtering-input-accessor.cc @@ -13,13 +13,13 @@ bool FilteringInputAccessor::pathExists(const CanonPath & path) return isAllowed(path) && next->pathExists(prefix / path); } -std::optional FilteringInputAccessor::maybeLstat(const CanonPath & path) +std::optional FilteringInputAccessor::maybeLstat(const CanonPath & path) { checkAccess(path); return next->maybeLstat(prefix / path); } -InputAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath & path) +SourceAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath & path) { checkAccess(path); DirEntries entries; @@ -54,7 +54,7 @@ struct AllowListInputAccessorImpl : AllowListInputAccessor std::set allowedPrefixes; AllowListInputAccessorImpl( - ref next, + ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) : AllowListInputAccessor(SourcePath(next), std::move(makeNotAllowedError)) @@ -73,7 +73,7 @@ struct AllowListInputAccessorImpl : AllowListInputAccessor }; ref AllowListInputAccessor::create( - ref next, + ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) { diff --git a/src/libfetchers/filtering-input-accessor.hh b/src/libfetchers/filtering-input-accessor.hh index 133a6cee3..ddf18eea4 100644 --- a/src/libfetchers/filtering-input-accessor.hh +++ b/src/libfetchers/filtering-input-accessor.hh @@ -1,6 +1,5 @@ #pragma once -#include "input-accessor.hh" #include "source-path.hh" namespace nix { @@ -17,9 +16,9 @@ typedef std::function MakeNotAllowe * control. Subclasses should override `isAllowed()` to implement an * access control policy. The error message is customized at construction. */ -struct FilteringInputAccessor : InputAccessor +struct FilteringInputAccessor : SourceAccessor { - ref next; + ref next; CanonPath prefix; MakeNotAllowedError makeNotAllowedError; @@ -67,7 +66,7 @@ struct AllowListInputAccessor : public FilteringInputAccessor virtual void allowPrefix(CanonPath prefix) = 0; static ref create( - ref next, + ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError); diff --git a/src/libfetchers/fs-input-accessor.cc b/src/libfetchers/fs-input-accessor.cc index 2bbe53e11..bd4e4e2cd 100644 --- a/src/libfetchers/fs-input-accessor.cc +++ b/src/libfetchers/fs-input-accessor.cc @@ -4,22 +4,17 @@ namespace nix { -struct FSInputAccessor : InputAccessor, PosixSourceAccessor +ref makeFSInputAccessor() { - using PosixSourceAccessor::PosixSourceAccessor; -}; - -ref makeFSInputAccessor() -{ - return make_ref(); + return make_ref(); } -ref makeFSInputAccessor(std::filesystem::path root) +ref makeFSInputAccessor(std::filesystem::path root) { - return make_ref(std::move(root)); + return make_ref(std::move(root)); } -ref makeStorePathAccessor( +ref makeStorePathAccessor( ref store, const StorePath & storePath) { diff --git a/src/libfetchers/fs-input-accessor.hh b/src/libfetchers/fs-input-accessor.hh index e60906bd8..80dc74725 100644 --- a/src/libfetchers/fs-input-accessor.hh +++ b/src/libfetchers/fs-input-accessor.hh @@ -1,6 +1,5 @@ #pragma once -#include "input-accessor.hh" #include "source-path.hh" namespace nix { @@ -8,11 +7,11 @@ namespace nix { class StorePath; class Store; -ref makeFSInputAccessor(); +ref makeFSInputAccessor(); -ref makeFSInputAccessor(std::filesystem::path root); +ref makeFSInputAccessor(std::filesystem::path root); -ref makeStorePathAccessor( +ref makeStorePathAccessor( ref store, const StorePath & storePath); diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index e310af063..5657a6b4f 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -1,8 +1,5 @@ #include "git-utils.hh" #include "fs-input-accessor.hh" -#include "input-accessor.hh" -#include "filtering-input-accessor.hh" -#include "memory-input-accessor.hh" #include "cache.hh" #include "finally.hh" #include "processes.hh" @@ -338,9 +335,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this */ ref getRawAccessor(const Hash & rev); - ref getAccessor(const Hash & rev, bool exportIgnore) override; + ref getAccessor(const Hash & rev, bool exportIgnore) override; - ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError e) override; + ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError e) override; ref getFileSystemObjectSink() override; @@ -477,7 +474,7 @@ ref GitRepo::openRepo(const std::filesystem::path & path, bool create, /** * Raw git tree input accessor. */ -struct GitInputAccessor : InputAccessor +struct GitInputAccessor : SourceAccessor { ref repo; Tree root; @@ -710,7 +707,7 @@ struct GitExportIgnoreInputAccessor : CachingFilteringInputAccessor { ref repo; std::optional rev; - GitExportIgnoreInputAccessor(ref repo, ref next, std::optional rev) + GitExportIgnoreInputAccessor(ref repo, ref next, std::optional rev) : CachingFilteringInputAccessor(next, [&](const CanonPath & path) { return RestrictedPathError(fmt("'%s' does not exist because it was fetched with exportIgnore enabled", path)); }) @@ -928,7 +925,7 @@ ref GitRepoImpl::getRawAccessor(const Hash & rev) return make_ref(self, rev); } -ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) +ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) { auto self = ref(shared_from_this()); ref rawGitAccessor = getRawAccessor(rev); @@ -940,20 +937,20 @@ ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) } } -ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) +ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) { auto self = ref(shared_from_this()); /* In case of an empty workdir, return an empty in-memory tree. We cannot use AllowListInputAccessor because it would return an error for the root (and we can't add the root to the allow-list since that would allow access to all its children). */ - ref fileAccessor = + ref fileAccessor = wd.files.empty() - ? makeEmptyInputAccessor() + ? makeEmptySourceAccessor() : AllowListInputAccessor::create( makeFSInputAccessor(path), std::set { wd.files }, - std::move(makeNotAllowedError)).cast(); + std::move(makeNotAllowedError)).cast(); if (exportIgnore) return make_ref(self, fileAccessor, std::nullopt); else diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 600a42da0..e264b2f63 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -1,7 +1,6 @@ #pragma once #include "filtering-input-accessor.hh" -#include "input-accessor.hh" #include "fs-sink.hh" namespace nix { @@ -75,9 +74,9 @@ struct GitRepo virtual bool hasObject(const Hash & oid) = 0; - virtual ref getAccessor(const Hash & rev, bool exportIgnore) = 0; + virtual ref getAccessor(const Hash & rev, bool exportIgnore) = 0; - virtual ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) = 0; + virtual ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) = 0; virtual ref getFileSystemObjectSink() = 0; diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 985f2e479..b9a3d5c0d 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -272,7 +272,7 @@ struct GitArchiveInputScheme : InputScheme return {std::move(input), tarballInfo}; } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { auto [input, tarballInfo] = downloadArchive(store, _input); diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index 3f21445e1..ba5078631 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -97,7 +97,7 @@ struct IndirectInputScheme : InputScheme return input; } - std::pair, Input> getAccessor(ref store, const Input & input) const override + std::pair, Input> getAccessor(ref store, const Input & input) const override { throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); } diff --git a/src/libfetchers/memory-input-accessor.cc b/src/libfetchers/memory-input-accessor.cc deleted file mode 100644 index 34a801f67..000000000 --- a/src/libfetchers/memory-input-accessor.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include "memory-input-accessor.hh" -#include "memory-source-accessor.hh" -#include "source-path.hh" - -namespace nix { - -struct MemoryInputAccessorImpl : MemoryInputAccessor, MemorySourceAccessor -{ - SourcePath addFile(CanonPath path, std::string && contents) override - { - return { - ref(shared_from_this()), - MemorySourceAccessor::addFile(path, std::move(contents)) - }; - } -}; - -ref makeMemoryInputAccessor() -{ - return make_ref(); -} - -ref makeEmptyInputAccessor() -{ - static auto empty = makeMemoryInputAccessor().cast(); - return empty; -} - -} diff --git a/src/libfetchers/memory-input-accessor.hh b/src/libfetchers/memory-input-accessor.hh deleted file mode 100644 index 63afadd2a..000000000 --- a/src/libfetchers/memory-input-accessor.hh +++ /dev/null @@ -1,18 +0,0 @@ -#include "input-accessor.hh" -#include "source-path.hh" - -namespace nix { - -/** - * An input accessor for an in-memory file system. - */ -struct MemoryInputAccessor : InputAccessor -{ - virtual SourcePath addFile(CanonPath path, std::string && contents) = 0; -}; - -ref makeMemoryInputAccessor(); - -ref makeEmptyInputAccessor(); - -} diff --git a/src/libfetchers/mounted-input-accessor.cc b/src/libfetchers/mounted-input-accessor.cc index b1eeaa97d..4d086c7ad 100644 --- a/src/libfetchers/mounted-input-accessor.cc +++ b/src/libfetchers/mounted-input-accessor.cc @@ -2,11 +2,11 @@ namespace nix { -struct MountedInputAccessor : InputAccessor +struct MountedInputAccessor : SourceAccessor { - std::map> mounts; + std::map> mounts; - MountedInputAccessor(std::map> _mounts) + MountedInputAccessor(std::map> _mounts) : mounts(std::move(_mounts)) { displayPrefix.clear(); @@ -53,7 +53,7 @@ struct MountedInputAccessor : InputAccessor return displayPrefix + accessor->showPath(subpath) + displaySuffix; } - std::pair, CanonPath> resolve(CanonPath path) + std::pair, CanonPath> resolve(CanonPath path) { // Find the nearest parent of `path` that is a mount point. std::vector subpath; @@ -71,7 +71,7 @@ struct MountedInputAccessor : InputAccessor } }; -ref makeMountedInputAccessor(std::map> mounts) +ref makeMountedInputAccessor(std::map> mounts) { return make_ref(std::move(mounts)); } diff --git a/src/libfetchers/mounted-input-accessor.hh b/src/libfetchers/mounted-input-accessor.hh index b557c5dad..74e040f44 100644 --- a/src/libfetchers/mounted-input-accessor.hh +++ b/src/libfetchers/mounted-input-accessor.hh @@ -1,9 +1,9 @@ #pragma once -#include "input-accessor.hh" +#include "source-accessor.hh" namespace nix { -ref makeMountedInputAccessor(std::map> mounts); +ref makeMountedInputAccessor(std::map> mounts); } diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 67a9fc2f2..1e9683ae1 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -114,7 +114,7 @@ struct PathInputScheme : InputScheme throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string()); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); std::string absPath; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index a1f934c35..8ebc2c296 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -297,7 +297,7 @@ struct FileInputScheme : CurlInputScheme : (!requireTree && !hasTarballExtension(url.path))); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { auto input(_input); @@ -332,7 +332,7 @@ struct TarballInputScheme : CurlInputScheme : (requireTree || hasTarballExtension(url.path))); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { auto input(_input); diff --git a/src/libfetchers/tarball.hh b/src/libfetchers/tarball.hh index bcb5dcc5e..ba0dfd623 100644 --- a/src/libfetchers/tarball.hh +++ b/src/libfetchers/tarball.hh @@ -8,7 +8,7 @@ namespace nix { class Store; -struct InputAccessor; +struct SourceAccessor; } namespace nix::fetchers { @@ -32,7 +32,7 @@ struct DownloadTarballResult Hash treeHash; time_t lastModified; std::optional immutableUrl; - ref accessor; + ref accessor; }; /** diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 0c54c5504..be44b2eda 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -495,7 +495,7 @@ struct GitInputScheme : InputScheme } } - std::pair, Input> getAccessorFromCommit( + std::pair, Input> getAccessorFromCommit( ref store, RepoInfo & repoInfo, Input && input) const @@ -629,7 +629,7 @@ struct GitInputScheme : InputScheme input accessor consisting of the accessor for the top-level repo and the accessors for the submodules. */ if (getSubmodulesAttr(input)) { - std::map> mounts; + std::map> mounts; for (auto & [submodule, submoduleRev] : repo->getSubmodules(rev, exportIgnore)) { auto resolved = repo->resolveSubmoduleUrl(submodule.url); @@ -665,7 +665,7 @@ struct GitInputScheme : InputScheme return {accessor, std::move(input)}; } - std::pair, Input> getAccessorFromWorkdir( + std::pair, Input> getAccessorFromWorkdir( ref store, RepoInfo & repoInfo, Input && input) const @@ -679,7 +679,7 @@ struct GitInputScheme : InputScheme auto exportIgnore = getExportIgnoreAttr(input); - ref accessor = + ref accessor = repo->getAccessor(repoInfo.workdirInfo, exportIgnore, makeNotAllowedError(repoInfo.url)); @@ -690,7 +690,7 @@ struct GitInputScheme : InputScheme consisting of the accessor for the top-level repo and the accessors for the submodule workdirs. */ if (getSubmodulesAttr(input) && !repoInfo.workdirInfo.submodules.empty()) { - std::map> mounts; + std::map> mounts; for (auto & submodule : repoInfo.workdirInfo.submodules) { auto submodulePath = CanonPath(repoInfo.url) / submodule.path; @@ -755,7 +755,7 @@ struct GitInputScheme : InputScheme return {accessor, std::move(input)}; } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index df6bc5335..e85f7e854 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -346,7 +346,7 @@ struct MercurialInputScheme : InputScheme return makeResult(infoAttrs, std::move(storePath)); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); diff --git a/src/libutil/input-accessor.hh b/src/libutil/input-accessor.hh deleted file mode 100644 index 55b7c2f2f..000000000 --- a/src/libutil/input-accessor.hh +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -///@file - -#include "source-accessor.hh" -#include "ref.hh" -#include "repair-flag.hh" - -namespace nix { - -MakeError(RestrictedPathError, Error); - -struct InputAccessor : virtual SourceAccessor, std::enable_shared_from_this -{ - std::optional fingerprint; - - /** - * Return the maximum last-modified time of the files in this - * tree, if available. - */ - virtual std::optional getLastModified() - { - return std::nullopt; - } - -}; - -} diff --git a/src/libutil/memory-source-accessor.cc b/src/libutil/memory-source-accessor.cc index 880fa61b7..b7207cffb 100644 --- a/src/libutil/memory-source-accessor.cc +++ b/src/libutil/memory-source-accessor.cc @@ -108,7 +108,7 @@ std::string MemorySourceAccessor::readLink(const CanonPath & path) throw Error("file '%s' is not a symbolic link", path); } -CanonPath MemorySourceAccessor::addFile(CanonPath path, std::string && contents) +SourcePath MemorySourceAccessor::addFile(CanonPath path, std::string && contents) { auto * f = open(path, File { File::Regular {} }); if (!f) @@ -118,7 +118,7 @@ CanonPath MemorySourceAccessor::addFile(CanonPath path, std::string && contents) else throw Error("file '%s' is not a regular file", path); - return path; + return SourcePath{ref(shared_from_this()), path}; } @@ -184,4 +184,10 @@ void MemorySink::createSymlink(const Path & path, const std::string & target) throw Error("file '%s' is not a symbolic link", path); } +ref makeEmptySourceAccessor() +{ + static auto empty = make_ref().cast(); + return empty; +} + } diff --git a/src/libutil/memory-source-accessor.hh b/src/libutil/memory-source-accessor.hh index 7a1990d2f..c8f793922 100644 --- a/src/libutil/memory-source-accessor.hh +++ b/src/libutil/memory-source-accessor.hh @@ -1,4 +1,4 @@ -#include "source-accessor.hh" +#include "source-path.hh" #include "fs-sink.hh" #include "variant-wrapper.hh" @@ -69,7 +69,7 @@ struct MemorySourceAccessor : virtual SourceAccessor */ File * open(const CanonPath & path, std::optional create); - CanonPath addFile(CanonPath path, std::string && contents); + SourcePath addFile(CanonPath path, std::string && contents); }; /** diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 1f272327f..5f1afb946 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -35,7 +35,7 @@ enum class SymlinkResolution { * filesystem-like entities (such as the real filesystem, tarballs or * Git repositories). */ -struct SourceAccessor +struct SourceAccessor : std::enable_shared_from_this { const size_t number; @@ -168,6 +168,30 @@ struct SourceAccessor CanonPath resolveSymlinks( const CanonPath & path, SymlinkResolution mode = SymlinkResolution::Full); + + /** + * A string that uniquely represents the contents of this + * accessor. This is used for caching lookups (see `fetchToStore()`). + */ + std::optional fingerprint; + + /** + * Return the maximum last-modified time of the files in this + * tree, if available. + */ + virtual std::optional getLastModified() + { return std::nullopt; } }; +/** + * Return a source accessor that contains only an empty root directory. + */ +ref makeEmptySourceAccessor(); + +/** + * Exception thrown when accessing a filtered path (see + * `FilteringInputAccessor`). + */ +MakeError(RestrictedPathError, Error); + } diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc index 2a5b20858..023b5ed4b 100644 --- a/src/libutil/source-path.cc +++ b/src/libutil/source-path.cc @@ -18,13 +18,13 @@ std::string SourcePath::readFile() const bool SourcePath::pathExists() const { return accessor->pathExists(path); } -InputAccessor::Stat SourcePath::lstat() const +SourceAccessor::Stat SourcePath::lstat() const { return accessor->lstat(path); } -std::optional SourcePath::maybeLstat() const +std::optional SourcePath::maybeLstat() const { return accessor->maybeLstat(path); } -InputAccessor::DirEntries SourcePath::readDirectory() const +SourceAccessor::DirEntries SourcePath::readDirectory() const { return accessor->readDirectory(path); } std::string SourcePath::readLink() const diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index b8f69af12..7e4c3c65d 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -7,7 +7,7 @@ #include "ref.hh" #include "canon-path.hh" -#include "input-accessor.hh" +#include "source-accessor.hh" namespace nix { @@ -19,10 +19,10 @@ namespace nix { */ struct SourcePath { - ref accessor; + ref accessor; CanonPath path; - SourcePath(ref accessor, CanonPath path = CanonPath::root) + SourcePath(ref accessor, CanonPath path = CanonPath::root) : accessor(std::move(accessor)) , path(std::move(path)) { } @@ -51,19 +51,19 @@ struct SourcePath * Return stats about this `SourcePath`, or throw an exception if * it doesn't exist. */ - InputAccessor::Stat lstat() const; + SourceAccessor::Stat lstat() const; /** * Return stats about this `SourcePath`, or std::nullopt if it * doesn't exist. */ - std::optional maybeLstat() const; + std::optional maybeLstat() const; /** * If this `SourcePath` denotes a directory (not a symlink), * return its directory entries; otherwise throw an error. */ - InputAccessor::DirEntries readDirectory() const; + SourceAccessor::DirEntries readDirectory() const; /** * If this `SourcePath` denotes a symlink, return its target; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 25c8f43c2..b5e13cc23 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -94,11 +94,11 @@ static bool parseInstallSourceOptions(Globals & globals, } -static bool isNixExpr(const SourcePath & path, struct InputAccessor::Stat & st) +static bool isNixExpr(const SourcePath & path, struct SourceAccessor::Stat & st) { return - st.type == InputAccessor::tRegular - || (st.type == InputAccessor::tDirectory && (path / "default.nix").resolveSymlinks().pathExists()); + st.type == SourceAccessor::tRegular + || (st.type == SourceAccessor::tDirectory && (path / "default.nix").resolveSymlinks().pathExists()); } @@ -119,14 +119,14 @@ static void getAllExprs(EvalState & state, auto path2 = (path / i).resolveSymlinks(); - InputAccessor::Stat st; + SourceAccessor::Stat st; try { st = path2.lstat(); } catch (Error &) { continue; // ignore dangling symlinks in ~/.nix-defexpr } - if (isNixExpr(path2, st) && (st.type != InputAccessor::tRegular || hasSuffix(path2.baseName(), ".nix"))) { + if (isNixExpr(path2, st) && (st.type != SourceAccessor::tRegular || hasSuffix(path2.baseName(), ".nix"))) { /* Strip off the `.nix' filename suffix (if applicable), otherwise the attribute cannot be selected with the `-A' option. Useful if you want to stick a Nix @@ -149,7 +149,7 @@ static void getAllExprs(EvalState & state, throw Error("too many Nix expressions in directory '%1%'", path); attrs.alloc(attrName).mkApp(&state.getBuiltin("import"), vArg); } - else if (st.type == InputAccessor::tDirectory) + else if (st.type == SourceAccessor::tDirectory) /* `path2' is a directory (with no default.nix in it); recurse into it. */ getAllExprs(state, path2, seen, attrs); @@ -171,7 +171,7 @@ static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v set flat, not nested, to make it easier for a user to have a ~/.nix-defexpr directory that includes some system-wide directory). */ - else if (st.type == InputAccessor::tDirectory) { + else if (st.type == SourceAccessor::tDirectory) { auto attrs = state.buildBindings(maxAttrs); attrs.insert(state.symbols.create("_combineChannels"), &state.vEmptyList); StringSet seen; diff --git a/src/nix/main.cc b/src/nix/main.cc index 7b0478a9f..8ea2f7748 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -14,7 +14,7 @@ #include "finally.hh" #include "loggers.hh" #include "markdown.hh" -#include "memory-input-accessor.hh" +#include "memory-source-accessor.hh" #include "terminal.hh" #include "users.hh" diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index 5ddc031f7..10c250d74 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -2,7 +2,6 @@ #include #include "eval-settings.hh" -#include "memory-input-accessor.hh" #include "tests/libexpr.hh" From 20558e0462bd10c79a655756de08655e3474d18b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 3 May 2024 12:30:28 +0200 Subject: [PATCH 037/910] Remove FSInputAccessor --- src/libcmd/installables.cc | 3 +- src/libexpr/eval.cc | 5 ++- src/libexpr/primops.cc | 1 - src/libfetchers/fetch-to-store.cc | 2 +- src/libfetchers/fs-input-accessor.cc | 34 ------------------- src/libfetchers/git-utils.cc | 3 +- src/libfetchers/path.cc | 3 +- src/libfetchers/store-path-accessor.cc | 17 ++++++++++ ...put-accessor.hh => store-path-accessor.hh} | 4 --- src/libfetchers/tarball.cc | 3 +- src/libfetchers/unix/git.cc | 1 - src/libfetchers/unix/mercurial.cc | 9 ++--- src/libutil/posix-source-accessor.cc | 10 ++++++ src/libutil/source-accessor.hh | 10 ++++++ tests/unit/libexpr/primops.cc | 1 + 15 files changed, 48 insertions(+), 58 deletions(-) delete mode 100644 src/libfetchers/fs-input-accessor.cc create mode 100644 src/libfetchers/store-path-accessor.cc rename src/libfetchers/{fs-input-accessor.hh => store-path-accessor.hh} (67%) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index b93c7f7e8..0d42a62cc 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -21,7 +21,6 @@ #include "url.hh" #include "registry.hh" #include "build-result.hh" -#include "fs-input-accessor.hh" #include #include @@ -147,7 +146,7 @@ MixFlakeOptions::MixFlakeOptions() .category = category, .labels = {"flake-lock-path"}, .handler = {[&](std::string lockFilePath) { - lockFlags.referenceLockFilePath = getUnfilteredRootPath(CanonPath(absPath(lockFilePath))); + lockFlags.referenceLockFilePath = {makeFSSourceAccessor(), CanonPath(absPath(lockFilePath))}; }}, .completer = completePath }); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fbd846d14..6fd60084d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -15,7 +15,6 @@ #include "function-trace.hh" #include "profiles.hh" #include "print.hh" -#include "fs-input-accessor.hh" #include "filtering-input-accessor.hh" #include "memory-source-accessor.hh" #include "signals.hh" @@ -400,14 +399,14 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListInputAccessor::create(makeFSInputAccessor(), {}, + ? ref(AllowListInputAccessor::create(makeFSSourceAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" : "in restricted mode"; throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); })) - : makeFSInputAccessor()) + : makeFSSourceAccessor()) , corepkgsFS(make_ref()) , internalFS(make_ref()) , derivationInternal{corepkgsFS->addFile( diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a3ccc9771..109127d1d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -15,7 +15,6 @@ #include "value-to-json.hh" #include "value-to-xml.hh" #include "primops.hh" -#include "fs-input-accessor.hh" #include "fetch-to-store.hh" #include diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 398286065..c105fe1fc 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -14,7 +14,7 @@ StorePath fetchToStore( RepairFlag repair) { // FIXME: add an optimisation for the case where the accessor is - // an FSInputAccessor pointing to a store path. + // a `PosixSourceAccessor` pointing to a store path. std::optional cacheKey; diff --git a/src/libfetchers/fs-input-accessor.cc b/src/libfetchers/fs-input-accessor.cc deleted file mode 100644 index bd4e4e2cd..000000000 --- a/src/libfetchers/fs-input-accessor.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "fs-input-accessor.hh" -#include "posix-source-accessor.hh" -#include "store-api.hh" - -namespace nix { - -ref makeFSInputAccessor() -{ - return make_ref(); -} - -ref makeFSInputAccessor(std::filesystem::path root) -{ - return make_ref(std::move(root)); -} - -ref makeStorePathAccessor( - ref store, - const StorePath & storePath) -{ - // FIXME: should use `store->getFSAccessor()` - auto root = std::filesystem::path { store->toRealPath(storePath) }; - auto accessor = makeFSInputAccessor(root); - accessor->setPathDisplay(root.string()); - return accessor; -} - -SourcePath getUnfilteredRootPath(CanonPath path) -{ - static auto rootFS = makeFSInputAccessor(); - return {rootFS, path}; -} - -} diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5657a6b4f..a91587cb4 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -1,5 +1,4 @@ #include "git-utils.hh" -#include "fs-input-accessor.hh" #include "cache.hh" #include "finally.hh" #include "processes.hh" @@ -948,7 +947,7 @@ ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool export wd.files.empty() ? makeEmptySourceAccessor() : AllowListInputAccessor::create( - makeFSInputAccessor(path), + makeFSSourceAccessor(path), std::set { wd.files }, std::move(makeNotAllowedError)).cast(); if (exportIgnore) diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 1e9683ae1..68958d559 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -1,8 +1,7 @@ #include "fetchers.hh" #include "store-api.hh" #include "archive.hh" -#include "fs-input-accessor.hh" -#include "posix-source-accessor.hh" +#include "store-path-accessor.hh" namespace nix::fetchers { diff --git a/src/libfetchers/store-path-accessor.cc b/src/libfetchers/store-path-accessor.cc new file mode 100644 index 000000000..6eeeba3e8 --- /dev/null +++ b/src/libfetchers/store-path-accessor.cc @@ -0,0 +1,17 @@ +#include "store-path-accessor.hh" +#include "store-api.hh" + +namespace nix { + +ref makeStorePathAccessor( + ref store, + const StorePath & storePath) +{ + // FIXME: should use `store->getFSAccessor()` + auto root = std::filesystem::path { store->toRealPath(storePath) }; + auto accessor = makeFSSourceAccessor(root); + accessor->setPathDisplay(root.string()); + return accessor; +} + +} diff --git a/src/libfetchers/fs-input-accessor.hh b/src/libfetchers/store-path-accessor.hh similarity index 67% rename from src/libfetchers/fs-input-accessor.hh rename to src/libfetchers/store-path-accessor.hh index 80dc74725..4aa55c4df 100644 --- a/src/libfetchers/fs-input-accessor.hh +++ b/src/libfetchers/store-path-accessor.hh @@ -7,10 +7,6 @@ namespace nix { class StorePath; class Store; -ref makeFSInputAccessor(); - -ref makeFSInputAccessor(std::filesystem::path root); - ref makeStorePathAccessor( ref store, const StorePath & storePath); diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 8ebc2c296..33c8f3c0c 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -8,8 +8,7 @@ #include "tarfile.hh" #include "types.hh" #include "split.hh" -#include "posix-source-accessor.hh" -#include "fs-input-accessor.hh" +#include "store-path-accessor.hh" #include "store-api.hh" #include "git-utils.hh" diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index be44b2eda..c8fd295c0 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -9,7 +9,6 @@ #include "pathlocks.hh" #include "processes.hh" #include "git.hh" -#include "fs-input-accessor.hh" #include "mounted-input-accessor.hh" #include "git-utils.hh" #include "logging.hh" diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index e85f7e854..4d95f54f0 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -6,8 +6,7 @@ #include "tarfile.hh" #include "store-api.hh" #include "url-parts.hh" -#include "fs-input-accessor.hh" -#include "posix-source-accessor.hh" +#include "store-path-accessor.hh" #include "fetch-settings.hh" #include @@ -211,10 +210,9 @@ struct MercurialInputScheme : InputScheme return files.count(file); }; - PosixSourceAccessor accessor; auto storePath = store->addToStore( input.getName(), - accessor, CanonPath { actualPath }, + *makeFSSourceAccessor(), CanonPath { actualPath }, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, filter); @@ -320,8 +318,7 @@ struct MercurialInputScheme : InputScheme deletePath(tmpDir + "/.hg_archival.txt"); - PosixSourceAccessor accessor; - auto storePath = store->addToStore(name, accessor, CanonPath { tmpDir }); + auto storePath = store->addToStore(name, *makeFSSourceAccessor(), CanonPath { tmpDir }); Attrs infoAttrs({ {"rev", input.getRev()->gitRev()}, diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index a589bfd3d..f7dffb871 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -166,4 +166,14 @@ void PosixSourceAccessor::assertNoSymlinks(CanonPath path) } } +ref makeFSSourceAccessor() +{ + static auto rootFS = make_ref(); + return rootFS; +} + +ref makeFSSourceAccessor(std::filesystem::path root) +{ + return make_ref(std::move(root)); +} } diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 5f1afb946..548feddfd 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -194,4 +194,14 @@ ref makeEmptySourceAccessor(); */ MakeError(RestrictedPathError, Error); +/** + * Return an accessor for the root filesystem. + */ +ref makeFSSourceAccessor(); + +/** + * Return an accessor for the filesystem rooted at `root`. + */ +ref makeFSSourceAccessor(std::filesystem::path root); + } diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index 10c250d74..5b5898237 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -2,6 +2,7 @@ #include #include "eval-settings.hh" +#include "memory-source-accessor.hh" #include "tests/libexpr.hh" From ffc280f27a57afea8480c0e2505408d730c9e61c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 3 May 2024 15:41:03 +0200 Subject: [PATCH 038/910] Formatting --- src/libfetchers/store-path-accessor.cc | 6 ++---- src/libfetchers/store-path-accessor.hh | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libfetchers/store-path-accessor.cc b/src/libfetchers/store-path-accessor.cc index 6eeeba3e8..528bf2a4f 100644 --- a/src/libfetchers/store-path-accessor.cc +++ b/src/libfetchers/store-path-accessor.cc @@ -3,12 +3,10 @@ namespace nix { -ref makeStorePathAccessor( - ref store, - const StorePath & storePath) +ref makeStorePathAccessor(ref store, const StorePath & storePath) { // FIXME: should use `store->getFSAccessor()` - auto root = std::filesystem::path { store->toRealPath(storePath) }; + auto root = std::filesystem::path{store->toRealPath(storePath)}; auto accessor = makeFSSourceAccessor(root); accessor->setPathDisplay(root.string()); return accessor; diff --git a/src/libfetchers/store-path-accessor.hh b/src/libfetchers/store-path-accessor.hh index 4aa55c4df..989cf3fa2 100644 --- a/src/libfetchers/store-path-accessor.hh +++ b/src/libfetchers/store-path-accessor.hh @@ -7,9 +7,7 @@ namespace nix { class StorePath; class Store; -ref makeStorePathAccessor( - ref store, - const StorePath & storePath); +ref makeStorePathAccessor(ref store, const StorePath & storePath); SourcePath getUnfilteredRootPath(CanonPath path); From 460d8fbaea6c5600b88efe1be84623c7fa32b073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20Hamb=C3=BCchen?= Date: Mon, 29 Apr 2024 03:57:28 +0200 Subject: [PATCH 039/910] language: Link examples to detail explanations. Also, warn of the scoping caveats of `with`. --- doc/manual/src/language/constructs.md | 28 +++++++++ doc/manual/src/language/index.md | 84 ++++++++++++++++----------- 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md index a82ec5960..ad1fdfe5f 100644 --- a/doc/manual/src/language/constructs.md +++ b/doc/manual/src/language/constructs.md @@ -402,7 +402,35 @@ establishes the same scope as let a = 1; in let a = 2; in let a = 3; in let a = 4; in ... ``` +Variables coming from outer `with` expressions *are* shadowed: + +```nix +with { a = "outer"; }; +with { a = "inner"; }; +a +``` + +Does evaluate to `"inner"`. + ## Comments Comments can be single-line, started with a `#` character, or inline/multi-line, enclosed within `/* ... */`. + +`#` comments last until the end of the line. + +`/*` comments run until the next occurrence of `*/`; this cannot be escaped. + +## Scoping rules + +Nix has constructs with + +* dynamic scope + * [`with`](#with-expressions) +* static scope + * [`let`](#let-expressions) + * [`inherit`](#inheriting-attributes) + * function arguments + +Static scope takes precedence over dynamic scope. +See [`with`](#with-expressions) for a detailed example. diff --git a/doc/manual/src/language/index.md b/doc/manual/src/language/index.md index 650412f1b..3694480d7 100644 --- a/doc/manual/src/language/index.md +++ b/doc/manual/src/language/index.md @@ -53,7 +53,7 @@ This is an incomplete overview of language features, by example. - *Basic values* + *Basic values ([primitives](@docroot@/language/values.md#primitives))* @@ -71,7 +71,7 @@ This is an incomplete overview of language features, by example. - A string + A [string](@docroot@/language/values.md#type-string) @@ -94,6 +94,18 @@ This is an incomplete overview of language features, by example. + + + + `# Explanation` + + + + + A [comment](@docroot@/language/constructs.md#comments). + + + @@ -106,7 +118,7 @@ This is an incomplete overview of language features, by example. - String interpolation (expands to `"hello world"`, `"1 2 3"`, `"/nix/store/-bash-/bin/sh"`) + [String interpolation](@docroot@/language/string-interpolation.md) (expands to `"hello world"`, `"1 2 3"`, `"/nix/store/-bash-/bin/sh"`) @@ -118,7 +130,7 @@ This is an incomplete overview of language features, by example. - Booleans + [Booleans](@docroot@/language/values.md#type-boolean) @@ -130,7 +142,7 @@ This is an incomplete overview of language features, by example. - Null value + [Null](@docroot@/language/values.md#type-null) value @@ -142,7 +154,7 @@ This is an incomplete overview of language features, by example. - An integer + An [integer](@docroot@/language/values.md#type-number) @@ -154,7 +166,7 @@ This is an incomplete overview of language features, by example. - A floating point number + A [floating point number](@docroot@/language/values.md#type-number) @@ -166,7 +178,7 @@ This is an incomplete overview of language features, by example. - An absolute path + An absolute [path](@docroot@/language/values.md#type-path) @@ -178,7 +190,7 @@ This is an incomplete overview of language features, by example. - A path relative to the file containing this Nix expression + A [path](@docroot@/language/values.md#type-path) relative to the file containing this Nix expression @@ -190,7 +202,7 @@ This is an incomplete overview of language features, by example. - A home path. Evaluates to the `"/.config"`. + A home [path](@docroot@/language/values.md#type-path). Evaluates to the `"/.config"`. @@ -202,7 +214,7 @@ This is an incomplete overview of language features, by example. - Search path for Nix files. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH). + A [lookup path](@docroot@/language/constructs/lookup-path.md) for Nix files. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH). @@ -226,7 +238,7 @@ This is an incomplete overview of language features, by example. - A set with attributes named `x` and `y` + An [attribute set](@docroot@/language/values.md#attribute-set) with attributes named `x` and `y` @@ -250,7 +262,7 @@ This is an incomplete overview of language features, by example. - A recursive set, equivalent to `{ x = "foo"; y = "foobar"; }` + A [recursive set](@docroot@/language/constructs.md#recursive-sets), equivalent to `{ x = "foo"; y = "foobar"; }`. @@ -266,7 +278,7 @@ This is an incomplete overview of language features, by example. - Lists with three elements. + [Lists](@docroot@/language/values.md#list) with three elements. @@ -350,7 +362,7 @@ This is an incomplete overview of language features, by example. - Attribute selection (evaluates to `1`) + [Attribute selection](@docroot@/language/values.md#attribute-set) (evaluates to `1`) @@ -362,7 +374,7 @@ This is an incomplete overview of language features, by example. - Attribute selection with default (evaluates to `3`) + [Attribute selection](@docroot@/language/values.md#attribute-set) with default (evaluates to `3`) @@ -398,7 +410,7 @@ This is an incomplete overview of language features, by example. - Conditional expression + [Conditional expression](@docroot@/language/constructs.md#conditionals). @@ -410,7 +422,7 @@ This is an incomplete overview of language features, by example. - Assertion check (evaluates to `"yes!"`). + [Assertion](@docroot@/language/constructs.md#assertions) check (evaluates to `"yes!"`). @@ -422,7 +434,7 @@ This is an incomplete overview of language features, by example. - Variable definition + Variable definition. See [`let`-expressions](@docroot@/language/constructs.md#let-expressions). @@ -434,7 +446,9 @@ This is an incomplete overview of language features, by example. - Add all attributes from the given set to the scope (evaluates to `1`) + Add all attributes from the given set to the scope (evaluates to `1`). + + See [`with`-expressions](@docroot@/language/constructs.md#with-expressions) for details and shadowing caveats. @@ -447,7 +461,8 @@ This is an incomplete overview of language features, by example. Adds the variables to the current scope (attribute set or `let` binding). - Desugars to `pkgs = pkgs; src = src;` + Desugars to `pkgs = pkgs; src = src;`. + See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). @@ -460,14 +475,15 @@ This is an incomplete overview of language features, by example. Adds the attributes, from the attribute set in parentheses, to the current scope (attribute set or `let` binding). - Desugars to `lib = pkgs.lib; stdenv = pkgs.stdenv;` + Desugars to `lib = pkgs.lib; stdenv = pkgs.stdenv;`. + See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). - *Functions (lambdas)* + *[Functions](@docroot@/language/constructs.md#functions) (lambdas)* @@ -484,7 +500,7 @@ This is an incomplete overview of language features, by example. - A function that expects an integer and returns it increased by 1 + A [function](@docroot@/language/constructs.md#functions) that expects an integer and returns it increased by 1. @@ -496,7 +512,7 @@ This is an incomplete overview of language features, by example. - Curried function, equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. + Curried [function](@docroot@/language/constructs.md#functions), equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. @@ -508,7 +524,7 @@ This is an incomplete overview of language features, by example. - A function call (evaluates to 101) + A [function](@docroot@/language/constructs.md#functions) call (evaluates to 101) @@ -520,7 +536,7 @@ This is an incomplete overview of language features, by example. - A function bound to a variable and subsequently called by name (evaluates to 103) + A [function](@docroot@/language/constructs.md#functions) bound to a variable and subsequently called by name (evaluates to 103) @@ -532,7 +548,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attributes `x` and `y` and concatenates them + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and concatenates them @@ -544,7 +560,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` @@ -556,7 +572,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attributes `x` and `y` and ignores any other attributes + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and ignores any other attributes @@ -570,7 +586,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attributes `x` and `y`, and binds the whole set to `args` + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y`, and binds the whole set to `args` @@ -594,7 +610,8 @@ This is an incomplete overview of language features, by example. - Load and return Nix expression in given file + Load and return Nix expression in given file. + See [import](@docroot@/language/builtins.md#builtins-import). @@ -606,7 +623,8 @@ This is an incomplete overview of language features, by example. - Apply a function to every element of a list (evaluates to `[ 2 4 6 ]`) + Apply a function to every element of a list (evaluates to `[ 2 4 6 ]`). + See [`map`](@docroot@/language/builtins.md#builtins-map). From 71c66de227c13f4ba6cf36d922c079ada259b742 Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Sun, 5 May 2024 17:37:04 +0000 Subject: [PATCH 040/910] document store url `trusted=true` option behavior --- src/libstore/globals.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 933fc2e5a..0c71a7515 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -782,6 +782,7 @@ public: - the store object has been signed using a key in the trusted keys list - the [`require-sigs`](#conf-require-sigs) option has been set to `false` + - the store URL is configured with `trusted=true` - the store object is [content-addressed](@docroot@/glossary.md#gloss-content-addressed-store-object) )", {"binary-cache-public-keys"}}; From feb1d10f60a2b9ae9cce48817b6a209a7ab007c8 Mon Sep 17 00:00:00 2001 From: HaeNoe <57222371+haenoe@users.noreply.github.com> Date: Mon, 6 May 2024 15:50:26 +0200 Subject: [PATCH 041/910] _not_ round-trip tests for `fetchers::PublicKey` default `type` (#10637) Another continuation of #10602 --- tests/unit/libfetchers/data/public-key/noRoundTrip.json | 3 +++ tests/unit/libfetchers/public-key.cc | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/unit/libfetchers/data/public-key/noRoundTrip.json diff --git a/tests/unit/libfetchers/data/public-key/noRoundTrip.json b/tests/unit/libfetchers/data/public-key/noRoundTrip.json new file mode 100644 index 000000000..4dcbf9148 --- /dev/null +++ b/tests/unit/libfetchers/data/public-key/noRoundTrip.json @@ -0,0 +1,3 @@ +{ + "key": "ABCDE" +} diff --git a/tests/unit/libfetchers/public-key.cc b/tests/unit/libfetchers/public-key.cc index 941ae9db7..7b763bb05 100644 --- a/tests/unit/libfetchers/public-key.cc +++ b/tests/unit/libfetchers/public-key.cc @@ -42,4 +42,13 @@ TEST_JSON(PublicKeyTest, simple, (fetchers::PublicKey { .type = "ssh-rsa", .key TEST_JSON(PublicKeyTest, defaultType, fetchers::PublicKey { .key = "ABCDE" }) #undef TEST_JSON + +TEST_F(PublicKeyTest, PublicKey_noRoundTrip_from_json) { + readTest("noRoundTrip.json", [&](const auto & encoded_) { + fetchers::PublicKey expected = { .type = "ssh-ed25519", .key = "ABCDE" }; + fetchers::PublicKey got = nlohmann::json::parse(encoded_); + ASSERT_EQ(got, nlohmann::json(expected)); + }); +} + } From 27a02bc7d10821f5788e4f2752d56e3f7aa19086 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 6 May 2024 15:17:27 +0200 Subject: [PATCH 042/910] tests: remove unneeded indirection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the additional function calls obscured the actual logic Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- mk/common-test.sh | 8 -------- mk/debug-test.sh | 4 ++-- mk/run-test.sh | 4 ++-- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/mk/common-test.sh b/mk/common-test.sh index 2783d293b..2abea7887 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -17,11 +17,3 @@ TESTS_ENVIRONMENT=( run () { cd "$(dirname $1)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -x -e -u -o pipefail $(basename $1) } - -init_test () { - run "$init" 2>/dev/null > /dev/null -} - -run_test_proper () { - run "$test" -} diff --git a/mk/debug-test.sh b/mk/debug-test.sh index 52482c01e..1cd6f9dce 100755 --- a/mk/debug-test.sh +++ b/mk/debug-test.sh @@ -9,6 +9,6 @@ dir="$(dirname "${BASH_SOURCE[0]}")" source "$dir/common-test.sh" if [ -n "$init" ]; then - (init_test) + (run "$init" 2>/dev/null > /dev/null) fi -run_test_proper +run "$test" diff --git a/mk/run-test.sh b/mk/run-test.sh index da9c5a473..177a452e8 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -23,9 +23,9 @@ fi run_test () { if [ -n "$init" ]; then - (init_test 2>/dev/null > /dev/null) + (run "$init" 2>/dev/null > /dev/null) fi - log="$(run_test_proper 2>&1)" && status=0 || status=$? + log="$(run "$test" 2>&1)" && status=0 || status=$? } run_test From 709cd44d3e30f37ac288f143524e47b64cb85546 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 17:29:03 +0200 Subject: [PATCH 043/910] Rename remaining instances of "InputAccessor" to "SourceAccessor" --- maintainers/flake-module.nix | 12 ++++---- src/libexpr/eval.cc | 14 ++++----- src/libexpr/flake/call-flake.nix | 2 +- ...cessor.cc => filtering-source-accessor.cc} | 28 ++++++++--------- ...cessor.hh => filtering-source-accessor.hh} | 20 ++++++------- src/libfetchers/git-utils.cc | 30 +++++++++---------- src/libfetchers/git-utils.hh | 2 +- src/libfetchers/mounted-input-accessor.hh | 9 ------ ...accessor.cc => mounted-source-accessor.cc} | 10 +++---- src/libfetchers/mounted-source-accessor.hh | 9 ++++++ src/libfetchers/unix/git.cc | 6 ++-- src/libutil/source-accessor.hh | 2 +- 12 files changed, 72 insertions(+), 72 deletions(-) rename src/libfetchers/{filtering-input-accessor.cc => filtering-source-accessor.cc} (61%) rename src/libfetchers/{filtering-input-accessor.hh => filtering-source-accessor.hh} (73%) delete mode 100644 src/libfetchers/mounted-input-accessor.hh rename src/libfetchers/{mounted-input-accessor.cc => mounted-source-accessor.cc} (86%) create mode 100644 src/libfetchers/mounted-source-accessor.hh diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index d35b03148..fdac87be3 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -113,15 +113,15 @@ ''^src/libfetchers/fetch-to-store\.cc$'' ''^src/libfetchers/fetchers\.cc$'' ''^src/libfetchers/fetchers\.hh$'' - ''^src/libfetchers/filtering-input-accessor\.cc$'' - ''^src/libfetchers/filtering-input-accessor\.hh$'' - ''^src/libfetchers/fs-input-accessor\.cc$'' - ''^src/libfetchers/fs-input-accessor\.hh$'' + ''^src/libfetchers/filtering-source-accessor\.cc$'' + ''^src/libfetchers/filtering-source-accessor\.hh$'' + ''^src/libfetchers/fs-source-accessor\.cc$'' + ''^src/libfetchers/fs-source-accessor\.hh$'' ''^src/libfetchers/git-utils\.cc$'' ''^src/libfetchers/git-utils\.hh$'' ''^src/libfetchers/github\.cc$'' ''^src/libfetchers/indirect\.cc$'' - ''^src/libfetchers/memory-input-accessor\.cc$'' + ''^src/libfetchers/memory-source-accessor\.cc$'' ''^src/libfetchers/path\.cc$'' ''^src/libfetchers/registry\.cc$'' ''^src/libfetchers/registry\.hh$'' @@ -302,7 +302,7 @@ ''^src/libutil/hash\.hh$'' ''^src/libutil/hilite\.cc$'' ''^src/libutil/hilite\.hh$'' - ''^src/libutil/input-accessor\.hh$'' + ''^src/libutil/source-accessor\.hh$'' ''^src/libutil/json-impls\.hh$'' ''^src/libutil/json-utils\.cc$'' ''^src/libutil/json-utils\.hh$'' diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6fd60084d..ad6cdc6d2 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -15,7 +15,7 @@ #include "function-trace.hh" #include "profiles.hh" #include "print.hh" -#include "filtering-input-accessor.hh" +#include "filtering-source-accessor.hh" #include "memory-source-accessor.hh" #include "signals.hh" #include "gc-small-vector.hh" @@ -399,7 +399,7 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListInputAccessor::create(makeFSSourceAccessor(), {}, + ? ref(AllowListSourceAccessor::create(makeFSSourceAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" @@ -460,7 +460,7 @@ EvalState::EvalState( } /* Allow access to all paths in the search path. */ - if (rootFS.dynamic_pointer_cast()) + if (rootFS.dynamic_pointer_cast()) for (auto & i : lookupPath.elements) resolveLookupPathPath(i.path, true); @@ -480,13 +480,13 @@ EvalState::~EvalState() void EvalState::allowPath(const Path & path) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->allowPrefix(CanonPath(path)); } void EvalState::allowPath(const StorePath & storePath) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath))); } @@ -540,13 +540,13 @@ void EvalState::checkURI(const std::string & uri) /* If the URI is a path, then check it against allowedPaths as well. */ if (hasPrefix(uri, "/")) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->checkAccess(CanonPath(uri)); return; } if (hasPrefix(uri, "file://")) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->checkAccess(CanonPath(uri.substr(7))); return; } diff --git a/src/libexpr/flake/call-flake.nix b/src/libexpr/flake/call-flake.nix index d0ccb1e37..a411564df 100644 --- a/src/libexpr/flake/call-flake.nix +++ b/src/libexpr/flake/call-flake.nix @@ -4,7 +4,7 @@ lockFileStr: # A mapping of lock file node IDs to { sourceInfo, subdir } attrsets, -# with sourceInfo.outPath providing an InputAccessor to a previously +# with sourceInfo.outPath providing an SourceAccessor to a previously # fetched tree. This is necessary for possibly unlocked inputs, in # particular the root input, but also --override-inputs pointing to # unlocked trees. diff --git a/src/libfetchers/filtering-input-accessor.cc b/src/libfetchers/filtering-source-accessor.cc similarity index 61% rename from src/libfetchers/filtering-input-accessor.cc rename to src/libfetchers/filtering-source-accessor.cc index d2b47b5e5..dfd9e536d 100644 --- a/src/libfetchers/filtering-input-accessor.cc +++ b/src/libfetchers/filtering-source-accessor.cc @@ -1,25 +1,25 @@ -#include "filtering-input-accessor.hh" +#include "filtering-source-accessor.hh" namespace nix { -std::string FilteringInputAccessor::readFile(const CanonPath & path) +std::string FilteringSourceAccessor::readFile(const CanonPath & path) { checkAccess(path); return next->readFile(prefix / path); } -bool FilteringInputAccessor::pathExists(const CanonPath & path) +bool FilteringSourceAccessor::pathExists(const CanonPath & path) { return isAllowed(path) && next->pathExists(prefix / path); } -std::optional FilteringInputAccessor::maybeLstat(const CanonPath & path) +std::optional FilteringSourceAccessor::maybeLstat(const CanonPath & path) { checkAccess(path); return next->maybeLstat(prefix / path); } -SourceAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath & path) +SourceAccessor::DirEntries FilteringSourceAccessor::readDirectory(const CanonPath & path) { checkAccess(path); DirEntries entries; @@ -30,18 +30,18 @@ SourceAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath return entries; } -std::string FilteringInputAccessor::readLink(const CanonPath & path) +std::string FilteringSourceAccessor::readLink(const CanonPath & path) { checkAccess(path); return next->readLink(prefix / path); } -std::string FilteringInputAccessor::showPath(const CanonPath & path) +std::string FilteringSourceAccessor::showPath(const CanonPath & path) { return displayPrefix + next->showPath(prefix / path) + displaySuffix; } -void FilteringInputAccessor::checkAccess(const CanonPath & path) +void FilteringSourceAccessor::checkAccess(const CanonPath & path) { if (!isAllowed(path)) throw makeNotAllowedError @@ -49,15 +49,15 @@ void FilteringInputAccessor::checkAccess(const CanonPath & path) : RestrictedPathError("access to path '%s' is forbidden", showPath(path)); } -struct AllowListInputAccessorImpl : AllowListInputAccessor +struct AllowListSourceAccessorImpl : AllowListSourceAccessor { std::set allowedPrefixes; - AllowListInputAccessorImpl( + AllowListSourceAccessorImpl( ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) - : AllowListInputAccessor(SourcePath(next), std::move(makeNotAllowedError)) + : AllowListSourceAccessor(SourcePath(next), std::move(makeNotAllowedError)) , allowedPrefixes(std::move(allowedPrefixes)) { } @@ -72,15 +72,15 @@ struct AllowListInputAccessorImpl : AllowListInputAccessor } }; -ref AllowListInputAccessor::create( +ref AllowListSourceAccessor::create( ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) { - return make_ref(next, std::move(allowedPrefixes), std::move(makeNotAllowedError)); + return make_ref(next, std::move(allowedPrefixes), std::move(makeNotAllowedError)); } -bool CachingFilteringInputAccessor::isAllowed(const CanonPath & path) +bool CachingFilteringSourceAccessor::isAllowed(const CanonPath & path) { auto i = cache.find(path); if (i != cache.end()) return i->second; diff --git a/src/libfetchers/filtering-input-accessor.hh b/src/libfetchers/filtering-source-accessor.hh similarity index 73% rename from src/libfetchers/filtering-input-accessor.hh rename to src/libfetchers/filtering-source-accessor.hh index ddf18eea4..9ec7bc21f 100644 --- a/src/libfetchers/filtering-input-accessor.hh +++ b/src/libfetchers/filtering-source-accessor.hh @@ -12,17 +12,17 @@ namespace nix { typedef std::function MakeNotAllowedError; /** - * An abstract wrapping `InputAccessor` that performs access + * An abstract wrapping `SourceAccessor` that performs access * control. Subclasses should override `isAllowed()` to implement an * access control policy. The error message is customized at construction. */ -struct FilteringInputAccessor : SourceAccessor +struct FilteringSourceAccessor : SourceAccessor { ref next; CanonPath prefix; MakeNotAllowedError makeNotAllowedError; - FilteringInputAccessor(const SourcePath & src, MakeNotAllowedError && makeNotAllowedError) + FilteringSourceAccessor(const SourcePath & src, MakeNotAllowedError && makeNotAllowedError) : next(src.accessor) , prefix(src.path) , makeNotAllowedError(std::move(makeNotAllowedError)) @@ -55,32 +55,32 @@ struct FilteringInputAccessor : SourceAccessor }; /** - * A wrapping `InputAccessor` that checks paths against a set of + * A wrapping `SourceAccessor` that checks paths against a set of * allowed prefixes. */ -struct AllowListInputAccessor : public FilteringInputAccessor +struct AllowListSourceAccessor : public FilteringSourceAccessor { /** * Grant access to the specified prefix. */ virtual void allowPrefix(CanonPath prefix) = 0; - static ref create( + static ref create( ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError); - using FilteringInputAccessor::FilteringInputAccessor; + using FilteringSourceAccessor::FilteringSourceAccessor; }; /** - * A wrapping `InputAccessor` mix-in where `isAllowed()` caches the result of virtual `isAllowedUncached()`. + * A wrapping `SourceAccessor` mix-in where `isAllowed()` caches the result of virtual `isAllowedUncached()`. */ -struct CachingFilteringInputAccessor : FilteringInputAccessor +struct CachingFilteringSourceAccessor : FilteringSourceAccessor { std::map cache; - using FilteringInputAccessor::FilteringInputAccessor; + using FilteringSourceAccessor::FilteringSourceAccessor; bool isAllowed(const CanonPath & path) override; diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index a91587cb4..160d1ac05 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -53,7 +53,7 @@ bool operator == (const git_oid & oid1, const git_oid & oid2) namespace nix { -struct GitInputAccessor; +struct GitSourceAccessor; // Some wrapper types that ensure that the git_*_free functions get called. template @@ -330,9 +330,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this } /** - * A 'GitInputAccessor' with no regard for export-ignore or any other transformations. + * A 'GitSourceAccessor' with no regard for export-ignore or any other transformations. */ - ref getRawAccessor(const Hash & rev); + ref getRawAccessor(const Hash & rev); ref getAccessor(const Hash & rev, bool exportIgnore) override; @@ -473,12 +473,12 @@ ref GitRepo::openRepo(const std::filesystem::path & path, bool create, /** * Raw git tree input accessor. */ -struct GitInputAccessor : SourceAccessor +struct GitSourceAccessor : SourceAccessor { ref repo; Tree root; - GitInputAccessor(ref repo_, const Hash & rev) + GitSourceAccessor(ref repo_, const Hash & rev) : repo(repo_) , root(peelObject(*repo, lookupObject(*repo, hashToOID(rev)).get(), GIT_OBJECT_TREE)) { @@ -702,12 +702,12 @@ struct GitInputAccessor : SourceAccessor } }; -struct GitExportIgnoreInputAccessor : CachingFilteringInputAccessor { +struct GitExportIgnoreSourceAccessor : CachingFilteringSourceAccessor { ref repo; std::optional rev; - GitExportIgnoreInputAccessor(ref repo, ref next, std::optional rev) - : CachingFilteringInputAccessor(next, [&](const CanonPath & path) { + GitExportIgnoreSourceAccessor(ref repo, ref next, std::optional rev) + : CachingFilteringSourceAccessor(next, [&](const CanonPath & path) { return RestrictedPathError(fmt("'%s' does not exist because it was fetched with exportIgnore enabled", path)); }) , repo(repo) @@ -918,18 +918,18 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink } }; -ref GitRepoImpl::getRawAccessor(const Hash & rev) +ref GitRepoImpl::getRawAccessor(const Hash & rev) { auto self = ref(shared_from_this()); - return make_ref(self, rev); + return make_ref(self, rev); } ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) { auto self = ref(shared_from_this()); - ref rawGitAccessor = getRawAccessor(rev); + ref rawGitAccessor = getRawAccessor(rev); if (exportIgnore) { - return make_ref(self, rawGitAccessor, rev); + return make_ref(self, rawGitAccessor, rev); } else { return rawGitAccessor; @@ -940,18 +940,18 @@ ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool export { auto self = ref(shared_from_this()); /* In case of an empty workdir, return an empty in-memory tree. We - cannot use AllowListInputAccessor because it would return an + cannot use AllowListSourceAccessor because it would return an error for the root (and we can't add the root to the allow-list since that would allow access to all its children). */ ref fileAccessor = wd.files.empty() ? makeEmptySourceAccessor() - : AllowListInputAccessor::create( + : AllowListSourceAccessor::create( makeFSSourceAccessor(path), std::set { wd.files }, std::move(makeNotAllowedError)).cast(); if (exportIgnore) - return make_ref(self, fileAccessor, std::nullopt); + return make_ref(self, fileAccessor, std::nullopt); else return fileAccessor; } diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index e264b2f63..29d799554 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -1,6 +1,6 @@ #pragma once -#include "filtering-input-accessor.hh" +#include "filtering-source-accessor.hh" #include "fs-sink.hh" namespace nix { diff --git a/src/libfetchers/mounted-input-accessor.hh b/src/libfetchers/mounted-input-accessor.hh deleted file mode 100644 index 74e040f44..000000000 --- a/src/libfetchers/mounted-input-accessor.hh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "source-accessor.hh" - -namespace nix { - -ref makeMountedInputAccessor(std::map> mounts); - -} diff --git a/src/libfetchers/mounted-input-accessor.cc b/src/libfetchers/mounted-source-accessor.cc similarity index 86% rename from src/libfetchers/mounted-input-accessor.cc rename to src/libfetchers/mounted-source-accessor.cc index 4d086c7ad..68f3a546b 100644 --- a/src/libfetchers/mounted-input-accessor.cc +++ b/src/libfetchers/mounted-source-accessor.cc @@ -1,12 +1,12 @@ -#include "mounted-input-accessor.hh" +#include "mounted-source-accessor.hh" namespace nix { -struct MountedInputAccessor : SourceAccessor +struct MountedSourceAccessor : SourceAccessor { std::map> mounts; - MountedInputAccessor(std::map> _mounts) + MountedSourceAccessor(std::map> _mounts) : mounts(std::move(_mounts)) { displayPrefix.clear(); @@ -71,9 +71,9 @@ struct MountedInputAccessor : SourceAccessor } }; -ref makeMountedInputAccessor(std::map> mounts) +ref makeMountedSourceAccessor(std::map> mounts) { - return make_ref(std::move(mounts)); + return make_ref(std::move(mounts)); } } diff --git a/src/libfetchers/mounted-source-accessor.hh b/src/libfetchers/mounted-source-accessor.hh new file mode 100644 index 000000000..45cbcb09a --- /dev/null +++ b/src/libfetchers/mounted-source-accessor.hh @@ -0,0 +1,9 @@ +#pragma once + +#include "source-accessor.hh" + +namespace nix { + +ref makeMountedSourceAccessor(std::map> mounts); + +} diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index c8fd295c0..46263c872 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -9,7 +9,7 @@ #include "pathlocks.hh" #include "processes.hh" #include "git.hh" -#include "mounted-input-accessor.hh" +#include "mounted-source-accessor.hh" #include "git-utils.hh" #include "logging.hh" #include "finally.hh" @@ -652,7 +652,7 @@ struct GitInputScheme : InputScheme if (!mounts.empty()) { mounts.insert_or_assign(CanonPath::root, accessor); - accessor = makeMountedInputAccessor(std::move(mounts)); + accessor = makeMountedSourceAccessor(std::move(mounts)); } } @@ -715,7 +715,7 @@ struct GitInputScheme : InputScheme } mounts.insert_or_assign(CanonPath::root, accessor); - accessor = makeMountedInputAccessor(std::move(mounts)); + accessor = makeMountedSourceAccessor(std::move(mounts)); } if (!repoInfo.workdirInfo.isDirty) { diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 548feddfd..b3fb9fe08 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -190,7 +190,7 @@ ref makeEmptySourceAccessor(); /** * Exception thrown when accessing a filtered path (see - * `FilteringInputAccessor`). + * `FilteringSourceAccessor`). */ MakeError(RestrictedPathError, Error); From 9bd1191fccb80a895af976e2445fb92167d9d2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 6 May 2024 15:10:18 +0200 Subject: [PATCH 044/910] libstore: check additionalSandboxProfile Make sure that `extraSandboxProfile` is set before we check whether it's empty or not (in the `sandbox=true` case). Also adds a test case for this. Co-Authored-By: Artemis Tosini Co-Authored-By: Eelco Dolstra --- .../unix/build/local-derivation-goal.cc | 8 +++---- tests/functional/extra-sandbox-profile.nix | 19 +++++++++++++++ tests/functional/extra-sandbox-profile.sh | 23 +++++++++++++++++++ tests/functional/local.mk | 1 + 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 tests/functional/extra-sandbox-profile.nix create mode 100644 tests/functional/extra-sandbox-profile.sh diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 72125cb82..0ebd22c75 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -177,6 +177,10 @@ void LocalDerivationGoal::killSandbox(bool getStats) void LocalDerivationGoal::tryLocalBuild() { +#if __APPLE__ + additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); +#endif + unsigned int curBuilds = worker.getNrLocalBuilds(); if (curBuilds >= settings.maxBuildJobs) { state = &DerivationGoal::tryToBuild; @@ -495,10 +499,6 @@ void LocalDerivationGoal::startBuilder() settings.thisSystem, concatStringsSep(", ", worker.store.systemFeatures)); -#if __APPLE__ - additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); -#endif - /* Create a temporary directory where the build will take place. */ tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); diff --git a/tests/functional/extra-sandbox-profile.nix b/tests/functional/extra-sandbox-profile.nix new file mode 100644 index 000000000..aa680b918 --- /dev/null +++ b/tests/functional/extra-sandbox-profile.nix @@ -0,0 +1,19 @@ +{ destFile, seed }: + +with import ./config.nix; + +mkDerivation { + name = "simple"; + __sandboxProfile = '' + # Allow writing any file in the filesystem + (allow file*) + ''; + inherit seed; + buildCommand = '' + ( + set -x + touch ${destFile} + touch $out + ) + ''; +} diff --git a/tests/functional/extra-sandbox-profile.sh b/tests/functional/extra-sandbox-profile.sh new file mode 100644 index 000000000..ac3ca036f --- /dev/null +++ b/tests/functional/extra-sandbox-profile.sh @@ -0,0 +1,23 @@ +source common.sh + +if [[ $(uname) != Darwin ]]; then skipTest "Need Darwin"; fi + +DEST_FILE="${TEST_ROOT}/foo" + +testSandboxProfile () ( + set -e + + sandboxMode="$1" + + rm -f "${DEST_FILE}" + nix-build --no-out-link ./extra-sandbox-profile.nix \ + --option sandbox "$sandboxMode" \ + --argstr seed "$RANDOM" \ + --argstr destFile "${DEST_FILE}" + + ls -l "${DEST_FILE}" +) + +testSandboxProfile "false" +expectStderr 2 testSandboxProfile "true" +testSandboxProfile "relaxed" diff --git a/tests/functional/local.mk b/tests/functional/local.mk index ca9837d32..65ab20f9a 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -130,6 +130,7 @@ nix_tests = \ nested-sandboxing.sh \ impure-env.sh \ debugger.sh \ + extra-sandbox-profile.sh \ help.sh ifeq ($(HAVE_LIBCPUID), 1) From 020edac1ca3de964e36bda2841e47433f0f9db6c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 14:58:10 +0200 Subject: [PATCH 045/910] doc/values: Improve Path See https://github.com/NixOS/nix/issues/8738 for a more pointed criticism of absolute paths. --- doc/manual/src/language/values.md | 49 ++++++++++++++++++------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index d4338e91e..2b5f9bf93 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -92,39 +92,48 @@ - Path - *Paths*, e.g., `/bin/sh` or `./builder.sh`. A path must contain at - least one slash to be recognised as such. For instance, `builder.sh` - is not a path: it's parsed as an expression that selects the - attribute `sh` from the variable `builder`. If the file name is - relative, i.e., if it does not begin with a slash, it is made - absolute at parse time relative to the [base directory](@docroot@/glossary.md#gloss-base-directory). - For instance, if a Nix expression in - `/foo/bar/bla.nix` refers to `../xyzzy/fnord.nix`, the absolute path - is `/foo/xyzzy/fnord.nix`. + *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - If the first component of a path is a `~`, it is interpreted as if - the rest of the path were relative to the user's home directory. - e.g. `~/foo` would be equivalent to `/home/edolstra/foo` for a user - whose home directory is `/home/edolstra`. + Paths are the preferred type for referring to local files. + This is thanks to the following properties: + - Path values are always in a canonical form, so that you are relieved from trailing slashes, `.` and `..`. + - Path literals are automatically resolved relative to the location of the Nix expression file that contains them. + - Path values are automatically copied into the Nix store when used in a string interpolation or concatenation. + - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the base directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. + A path literal must contain at least one slash to be recognised as such. + For instance, `builder.sh` is not a path: + it's parsed as an expression that selects the attribute `sh` from the variable `builder`. - Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. + Path literals may also refer to absolute paths by starting with a slash. + This is generally not recommended, because it makes the expression less portable. + In the case where a path literal is translated into an absolute path string for a configuration file, it is recommended to just use strings. + This avoids some confusion about whether files at that location will be used during evaluation, + and it avoids unintentional situations where some function might try to copy everything at the location into the store. + + If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. + For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. + Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. + + Paths can be used in [string interpolation] and string concatenation. + For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. + + Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. For example, assume you used a file path in an interpolated string during a `nix repl` session. - Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. + Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. [store path]: @docroot@/glossary.md#gloss-store-path - Paths can include [string interpolation] and can themselves be [interpolated in other expressions]. + Path literals can also include [string interpolation], besides being [interpolated into other expressions]. - [interpolated in other expressions]: ./string-interpolation.md#interpolated-expressions + [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. - `a.${foo}/b.${bar}` is a syntactically valid division operation. + `a.${foo}/b.${bar}` is a syntactically valid number division operation. `./a.${foo}/b.${bar}` is a path. - [Lookup paths](./constructs/lookup-path.md) such as `` resolve to path values. + [Lookup path](./constructs/lookup-path.md) literals such as `` also resolve to path values. - Boolean From 038573279c7ce17aeb5e51df0745599642f0be65 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 15:05:40 +0200 Subject: [PATCH 046/910] doc/values: Refer to base directory definition --- doc/manual/src/language/values.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 2b5f9bf93..d15d52e73 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -97,7 +97,7 @@ Paths are the preferred type for referring to local files. This is thanks to the following properties: - Path values are always in a canonical form, so that you are relieved from trailing slashes, `.` and `..`. - - Path literals are automatically resolved relative to the location of the Nix expression file that contains them. + - Path literals are automatically resolved [relative to the file](@docroot@/glossary.md#gloss-base-directory). - Path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. From 0eababb5f7eadc96bdca55239ae63f926b295c61 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 16:55:23 +0200 Subject: [PATCH 047/910] doc: Edit Co-authored-by: Valentin Gagarin --- doc/manual/src/language/values.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index d15d52e73..2dd52b379 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -94,11 +94,10 @@ *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - Paths are the preferred type for referring to local files. - This is thanks to the following properties: - - Path values are always in a canonical form, so that you are relieved from trailing slashes, `.` and `..`. - - Path literals are automatically resolved [relative to the file](@docroot@/glossary.md#gloss-base-directory). - - Path values are automatically copied into the Nix store when used in a string interpolation or concatenation. + Paths are suitable for referring to local files, and are often preferable over strings. + - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. + - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). + - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. A path literal must contain at least one slash to be recognised as such. @@ -106,10 +105,13 @@ it's parsed as an expression that selects the attribute `sh` from the variable `builder`. Path literals may also refer to absolute paths by starting with a slash. - This is generally not recommended, because it makes the expression less portable. - In the case where a path literal is translated into an absolute path string for a configuration file, it is recommended to just use strings. - This avoids some confusion about whether files at that location will be used during evaluation, - and it avoids unintentional situations where some function might try to copy everything at the location into the store. + + > **Note** + > + > Absolute paths make expressions less portable. + > In the case where a function translates a path literal into an absolute path string for a configuration file, it is recommended to write a string literal instead. + > This avoids some confusion about whether files at that location will be used during evaluation. + > It also avoids unintentional situations where some function might try to copy everything at the location into the store. If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. From eab2919119b8c41d70f62411d7e7dd972d6820da Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 19:05:42 +0200 Subject: [PATCH 048/910] Use SourcePath in more places Now that SourcePath uses a SourceAccessor instead of an InputAccessor, we can use it in function signatures instead of passing a SourceAccessor and CanonPath separately. --- src/libfetchers/fetch-to-store.cc | 4 ++-- src/libfetchers/unix/mercurial.cc | 4 ++-- src/libstore/binary-cache-store.cc | 7 +++--- src/libstore/binary-cache-store.hh | 3 +-- src/libstore/legacy-ssh-store.hh | 3 +-- src/libstore/store-api.cc | 20 +++++++---------- src/libstore/store-api.hh | 6 ++--- src/libstore/store-dir-config.hh | 5 +++-- .../unix/build/local-derivation-goal.cc | 16 +++++--------- src/libstore/unix/build/worker.cc | 2 +- src/libstore/unix/local-store.cc | 10 ++++----- src/libstore/unix/optimise-store.cc | 6 ++--- src/libutil/archive.cc | 7 +++--- src/libutil/file-content-address.cc | 17 +++++++------- src/libutil/file-content-address.hh | 8 ++++--- src/libutil/git.cc | 22 +++++++++---------- src/libutil/git.hh | 14 ++++++------ src/libutil/posix-source-accessor.cc | 5 +++-- src/libutil/posix-source-accessor.hh | 4 +++- src/libutil/source-path.hh | 5 +++++ src/nix-store/nix-store.cc | 8 +++---- src/nix/add-to-store.cc | 4 ++-- src/nix/hash.cc | 11 +++++----- src/nix/prefetch.cc | 3 +-- tests/unit/libutil/git.cc | 16 +++++++------- 25 files changed, 101 insertions(+), 109 deletions(-) diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index c105fe1fc..4fce6180d 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -42,9 +42,9 @@ StorePath fetchToStore( auto storePath = mode == FetchMode::DryRun ? store.computeStorePath( - name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2).first + name, path, method, HashAlgorithm::SHA256, {}, filter2).first : store.addToStore( - name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); + name, path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) fetchers::getCache()->add(store, *cacheKey, {}, storePath, true); diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 4d95f54f0..838cb41bf 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -212,7 +212,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), - *makeFSSourceAccessor(), CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, filter); @@ -318,7 +318,7 @@ struct MercurialInputScheme : InputScheme deletePath(tmpDir + "/.hg_archival.txt"); - auto storePath = store->addToStore(name, *makeFSSourceAccessor(), CanonPath { tmpDir }); + auto storePath = store->addToStore(name, {makeFSSourceAccessor(), CanonPath(tmpDir)}); Attrs infoAttrs({ {"rev", input.getRev()->gitRev()}, diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 97b6ec052..5153ca64f 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -442,8 +442,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath, StorePath BinaryCacheStore::addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, @@ -454,10 +453,10 @@ StorePath BinaryCacheStore::addToStore( non-recursive+sha256 so we can just use the default implementation of this method in terms of addToStoreFromDump. */ - auto h = hashPath(accessor, path, method.getFileIngestionMethod(), hashAlgo, filter); + auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); auto source = sinkToSource([&](Sink & sink) { - accessor.dumpPath(path, sink, filter); + path.dumpPath(sink, filter); }); return addToStoreCommon(*source, repair, CheckSigs, [&](HashResult nar) { ValidPathInfo info { diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh index 7c2828309..695bc9252 100644 --- a/src/libstore/binary-cache-store.hh +++ b/src/libstore/binary-cache-store.hh @@ -133,8 +133,7 @@ public: StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index ca2f115d2..343823693 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -60,8 +60,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 118e5de9f..008cc11e7 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -167,14 +167,13 @@ StorePath StoreDirConfig::makeFixedOutputPathFromCA(std::string_view name, const std::pair StoreDirConfig::computeStorePath( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, PathFilter & filter) const { - auto h = hashPath(accessor, path, method.getFileIngestionMethod(), hashAlgo, filter); + auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); return { makeFixedOutputPathFromCA( name, @@ -192,8 +191,7 @@ std::pair StoreDirConfig::computeStorePath( StorePath Store::addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, @@ -214,7 +212,7 @@ StorePath Store::addToStore( break; } auto source = sinkToSource([&](Sink & sink) { - dumpPath(accessor, path, sink, fsm, filter); + dumpPath(path, sink, fsm, filter); }); return addToStoreFromDump(*source, name, fsm, method, hashAlgo, references, repair); } @@ -343,8 +341,7 @@ digraph graphname { */ ValidPathInfo Store::addToStoreSlow( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & srcPath, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, std::optional expectedCAHash) @@ -366,7 +363,7 @@ ValidPathInfo Store::addToStoreSlow( srcPath. The fact that we use scratchpadSink as a temporary buffer here is an implementation detail. */ auto fileSource = sinkToSource([&](Sink & scratchpadSink) { - accessor.dumpPath(srcPath, scratchpadSink); + srcPath.dumpPath(scratchpadSink); }); /* tapped provides the same data as fileSource, but we also write all the @@ -389,13 +386,12 @@ ValidPathInfo Store::addToStoreSlow( auto hash = method == FileIngestionMethod::Recursive && hashAlgo == HashAlgorithm::SHA256 ? narHash : method == FileIngestionMethod::Git - ? git::dumpHash(hashAlgo, accessor, srcPath).hash + ? git::dumpHash(hashAlgo, srcPath).hash : caHashSink.finish().first; if (expectedCAHash && expectedCAHash != hash) throw Error("hash mismatch for '%s'", srcPath); - ValidPathInfo info { *this, name, @@ -412,7 +408,7 @@ ValidPathInfo Store::addToStoreSlow( if (!isValidPath(info.path)) { auto source = sinkToSource([&](Sink & scratchpadSink) { - accessor.dumpPath(srcPath, scratchpadSink); + srcPath.dumpPath(scratchpadSink); }); addToStore(info, *source); } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 5f683a211..ae8c22437 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -439,8 +439,7 @@ public: */ virtual StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), @@ -454,8 +453,7 @@ public: */ ValidPathInfo addToStoreSlow( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), diff --git a/src/libstore/store-dir-config.hh b/src/libstore/store-dir-config.hh index 7ca8c2665..643f8854d 100644 --- a/src/libstore/store-dir-config.hh +++ b/src/libstore/store-dir-config.hh @@ -13,6 +13,8 @@ namespace nix { +struct SourcePath; + MakeError(BadStorePath, Error); struct StoreDirConfig : public Config @@ -94,8 +96,7 @@ struct StoreDirConfig : public Config */ std::pair computeStorePath( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = {}, diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 0ebd22c75..24f6897c6 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1306,8 +1306,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual In StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & srcPath, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, @@ -2485,7 +2484,6 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() /* FIXME optimize and deduplicate with addToStore */ std::string oldHashPart { scratchPath->hashPart() }; auto got = [&]{ - PosixSourceAccessor accessor; auto fim = outputHash.method.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: @@ -2494,15 +2492,15 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() HashModuloSink caSink { outputHash.hashAlgo, oldHashPart }; auto fim = outputHash.method.getFileIngestionMethod(); dumpPath( - accessor, CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, caSink, (FileSerialisationMethod) fim); return caSink.finish().first; } case FileIngestionMethod::Git: { return git::dumpHash( - outputHash.hashAlgo, accessor, - CanonPath { tmpDir + "/tmp" }).hash; + outputHash.hashAlgo, + {makeFSSourceAccessor(), CanonPath(tmpDir + "/tmp")}).hash; } } assert(false); @@ -2529,9 +2527,8 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() } { - PosixSourceAccessor accessor; HashResult narHashAndSize = hashPath( - accessor, CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); newInfo0.narHash = narHashAndSize.first; newInfo0.narSize = narHashAndSize.second; @@ -2553,9 +2550,8 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() std::string { scratchPath->hashPart() }, std::string { requiredFinalPath.hashPart() }); rewriteOutput(outputRewrites); - PosixSourceAccessor accessor; HashResult narHashAndSize = hashPath( - accessor, CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first }; newInfo0.narSize = narHashAndSize.second; diff --git a/src/libstore/unix/build/worker.cc b/src/libstore/unix/build/worker.cc index 815ded3d5..03fc280a4 100644 --- a/src/libstore/unix/build/worker.cc +++ b/src/libstore/unix/build/worker.cc @@ -530,7 +530,7 @@ bool Worker::pathContentsGood(const StorePath & path) res = false; else { Hash current = hashPath( - *store.getFSAccessor(), CanonPath { store.printStorePath(path) }, + {store.getFSAccessor(), CanonPath(store.printStorePath(path))}, FileIngestionMethod::Recursive, info->narHash.algo); Hash nullHash(HashAlgorithm::SHA256); res = info->narHash == nullHash || info->narHash == current; diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index 1593affd6..7b3ba1347 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1132,12 +1132,12 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, specified.hash.algo, std::string { info.path.hashPart() }, }; - dumpPath(*accessor, path, caSink, (FileSerialisationMethod) fim); + dumpPath({accessor, path}, caSink, (FileSerialisationMethod) fim); h = caSink.finish().first; break; } case FileIngestionMethod::Git: - h = git::dumpHash(specified.hash.algo, *accessor, path).hash; + h = git::dumpHash(specified.hash.algo, {accessor, path}).hash; break; } ContentAddress { @@ -1247,14 +1247,12 @@ StorePath LocalStore::addToStoreFromDump( auto [dumpHash, size] = hashSink->finish(); - PosixSourceAccessor accessor; - auto desc = ContentAddressWithReferences::fromParts( hashMethod, methodsMatch ? dumpHash : hashPath( - accessor, CanonPath { tempPath }, + {makeFSSourceAccessor(), CanonPath(tempPath)}, hashMethod.getFileIngestionMethod(), hashAlgo), { .others = references, @@ -1394,7 +1392,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) Path linkPath = linksDir + "/" + link.name; PosixSourceAccessor accessor; std::string hash = hashPath( - accessor, CanonPath { linkPath }, + {makeFSSourceAccessor(), CanonPath(linkPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); if (hash != link.name) { printError("link '%s' was modified! expected hash '%s', got '%s'", diff --git a/src/libstore/unix/optimise-store.cc b/src/libstore/unix/optimise-store.cc index daaaaf073..bedb77849 100644 --- a/src/libstore/unix/optimise-store.cc +++ b/src/libstore/unix/optimise-store.cc @@ -148,9 +148,8 @@ 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 = ({ - PosixSourceAccessor accessor; hashPath( - accessor, CanonPath { path }, + {make_ref(), CanonPath(path)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; }); debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true)); @@ -163,9 +162,8 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, auto stLink = lstat(linkPath); if (st.st_size != stLink.st_size || (repair && hash != ({ - PosixSourceAccessor accessor; hashPath( - accessor, CanonPath { linkPath }, + {make_ref(), CanonPath(linkPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; }))) { diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 351ee094b..04f777d00 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -8,6 +8,7 @@ #include "archive.hh" #include "config.hh" #include "posix-source-accessor.hh" +#include "source-path.hh" #include "file-system.hh" #include "signals.hh" @@ -110,9 +111,9 @@ void SourceAccessor::dumpPath( time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter) { - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(path); - accessor.dumpPath(canonPath, sink, filter); - return accessor.mtime; + auto path2 = PosixSourceAccessor::createAtRoot(path); + path2.dumpPath(sink, filter); + return path2.accessor.dynamic_pointer_cast()->mtime; } void dumpPath(const Path & path, Sink & sink, PathFilter & filter) diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 570247b9e..769042d00 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -1,6 +1,7 @@ #include "file-content-address.hh" #include "archive.hh" #include "git.hh" +#include "source-path.hh" namespace nix { @@ -68,17 +69,17 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method) void dumpPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, FileSerialisationMethod method, PathFilter & filter) { switch (method) { case FileSerialisationMethod::Flat: - accessor.readFile(path, sink); + path.readFile(sink); break; case FileSerialisationMethod::Recursive: - accessor.dumpPath(path, sink, filter); + path.dumpPath(sink, filter); break; } } @@ -101,27 +102,27 @@ void restorePath( HashResult hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileSerialisationMethod method, HashAlgorithm ha, PathFilter & filter) { HashSink sink { ha }; - dumpPath(accessor, path, sink, method, filter); + dumpPath(path, sink, method, filter); return sink.finish(); } Hash hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileIngestionMethod method, HashAlgorithm ht, PathFilter & filter) { switch (method) { case FileIngestionMethod::Flat: case FileIngestionMethod::Recursive: - return hashPath(accessor, path, (FileSerialisationMethod) method, ht, filter).first; + return hashPath(path, (FileSerialisationMethod) method, ht, filter).first; case FileIngestionMethod::Git: - return git::dumpHash(ht, accessor, path, filter).hash; + return git::dumpHash(ht, path, filter).hash; } assert(false); } diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index b361ab243..145a8fb1f 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -7,6 +7,8 @@ namespace nix { +struct SourcePath; + /** * An enumeration of the ways we can serialize file system * objects. @@ -45,7 +47,7 @@ std::string_view renderFileSerialisationMethod(FileSerialisationMethod method); * Dump a serialization of the given file system object. */ void dumpPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, FileSerialisationMethod method, PathFilter & filter = defaultPathFilter); @@ -72,7 +74,7 @@ void restorePath( * ``` */ HashResult hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileSerialisationMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter); @@ -138,7 +140,7 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method); * useful defined for a merkle format. */ Hash hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileIngestionMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter); diff --git a/src/libutil/git.cc b/src/libutil/git.cc index a60589baa..8c538c988 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -8,7 +8,6 @@ #include "signals.hh" #include "config.hh" #include "hash.hh" -#include "posix-source-accessor.hh" #include "git.hh" #include "serialise.hh" @@ -269,18 +268,18 @@ void dumpTree(const Tree & entries, Sink & sink, Mode dump( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, std::function hook, PathFilter & filter, const ExperimentalFeatureSettings & xpSettings) { - auto st = accessor.lstat(path); + auto st = path.lstat(); switch (st.type) { case SourceAccessor::tRegular: { - accessor.readFile(path, sink, [&](uint64_t size) { + path.readFile(sink, [&](uint64_t size) { dumpBlobPrefix(size, sink, xpSettings); }); return st.isExecutable @@ -291,9 +290,9 @@ Mode dump( case SourceAccessor::tDirectory: { Tree entries; - for (auto & [name, _] : accessor.readDirectory(path)) { + for (auto & [name, _] : path.readDirectory()) { auto child = path / name; - if (!filter(child.abs())) continue; + if (!filter(child.path.abs())) continue; auto entry = hook(child); @@ -309,7 +308,7 @@ Mode dump( case SourceAccessor::tSymlink: { - auto target = accessor.readLink(path); + auto target = path.readLink(); dumpBlobPrefix(target.size(), sink, xpSettings); sink(target); return Mode::Symlink; @@ -323,13 +322,14 @@ Mode dump( TreeEntry dumpHash( - HashAlgorithm ha, - SourceAccessor & accessor, const CanonPath & path, PathFilter & filter) + HashAlgorithm ha, + const SourcePath & path, + PathFilter & filter) { std::function hook; - hook = [&](const CanonPath & path) -> TreeEntry { + hook = [&](const SourcePath & path) -> TreeEntry { auto hashSink = HashSink(ha); - auto mode = dump(accessor, path, hashSink, hook, filter); + auto mode = dump(path, hashSink, hook, filter); auto hash = hashSink.finish().first; return { .mode = mode, diff --git a/src/libutil/git.hh b/src/libutil/git.hh index cfea48fbe..a65edb964 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -8,7 +8,7 @@ #include "types.hh" #include "serialise.hh" #include "hash.hh" -#include "source-accessor.hh" +#include "source-path.hh" #include "fs-sink.hh" namespace nix::git { @@ -125,7 +125,7 @@ std::optional convertMode(SourceAccessor::Type type); * Given a `Hash`, return a `SourceAccessor` and `CanonPath` pointing to * the file system object with that path. */ -using RestoreHook = std::pair(Hash); +using RestoreHook = SourcePath(Hash); /** * Wrapper around `parse` and `RestoreSink` @@ -157,10 +157,10 @@ void dumpTree( * Note that if the child is a directory, its child in must also be so * processed in order to compute this information. */ -using DumpHook = TreeEntry(const CanonPath & path); +using DumpHook = TreeEntry(const SourcePath & path); Mode dump( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, std::function hook, PathFilter & filter = defaultPathFilter, @@ -172,9 +172,9 @@ Mode dump( * A smaller wrapper around `dump`. */ TreeEntry dumpHash( - HashAlgorithm ha, - SourceAccessor & accessor, const CanonPath & path, - PathFilter & filter = defaultPathFilter); + HashAlgorithm ha, + const SourcePath & path, + PathFilter & filter = defaultPathFilter); /** * A line from the output of `git ls-remote --symref`. diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index f7dffb871..f3551db42 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -1,4 +1,5 @@ #include "posix-source-accessor.hh" +#include "source-path.hh" #include "signals.hh" #include "sync.hh" @@ -17,11 +18,11 @@ PosixSourceAccessor::PosixSourceAccessor() : PosixSourceAccessor(std::filesystem::path {}) { } -std::pair PosixSourceAccessor::createAtRoot(const std::filesystem::path & path) +SourcePath PosixSourceAccessor::createAtRoot(const std::filesystem::path & path) { std::filesystem::path path2 = absPath(path.string()); return { - PosixSourceAccessor { path2.root_path() }, + make_ref(path2.root_path()), CanonPath { path2.relative_path().string() }, }; } diff --git a/src/libutil/posix-source-accessor.hh b/src/libutil/posix-source-accessor.hh index 717c8f017..40f60bb54 100644 --- a/src/libutil/posix-source-accessor.hh +++ b/src/libutil/posix-source-accessor.hh @@ -4,6 +4,8 @@ namespace nix { +struct SourcePath; + /** * A source accessor that uses the Unix filesystem. */ @@ -53,7 +55,7 @@ struct PosixSourceAccessor : virtual SourceAccessor * and * [`std::filesystem::path::relative_path`](https://en.cppreference.com/w/cpp/filesystem/path/relative_path). */ - static std::pair createAtRoot(const std::filesystem::path & path); + static SourcePath createAtRoot(const std::filesystem::path & path); private: diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 7e4c3c65d..83ec6295d 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -41,6 +41,11 @@ struct SourcePath */ std::string readFile() const; + void readFile( + Sink & sink, + std::function sizeCallback = [](uint64_t size){}) const + { return accessor->readFile(path, sink, sizeCallback); } + /** * Return whether this `SourcePath` denotes a file (of any type) * that exists diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 719675cba..b23d99ad6 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -184,7 +184,7 @@ static void opAdd(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(i); cout << fmt("%s\n", store->printStorePath(store->addToStore( - std::string(baseNameOf(i)), accessor, canonPath))); + std::string(baseNameOf(i)), {accessor, canonPath}))); } } @@ -209,8 +209,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs) auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(i); std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow( baseNameOf(i), - accessor, - canonPath, + {accessor, canonPath}, method, hashAlgo).path)); } @@ -562,8 +561,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) #endif if (!hashGiven) { HashResult hash = hashPath( - *store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }, - + {store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); info->narHash = hash.first; info->narSize = hash.second; diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index 02154715f..af6743375 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -41,9 +41,9 @@ struct CmdAddToStore : MixDryRun, StoreCommand auto storePath = dryRun ? store->computeStorePath( - *namePart, accessor, path2, caMethod, hashAlgo, {}).first + *namePart, {accessor, path2}, caMethod, hashAlgo, {}).first : store->addToStoreSlow( - *namePart, accessor, path2, caMethod, hashAlgo, {}).path; + *namePart, {accessor, path2}, caMethod, hashAlgo, {}).path; logger->cout("%s", store->printStorePath(storePath)); } diff --git a/src/nix/hash.cc b/src/nix/hash.cc index f849bf0cf..f969886ea 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -87,30 +87,29 @@ struct CmdHashBase : Command return std::make_unique(hashAlgo); }; - auto [accessor_, canonPath] = PosixSourceAccessor::createAtRoot(path); - auto & accessor = accessor_; + auto path2 = PosixSourceAccessor::createAtRoot(path); Hash h { HashAlgorithm::SHA256 }; // throwaway def to appease C++ switch (mode) { case FileIngestionMethod::Flat: case FileIngestionMethod::Recursive: { auto hashSink = makeSink(); - dumpPath(accessor, canonPath, *hashSink, (FileSerialisationMethod) mode); + dumpPath(path2, *hashSink, (FileSerialisationMethod) mode); h = hashSink->finish().first; break; } case FileIngestionMethod::Git: { std::function hook; - hook = [&](const CanonPath & path) -> git::TreeEntry { + hook = [&](const SourcePath & path) -> git::TreeEntry { auto hashSink = makeSink(); - auto mode = dump(accessor, path, *hashSink, hook); + auto mode = dump(path, *hashSink, hook); auto hash = hashSink->finish().first; return { .mode = mode, .hash = hash, }; }; - h = hook(canonPath).hash; + h = hook(path2).hash; break; } } diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index f905cabef..6c2eb5aaf 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -125,9 +125,8 @@ std::tuple prefetchFile( Activity act(*logger, lvlChatty, actUnknown, fmt("adding '%s' to the store", url)); - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(tmpFile); auto info = store->addToStoreSlow( - *name, accessor, canonPath, + *name, PosixSourceAccessor::createAtRoot(tmpFile), ingestionMethod, hashAlgo, {}, expectedHash); storePath = info.path; assert(info.ca); diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index 4f92488d6..ff934c117 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -154,8 +154,8 @@ TEST_F(GitTest, tree_write) { TEST_F(GitTest, both_roundrip) { using File = MemorySourceAccessor::File; - MemorySourceAccessor files; - files.root = File::Directory { + auto files = make_ref(); + files->root = File::Directory { .contents { { "foo", @@ -189,12 +189,12 @@ TEST_F(GitTest, both_roundrip) { std::map cas; std::function dumpHook; - dumpHook = [&](const CanonPath & path) { + dumpHook = [&](const SourcePath & path) { StringSink s; HashSink hashSink { HashAlgorithm::SHA1 }; TeeSink s2 { s, hashSink }; auto mode = dump( - files, path, s2, dumpHook, + path, s2, dumpHook, defaultPathFilter, mockXpSettings); auto hash = hashSink.finish().first; cas.insert_or_assign(hash, std::move(s.s)); @@ -204,11 +204,11 @@ TEST_F(GitTest, both_roundrip) { }; }; - auto root = dumpHook(CanonPath::root); + auto root = dumpHook({files}); - MemorySourceAccessor files2; + auto files2 = make_ref(); - MemorySink sinkFiles2 { files2 }; + MemorySink sinkFiles2 { *files2 }; std::function mkSinkHook; mkSinkHook = [&](auto prefix, auto & hash, auto blobMode) { @@ -229,7 +229,7 @@ TEST_F(GitTest, both_roundrip) { mkSinkHook("", root.hash, BlobMode::Regular); - ASSERT_EQ(files, files2); + ASSERT_EQ(*files, *files2); } TEST(GitLsRemote, parseSymrefLineWithReference) { From ef28c7329c68c8e792400c9e9dec65bd4c20584d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 19:16:52 +0200 Subject: [PATCH 049/910] Rename makeFSSourceAccessor -> getFSSourceAccessor() This makes it clearer that it returns a shared accessor object. --- src/libcmd/installables.cc | 2 +- src/libexpr/eval.cc | 4 ++-- src/libfetchers/unix/mercurial.cc | 4 ++-- src/libstore/unix/build/local-derivation-goal.cc | 8 ++++---- src/libstore/unix/local-store.cc | 4 ++-- src/libutil/posix-source-accessor.cc | 2 +- src/libutil/source-accessor.hh | 7 +++++-- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 0d42a62cc..43e312540 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -146,7 +146,7 @@ MixFlakeOptions::MixFlakeOptions() .category = category, .labels = {"flake-lock-path"}, .handler = {[&](std::string lockFilePath) { - lockFlags.referenceLockFilePath = {makeFSSourceAccessor(), CanonPath(absPath(lockFilePath))}; + lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))}; }}, .completer = completePath }); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ad6cdc6d2..d7e3a2cdb 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -399,14 +399,14 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListSourceAccessor::create(makeFSSourceAccessor(), {}, + ? ref(AllowListSourceAccessor::create(getFSSourceAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" : "in restricted mode"; throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); })) - : makeFSSourceAccessor()) + : getFSSourceAccessor()) , corepkgsFS(make_ref()) , internalFS(make_ref()) , derivationInternal{corepkgsFS->addFile( diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 838cb41bf..049faffa8 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -212,7 +212,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, filter); @@ -318,7 +318,7 @@ struct MercurialInputScheme : InputScheme deletePath(tmpDir + "/.hg_archival.txt"); - auto storePath = store->addToStore(name, {makeFSSourceAccessor(), CanonPath(tmpDir)}); + auto storePath = store->addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir)}); Attrs infoAttrs({ {"rev", input.getRev()->gitRev()}, diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 24f6897c6..3b010350d 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -2492,7 +2492,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() HashModuloSink caSink { outputHash.hashAlgo, oldHashPart }; auto fim = outputHash.method.getFileIngestionMethod(); dumpPath( - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, caSink, (FileSerialisationMethod) fim); return caSink.finish().first; @@ -2500,7 +2500,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() case FileIngestionMethod::Git: { return git::dumpHash( outputHash.hashAlgo, - {makeFSSourceAccessor(), CanonPath(tmpDir + "/tmp")}).hash; + {getFSSourceAccessor(), CanonPath(tmpDir + "/tmp")}).hash; } } assert(false); @@ -2528,7 +2528,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() { HashResult narHashAndSize = hashPath( - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); newInfo0.narHash = narHashAndSize.first; newInfo0.narSize = narHashAndSize.second; @@ -2551,7 +2551,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() std::string { requiredFinalPath.hashPart() }); rewriteOutput(outputRewrites); HashResult narHashAndSize = hashPath( - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first }; newInfo0.narSize = narHashAndSize.second; diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index 7b3ba1347..a3de523a3 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1252,7 +1252,7 @@ StorePath LocalStore::addToStoreFromDump( methodsMatch ? dumpHash : hashPath( - {makeFSSourceAccessor(), CanonPath(tempPath)}, + {getFSSourceAccessor(), CanonPath(tempPath)}, hashMethod.getFileIngestionMethod(), hashAlgo), { .others = references, @@ -1392,7 +1392,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) Path linkPath = linksDir + "/" + link.name; PosixSourceAccessor accessor; std::string hash = hashPath( - {makeFSSourceAccessor(), CanonPath(linkPath)}, + {getFSSourceAccessor(), CanonPath(linkPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); if (hash != link.name) { printError("link '%s' was modified! expected hash '%s', got '%s'", diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index f3551db42..e9c554939 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -167,7 +167,7 @@ void PosixSourceAccessor::assertNoSymlinks(CanonPath path) } } -ref makeFSSourceAccessor() +ref getFSSourceAccessor() { static auto rootFS = make_ref(); return rootFS; diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index b3fb9fe08..d7fb0af5f 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -197,10 +197,13 @@ MakeError(RestrictedPathError, Error); /** * Return an accessor for the root filesystem. */ -ref makeFSSourceAccessor(); +ref getFSSourceAccessor(); /** - * Return an accessor for the filesystem rooted at `root`. + * Construct an accessor for the filesystem rooted at `root`. Note + * that it is not possible to escape `root` by appending `..` path + * elements, and that absolute symlinks are resolved relative to + * `root`. */ ref makeFSSourceAccessor(std::filesystem::path root); From 5e189025ca3d1f7bc629b3ff41d104f1e9d920c2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 6 May 2024 13:38:21 -0400 Subject: [PATCH 050/910] Fix build failure with clang A slight issue with feb1d10f60a2b9ae9cce48817b6a209a7ab007c8. --- src/libfetchers/fetchers.hh | 2 ++ tests/unit/libfetchers/public-key.cc | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 42b184393..551be9a1f 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -230,6 +230,8 @@ struct PublicKey { std::string type = "ssh-ed25519"; std::string key; + + auto operator <=>(const PublicKey &) const = default; }; std::string publicKeys_to_string(const std::vector&); diff --git a/tests/unit/libfetchers/public-key.cc b/tests/unit/libfetchers/public-key.cc index 7b763bb05..8a639da9f 100644 --- a/tests/unit/libfetchers/public-key.cc +++ b/tests/unit/libfetchers/public-key.cc @@ -22,7 +22,7 @@ public: TEST_F(FIXTURE, PublicKey_ ## NAME ## _from_json) { \ readTest(#NAME ".json", [&](const auto & encoded_) { \ fetchers::PublicKey expected { VAL }; \ - auto got = nlohmann::json::parse(encoded_); \ + fetchers::PublicKey got = nlohmann::json::parse(encoded_); \ ASSERT_EQ(got, expected); \ }); \ } \ @@ -47,7 +47,7 @@ TEST_F(PublicKeyTest, PublicKey_noRoundTrip_from_json) { readTest("noRoundTrip.json", [&](const auto & encoded_) { fetchers::PublicKey expected = { .type = "ssh-ed25519", .key = "ABCDE" }; fetchers::PublicKey got = nlohmann::json::parse(encoded_); - ASSERT_EQ(got, nlohmann::json(expected)); + ASSERT_EQ(got, expected); }); } From b7eb26e362c2b963b7300316d37c618f07505ccd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 20:00:44 +0200 Subject: [PATCH 051/910] Fix perl build --- perl/lib/Nix/Store.xs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index 1d56e0127..ee211ef64 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -256,9 +256,8 @@ SV * hashPath(char * algo, int base32, char * path) PPCODE: try { - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(path); Hash h = hashPath( - accessor, canonPath, + PosixSourceAccessor::createAtRoot(path), FileIngestionMethod::Recursive, parseHashAlgo(algo)); auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); @@ -336,10 +335,9 @@ StoreWrapper::addToStore(char * srcPath, int recursive, char * algo) PPCODE: try { auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(srcPath); auto path = THIS->store->addToStore( std::string(baseNameOf(srcPath)), - accessor, canonPath, + PosixSourceAccessor::createAtRoot(srcPath), method, parseHashAlgo(algo)); XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(path).c_str(), 0))); } catch (Error & e) { From c7216a416fb5ad902775520c18b3b5e3cf49fbe0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 21:11:41 +0200 Subject: [PATCH 052/910] Update src/libfetchers/cache.hh Co-authored-by: Robert Hensing --- src/libfetchers/cache.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 1a72162d7..4d834fe0c 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -16,7 +16,7 @@ struct Cache /** * A domain is a partition of the key/value cache for a particular - * purpose, e.g. "Git revision to revcount". + * purpose, e.g. git revision to revcount. */ using Domain = std::string_view; From b4950404ba7c8f14f5892bd1327b035590c2f7bc Mon Sep 17 00:00:00 2001 From: ramboman Date: Mon, 6 May 2024 19:39:22 +0000 Subject: [PATCH 053/910] Honor the same set of proxy environment variables (#10611) Different parts of the project honor different sets of proxy environment variables. With this commit all parts of the project will honor the same set of proxy environment variables. --------- Co-authored-by: Your Name Co-authored-by: John Ericson --- doc/manual/src/installation/env-variables.md | 3 +- scripts/install-systemd-multi-user.sh | 2 +- src/libcmd/network-proxy.cc | 45 ++++++++++++++++++++ src/libcmd/network-proxy.hh | 22 ++++++++++ src/libexpr/fetchurl.nix | 1 + src/nix-build/nix-build.cc | 3 +- src/nix/main.cc | 24 +---------- tests/nixos/nss-preload.nix | 1 + 8 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 src/libcmd/network-proxy.cc create mode 100644 src/libcmd/network-proxy.hh diff --git a/doc/manual/src/installation/env-variables.md b/doc/manual/src/installation/env-variables.md index db98f52ff..035090421 100644 --- a/doc/manual/src/installation/env-variables.md +++ b/doc/manual/src/installation/env-variables.md @@ -53,7 +53,8 @@ ssl-cert-file = /etc/ssl/my-certificate-bundle.crt The Nix installer has special handling for these proxy-related environment variables: `http_proxy`, `https_proxy`, `ftp_proxy`, -`no_proxy`, `HTTP_PROXY`, `HTTPS_PROXY`, `FTP_PROXY`, `NO_PROXY`. +`all_proxy`, `no_proxy`, `HTTP_PROXY`, `HTTPS_PROXY`, `FTP_PROXY`, +`ALL_PROXY`, `NO_PROXY`. If any of these variables are set when running the Nix installer, then the installer will create an override file at diff --git a/scripts/install-systemd-multi-user.sh b/scripts/install-systemd-multi-user.sh index 202a9bb54..a62ed7e3a 100755 --- a/scripts/install-systemd-multi-user.sh +++ b/scripts/install-systemd-multi-user.sh @@ -35,7 +35,7 @@ escape_systemd_env() { # Gather all non-empty proxy environment variables into a string create_systemd_proxy_env() { - vars="http_proxy https_proxy ftp_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY" + vars="http_proxy https_proxy ftp_proxy all_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY" for v in $vars; do if [ "x${!v:-}" != "x" ]; then echo "Environment=${v}=$(escape_systemd_env ${!v})" diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc new file mode 100644 index 000000000..633b2c005 --- /dev/null +++ b/src/libcmd/network-proxy.cc @@ -0,0 +1,45 @@ +#include "network-proxy.hh" + +#include +#include + +#include "environment-variables.hh" + +namespace nix { + +static const StringSet lowercaseVariables{"http_proxy", "https_proxy", "ftp_proxy", "all_proxy", "no_proxy"}; + +static StringSet getAllVariables() +{ + StringSet variables = lowercaseVariables; + for (const auto & variable : lowercaseVariables) { + variables.insert(boost::to_upper_copy(variable)); + } + return variables; +} + +const StringSet networkProxyVariables = getAllVariables(); + +static StringSet getExcludingNoProxyVariables() +{ + static const StringSet excludeVariables{"no_proxy", "NO_PROXY"}; + StringSet variables; + std::set_difference( + networkProxyVariables.begin(), networkProxyVariables.end(), excludeVariables.begin(), excludeVariables.end(), + std::inserter(variables, variables.begin())); + return variables; +} + +static const StringSet excludingNoProxyVariables = getExcludingNoProxyVariables(); + +bool haveNetworkProxyConnection() +{ + for (const auto & variable : excludingNoProxyVariables) { + if (getEnv(variable).has_value()) { + return true; + } + } + return false; +} + +} diff --git a/src/libcmd/network-proxy.hh b/src/libcmd/network-proxy.hh new file mode 100644 index 000000000..0b6856acb --- /dev/null +++ b/src/libcmd/network-proxy.hh @@ -0,0 +1,22 @@ +#pragma once +///@file + +#include "types.hh" + +namespace nix { + +/** + * Environment variables relating to network proxying. These are used by + * a few misc commands. + * + * See the Environment section of https://curl.se/docs/manpage.html for details. + */ +extern const StringSet networkProxyVariables; + +/** + * Heuristically check if there is a proxy connection by checking for defined + * proxy variables. + */ +bool haveNetworkProxyConnection(); + +} diff --git a/src/libexpr/fetchurl.nix b/src/libexpr/fetchurl.nix index 9d1b61d7f..aef4058fb 100644 --- a/src/libexpr/fetchurl.nix +++ b/src/libexpr/fetchurl.nix @@ -34,6 +34,7 @@ derivation ({ # derivation like fetchurl is allowed to do so since its result is # by definition pure. "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" + "HTTP_PROXY" "HTTPS_PROXY" "FTP_PROXY" "ALL_PROXY" "NO_PROXY" ]; # To make "nix-prefetch-url" work. diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30ebc9498..46533b34b 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -25,6 +25,7 @@ #include "attr-path.hh" #include "legacy.hh" #include "users.hh" +#include "network-proxy.hh" using namespace nix; using namespace std::string_literals; @@ -121,8 +122,8 @@ static void main_nix_build(int argc, char * * argv) "HOME", "XDG_RUNTIME_DIR", "USER", "LOGNAME", "DISPLAY", "WAYLAND_DISPLAY", "WAYLAND_SOCKET", "PATH", "TERM", "IN_NIX_SHELL", "NIX_SHELL_PRESERVE_PROMPT", "TZ", "PAGER", "NIX_BUILD_SHELL", "SHLVL", - "http_proxy", "https_proxy", "ftp_proxy", "all_proxy", "no_proxy" }; + keepVars.insert(networkProxyVariables.begin(), networkProxyVariables.end()); Strings args; for (int i = 1; i < argc; ++i) diff --git a/src/nix/main.cc b/src/nix/main.cc index 8ea2f7748..bc13a4df5 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -17,6 +17,7 @@ #include "memory-source-accessor.hh" #include "terminal.hh" #include "users.hh" +#include "network-proxy.hh" #include #include @@ -41,27 +42,6 @@ void chrootHelper(int argc, char * * argv); namespace nix { -#ifdef _WIN32 -[[maybe_unused]] -#endif -static bool haveProxyEnvironmentVariables() -{ - static const std::vector proxyVariables = { - "http_proxy", - "https_proxy", - "ftp_proxy", - "HTTP_PROXY", - "HTTPS_PROXY", - "FTP_PROXY" - }; - for (auto & proxyVariable: proxyVariables) { - if (getEnv(proxyVariable).has_value()) { - return true; - } - } - return false; -} - /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { @@ -86,7 +66,7 @@ static bool haveInternet() } } - if (haveProxyEnvironmentVariables()) return true; + if (haveNetworkProxyConnection()) return true; return false; #else diff --git a/tests/nixos/nss-preload.nix b/tests/nixos/nss-preload.nix index 00505d114..610769c8d 100644 --- a/tests/nixos/nss-preload.nix +++ b/tests/nixos/nss-preload.nix @@ -32,6 +32,7 @@ let impureEnvVars = [ "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" + "HTTP_PROXY" "HTTPS_PROXY" "FTP_PROXY" "ALL_PROXY" "NO_PROXY" ]; urls = [ "http://example.com" ]; From c371070580afbb7f18972ed52e973f581496f881 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 7 May 2024 00:14:49 -0400 Subject: [PATCH 054/910] Use `std::filesystem` functions in more places This makes for shorter and more portable code. The only tricky part is catching exceptions: I just searched for near by `catch (Error &)` or `catch (SysError &)` and adjusted them to `catch (std::filesystem::filesystem_error &)` according to my human judgement. Good for windows portability; will help @siddhantk232 with his GSOC project. --- configure.ac | 1 - src/libcmd/repl.cc | 1 + src/libstore/builtins/buildenv.cc | 4 +- src/libstore/globals.cc | 4 +- src/libstore/profiles.cc | 2 + src/libstore/unix/gc.cc | 14 ++-- src/libstore/unix/local-store.hh | 2 +- src/libutil/file-system.cc | 83 ++++++------------- src/libutil/file-system.hh | 19 +---- src/libutil/linux/cgroup.cc | 2 +- src/libutil/posix-source-accessor.cc | 14 ++-- src/libutil/unix/file-descriptor.cc | 1 + .../nix-collect-garbage.cc | 10 +-- src/nix/config-check.cc | 3 +- 14 files changed, 61 insertions(+), 99 deletions(-) diff --git a/configure.ac b/configure.ac index 8f60bf4be..b2a5794b5 100644 --- a/configure.ac +++ b/configure.ac @@ -63,7 +63,6 @@ AC_SYS_LARGEFILE # Solaris-specific stuff. -AC_STRUCT_DIRENT_D_TYPE case "$host_os" in solaris*) # Solaris requires -lsocket -lnsl for network functions diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index bade1b538..d2f2a9648 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -264,6 +264,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) completions.insert(prev + dir + "/" + entry.name); } } catch (Error &) { + } catch (std::filesystem::filesystem_error &) { } } else if ((dot = cur.rfind('.')) == std::string::npos) { /* This is a variable name; look it up in the current scope. */ diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index e009f5b9d..ebd8f1348 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -21,8 +21,8 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, try { srcFiles = readDirectory(srcDir); - } catch (SysError & e) { - if (e.errNo == ENOTDIR) { + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::not_a_directory) { warn("not including '%s' in the user environment because it's not a directory", srcDir); return; } diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index dfe3044ea..f0096b981 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -348,8 +348,8 @@ void initPlugins() auto ents = readDirectory(pluginFile); for (const auto & ent : ents) pluginFiles.emplace_back(pluginFile + "/" + ent.name); - } catch (SysError & e) { - if (e.errNo != ENOTDIR) + } catch (std::filesystem::filesystem_error & e) { + if (e.code() != std::errc::not_a_directory) throw; pluginFiles.emplace_back(pluginFile); } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 73d3976f4..a0bb60410 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -338,6 +338,8 @@ Path getDefaultProfile() return absPath(readLink(profileLink), dirOf(profileLink)); } catch (Error &) { return profileLink; + } catch (std::filesystem::filesystem_error &) { + return profileLink; } } diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index be5794395..6677946aa 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -203,7 +203,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) } -void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) +void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, Roots & roots) { auto foundRoot = [&](const Path & path, const Path & target) { try { @@ -217,15 +217,15 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) try { - if (type == DT_UNKNOWN) + if (type == std::filesystem::file_type::unknown) type = getFileType(path); - if (type == DT_DIR) { + if (type == std::filesystem::file_type::directory) { for (auto & i : readDirectory(path)) findRoots(path + "/" + i.name, i.type, roots); } - else if (type == DT_LNK) { + else if (type == std::filesystem::file_type::symlink) { Path target = readLink(path); if (isInStore(target)) foundRoot(path, target); @@ -247,7 +247,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) } } - else if (type == DT_REG) { + else if (type == std::filesystem::file_type::regular) { auto storePath = maybeParseStorePath(storeDir + "/" + std::string(baseNameOf(path))); if (storePath && isValidPath(*storePath)) roots[std::move(*storePath)].emplace(path); @@ -268,8 +268,8 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) void LocalStore::findRootsNoTemp(Roots & roots, bool censor) { /* Process direct roots in {gcroots,profiles}. */ - findRoots(stateDir + "/" + gcRootsDir, DT_UNKNOWN, roots); - findRoots(stateDir + "/profiles", DT_UNKNOWN, roots); + findRoots(stateDir + "/" + gcRootsDir, std::filesystem::file_type::unknown, roots); + findRoots(stateDir + "/profiles", std::filesystem::file_type::unknown, roots); /* Add additional roots returned by different platforms-specific heuristics. This is typically used to add running programs to diff --git a/src/libstore/unix/local-store.hh b/src/libstore/unix/local-store.hh index 47d3c04bc..15bcc826f 100644 --- a/src/libstore/unix/local-store.hh +++ b/src/libstore/unix/local-store.hh @@ -371,7 +371,7 @@ private: PathSet queryValidPathsOld(); ValidPathInfo queryPathInfoOld(const Path & path); - void findRoots(const Path & path, unsigned char type, Roots & roots); + void findRoots(const Path & path, std::filesystem::file_type type, Roots & roots); void findRootsNoTemp(Roots & roots, bool censor); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index b03bb767b..0e68241fc 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -120,10 +120,10 @@ Path canonPath(PathView path, bool resolveSymlinks) Path dirOf(const PathView path) { - Path::size_type pos = path.rfind('/'); + Path::size_type pos = NativePathTrait::rfindPathSep(path); if (pos == path.npos) return "."; - return pos == 0 ? "/" : Path(path, 0, pos); + return fs::path{path}.parent_path().string(); } @@ -217,72 +217,36 @@ bool pathAccessible(const Path & path) Path readLink(const Path & path) { -#ifndef _WIN32 checkInterrupt(); - std::vector buf; - for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) { - buf.resize(bufSize); - ssize_t rlSize = readlink(path.c_str(), buf.data(), bufSize); - if (rlSize == -1) - if (errno == EINVAL) - throw Error("'%1%' is not a symlink", path); - else - throw SysError("reading symbolic link '%1%'", path); - else if (rlSize < bufSize) - return std::string(buf.data(), rlSize); - } -#else - // TODO modern Windows does in fact support symlinks - throw UnimplementedError("reading symbolic link '%1%'", path); -#endif + return fs::read_symlink(path).string(); } bool isLink(const Path & path) { - return getFileType(path) == DT_LNK; + return getFileType(path) == fs::file_type::symlink; } -DirEntries readDirectory(DIR *dir, const Path & path) +DirEntries readDirectory(const Path & path) { DirEntries entries; entries.reserve(64); - struct dirent * dirent; - while (errno = 0, dirent = readdir(dir)) { /* sic */ + for (auto & entry : fs::directory_iterator{path}) { checkInterrupt(); - std::string name = dirent->d_name; - if (name == "." || name == "..") continue; - entries.emplace_back(name, dirent->d_ino, -#ifdef HAVE_STRUCT_DIRENT_D_TYPE - dirent->d_type -#else - DT_UNKNOWN -#endif - ); + entries.emplace_back( + entry.path().filename().string(), + entry.symlink_status().type()); } - if (errno) throw SysError("reading directory '%1%'", path); return entries; } -DirEntries readDirectory(const Path & path) + +fs::file_type getFileType(const Path & path) { - AutoCloseDir dir(opendir(path.c_str())); - if (!dir) throw SysError("opening directory '%1%'", path); - - return readDirectory(dir.get(), path); -} - - -unsigned char getFileType(const Path & path) -{ - struct stat st = lstat(path); - if (S_ISDIR(st.st_mode)) return DT_DIR; - if (S_ISLNK(st.st_mode)) return DT_LNK; - if (S_ISREG(st.st_mode)) return DT_REG; - return DT_UNKNOWN; + return fs::symlink_status(path).type(); } @@ -432,8 +396,15 @@ static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytes AutoCloseDir dir(fdopendir(fd)); if (!dir) throw SysError("opening directory '%1%'", path); - for (auto & i : readDirectory(dir.get(), path)) - _deletePath(dirfd(dir.get()), path + "/" + i.name, bytesFreed); + + struct dirent * dirent; + while (errno = 0, dirent = readdir(dir.get())) { /* sic */ + checkInterrupt(); + std::string childName = dirent->d_name; + if (childName == "." || childName == "..") continue; + _deletePath(dirfd(dir.get()), path + "/" + childName, bytesFreed); + } + if (errno) throw SysError("reading directory '%1%'", path); } int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0; @@ -611,13 +582,7 @@ std::pair createTempFile(const Path & prefix) void createSymlink(const Path & target, const Path & link) { -#ifndef _WIN32 - if (symlink(target.c_str(), link.c_str())) - throw SysError("creating symlink from '%1%' to '%2%'", link, target); -#else - // TODO modern Windows does in fact support symlinks - throw UnimplementedError("createSymlink"); -#endif + fs::create_symlink(target, link); } void replaceSymlink(const Path & target, const Path & link) @@ -627,8 +592,8 @@ void replaceSymlink(const Path & target, const Path & link) try { createSymlink(target, tmp); - } catch (SysError & e) { - if (e.errNo == EEXIST) continue; + } catch (fs::filesystem_error & e) { + if (e.code() == std::errc::file_exists) continue; throw; } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 0c4e7cfdd..2ecf2881c 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -27,13 +27,6 @@ #include #include -#ifndef HAVE_STRUCT_DIRENT_D_TYPE -#define DT_UNKNOWN 0 -#define DT_REG 1 -#define DT_LNK 2 -#define DT_DIR 3 -#endif - /** * Polyfill for MinGW * @@ -132,20 +125,16 @@ bool isLink(const Path & path); struct DirEntry { std::string name; - ino_t ino; - /** - * one of DT_* - */ - unsigned char type; - DirEntry(std::string name, ino_t ino, unsigned char type) - : name(std::move(name)), ino(ino), type(type) { } + std::filesystem::file_type type; + DirEntry(std::string name, std::filesystem::file_type type) + : name(std::move(name)), type(type) { } }; typedef std::vector DirEntries; DirEntries readDirectory(const Path & path); -unsigned char getFileType(const Path & path); +std::filesystem::file_type getFileType(const Path & path); /** * Read the contents of a file into a string. diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 8b8942643..9234a4b2e 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -65,7 +65,7 @@ static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats) /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ for (auto & entry : readDirectory(cgroup)) { - if (entry.type != DT_DIR) continue; + if (entry.type != std::filesystem::file_type::directory) continue; destroyCgroup(cgroup + "/" + entry.name, false); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index e9c554939..a2559a0fb 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -134,13 +134,17 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & DirEntries res; for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { std::optional type; + // cannot exhaustively enumerate because implementation-specific + // additional file types are allowed. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch-enum" switch (entry.type) { - case DT_REG: type = Type::tRegular; break; - #ifndef _WIN32 - case DT_LNK: type = Type::tSymlink; break; - #endif - case DT_DIR: type = Type::tDirectory; break; + case std::filesystem::file_type::regular: type = Type::tRegular; break; + case std::filesystem::file_type::symlink: type = Type::tSymlink; break; + case std::filesystem::file_type::directory: type = Type::tDirectory; break; + default: type = tMisc; } +#pragma GCC diagnostic pop res.emplace(entry.name, type); } return res; diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 27c8d821b..068b37843 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -133,6 +133,7 @@ void closeMostFDs(const std::set & exceptions) } return; } catch (SysError &) { + } catch (std::filesystem::filesystem_error &) { } #endif diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index bb3f1bc6a..9dfbea3f1 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -31,14 +31,14 @@ void removeOldGenerations(std::string dir) checkInterrupt(); auto path = dir + "/" + i.name; - auto type = i.type == DT_UNKNOWN ? getFileType(path) : i.type; + auto type = i.type == std::filesystem::file_type::unknown ? getFileType(path) : i.type; - if (type == DT_LNK && canWrite) { + if (type == std::filesystem::file_type::symlink && canWrite) { std::string link; try { link = readLink(path); - } catch (SysError & e) { - if (e.errNo == ENOENT) continue; + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::no_such_file_or_directory) continue; throw; } if (link.find("link") != std::string::npos) { @@ -49,7 +49,7 @@ void removeOldGenerations(std::string dir) } else deleteOldGenerations(path, dryRun); } - } else if (type == DT_DIR) { + } else if (type == std::filesystem::file_type::directory) { removeOldGenerations(path); } } diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index f7c4cebec..f23e36fb5 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -107,7 +107,8 @@ struct CmdConfigCheck : StoreCommand if (profileDir.find("/profiles/") == std::string::npos) dirs.insert(dir); } - } catch (SystemError &) {} + } catch (SystemError &) { + } catch (std::filesystem::filesystem_error &) {} } if (!dirs.empty()) { From d641e8f71793187d442368d3ad28ad31a683f5ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 7 May 2024 11:25:07 +0200 Subject: [PATCH 055/910] builtin:fetchurl: Revert impureEnvVars attribute This was changed in #10611, which caused the derivation paths of anything using builtin:fetchurl to change (i.e. all of Nixpkgs). However, impureEnvVars doesn't actually do anything for builtin:fetchurl, so we can just set it to its historical value. --- src/libexpr/fetchurl.nix | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libexpr/fetchurl.nix b/src/libexpr/fetchurl.nix index aef4058fb..85a01d161 100644 --- a/src/libexpr/fetchurl.nix +++ b/src/libexpr/fetchurl.nix @@ -28,13 +28,9 @@ derivation ({ # No need to double the amount of network traffic preferLocalBuild = true; + # This attribute does nothing; it's here to avoid changing evaluation results. impureEnvVars = [ - # We borrow these environment variables from the caller to allow - # easy proxy configuration. This is impure, but a fixed-output - # derivation like fetchurl is allowed to do so since its result is - # by definition pure. "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" - "HTTP_PROXY" "HTTPS_PROXY" "FTP_PROXY" "ALL_PROXY" "NO_PROXY" ]; # To make "nix-prefetch-url" work. From a3c573950bb7f46f2f58592bc7ad4cfcc7b31cef Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 7 May 2024 11:29:33 -0400 Subject: [PATCH 056/910] Replace our `DirEntry` with `std::filesystem`'s --- src/libcmd/repl.cc | 5 +++-- src/libstore/builtins/buildenv.cc | 9 +++++---- src/libstore/globals.cc | 4 ++-- src/libstore/local-binary-cache-store.cc | 7 ++++--- src/libstore/profiles.cc | 8 ++++---- src/libstore/unix/builtins/unpack-channel.cc | 2 +- src/libstore/unix/gc.cc | 9 +++++---- src/libstore/unix/local-store.cc | 11 ++++++----- src/libstore/unix/posix-fs-canonicalise.cc | 4 ++-- src/libutil/file-system.cc | 8 +++----- src/libutil/file-system.hh | 12 +----------- src/libutil/linux/cgroup.cc | 12 ++++++------ src/libutil/posix-source-accessor.cc | 4 ++-- src/libutil/unix/file-descriptor.cc | 2 +- src/nix-collect-garbage/nix-collect-garbage.cc | 4 ++-- src/nix/flake.cc | 4 ++-- src/nix/prefetch.cc | 2 +- src/nix/run.cc | 4 ++-- 18 files changed, 52 insertions(+), 59 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index d2f2a9648..8a9155ab6 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -260,8 +260,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix) auto dir = std::string(cur, 0, slash); auto prefix2 = std::string(cur, slash + 1); for (auto & entry : readDirectory(dir == "" ? "/" : dir)) { - if (entry.name[0] != '.' && hasPrefix(entry.name, prefix2)) - completions.insert(prev + dir + "/" + entry.name); + auto name = entry.path().filename().string(); + if (name[0] != '.' && hasPrefix(name, prefix2)) + completions.insert(prev + entry.path().string()); } } catch (Error &) { } catch (std::filesystem::filesystem_error &) { diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index ebd8f1348..5fcdf6f15 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -17,7 +17,7 @@ struct State /* For each activated package, create symlinks */ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority) { - DirEntries srcFiles; + std::vector srcFiles; try { srcFiles = readDirectory(srcDir); @@ -30,11 +30,12 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, } for (const auto & ent : srcFiles) { - if (ent.name[0] == '.') + auto name = ent.path().filename(); + if (name.string()[0] == '.') /* not matched by glob */ continue; - auto srcFile = srcDir + "/" + ent.name; - auto dstFile = dstDir + "/" + ent.name; + auto srcFile = (std::filesystem::path{srcDir} / name).string(); + auto dstFile = (std::filesystem::path{dstDir} / name).string(); struct stat srcSt; try { diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index f0096b981..4df2880e6 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -343,11 +343,11 @@ void initPlugins() { assert(!settings.pluginFiles.pluginsLoaded); for (const auto & pluginFile : settings.pluginFiles.get()) { - Paths pluginFiles; + std::vector pluginFiles; try { auto ents = readDirectory(pluginFile); for (const auto & ent : ents) - pluginFiles.emplace_back(pluginFile + "/" + ent.name); + pluginFiles.emplace_back(ent.path()); } catch (std::filesystem::filesystem_error & e) { if (e.code() != std::errc::not_a_directory) throw; diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 5481dd762..3a48f4480 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -84,11 +84,12 @@ protected: StorePathSet paths; for (auto & entry : readDirectory(binaryCacheDir)) { - if (entry.name.size() != 40 || - !hasSuffix(entry.name, ".narinfo")) + auto name = entry.path().filename().string(); + if (name.size() != 40 || + !hasSuffix(name, ".narinfo")) continue; paths.insert(parseStorePath( - storeDir + "/" + entry.name.substr(0, entry.name.size() - 8) + storeDir + "/" + name.substr(0, name.size() - 8) + "-" + MissingName)); } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index a0bb60410..fa8026703 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -34,12 +34,12 @@ std::pair> findGenerations(Path pro { Generations gens; - Path profileDir = dirOf(profile); + std::filesystem::path profileDir = dirOf(profile); auto profileName = std::string(baseNameOf(profile)); - for (auto & i : readDirectory(profileDir)) { - if (auto n = parseName(profileName, i.name)) { - auto path = profileDir + "/" + i.name; + for (auto & i : readDirectory(profileDir.string())) { + if (auto n = parseName(profileName, i.path().filename().string())) { + auto path = i.path().string(); gens.push_back({ .number = *n, .path = path, diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index 6f68d4c0b..47bf5d49c 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -24,7 +24,7 @@ void builtinUnpackChannel( auto entries = readDirectory(out); if (entries.size() != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile((out + "/" + entries[0].name), (out + "/" + channelName)); + renameFile(entries[0].path().string(), (out + "/" + channelName)); } } diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index 6677946aa..c10ed5564 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -160,14 +160,15 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) /* Read the `temproots' directory for per-process temporary root files. */ for (auto & i : readDirectory(tempRootsDir)) { - if (i.name[0] == '.') { + auto name = i.path().filename().string(); + if (name[0] == '.') { // Ignore hidden files. Some package managers (notably portage) create // those to keep the directory alive. continue; } - Path path = tempRootsDir + "/" + i.name; + Path path = i.path(); - pid_t pid = std::stoi(i.name); + pid_t pid = std::stoi(name); debug("reading temporary root file '%1%'", path); AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)); @@ -222,7 +223,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R if (type == std::filesystem::file_type::directory) { for (auto & i : readDirectory(path)) - findRoots(path + "/" + i.name, i.type, roots); + findRoots(i.path().string(), i.symlink_status().type(), roots); } else if (type == std::filesystem::file_type::symlink) { diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index a3de523a3..7a47d1524 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1388,15 +1388,16 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) printInfo("checking link hashes..."); for (auto & link : readDirectory(linksDir)) { - printMsg(lvlTalkative, "checking contents of '%s'", link.name); - Path linkPath = linksDir + "/" + link.name; + auto name = link.path().filename(); + printMsg(lvlTalkative, "checking contents of '%s'", name); + Path linkPath = linksDir / name; PosixSourceAccessor accessor; std::string hash = hashPath( {getFSSourceAccessor(), CanonPath(linkPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); - if (hash != link.name) { + if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", - linkPath, link.name, hash); + linkPath, name, hash); if (repair) { if (unlink(linkPath.c_str()) == 0) printInfo("removed link '%s'", linkPath); @@ -1483,7 +1484,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair */ for (auto & i : readDirectory(realStoreDir)) { try { - storePathsInStoreDir.insert({i.name}); + storePathsInStoreDir.insert({i.path().filename().string()}); } catch (BadStorePath &) { } } diff --git a/src/libstore/unix/posix-fs-canonicalise.cc b/src/libstore/unix/posix-fs-canonicalise.cc index 8b29e90d4..916db49ac 100644 --- a/src/libstore/unix/posix-fs-canonicalise.cc +++ b/src/libstore/unix/posix-fs-canonicalise.cc @@ -136,9 +136,9 @@ static void canonicalisePathMetaData_( } if (S_ISDIR(st.st_mode)) { - DirEntries entries = readDirectory(path); + std::vector entries = readDirectory(path); for (auto & i : entries) - canonicalisePathMetaData_(path + "/" + i.name, uidRange, inodesSeen); + canonicalisePathMetaData_(i.path().string(), uidRange, inodesSeen); } } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 0e68241fc..b9abcade9 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -228,16 +228,14 @@ bool isLink(const Path & path) } -DirEntries readDirectory(const Path & path) +std::vector readDirectory(const Path & path) { - DirEntries entries; + std::vector entries; entries.reserve(64); for (auto & entry : fs::directory_iterator{path}) { checkInterrupt(); - entries.emplace_back( - entry.path().filename().string(), - entry.symlink_status().type()); + entries.push_back(std::move(entry)); } return entries; diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 2ecf2881c..2bf703276 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -122,17 +122,7 @@ bool isLink(const Path & path); * Read the contents of a directory. The entries `.` and `..` are * removed. */ -struct DirEntry -{ - std::string name; - std::filesystem::file_type type; - DirEntry(std::string name, std::filesystem::file_type type) - : name(std::move(name)), type(type) { } -}; - -typedef std::vector DirEntries; - -DirEntries readDirectory(const Path & path); +std::vector readDirectory(const Path & path); std::filesystem::file_type getFileType(const Path & path); diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 9234a4b2e..619ef7764 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -47,26 +47,26 @@ std::map getCgroups(const Path & cgroupFile) return cgroups; } -static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats) +static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool returnStats) { if (!pathExists(cgroup)) return {}; - auto procsFile = cgroup + "/cgroup.procs"; + auto procsFile = cgroup / "cgroup.procs"; if (!pathExists(procsFile)) throw Error("'%s' is not a cgroup", cgroup); /* Use the fast way to kill every process in a cgroup, if available. */ - auto killFile = cgroup + "/cgroup.kill"; + auto killFile = cgroup / "cgroup.kill"; if (pathExists(killFile)) writeFile(killFile, "1"); /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ for (auto & entry : readDirectory(cgroup)) { - if (entry.type != std::filesystem::file_type::directory) continue; - destroyCgroup(cgroup + "/" + entry.name, false); + if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue; + destroyCgroup(cgroup / entry.path().filename(), false); } int round = 1; @@ -111,7 +111,7 @@ static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats) CgroupStats stats; if (returnStats) { - auto cpustatPath = cgroup + "/cpu.stat"; + auto cpustatPath = cgroup / "cpu.stat"; if (pathExists(cpustatPath)) { for (auto & line : tokenizeString>(readFile(cpustatPath), "\n")) { diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index a2559a0fb..4ff61f6bb 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -138,14 +138,14 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & // additional file types are allowed. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" - switch (entry.type) { + switch (entry.symlink_status().type()) { case std::filesystem::file_type::regular: type = Type::tRegular; break; case std::filesystem::file_type::symlink: type = Type::tSymlink; break; case std::filesystem::file_type::directory: type = Type::tDirectory; break; default: type = tMisc; } #pragma GCC diagnostic pop - res.emplace(entry.name, type); + res.emplace(entry.path().filename().string(), type); } return res; } diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 068b37843..222d077e5 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -125,7 +125,7 @@ void closeMostFDs(const std::set & exceptions) #if __linux__ try { for (auto & s : readDirectory("/proc/self/fd")) { - auto fd = std::stoi(s.name); + auto fd = std::stoi(s.path().filename()); if (!exceptions.count(fd)) { debug("closing leaked FD %d", fd); close(fd); diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 9dfbea3f1..02a3a4b83 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -30,8 +30,8 @@ void removeOldGenerations(std::string dir) for (auto & i : readDirectory(dir)) { checkInterrupt(); - auto path = dir + "/" + i.name; - auto type = i.type == std::filesystem::file_type::unknown ? getFileType(path) : i.type; + auto path = i.path().string(); + auto type = i.symlink_status().type(); if (type == std::filesystem::file_type::symlink && canWrite) { std::string link; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d5987237f..9c1888aa0 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -867,8 +867,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand createDirs(to); for (auto & entry : readDirectory(from)) { - auto from2 = from + "/" + entry.name; - auto to2 = to + "/" + entry.name; + auto from2 = entry.path().string(); + auto to2 = to + "/" + entry.path().filename().string(); auto st = lstat(from2); if (S_ISDIR(st.st_mode)) copyDir(from2, to2); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 6c2eb5aaf..cff1c7988 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -117,7 +117,7 @@ std::tuple prefetchFile( that as the top-level. */ auto entries = readDirectory(unpacked); if (entries.size() == 1) - tmpFile = unpacked + "/" + entries[0].name; + tmpFile = entries[0].path().string(); else tmpFile = unpacked; } diff --git a/src/nix/run.cc b/src/nix/run.cc index 88821710d..9c559bdf6 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -249,8 +249,8 @@ void chrootHelper(int argc, char * * argv) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); for (auto entry : readDirectory("/")) { - auto src = "/" + entry.name; - Path dst = tmpDir + "/" + entry.name; + auto src = entry.path().string(); + Path dst = tmpDir + "/" + entry.path().filename().string(); if (pathExists(dst)) continue; auto st = lstat(src); if (S_ISDIR(st.st_mode)) { From 72a0d4b022b72a7f48a7784df04d50ec8bd874f5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 7 May 2024 13:57:39 -0400 Subject: [PATCH 057/910] Try to fix macOS Nixpkgs lib test failure Sometimes we read a directory with children we cannot stat. It's a pitty we even try to stat at all (wasteful) in the `DT_UNKNOWN` case, but at least this should get rid of the failure. --- src/libstore/unix/gc.cc | 8 +++++++ src/libutil/posix-source-accessor.cc | 31 ++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index c10ed5564..38cbf12b2 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -256,6 +256,14 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R } + catch (std::filesystem::filesystem_error & e) { + /* We only ignore permanent failures. */ + if (e.code() == std::errc::permission_denied || e.code() == std::errc::no_such_file_or_directory || e.code() == std::errc::not_a_directory) + printInfo("cannot read potential root '%1%'", path); + else + throw; + } + catch (SysError & e) { /* We only ignore permanent failures. */ if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR) diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 4ff61f6bb..aa13f4c56 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -133,18 +133,31 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & assertNoSymlinks(path); DirEntries res; for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { - std::optional type; - // cannot exhaustively enumerate because implementation-specific - // additional file types are allowed. + auto type = [&]() -> std::optional { + std::filesystem::file_type nativeType; + try { + nativeType = entry.symlink_status().type(); + } catch (std::filesystem::filesystem_error & e) { + // We cannot always stat the child. (Ideally there is no + // stat because the native directory entry has the type + // already, but this isn't always the case.) + if (e.code() == std::errc::permission_denied || e.code() == std::errc::operation_not_permitted) + return std::nullopt; + else throw; + } + + // cannot exhaustively enumerate because implementation-specific + // additional file types are allowed. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" - switch (entry.symlink_status().type()) { - case std::filesystem::file_type::regular: type = Type::tRegular; break; - case std::filesystem::file_type::symlink: type = Type::tSymlink; break; - case std::filesystem::file_type::directory: type = Type::tDirectory; break; - default: type = tMisc; - } + switch (nativeType) { + case std::filesystem::file_type::regular: return Type::tRegular; break; + case std::filesystem::file_type::symlink: return Type::tSymlink; break; + case std::filesystem::file_type::directory: return Type::tDirectory; break; + default: return tMisc; + } #pragma GCC diagnostic pop + }(); res.emplace(entry.path().filename().string(), type); } return res; From fcbc36cf78a1eb7be7458683da99a4dd08144a6f Mon Sep 17 00:00:00 2001 From: Siddhant Kumar Date: Wed, 8 May 2024 03:58:50 +0530 Subject: [PATCH 058/910] Use `std::filesystem::path` in more places (#10657) Progress on #9205 Co-Authored-By: John Ericson * Get rid of `PathNG`, just use `std::filesystem::path` --- src/libstore/ssh-store.cc | 2 +- src/libstore/ssh.cc | 6 +++--- src/libstore/unix/local-store.cc | 18 ++++++++-------- src/libstore/unix/local-store.hh | 2 +- src/libutil/experimental-features.cc | 2 +- src/libutil/file-path.hh | 32 +++++++++++++++++----------- src/libutil/file-system.cc | 27 +++++++++++------------ src/libutil/file-system.hh | 21 ++++++++++++------ src/libutil/unix/file-path.cc | 6 +++--- src/libutil/windows/file-path.cc | 16 +++++++------- src/nix-build/nix-build.cc | 12 +++++------ src/nix/develop.cc | 32 +++++++++++++++++----------- src/nix/prefetch.cc | 10 ++++----- 13 files changed, 104 insertions(+), 82 deletions(-) diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 0cf92b114..220d5d31b 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -108,7 +108,7 @@ struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfi { } - const std::string name() override { return "Experimental SSH Store with filesytem mounted"; } + const std::string name() override { return "Experimental SSH Store with filesystem mounted"; } std::string doc() override { diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 04f458279..7e730299a 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -31,11 +31,11 @@ void SSHMaster::addCommonSSHOpts(Strings & args) if (!keyFile.empty()) args.insert(args.end(), {"-i", keyFile}); if (!sshPublicHostKey.empty()) { - Path fileName = (Path) *state->tmpDir + "/host-key"; + std::filesystem::path fileName = state->tmpDir->path() / "host-key"; auto p = host.rfind("@"); std::string thost = p != std::string::npos ? std::string(host, p + 1) : host; - writeFile(fileName, thost + " " + base64Decode(sshPublicHostKey) + "\n"); - args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName}); + writeFile(fileName.string(), thost + " " + base64Decode(sshPublicHostKey) + "\n"); + args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName.string()}); } if (compress) args.push_back("-C"); diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index 7a47d1524..f33d92717 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1220,8 +1220,8 @@ StorePath LocalStore::addToStoreFromDump( } std::unique_ptr delTempDir; - Path tempPath; - Path tempDir; + std::filesystem::path tempPath; + std::filesystem::path tempDir; AutoCloseFD tempDirFd; bool methodsMatch = ContentAddressMethod(FileIngestionMethod(dumpMethod)) == hashMethod; @@ -1237,9 +1237,9 @@ StorePath LocalStore::addToStoreFromDump( std::tie(tempDir, tempDirFd) = createTempDirInStore(); delTempDir = std::make_unique(tempDir); - tempPath = tempDir + "/x"; + tempPath = tempDir / "x"; - restorePath(tempPath, bothSource, dumpMethod); + restorePath(tempPath.string(), bothSource, dumpMethod); dumpBuffer.reset(); dump = {}; @@ -1252,7 +1252,7 @@ StorePath LocalStore::addToStoreFromDump( methodsMatch ? dumpHash : hashPath( - {getFSSourceAccessor(), CanonPath(tempPath)}, + PosixSourceAccessor::createAtRoot(tempPath), hashMethod.getFileIngestionMethod(), hashAlgo), { .others = references, @@ -1295,7 +1295,7 @@ StorePath LocalStore::addToStoreFromDump( } } else { /* Move the temporary path we restored above. */ - moveFile(tempPath, realPath); + moveFile(tempPath.string(), realPath); } /* For computing the nar hash. In recursive SHA-256 mode, this @@ -1330,9 +1330,9 @@ StorePath LocalStore::addToStoreFromDump( /* Create a temporary directory in the store that won't be garbage-collected until the returned FD is closed. */ -std::pair LocalStore::createTempDirInStore() +std::pair LocalStore::createTempDirInStore() { - Path tmpDirFn; + std::filesystem::path tmpDirFn; AutoCloseFD tmpDirFd; bool lockedByUs = false; do { @@ -1345,7 +1345,7 @@ std::pair LocalStore::createTempDirInStore() continue; } lockedByUs = lockFile(tmpDirFd.get(), ltWrite, true); - } while (!pathExists(tmpDirFn) || !lockedByUs); + } while (!pathExists(tmpDirFn.string()) || !lockedByUs); return {tmpDirFn, std::move(tmpDirFd)}; } diff --git a/src/libstore/unix/local-store.hh b/src/libstore/unix/local-store.hh index 15bcc826f..2b6e2e25f 100644 --- a/src/libstore/unix/local-store.hh +++ b/src/libstore/unix/local-store.hh @@ -377,7 +377,7 @@ private: void findRuntimeRoots(Roots & roots, bool censor); - std::pair createTempDirInStore(); + std::pair createTempDirInStore(); typedef std::unordered_set InodeHash; diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 1e7469cad..9b7000f9f 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -282,7 +282,7 @@ constexpr std::array xpFeatureDetails .tag = Xp::MountedSSHStore, .name = "mounted-ssh-store", .description = R"( - Allow the use of the [`mounted SSH store`](@docroot@/command-ref/new-cli/nix3-help-stores.html#experimental-ssh-store-with-filesytem-mounted). + Allow the use of the [`mounted SSH store`](@docroot@/command-ref/new-cli/nix3-help-stores.html#experimental-ssh-store-with-filesystem-mounted). )", .trackingUrl = "https://github.com/NixOS/nix/milestone/43", }, diff --git a/src/libutil/file-path.hh b/src/libutil/file-path.hh index 6fb100125..6589c4060 100644 --- a/src/libutil/file-path.hh +++ b/src/libutil/file-path.hh @@ -13,9 +13,8 @@ namespace nix { * * @todo drop `NG` suffix and replace the ones in `types.hh`. */ -typedef std::filesystem::path PathNG; -typedef std::list PathsNG; -typedef std::set PathSetNG; +typedef std::list PathsNG; +typedef std::set PathSetNG; /** * Stop gap until `std::filesystem::path_view` from P1030R6 exists in a @@ -23,18 +22,18 @@ typedef std::set PathSetNG; * * @todo drop `NG` suffix and replace the one in `types.hh`. */ -struct PathViewNG : std::basic_string_view +struct PathViewNG : std::basic_string_view { - using string_view = std::basic_string_view; + using string_view = std::basic_string_view; using string_view::string_view; - PathViewNG(const PathNG & path) - : std::basic_string_view(path.native()) + PathViewNG(const std::filesystem::path & path) + : std::basic_string_view(path.native()) { } - PathViewNG(const PathNG::string_type & path) - : std::basic_string_view(path) + PathViewNG(const std::filesystem::path::string_type & path) + : std::basic_string_view(path) { } const string_view & native() const { return *this; } @@ -43,10 +42,19 @@ struct PathViewNG : std::basic_string_view std::string os_string_to_string(PathViewNG::string_view path); -PathNG::string_type string_to_os_string(std::string_view s); +std::filesystem::path::string_type string_to_os_string(std::string_view s); -std::optional maybePathNG(PathView path); +std::optional maybePath(PathView path); -PathNG pathNG(PathView path); +std::filesystem::path pathNG(PathView path); + +/** + * Create string literals with the native character width of paths + */ +#ifndef _WIN32 +# define PATHNG_LITERAL(s) s +#else +# define PATHNG_LITERAL(s) L ## s +#endif } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index b9abcade9..03f64edc7 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -228,9 +228,9 @@ bool isLink(const Path & path) } -std::vector readDirectory(const Path & path) +std::vector readDirectory(const Path & path) { - std::vector entries; + std::vector entries; entries.reserve(64); for (auto & entry : fs::directory_iterator{path}) { @@ -342,12 +342,12 @@ void syncParent(const Path & path) } -static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytesFreed) +static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & bytesFreed) { #ifndef _WIN32 checkInterrupt(); - std::string name(baseNameOf(path)); + std::string name(baseNameOf(path.native())); struct stat st; if (fstatat(parentfd, name.c_str(), &st, @@ -416,9 +416,9 @@ static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytes #endif } -static void _deletePath(const Path & path, uint64_t & bytesFreed) +static void _deletePath(const fs::path & path, uint64_t & bytesFreed) { - Path dir = dirOf(path); + Path dir = dirOf(path.string()); if (dir == "") dir = "/"; @@ -432,7 +432,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed) } -void deletePath(const Path & path) +void deletePath(const fs::path & path) { uint64_t dummy; deletePath(path, dummy); @@ -466,7 +466,7 @@ Paths createDirs(const Path & path) } -void deletePath(const Path & path, uint64_t & bytesFreed) +void deletePath(const fs::path & path, uint64_t & bytesFreed) { //Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path); bytesFreed = 0; @@ -478,7 +478,7 @@ void deletePath(const Path & path, uint64_t & bytesFreed) AutoDelete::AutoDelete() : del{false} {} -AutoDelete::AutoDelete(const std::string & p, bool recursive) : path(p) +AutoDelete::AutoDelete(const fs::path & p, bool recursive) : _path(p) { del = true; this->recursive = recursive; @@ -489,10 +489,9 @@ AutoDelete::~AutoDelete() try { if (del) { if (recursive) - deletePath(path); + deletePath(_path); else { - if (remove(path.c_str()) == -1) - throw SysError("cannot unlink '%1%'", path); + fs::remove(_path); } } } catch (...) { @@ -505,8 +504,8 @@ void AutoDelete::cancel() del = false; } -void AutoDelete::reset(const Path & p, bool recursive) { - path = p; +void AutoDelete::reset(const fs::path & p, bool recursive) { + _path = p; this->recursive = recursive; del = true; } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 2bf703276..40ed82f02 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -9,6 +9,7 @@ #include "error.hh" #include "logging.hh" #include "file-descriptor.hh" +#include "file-path.hh" #include #include @@ -149,9 +150,9 @@ void syncParent(const Path & path); * recursively. It's not an error if the path does not exist. The * second variant returns the number of bytes and blocks freed. */ -void deletePath(const Path & path); +void deletePath(const std::filesystem::path & path); -void deletePath(const Path & path, uint64_t & bytesFreed); +void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed); /** * Create a directory and all its parents, if necessary. Returns the @@ -197,17 +198,23 @@ void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); */ class AutoDelete { - Path path; + std::filesystem::path _path; bool del; bool recursive; public: AutoDelete(); - AutoDelete(const Path & p, bool recursive = true); + AutoDelete(const std::filesystem::path & p, bool recursive = true); ~AutoDelete(); + void cancel(); - void reset(const Path & p, bool recursive = true); - operator Path() const { return path; } - operator PathView() const { return path; } + + void reset(const std::filesystem::path & p, bool recursive = true); + + const std::filesystem::path & path() const { return _path; } + PathViewNG view() const { return _path; } + + operator const std::filesystem::path & () const { return _path; } + operator PathViewNG () const { return _path; } }; diff --git a/src/libutil/unix/file-path.cc b/src/libutil/unix/file-path.cc index 54a1cc278..294048a2f 100644 --- a/src/libutil/unix/file-path.cc +++ b/src/libutil/unix/file-path.cc @@ -13,17 +13,17 @@ std::string os_string_to_string(PathViewNG::string_view path) return std::string { path }; } -PathNG::string_type string_to_os_string(std::string_view s) +std::filesystem::path::string_type string_to_os_string(std::string_view s) { return std::string { s }; } -std::optional maybePathNG(PathView path) +std::optional maybePath(PathView path) { return { path }; } -PathNG pathNG(PathView path) +std::filesystem::path pathNG(PathView path) { return path; } diff --git a/src/libutil/windows/file-path.cc b/src/libutil/windows/file-path.cc index d2f385f50..3114ac4df 100644 --- a/src/libutil/windows/file-path.cc +++ b/src/libutil/windows/file-path.cc @@ -12,35 +12,35 @@ namespace nix { std::string os_string_to_string(PathViewNG::string_view path) { std::wstring_convert> converter; - return converter.to_bytes(PathNG::string_type { path }); + return converter.to_bytes(std::filesystem::path::string_type { path }); } -PathNG::string_type string_to_os_string(std::string_view s) +std::filesystem::path::string_type string_to_os_string(std::string_view s) { std::wstring_convert> converter; return converter.from_bytes(std::string { s }); } -std::optional maybePathNG(PathView path) +std::optional maybePath(PathView path) { if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait::isPathSep(path[2])) { - PathNG::string_type sw = string_to_os_string( + std::filesystem::path::string_type sw = string_to_os_string( std::string { "\\\\?\\" } + path); std::replace(sw.begin(), sw.end(), '/', '\\'); return sw; } if (path.length() >= 7 && path[0] == '\\' && path[1] == '\\' && (path[2] == '.' || path[2] == '?') && path[3] == '\\' && ('A' <= path[4] && path[4] <= 'Z') && path[5] == ':' && WindowsPathTrait::isPathSep(path[6])) { - PathNG::string_type sw = string_to_os_string(path); + std::filesystem::path::string_type sw = string_to_os_string(path); std::replace(sw.begin(), sw.end(), '/', '\\'); return sw; } - return std::optional(); + return std::optional(); } -PathNG pathNG(PathView path) +std::filesystem::path pathNG(PathView path) { - std::optional sw = maybePathNG(path); + std::optional sw = maybePath(path); if (!sw) { // FIXME why are we not using the regular error handling? std::cerr << "invalid path for WinAPI call ["< store, const BuildEnvironment & buildEnvironment, - const Path & tmpDir, - const Path & outputsDir = absPath(".") + "/outputs") + const std::filesystem::path & tmpDir, + const std::filesystem::path & outputsDir = std::filesystem::path { absPath(".") } / "outputs") { // A list of colon-separated environment variables that should be // prepended to, rather than overwritten, in order to keep the shell usable. @@ -376,13 +376,19 @@ struct Common : InstallableCommand, MixProfile StringMap rewrites; if (buildEnvironment.providesStructuredAttrs()) { for (auto & [outputName, from] : BuildEnvironment::getAssociative(outputs->second)) { - rewrites.insert({from, outputsDir + "/" + outputName}); + rewrites.insert({ + from, + (outputsDir / outputName).string() + }); } } else { for (auto & outputName : BuildEnvironment::getStrings(outputs->second)) { auto from = buildEnvironment.vars.find(outputName); assert(from != buildEnvironment.vars.end()); - rewrites.insert({BuildEnvironment::getString(from->second), outputsDir + "/" + outputName}); + rewrites.insert({ + BuildEnvironment::getString(from->second), + (outputsDir / outputName).string(), + }); } } @@ -405,7 +411,7 @@ struct Common : InstallableCommand, MixProfile if (buildEnvironment.providesStructuredAttrs()) { fixupStructuredAttrs( - "sh", + PATHNG_LITERAL("sh"), "NIX_ATTRS_SH_FILE", buildEnvironment.getAttrsSH(), rewrites, @@ -413,7 +419,7 @@ struct Common : InstallableCommand, MixProfile tmpDir ); fixupStructuredAttrs( - "json", + PATHNG_LITERAL("json"), "NIX_ATTRS_JSON_FILE", buildEnvironment.getAttrsJSON(), rewrites, @@ -430,19 +436,21 @@ struct Common : InstallableCommand, MixProfile * that's accessible from the interactive shell session. */ void fixupStructuredAttrs( - const std::string & ext, + PathViewNG::string_view ext, const std::string & envVar, const std::string & content, StringMap & rewrites, const BuildEnvironment & buildEnvironment, - const Path & tmpDir) + const std::filesystem::path & tmpDir) { - auto targetFilePath = tmpDir + "/.attrs." + ext; - writeFile(targetFilePath, content); + auto targetFilePath = tmpDir / PATHNG_LITERAL(".attrs."); + targetFilePath += ext; + + writeFile(targetFilePath.string(), content); auto fileInBuilderEnv = buildEnvironment.vars.find(envVar); assert(fileInBuilderEnv != buildEnvironment.vars.end()); - rewrites.insert({BuildEnvironment::getString(fileInBuilderEnv->second), targetFilePath}); + rewrites.insert({BuildEnvironment::getString(fileInBuilderEnv->second), targetFilePath.string()}); } Strings getDefaultFlakeAttrPaths() override @@ -578,7 +586,7 @@ struct CmdDevelop : Common, MixEnvironment AutoDelete tmpDir(createTempDir("", "nix-develop"), true); - auto script = makeRcScript(store, buildEnvironment, (Path) tmpDir); + auto script = makeRcScript(store, buildEnvironment, tmpDir); if (verbosity >= lvlDebug) script += "set -x\n"; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index cff1c7988..e932170cf 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -87,7 +87,7 @@ std::tuple prefetchFile( if (!storePath) { AutoDelete tmpDir(createTempDir(), true); - Path tmpFile = (Path) tmpDir + "/tmp"; + std::filesystem::path tmpFile = tmpDir.path() / "tmp"; /* Download the file. */ { @@ -95,7 +95,7 @@ std::tuple prefetchFile( if (executable) mode = 0700; - AutoCloseFD fd = toDescriptor(open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)); + AutoCloseFD fd = toDescriptor(open(tmpFile.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)); if (!fd) throw SysError("creating temporary file '%s'", tmpFile); FdSink sink(fd.get()); @@ -109,15 +109,15 @@ std::tuple prefetchFile( if (unpack) { Activity act(*logger, lvlChatty, actUnknown, fmt("unpacking '%s'", url)); - Path unpacked = (Path) tmpDir + "/unpacked"; + auto unpacked = (tmpDir.path() / "unpacked").string(); createDirs(unpacked); - unpackTarfile(tmpFile, unpacked); + unpackTarfile(tmpFile.string(), unpacked); /* If the archive unpacks to a single file/directory, then use that as the top-level. */ auto entries = readDirectory(unpacked); if (entries.size() == 1) - tmpFile = entries[0].path().string(); + tmpFile = entries[0].path(); else tmpFile = unpacked; } From f83617f052d0251d32319939c2832a10e0d8cdd9 Mon Sep 17 00:00:00 2001 From: Sizhe Zhao Date: Wed, 8 May 2024 14:24:23 +0800 Subject: [PATCH 059/910] fix(doc/manual/src/command-ref/nix-env/install): fix typo --- doc/manual/src/command-ref/nix-env/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index c1fff50e8..d80bcb668 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -50,7 +50,7 @@ The arguments *args* map to store paths in a number of possible ways: Show the attribute paths of available packages with [`nix-env --query`](./query.md): ```console - nix-env --query --available --attr-path` + nix-env --query --available --attr-path ``` - If `--from-profile` *path* is given, *args* is a set of names From 52ccaf7971b90f387d7b309c9b9dc4af25d44525 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 8 May 2024 11:29:27 +0200 Subject: [PATCH 060/910] maintainers: update information on team meetings (#10663) - specify meeting times in terms of a time zone rather than standard time (the first encompasses standard time changes) - add information on who can participate and how - unrelated but still important: add GitHub handle to contact the team --- maintainers/README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/maintainers/README.md b/maintainers/README.md index fa321c7c0..bfa0cb5a1 100644 --- a/maintainers/README.md +++ b/maintainers/README.md @@ -36,11 +36,13 @@ We aim to achieve this by improving the contributor experience and attracting mo - Robert Hensing (@roberth) - John Ericson (@Ericson2314) +The team is on Github as [@NixOS/nix-team](https://github.com/orgs/NixOS/teams/nix-team). + ## Meeting protocol -The team meets twice a week: +The team meets twice a week (times are denoted in the [Europe/Amsterdam](https://en.m.wikipedia.org/wiki/Time_in_the_Netherlands) time zone): -- Discussion meeting: [Fridays 13:00-14:00 CET](https://calendar.google.com/calendar/event?eid=MHNtOGVuNWtrZXNpZHR2bW1sM3QyN2ZjaGNfMjAyMjExMjVUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) +- Discussion meeting: [Wednesday 21:00-22:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=ZG5rZzNyajRjajducGV2NGY5aGkzYWIwdnJfMjAyNDA1MDhUMTkwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) 1. Triage issues and pull requests from the [No Status](#no-status) column (30 min) 2. Discuss issues and pull requests from the [To discuss](#to-discuss) column (30 min). @@ -49,15 +51,19 @@ The team meets twice a week: - mark it as draft if it is blocked on the contributor - escalate it back to the team by moving it to To discuss, and leaving a comment as to why the issue needs to be discussed again. -- Work meeting: [Mondays 13:00-15:00 CET](https://calendar.google.com/calendar/event?eid=NTM1MG1wNGJnOGpmOTZhYms3bTB1bnY5cWxfMjAyMjExMjFUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) +- Work meeting: [Mondays 13:00-15:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=Ym52NDdzYnRic2NzcDcybjZiNDhpNzhpa3NfMjAyNDA1MTNUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) 1. Code review on pull requests from [In review](#in-review). 2. Other chores and tasks. Meeting notes are collected on a [collaborative scratchpad](https://pad.lassul.us/Cv7FpYx-Ri-4VjUykQOLAw). -Notes on issues and pull requests are posted as comments and linked from the meeting notes, so they are easy to find from both places. +Notes on issues and pull requests are posted as comments and linked from the meeting notes, so they are can be found from both places. [All meeting notes](https://discourse.nixos.org/search?expanded=true&q=Nix%20team%20meeting%20minutes%20%23%20%23dev%3Anix%20in%3Atitle%20order%3Alatest_topic) are published on Discourse under the [Nix category](https://discourse.nixos.org/c/dev/nix/50). +Team meetings are generally open to anyone interested. +We can make exceptions to discuss sensitive issues, such as security incidents or people matters. +Contact any team member to get a calendar invite for reminders and updates. + ## Project board protocol The team uses a [GitHub project board](https://github.com/orgs/NixOS/projects/19/views/1) for tracking its work. From ddea4c6debc96ea5c7836e3a4a98cf76179cb40b Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 8 May 2024 19:59:37 +0530 Subject: [PATCH 061/910] rm isLink isLink util is removed in favour of std::filesystem::is_symlink --- src/libstore/indirect-root-store.cc | 3 ++- src/libstore/store-api.cc | 2 +- src/libutil/file-system.cc | 8 +------- src/libutil/file-system.hh | 2 -- src/nix/config-check.cc | 2 +- src/nix/upgrade-nix.cc | 2 +- 6 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc index 9da05778d..082a458ab 100644 --- a/src/libstore/indirect-root-store.cc +++ b/src/libstore/indirect-root-store.cc @@ -33,8 +33,9 @@ Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _g /* Don't clobber the link if it already exists and doesn't point to the Nix store. */ - if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) + if (pathExists(gcRoot) && (!std::filesystem::is_symlink(gcRoot) || !isInStore(readLink(gcRoot)))) throw Error("cannot create symlink '%1%'; already exists", gcRoot); + makeSymlink(gcRoot, printStorePath(storePath)); addIndirectRoot(gcRoot); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 008cc11e7..cefb5befd 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -54,7 +54,7 @@ Path Store::followLinksToStore(std::string_view _path) const { Path path = absPath(std::string(_path)); while (!isInStore(path)) { - if (!isLink(path)) break; + if (!std::filesystem::is_symlink(path)) break; auto target = readLink(path); path = absPath(target, dirOf(path)); } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 03f64edc7..47251dbd7 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -94,7 +94,7 @@ Path canonPath(PathView path, bool resolveSymlinks) path, [&followCount, &temp, maxFollow, resolveSymlinks] (std::string & result, std::string_view & remaining) { - if (resolveSymlinks && isLink(result)) { + if (resolveSymlinks && std::filesystem::is_symlink(result)) { if (++followCount >= maxFollow) throw Error("infinite symlink recursion in path '%0%'", remaining); remaining = (temp = concatStrings(readLink(result), remaining)); @@ -222,12 +222,6 @@ Path readLink(const Path & path) } -bool isLink(const Path & path) -{ - return getFileType(path) == fs::file_type::symlink; -} - - std::vector readDirectory(const Path & path) { std::vector entries; diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 40ed82f02..5a0688104 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -117,8 +117,6 @@ bool pathAccessible(const Path & path); */ Path readLink(const Path & path); -bool isLink(const Path & path); - /** * Read the contents of a directory. The entries `.` and `..` are * removed. diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index f23e36fb5..9575bf338 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -101,7 +101,7 @@ struct CmdConfigCheck : StoreCommand Path userEnv = canonPath(profileDir, true); if (store->isStorePath(userEnv) && hasSuffix(userEnv, "user-environment")) { - while (profileDir.find("/profiles/") == std::string::npos && isLink(profileDir)) + while (profileDir.find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir)) profileDir = absPath(readLink(profileDir), dirOf(profileDir)); if (profileDir.find("/profiles/") == std::string::npos) diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index a64b6a56e..17d1edb97 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -121,7 +121,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Path profileDir = dirOf(where); // Resolve profile to /nix/var/nix/profiles/ link. - while (canonPath(profileDir).find("/profiles/") == std::string::npos && isLink(profileDir)) + while (canonPath(profileDir).find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir)) profileDir = readLink(profileDir); printInfo("found profile '%s'", profileDir); From a5252c99794a81730b030a8fcc356e1f74537198 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 8 May 2024 11:18:17 -0400 Subject: [PATCH 062/910] doc: Reword scoping section "dynamic scope" is not accurate, so reword. The underlying idea is good however. --- doc/manual/src/language/constructs.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md index ad1fdfe5f..4d75ea82c 100644 --- a/doc/manual/src/language/constructs.md +++ b/doc/manual/src/language/constructs.md @@ -423,14 +423,15 @@ inline/multi-line, enclosed within `/* ... */`. ## Scoping rules -Nix has constructs with +Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. -* dynamic scope - * [`with`](#with-expressions) -* static scope +* primary scope --- explicitly-bound variables * [`let`](#let-expressions) * [`inherit`](#inheriting-attributes) * function arguments -Static scope takes precedence over dynamic scope. +* secondary scope --- implicitly-bound variables + * [`with`](#with-expressions) + +Primary scope takes precedence over secondary scope. See [`with`](#with-expressions) for a detailed example. From 081faeda8cbd3f8a14775c2d3ad0ae2a76d88673 Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Wed, 8 May 2024 18:09:51 +0300 Subject: [PATCH 063/910] Forbid drvPath in strictDerivation outputs attribute builtins.strictDerivation returns an attribute set with drvPath and output paths. For some reason, current implementation forbids drv instead of drvPath. --- src/libexpr/primops.cc | 10 +- tests/unit/libexpr/error_traces.cc | 200 ++++++++++++++++++++--------- 2 files changed, 145 insertions(+), 65 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 109127d1d..6b947b40d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1184,11 +1184,11 @@ static void derivationStrictInternal( .debugThrow(); /* !!! Check whether j is a valid attribute name. */ - /* Derivations cannot be named ‘drv’, because - then we'd have an attribute ‘drvPath’ in - the resulting set. */ - if (j == "drv") - state.error("invalid derivation output name 'drv'") + /* Derivations cannot be named ‘drvPath’, because + we already have an attribute ‘drvPath’ in + the resulting set (see state.sDrvPath). */ + if (j == "drvPath") + state.error("invalid derivation output name 'drvPath'") .atPos(v) .debugThrow(); outputs.insert(j); diff --git a/tests/unit/libexpr/error_traces.cc b/tests/unit/libexpr/error_traces.cc index 7b32b320b..be379a909 100644 --- a/tests/unit/libexpr/error_traces.cc +++ b/tests/unit/libexpr/error_traces.cc @@ -102,6 +102,74 @@ namespace nix { , type \ ) +#define ASSERT_TRACE3(args, type, message, context1, context2) \ + ASSERT_THROW( \ + std::string expr(args); \ + std::string name = expr.substr(0, expr.find(" ")); \ + try { \ + Value v = eval("builtins." args); \ + state.forceValueDeep(v); \ + } catch (BaseError & e) { \ + ASSERT_EQ(PrintToString(e.info().msg), \ + PrintToString(message)); \ + ASSERT_EQ(e.info().traces.size(), 3) << "while testing " args << std::endl << e.what(); \ + auto trace = e.info().traces.rbegin(); \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context1)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context2)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(HintFmt("while calling the '%s' builtin", name))); \ + throw; \ + } \ + , type \ + ) + +#define ASSERT_TRACE4(args, type, message, context1, context2, context3) \ + ASSERT_THROW( \ + std::string expr(args); \ + std::string name = expr.substr(0, expr.find(" ")); \ + try { \ + Value v = eval("builtins." args); \ + state.forceValueDeep(v); \ + } catch (BaseError & e) { \ + ASSERT_EQ(PrintToString(e.info().msg), \ + PrintToString(message)); \ + ASSERT_EQ(e.info().traces.size(), 4) << "while testing " args << std::endl << e.what(); \ + auto trace = e.info().traces.rbegin(); \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context1)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context2)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context3)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(HintFmt("while calling the '%s' builtin", name))); \ + throw; \ + } \ + , type \ + ) + +// We assume that expr starts with "builtins.derivationStrict { name =", +// otherwise the name attribute position (1, 29) would be invalid. +#define DERIVATION_TRACE_HINTFMT(name) \ + HintFmt("while evaluating derivation '%s'\n" \ + " whose name attribute is located at %s", \ + name, Pos(1, 29, Pos::String{.source = make_ref(expr)})) + +// To keep things simple, we also assume that derivation name is "foo". +#define ASSERT_DERIVATION_TRACE1(args, type, message) \ + ASSERT_TRACE2(args, type, message, DERIVATION_TRACE_HINTFMT("foo")) +#define ASSERT_DERIVATION_TRACE2(args, type, message, context) \ + ASSERT_TRACE3(args, type, message, context, DERIVATION_TRACE_HINTFMT("foo")) +#define ASSERT_DERIVATION_TRACE3(args, type, message, context1, context2) \ + ASSERT_TRACE4(args, type, message, context1, context2, DERIVATION_TRACE_HINTFMT("foo")) + TEST_F(ErrorTraceTest, genericClosure) { ASSERT_TRACE2("genericClosure 1", TypeError, @@ -1185,7 +1253,6 @@ namespace nix { } - /* // Needs different ASSERTs TEST_F(ErrorTraceTest, derivationStrict) { ASSERT_TRACE2("derivationStrict \"\"", TypeError, @@ -1197,102 +1264,115 @@ namespace nix { HintFmt("attribute '%s' missing", "name"), HintFmt("in the attrset passed as argument to builtins.derivationStrict")); - ASSERT_TRACE2("derivationStrict { name = 1; }", + ASSERT_TRACE3("derivationStrict { name = 1; }", TypeError, - HintFmt("expected a string but found %s: %s", "an integer", "1"), - HintFmt("while evaluating the `name` attribute passed to builtins.derivationStrict")); + HintFmt("expected a string but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)), + HintFmt("while evaluating the `name` attribute passed to builtins.derivationStrict"), + HintFmt("while evaluating the derivation attribute 'name'")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; }", - TypeError, - HintFmt("required attribute 'builder' missing"), - HintFmt("while evaluating derivation 'foo'")); + ASSERT_DERIVATION_TRACE1("derivationStrict { name = \"foo\"; }", + EvalError, + HintFmt("required attribute 'builder' missing")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __structuredAttrs = 15; }", + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __structuredAttrs = 15; }", TypeError, - HintFmt("expected a Boolean but found %s: %s", "an integer", "15"), + HintFmt("expected a Boolean but found %s: %s", "an integer", Uncolored(ANSI_CYAN "15" ANSI_NORMAL)), HintFmt("while evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __ignoreNulls = 15; }", + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __ignoreNulls = 15; }", TypeError, - HintFmt("expected a Boolean but found %s: %s", "an integer", "15"), + HintFmt("expected a Boolean but found %s: %s", "an integer", Uncolored(ANSI_CYAN "15" ANSI_NORMAL)), HintFmt("while evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = 15; }", - TypeError, - HintFmt("invalid value '15' for 'outputHashMode' attribute"), - HintFmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = 15; }", + EvalError, + HintFmt("invalid value '%s' for 'outputHashMode' attribute", "15"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputHashMode", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = \"custom\"; }", - TypeError, - HintFmt("invalid value 'custom' for 'outputHashMode' attribute"), - HintFmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = \"custom\"; }", + EvalError, + HintFmt("invalid value '%s' for 'outputHashMode' attribute", "custom"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputHashMode", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = {}; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = {}; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating the attribute 'system' of derivation 'foo'")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "system", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = {}; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = {}; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"drv\"; }", - TypeError, - HintFmt("invalid derivation output name 'drv'"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"drvPath\"; }", + EvalError, + HintFmt("invalid derivation output name 'drvPath'"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = []; }", - TypeError, + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; outputs = \"out\"; __structuredAttrs = true; }", + EvalError, + HintFmt("expected a list but found %s: %s", "a string", "\"out\""), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); + + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = []; }", + EvalError, HintFmt("derivation cannot have an empty set of outputs"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"drv\" ]; }", - TypeError, - HintFmt("invalid derivation output name 'drv'"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"drvPath\" ]; }", + EvalError, + HintFmt("invalid derivation output name 'drvPath'"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"out\" \"out\" ]; }", - TypeError, - HintFmt("duplicate derivation output 'out'"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"out\" \"out\" ]; }", + EvalError, + HintFmt("duplicate derivation output '%s'", "out"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __contentAddressed = \"true\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __contentAddressed = \"true\"; }", TypeError, HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), - HintFmt("while evaluating the attribute '__contentAddressed' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "__contentAddressed", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", TypeError, HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), - HintFmt("while evaluating the attribute '__impure' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "__impure", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", TypeError, HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), - HintFmt("while evaluating the attribute '__impure' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "__impure", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = \"foo\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = \"foo\"; }", TypeError, HintFmt("expected a list but found %s: %s", "a string", "\"foo\""), - HintFmt("while evaluating the attribute 'args' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ {} ]; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ {} ]; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating an element of the argument list")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt("while evaluating an element of the argument list"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ \"a\" {} ]; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ \"a\" {} ]; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating an element of the argument list")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt("while evaluating an element of the argument list"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; FOO = {}; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; FOO = {}; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating the attribute 'FOO' of derivation 'foo'")); - + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "FOO", "foo")); } - */ } /* namespace nix */ From 79c7d6205c2f06cb2f0c00ea5cdfbdad5b7befa4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Feb 2023 15:16:30 +0100 Subject: [PATCH 064/910] Support unit prefixes in configuration settings E.g. you can now say `--min-free 1G`. --- doc/manual/src/command-ref/conf-file-prefix.md | 7 +++++++ src/libutil/config-impl.hh | 7 ++++--- tests/functional/config.sh | 7 ++++++- tests/functional/gc-auto.sh | 4 ++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/doc/manual/src/command-ref/conf-file-prefix.md b/doc/manual/src/command-ref/conf-file-prefix.md index 1e4085977..627806cfb 100644 --- a/doc/manual/src/command-ref/conf-file-prefix.md +++ b/doc/manual/src/command-ref/conf-file-prefix.md @@ -66,5 +66,12 @@ Configuration options can be set on the command line, overriding the values set The `extra-` prefix is supported for settings that take a list of items (e.g. `--extra-trusted users alice` or `--option extra-trusted-users alice`). +## Integer settings + +Settings that have an integer type support the suffixes `K`, `M`, `G` +and `T`. These cause the specified value to be multiplied by 2^10, +2^20, 2^30 and 2^40, respectively. For instance, `--min-free 1M` is +equivalent to `--min-free 1048576`. + # Available settings diff --git a/src/libutil/config-impl.hh b/src/libutil/config-impl.hh index 1da0cb638..1d349fab5 100644 --- a/src/libutil/config-impl.hh +++ b/src/libutil/config-impl.hh @@ -116,10 +116,11 @@ T BaseSetting::parse(const std::string & str) const { static_assert(std::is_integral::value, "Integer required."); - if (auto n = string2Int(str)) - return *n; - else + try { + return string2IntWithUnitPrefix(str); + } catch (...) { throw UsageError("setting '%s' has invalid value '%s'", name, str); + } } template diff --git a/tests/functional/config.sh b/tests/functional/config.sh index efdf2a958..efdafa8ca 100644 --- a/tests/functional/config.sh +++ b/tests/functional/config.sh @@ -66,4 +66,9 @@ exp_features=$(nix config show | grep '^experimental-features' | cut -d '=' -f 2 # Test that it's possible to retrieve a single setting's value val=$(nix config show | grep '^warn-dirty' | cut -d '=' -f 2 | xargs) val2=$(nix config show warn-dirty) -[[ $val == $val2 ]] \ No newline at end of file +[[ $val == $val2 ]] + +# Test unit prefixes. +[[ $(nix config show --min-free 64K min-free) = 65536 ]] +[[ $(nix config show --min-free 1M min-free) = 1048576 ]] +[[ $(nix config show --min-free 2G min-free) = 2147483648 ]] diff --git a/tests/functional/gc-auto.sh b/tests/functional/gc-auto.sh index 521d9e539..281eef20d 100644 --- a/tests/functional/gc-auto.sh +++ b/tests/functional/gc-auto.sh @@ -62,11 +62,11 @@ EOF ) nix build --impure -v -o $TEST_ROOT/result-A -L --expr "$expr" \ - --min-free 1000 --max-free 2000 --min-free-check-interval 1 & + --min-free 1K --max-free 2K --min-free-check-interval 1 & pid1=$! nix build --impure -v -o $TEST_ROOT/result-B -L --expr "$expr2" \ - --min-free 1000 --max-free 2000 --min-free-check-interval 1 & + --min-free 1K --max-free 2K --min-free-check-interval 1 & pid2=$! # Once the first build is done, unblock the second one. From 77a406a5a68d6f5fdd18629f7e2ce19bc21ec0ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Feb 2023 17:31:20 +0100 Subject: [PATCH 065/910] Fix warning --- src/libutil/util.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 11a0431da..8b049875a 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -120,7 +120,7 @@ std::optional string2Int(const std::string_view s) template N string2IntWithUnitPrefix(std::string_view s) { - N multiplier = 1; + uint64_t multiplier = 1; if (!s.empty()) { char u = std::toupper(*s.rbegin()); if (std::isalpha(u)) { From b5605217ae7c3c29dbf887ffdf4e72b176f535b3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 8 May 2024 17:14:00 -0400 Subject: [PATCH 066/910] Document string context (#8595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Document string context Now what we have enough primops, we can document how string contexts work. Co-authored-by: Robert Hensing Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> Co-authored-by: Valentin Gagarin Co-authored-by: Felix Uhl --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/glossary.md | 11 ++ doc/manual/src/language/string-context.md | 134 ++++++++++++++++++++++ src/libexpr/primops/context.cc | 19 ++- 4 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 doc/manual/src/language/string-context.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index c64c1daad..fdfd0a927 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -27,6 +27,7 @@ - [Language Constructs](language/constructs.md) - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) + - [String context](language/string-context.md) - [Operators](language/operators.md) - [Derivations](language/derivations.md) - [Advanced Attributes](language/advanced-attributes.md) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 88916bb50..cbffda187 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -218,6 +218,17 @@ - [output closure]{#gloss-output-closure}\ The [closure] of an [output path]. It only contains what is [reachable] from the output. +- [deriving path]{#gloss-deriving-path} + + Deriving paths are a way to refer to [store objects][store object] that ar not yet [realised][realise]. + This is necessary because, in general and particularly for [content-addressed derivations][content-addressed derivation], the [output path] of an [output] is not known in advance. + There are two forms: + + - *constant*: just a [store path] + It can be made [valid][validity] by copying it into the store: from the evaluator, command line interface or another store. + + - *output*: a pair of a [store path] to a [derivation] and an [output] name. + - [deriver]{#gloss-deriver} The [store derivation] that produced an [output path]. diff --git a/doc/manual/src/language/string-context.md b/doc/manual/src/language/string-context.md new file mode 100644 index 000000000..88ae0d8b0 --- /dev/null +++ b/doc/manual/src/language/string-context.md @@ -0,0 +1,134 @@ +# String context + +> **Note** +> +> This is an advanced topic. +> The Nix language is designed to be used without the programmer consciously dealing with string contexts or even knowing what they are. + +A string in the Nix language is not just a sequence of characters like strings in other languages. +It is actually a pair of a sequence of characters and a *string context*. +The string context is an (unordered) set of *string context elements*. + +The purpose of string contexts is to collect non-string values attached to strings via +[string concatenation](./operators.md#string-concatenation), +[string interpolation](./string-interpolation.md), +and similar operations. +The idea is that a user can combine together values to create a build instructions for derivations without manually keeping track of where they come from. +Then the Nix language implicitly does that bookkeeping to efficiently obtain the closure of derivation inputs. + +> **Note** +> +> String contexts are *not* explicitly manipulated in idiomatic Nix language code. + +String context elements come in different forms: + +- [deriving path]{#string-context-element-derived-path} + + A string context element of this type is a [deriving path](@docroot@/glossary.md#gloss-deriving-path). + They can be either of type [constant](#string-context-constant) or [output](#string-context-output), which correspond to the types of deriving paths. + + - [Constant string context elements]{#string-context-constant} + + > **Example** + > + > [`builtins.storePath`] creates a string with a single constant string context element: + > + > ```nix + > builtins.getContext (builtins.storePath "/nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10") + > ``` + > evaluates to + > ```nix + > { + > "/nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10" = { + > path = true; + > }; + > } + > ``` + + [deriving path]: @docroot@/glossary.md#gloss-deriving-path + [store path]: @docroot@/glossary.md#gloss-store-path + [`builtins.storePath`]: ./builtins.md#builtins-storePath + + - [Output string context elements]{#string-context-output} + + > **Example** + > + > The behavior of string contexts are best demonstrated with a built-in function that is still experimental: [`builtins.outputOf`]. + > This example will *not* work with stable Nix! + > + > ```nix + > builtins.getContext + > (builtins.outputOf + > (builtins.storePath "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv") + > "out") + > ``` + > evaluates to + > ```nix + > { + > "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv" = { + > outputs = [ "out" ]; + > }; + > } + > ``` + + [`builtins.outputOf`]: ./builtins.md#builtins-outputOf + +- [*derivation deep*]{#string-context-element-derivation-deep} + + *derivation deep* is an advanced feature intended to be used with the + [`exportReferencesGraph` derivation attribute](./advanced-attributes.html#adv-attr-exportReferencesGraph). + A *derivation deep* string context element is a derivation path, and refers to both its outputs and the entire build closure of that derivation: + all its outputs, all the other derivations the given derivation depends on, and all the outputs of those. + + > **Example** + > + > The best way to illustrate *derivation deep* string contexts is with [`builtins.addDrvOutputDependencies`]. + > Take a regular constant string context element pointing to a derivation, and transform it into a "Derivation deep" string context element. + > + > ```nix + > builtins.getContext + > (builtins.addDrvOutputDependencies + > (builtins.storePath "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv")) + > ``` + > evaluates to + > ```nix + > { + > "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv" = { + > allOutputs = true; + > }; + > } + > ``` + + [`builtins.addDrvOutputDependencies`]: ./builtins.md#builtins-addDrvOutputDependencies + [`builtins.unsafeDiscardOutputDependency`]: ./builtins.md#builtins-unsafeDiscardOutputDependency + +## Inspecting string contexts + +Most basically, [`builtins.hasContext`] will tell whether a string has a non-empty context. + +When more granular information is needed, [`builtins.getContext`] can be used. +It creates an [attribute set] representing the string context, which can be inspected as usual. + +[`builtins.hasContext`]: ./builtins.md#builtins-hasContext +[`builtins.getContext`]: ./builtins.md#builtins-getContext +[attribute set]: ./values.md#attribute-set + +## Clearing string contexts + +[`buitins.unsafeDiscardStringContext`](./builtins.md#builtins-unsafeDiscardStringContext) will make a copy of a string, but with an empty string context. +The returned string can be used in more ways, e.g. by operators that require the string context to be empty. +The requirement to explicitly discard the string context in such use cases helps ensure that string context elements are not lost by mistake. +The "unsafe" marker is only there to remind that Nix normally guarantees that dependencies are tracked, whereas the returned string has lost them. + +## Constructing string contexts + +[`builtins.appendContext`] will create a copy of a string, but with additional string context elements. +The context is specified explicitly by an [attribute set] in the format that [`builtins.hasContext`] produces. +A string with arbitrary contexts can be made like this: + +1. Create a string with the desired string context elements. + (The contents of the string do not matter.) +2. Dump its context with [`builtins.getContext`]. +3. Combine it with a base string and repeated [`builtins.appendContext`] calls. + +[`builtins.appendContext`]: ./builtins.md#builtins-appendContext diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 2d3013132..8c3f1b4e8 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -14,8 +14,11 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, static RegisterPrimOp primop_unsafeDiscardStringContext({ .name = "__unsafeDiscardStringContext", - .arity = 1, - .fun = prim_unsafeDiscardStringContext + .args = {"s"}, + .doc = R"( + Discard the [string context](@docroot@/language/string-context.md) from a value that can be coerced to a string. + )", + .fun = prim_unsafeDiscardStringContext, }); @@ -75,7 +78,11 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency({ .name = "__unsafeDiscardOutputDependency", .args = {"s"}, .doc = R"( - Create a copy of the given string where every "derivation deep" string context element is turned into a constant string context element. + Create a copy of the given string where every + [derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep) + string context element is turned into a + [constant](@docroot@/language/string-context.md#string-context-element-constant) + string context element. This is the opposite of [`builtins.addDrvOutputDependencies`](#builtins-addDrvOutputDependencies). @@ -137,7 +144,11 @@ static RegisterPrimOp primop_addDrvOutputDependencies({ .name = "__addDrvOutputDependencies", .args = {"s"}, .doc = R"( - Create a copy of the given string where a single constant string context element is turned into a "derivation deep" string context element. + Create a copy of the given string where a single + [constant](@docroot@/language/string-context.md#string-context-element-constant) + string context element is turned into a + [derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep) + string context element. The store path that is the constant string context element should point to a valid derivation, and end in `.drv`. From 9951e14ae085c818a11e28c0d530a111c1e2ab86 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 9 May 2024 19:33:09 +0200 Subject: [PATCH 067/910] Handle zip files containing symlinks In streaming mode, libarchive doesn't handle symlinks in zip files correctly. So write the entire file to disk so libarchive can access it in random-access mode. Fixes #10649. This was broken in cabee9815239af426cece729cb765810b8a716ce. --- src/libfetchers/tarball.cc | 20 +++++++++++++++++++- tests/functional/flakes/prefetch.sh | 6 ++++++ tests/functional/flakes/tree.zip | Bin 0 -> 455 bytes tests/functional/local.mk | 1 + 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/functional/flakes/prefetch.sh create mode 100644 tests/functional/flakes/tree.zip diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index e19b18505..d03ff82ce 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -145,9 +145,27 @@ DownloadTarballResult downloadTarball( // TODO: fall back to cached value if download fails. + AutoDelete cleanupTemp; + /* Note: if the download is cached, `importTarball()` will receive no data, which causes it to import an empty tarball. */ - TarArchive archive { *source }; + auto archive = + hasSuffix(toLower(parseURL(url).path), ".zip") + ? ({ + /* In streaming mode, libarchive doesn't handle + symlinks in zip files correctly (#10649). So write + the entire file to disk so libarchive can access it + in random-access mode. */ + auto [fdTemp, path] = createTempFile("nix-zipfile"); + cleanupTemp.reset(path); + debug("downloading '%s' into '%s'...", url, path); + { + FdSink sink(fdTemp.get()); + source->drainInto(sink); + } + TarArchive{path}; + }) + : TarArchive{*source}; auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); diff --git a/tests/functional/flakes/prefetch.sh b/tests/functional/flakes/prefetch.sh new file mode 100644 index 000000000..bfd0533f9 --- /dev/null +++ b/tests/functional/flakes/prefetch.sh @@ -0,0 +1,6 @@ +source common.sh + +# Test symlinks in zip files (#10649). +path=$(nix flake prefetch --json file://$(pwd)/tree.zip | jq -r .storePath) +[[ $(cat $path/foo) = foo ]] +[[ $(readlink $path/bar) = foo ]] diff --git a/tests/functional/flakes/tree.zip b/tests/functional/flakes/tree.zip new file mode 100644 index 0000000000000000000000000000000000000000..f9e4d225fe04d51a8fed588cb94352bece51efe9 GIT binary patch literal 455 zcmWIWW@h1H0D(ufD3m;6LW?X)e0GSR10t{~*K{V87tPr1J_zGeac7H*PVgwqpq|qK`6xf$Q t(?Gt&VH(I Date: Fri, 10 May 2024 11:31:36 +0200 Subject: [PATCH 068/910] Die rather than warn if a Docker image is missing The warning was done to handle older Nix releases that didn't have Docker images (091f2328962fccfd71602b0b7c072c8d08291c86), but this was a bad idea because it causes us to silently skip uploading Docker images if e.g. Hydra hasn't finished building them yet. Issue #10648. --- maintainers/upload-release.pl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 9a30f8227..9e73524a6 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -189,10 +189,7 @@ for my $platforms (["x86_64-linux", "amd64"], ["aarch64-linux", "arm64"]) { eval { downloadFile("dockerImage.$system", "1", $fn); }; - if ($@) { - warn "$@" if $@; - next; - } + die "$@" if $@; $haveDocker = 1; print STDERR "loading docker image for $dockerPlatform...\n"; From 0998a3ac01fdbd30cbb803eae9d8c6a73b8edb11 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 May 2024 09:39:04 -0400 Subject: [PATCH 069/910] Remove `LocalStore::OptimiseStats::blocksFreed` as it is dead code --- src/libstore/unix/local-store.hh | 1 - src/libstore/unix/optimise-store.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libstore/unix/local-store.hh b/src/libstore/unix/local-store.hh index 2b6e2e25f..b3d7bd6d0 100644 --- a/src/libstore/unix/local-store.hh +++ b/src/libstore/unix/local-store.hh @@ -32,7 +32,6 @@ struct OptimiseStats { unsigned long filesLinked = 0; uint64_t bytesFreed = 0; - uint64_t blocksFreed = 0; }; struct LocalStoreConfig : virtual LocalFSStoreConfig diff --git a/src/libstore/unix/optimise-store.cc b/src/libstore/unix/optimise-store.cc index bedb77849..17b8e4d92 100644 --- a/src/libstore/unix/optimise-store.cc +++ b/src/libstore/unix/optimise-store.cc @@ -256,7 +256,6 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, stats.filesLinked++; stats.bytesFreed += st.st_size; - stats.blocksFreed += st.st_blocks; if (act) act->result(resFileLinked, st.st_size, st.st_blocks); From e0ff8da9d585d7a3ea6be6ec4b0506dc5d7cc03c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 18 Apr 2024 17:49:17 -0400 Subject: [PATCH 070/910] Build the local store on Windows Fixes #10558 Co-Authored-By: Eugene Butler Co-authored-by: Eelco Dolstra --- maintainers/flake-module.nix | 12 +-- src/libfetchers/cache.cc | 2 +- .../{unix => }/ca-specific-schema.sql | 0 src/libstore/{unix => }/gc.cc | 88 ++++++++++++------- src/libstore/globals.hh | 2 + src/libstore/{unix => }/local-store.cc | 74 ++++++++++------ src/libstore/{unix => }/local-store.hh | 0 src/libstore/{unix => }/local-store.md | 0 src/libstore/local.mk | 4 +- src/libstore/{unix => }/optimise-store.cc | 75 ++++++++-------- .../{unix => }/posix-fs-canonicalise.cc | 40 +++++++-- .../{unix => }/posix-fs-canonicalise.hh | 12 ++- src/libstore/{unix => }/schema.sql | 0 src/libstore/store-api.cc | 18 +--- src/libstore/unix/build/derivation-goal.cc | 26 ------ src/libutil/file-system.cc | 2 +- src/libutil/file-system.hh | 5 ++ src/libutil/unix-domain-socket.hh | 1 + src/libutil/unix/file-system.cc | 10 +++ src/libutil/windows/file-system.cc | 12 +++ 20 files changed, 228 insertions(+), 155 deletions(-) rename src/libstore/{unix => }/ca-specific-schema.sql (100%) rename src/libstore/{unix => }/gc.cc (94%) rename src/libstore/{unix => }/local-store.cc (97%) rename src/libstore/{unix => }/local-store.hh (100%) rename src/libstore/{unix => }/local-store.md (100%) rename src/libstore/{unix => }/optimise-store.cc (80%) rename src/libstore/{unix => }/posix-fs-canonicalise.cc (89%) rename src/libstore/{unix => }/posix-fs-canonicalise.hh (85%) rename src/libstore/{unix => }/schema.sql (100%) create mode 100644 src/libutil/unix/file-system.cc create mode 100644 src/libutil/windows/file-system.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index fdac87be3..351a01fcb 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -241,17 +241,17 @@ ''^src/libstore/unix/build/worker\.hh$'' ''^src/libstore/unix/builtins/fetchurl\.cc$'' ''^src/libstore/unix/builtins/unpack-channel\.cc$'' - ''^src/libstore/unix/gc\.cc$'' + ''^src/libstore/gc\.cc$'' ''^src/libstore/unix/local-overlay-store\.cc$'' ''^src/libstore/unix/local-overlay-store\.hh$'' - ''^src/libstore/unix/local-store\.cc$'' - ''^src/libstore/unix/local-store\.hh$'' + ''^src/libstore/local-store\.cc$'' + ''^src/libstore/local-store\.hh$'' ''^src/libstore/unix/lock\.cc$'' ''^src/libstore/unix/lock\.hh$'' - ''^src/libstore/unix/optimise-store\.cc$'' + ''^src/libstore/optimise-store\.cc$'' ''^src/libstore/unix/pathlocks\.cc$'' - ''^src/libstore/unix/posix-fs-canonicalise\.cc$'' - ''^src/libstore/unix/posix-fs-canonicalise\.hh$'' + ''^src/libstore/posix-fs-canonicalise\.cc$'' + ''^src/libstore/posix-fs-canonicalise\.hh$'' ''^src/libstore/uds-remote-store\.cc$'' ''^src/libstore/uds-remote-store\.hh$'' ''^src/libstore/windows/build\.cc$'' diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 87a8e4702..7019b0325 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -109,7 +109,7 @@ struct CacheImpl : Cache Key key, Store & store, Attrs value, - const StorePath & storePath) + const StorePath & storePath) override { /* Add the store prefix to the cache key to handle multiple store prefixes. */ diff --git a/src/libstore/unix/ca-specific-schema.sql b/src/libstore/ca-specific-schema.sql similarity index 100% rename from src/libstore/unix/ca-specific-schema.sql rename to src/libstore/ca-specific-schema.sql diff --git a/src/libstore/unix/gc.cc b/src/libstore/gc.cc similarity index 94% rename from src/libstore/unix/gc.cc rename to src/libstore/gc.cc index 38cbf12b2..3cd4fb839 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/gc.cc @@ -7,7 +7,7 @@ #if !defined(__linux__) // For shelling out to lsof -# include "processes.hh" +# include "processes.hh" #endif #include @@ -19,18 +19,20 @@ #include #include #include -#include -#include #include -#include +#if HAVE_STATVFS +# include +#endif +#ifndef _WIN32 +# include +# include +# include +#endif #include -#include #include namespace nix { -using namespace nix::unix; - static std::string gcSocketPath = "/gc-socket/socket"; static std::string gcRootsDir = "gcroots"; @@ -64,7 +66,7 @@ void LocalStore::createTempRootsFile() /* Check whether the garbage collector didn't get in our way. */ struct stat st; - if (fstat(fdTempRoots->get(), &st) == -1) + if (fstat(fromDescriptorReadOnly(fdTempRoots->get()), &st) == -1) throw SysError("statting '%1%'", fnTempRoots); if (st.st_size == 0) break; @@ -108,7 +110,7 @@ void LocalStore::addTempRoot(const StorePath & path) debug("connecting to '%s'", socketPath); *fdRootsSocket = createUnixDomainSocket(); try { - nix::connect(fdRootsSocket->get(), socketPath); + nix::connect(toSocket(fdRootsSocket->get()), socketPath); } catch (SysError & e) { /* The garbage collector may have exited or not created the socket yet, so we need to restart. */ @@ -166,12 +168,16 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) // those to keep the directory alive. continue; } - Path path = i.path(); + Path path = i.path().string(); pid_t pid = std::stoi(name); debug("reading temporary root file '%1%'", path); - AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)); + AutoCloseFD fd(toDescriptor(open(path.c_str(), +#ifndef _WIN32 + O_CLOEXEC | +#endif + O_RDWR, 0666))); if (!fd) { /* It's okay if the file has disappeared. */ if (errno == ENOENT) continue; @@ -240,8 +246,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R unlink(path.c_str()); } } else { - struct stat st2 = lstat(target); - if (!S_ISLNK(st2.st_mode)) return; + if (!std::filesystem::is_symlink(target)) return; Path target2 = readLink(target); if (isInStore(target2)) foundRoot(target, target2); } @@ -297,24 +302,25 @@ Roots LocalStore::findRoots(bool censor) return roots; } -typedef std::unordered_map> UncheckedRoots; +/** + * Key is a mere string because cannot has path with macOS's libc++ + */ +typedef std::unordered_map> UncheckedRoots; -static void readProcLink(const std::string & file, UncheckedRoots & roots) +static void readProcLink(const std::filesystem::path & file, UncheckedRoots & roots) { - constexpr auto bufsiz = PATH_MAX; - char buf[bufsiz]; - auto res = readlink(file.c_str(), buf, bufsiz); - if (res == -1) { - if (errno == ENOENT || errno == EACCES || errno == ESRCH) + std::filesystem::path buf; + try { + buf = std::filesystem::read_symlink(file); + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::no_such_file_or_directory + || e.code() == std::errc::permission_denied + || e.code() == std::errc::no_such_process) return; - throw SysError("reading symlink"); + throw; } - if (res == bufsiz) { - throw Error("overly long symlink starting with '%1%'", std::string_view(buf, bufsiz)); - } - if (res > 0 && buf[0] == '/') - roots[std::string(static_cast(buf), res)] - .emplace(file); + if (buf.is_absolute()) + roots[buf.string()].emplace(file.string()); } static std::string quoteRegexChars(const std::string & raw) @@ -371,12 +377,12 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) } fdDir.reset(); - auto mapFile = fmt("/proc/%s/maps", ent->d_name); - auto mapLines = tokenizeString>(readFile(mapFile), "\n"); + std::filesystem::path mapFile = fmt("/proc/%s/maps", ent->d_name); + auto mapLines = tokenizeString>(readFile(mapFile.string()), "\n"); for (const auto & line : mapLines) { auto match = std::smatch{}; if (std::regex_match(line, match, mapRegex)) - unchecked[match[1]].emplace(mapFile); + unchecked[match[1]].emplace(mapFile.string()); } auto envFile = fmt("/proc/%s/environ", ent->d_name); @@ -407,7 +413,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) for (const auto & line : lsofLines) { std::smatch match; if (std::regex_match(line, match, lsofRegex)) - unchecked[match[1]].emplace("{lsof}"); + unchecked[match[1].str()].emplace("{lsof}"); } } catch (ExecError & e) { /* lsof not installed, lsof failed */ @@ -490,6 +496,10 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) createDirs(dirOf(socketPath)); auto fdServer = createUnixDomainSocket(socketPath, 0666); + // TODO nonblocking socket on windows? +#ifdef _WIN32 + throw UnimplementedError("External GC client not implemented yet"); +#else if (fcntl(fdServer.get(), F_SETFL, fcntl(fdServer.get(), F_GETFL) | O_NONBLOCK) == -1) throw SysError("making socket '%1%' non-blocking", socketPath); @@ -590,6 +600,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) if (serverThread.joinable()) serverThread.join(); }); +#endif + /* Find the roots. Since we've grabbed the GC lock, the set of permanent roots cannot increase now. */ printInfo("finding garbage collector roots..."); @@ -623,8 +635,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) by another process. We need to be sure that we can acquire an exclusive lock before deleting them. */ if (baseName.find("tmp-", 0) == 0) { - AutoCloseFD tmpDirFd = open(realPath.c_str(), O_RDONLY | O_DIRECTORY); - if (tmpDirFd.get() == -1 || !lockFile(tmpDirFd.get(), ltWrite, false)) { + AutoCloseFD tmpDirFd = openDirectory(realPath); + if (!tmpDirFd || !lockFile(tmpDirFd.get(), ltWrite, false)) { debug("skipping locked tempdir '%s'", realPath); return; } @@ -857,7 +869,13 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) struct stat st; if (stat(linksDir.c_str(), &st) == -1) throw SysError("statting '%1%'", linksDir); - int64_t overhead = st.st_blocks * 512ULL; + int64_t overhead = +#ifdef _WIN32 + 0 +#else + st.st_blocks * 512ULL +#endif + ; printInfo("note: currently hard linking saves %.2f MiB", ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))); @@ -870,6 +888,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) void LocalStore::autoGC(bool sync) { +#ifdef HAVE_STATVFS static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); auto getAvail = [this]() -> uint64_t { @@ -946,6 +965,7 @@ void LocalStore::autoGC(bool sync) sync: // Wait for the future outside of the state lock. if (sync) future.get(); +#endif } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 0c71a7515..108933422 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -424,8 +424,10 @@ public: Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", "Whether SQLite should use WAL mode."}; +#ifndef _WIN32 Setting syncBeforeRegistering{this, false, "sync-before-registering", "Whether to call `sync()` before registering a path as valid."}; +#endif Setting useSubstitutes{ this, true, "substitute", diff --git a/src/libstore/unix/local-store.cc b/src/libstore/local-store.cc similarity index 97% rename from src/libstore/unix/local-store.cc rename to src/libstore/local-store.cc index f33d92717..800e69309 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/local-store.cc @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -34,17 +33,20 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +#endif #if __linux__ -#include -#include -#include -#include +# include +# include +# include +# include #endif #ifdef __CYGWIN__ -#include +# include #endif #include @@ -52,8 +54,6 @@ namespace nix { -using namespace nix::unix; - std::string LocalStoreConfig::doc() { return @@ -224,6 +224,7 @@ LocalStore::LocalStore(const Params & params) } } +#ifndef _WIN32 /* Optionally, create directories and set permissions for a multi-user install. */ if (isRootUser() && settings.buildUsersGroup != "") { @@ -245,6 +246,7 @@ LocalStore::LocalStore(const Params & params) } } } +#endif /* Ensure that the store and its parents are not symlinks. */ if (!settings.allowSymlinkedStore) { @@ -270,14 +272,25 @@ LocalStore::LocalStore(const Params & params) if (stat(reservedPath.c_str(), &st) == -1 || st.st_size != settings.reservedSize) { - AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600); + AutoCloseFD fd = toDescriptor(open(reservedPath.c_str(), O_WRONLY | O_CREAT +#ifndef _WIN32 + | O_CLOEXEC +#endif + , 0600)); int res = -1; #if HAVE_POSIX_FALLOCATE res = posix_fallocate(fd.get(), 0, settings.reservedSize); #endif if (res == -1) { writeFull(fd.get(), std::string(settings.reservedSize, 'X')); - [[gnu::unused]] auto res2 = ftruncate(fd.get(), settings.reservedSize); + [[gnu::unused]] auto res2 = + +#ifdef _WIN32 + SetEndOfFile(fd.get()) +#else + ftruncate(fd.get(), settings.reservedSize) +#endif + ; } } } catch (SystemError & e) { /* don't care about errors */ @@ -460,10 +473,14 @@ LocalStore::LocalStore(std::string scheme, std::string path, const Params & para AutoCloseFD LocalStore::openGCLock() { Path fnGCLock = stateDir + "/gc.lock"; - auto fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600); + auto fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT +#ifndef _WIN32 + | O_CLOEXEC +#endif + , 0600); if (!fdGCLock) throw SysError("opening global GC lock '%1%'", fnGCLock); - return fdGCLock; + return toDescriptor(fdGCLock); } @@ -491,7 +508,7 @@ LocalStore::~LocalStore() try { auto fdTempRoots(_fdTempRoots.lock()); if (*fdTempRoots) { - *fdTempRoots = -1; + fdTempRoots->close(); unlink(fnTempRoots.c_str()); } } catch (...) { @@ -969,11 +986,13 @@ void LocalStore::registerValidPath(const ValidPathInfo & info) void LocalStore::registerValidPaths(const ValidPathInfos & infos) { +#ifndef _WIN32 /* SQLite will fsync by default, but the new valid paths may not be fsync-ed. So some may want to fsync them before registering the validity, at the expense of some speed of the path registering operation. */ if (settings.syncBeforeRegistering) sync(); +#endif return retrySQLite([&]() { auto state(_state.lock()); @@ -1155,7 +1174,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, autoGC(); - canonicalisePathMetaData(realPath, {}); + canonicalisePathMetaData(realPath); optimisePath(realPath, repair); // FIXME: combine with hashPath() @@ -1307,7 +1326,7 @@ StorePath LocalStore::addToStoreFromDump( narHash = narSink.finish(); } - canonicalisePathMetaData(realPath, {}); // FIXME: merge into restorePath + canonicalisePathMetaData(realPath); // FIXME: merge into restorePath optimisePath(realPath, repair); @@ -1340,8 +1359,8 @@ std::pair LocalStore::createTempDirInStore() the GC between createTempDir() and when we acquire a lock on it. We'll repeat until 'tmpDir' exists and we've locked it. */ tmpDirFn = createTempDir(realStoreDir, "tmp"); - tmpDirFd = open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY); - if (tmpDirFd.get() < 0) { + tmpDirFd = openDirectory(tmpDirFn); + if (!tmpDirFd) { continue; } lockedByUs = lockFile(tmpDirFd.get(), ltWrite, true); @@ -1390,19 +1409,16 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) for (auto & link : readDirectory(linksDir)) { auto name = link.path().filename(); printMsg(lvlTalkative, "checking contents of '%s'", name); - Path linkPath = linksDir / name; PosixSourceAccessor accessor; std::string hash = hashPath( - {getFSSourceAccessor(), CanonPath(linkPath)}, + PosixSourceAccessor::createAtRoot(link.path()), FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", - linkPath, name, hash); + link.path(), name, hash); if (repair) { - if (unlink(linkPath.c_str()) == 0) - printInfo("removed link '%s'", linkPath); - else - throw SysError("removing corrupt link '%s'", linkPath); + std::filesystem::remove(link.path()); + printInfo("removed link '%s'", link.path()); } else { errors = true; } @@ -1583,8 +1599,12 @@ static void makeMutable(const Path & path) /* The O_NOFOLLOW is important to prevent us from changing the mutable bit on the target of a symlink (which would be a security hole). */ - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC); - if (fd == -1) { + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW +#ifndef _WIN32 + | O_CLOEXEC +#endif + ); + if (fd == INVALID_DESCRIPTOR) { if (errno == ELOOP) return; // it's a symlink throw SysError("opening file '%1%'", path); } diff --git a/src/libstore/unix/local-store.hh b/src/libstore/local-store.hh similarity index 100% rename from src/libstore/unix/local-store.hh rename to src/libstore/local-store.hh diff --git a/src/libstore/unix/local-store.md b/src/libstore/local-store.md similarity index 100% rename from src/libstore/unix/local-store.md rename to src/libstore/local-store.md diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 590a230e5..cc67da786 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -89,11 +89,11 @@ else endif endif -$(d)/unix/local-store.cc: $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh +$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh $(d)/unix/build.cc: -clean-files += $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh +clean-files += $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh $(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libstore/unix/optimise-store.cc b/src/libstore/optimise-store.cc similarity index 80% rename from src/libstore/unix/optimise-store.cc rename to src/libstore/optimise-store.cc index 17b8e4d92..2477cf0c0 100644 --- a/src/libstore/unix/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -155,55 +155,53 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true)); /* Check if this is a known hash. */ - Path linkPath = linksDir + "/" + hash.to_string(HashFormat::Nix32, false); + std::filesystem::path linkPath = std::filesystem::path{linksDir} / hash.to_string(HashFormat::Nix32, false); /* Maybe delete the link, if it has been corrupted. */ - if (pathExists(linkPath)) { - auto stLink = lstat(linkPath); + if (std::filesystem::exists(std::filesystem::symlink_status(linkPath))) { + auto stLink = lstat(linkPath.string()); if (st.st_size != stLink.st_size || (repair && hash != ({ hashPath( - {make_ref(), CanonPath(linkPath)}, + PosixSourceAccessor::createAtRoot(linkPath), FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; }))) { // XXX: Consider overwriting linkPath with our valid version. - warn("removing corrupted link '%s'", linkPath); + warn("removing corrupted link %s", linkPath); warn("There may be more corrupted paths." "\nYou should run `nix-store --verify --check-contents --repair` to fix them all"); - unlink(linkPath.c_str()); + std::filesystem::remove(linkPath); } } - if (!pathExists(linkPath)) { + if (!std::filesystem::exists(std::filesystem::symlink_status(linkPath))) { /* Nope, create a hard link in the links directory. */ - if (link(path.c_str(), linkPath.c_str()) == 0) { + try { + std::filesystem::create_hard_link(path, linkPath); inodeHash.insert(st.st_ino); - return; - } + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::file_exists) { + /* Fall through if another process created ‘linkPath’ before + we did. */ + } - switch (errno) { - case EEXIST: - /* Fall through if another process created ‘linkPath’ before - we did. */ - break; + else if (e.code() == std::errc::no_space_on_device) { + /* On ext4, that probably means the directory index is + full. When that happens, it's fine to ignore it: we + just effectively disable deduplication of this + file. */ + printInfo("cannot link '%s' to '%s': %s", linkPath, path, strerror(errno)); + return; + } - case ENOSPC: - /* On ext4, that probably means the directory index is - full. When that happens, it's fine to ignore it: we - just effectively disable deduplication of this - file. */ - printInfo("cannot link '%s' to '%s': %s", linkPath, path, strerror(errno)); - return; - - default: - throw SysError("cannot link '%1%' to '%2%'", linkPath, path); + else throw; } } /* Yes! We've seen a file with the same contents. Replace the current file with a hard link to that file. */ - auto stLink = lstat(linkPath); + auto stLink = lstat(linkPath.string()); if (st.st_ino == stLink.st_ino) { debug("'%1%' is already linked to '%2%'", path, linkPath); @@ -223,10 +221,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, its timestamp back to 0. */ MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : ""); - Path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), random()); + std::filesystem::path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), rand()); - if (link(linkPath.c_str(), tempLink.c_str()) == -1) { - if (errno == EMLINK) { + try { + std::filesystem::create_hard_link(linkPath, tempLink); + inodeHash.insert(st.st_ino); + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::too_many_links) { /* Too many links to the same file (>= 32000 on most file systems). This is likely to happen with empty files. Just shrug and ignore. */ @@ -234,16 +235,16 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, printInfo("'%1%' has maximum number of links", linkPath); return; } - throw SysError("cannot link '%1%' to '%2%'", tempLink, linkPath); + throw; } /* Atomically replace the old file with the new hard link. */ try { - renameFile(tempLink, path); - } catch (SystemError & e) { - if (unlink(tempLink.c_str()) == -1) + std::filesystem::rename(tempLink, path); + } catch (std::filesystem::filesystem_error & e) { + std::filesystem::remove(tempLink); printError("unable to unlink '%1%'", tempLink); - if (errno == EMLINK) { + if (e.code() == std::errc::too_many_links) { /* Some filesystems generate too many links on the rename, rather than on the original link. (Probably it temporarily increases the st_nlink field before @@ -258,7 +259,11 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, stats.bytesFreed += st.st_size; if (act) - act->result(resFileLinked, st.st_size, st.st_blocks); + act->result(resFileLinked, st.st_size +#ifndef _WIN32 + , st.st_blocks +#endif + ); } diff --git a/src/libstore/unix/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc similarity index 89% rename from src/libstore/unix/posix-fs-canonicalise.cc rename to src/libstore/posix-fs-canonicalise.cc index 916db49ac..d70ef6fae 100644 --- a/src/libstore/unix/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -31,6 +31,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct } +#ifndef _WIN32 // TODO implement if (st.st_mtime != mtimeStore) { struct timeval times[2]; times[0].tv_sec = st.st_atime; @@ -46,6 +47,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct #endif throw SysError("changing modification time of '%1%'", path); } +#endif } @@ -57,7 +59,9 @@ void canonicaliseTimestampAndPermissions(const Path & path) static void canonicalisePathMetaData_( const Path & path, +#ifndef _WIN32 std::optional> uidRange, +#endif InodesSeen & inodesSeen) { checkInterrupt(); @@ -99,6 +103,7 @@ static void canonicalisePathMetaData_( } #endif +#ifndef _WIN32 /* Fail if the file is not owned by the build user. This prevents us from messing up the ownership/permissions of files hard-linked into the output (e.g. "ln /etc/shadow $out/foo"). @@ -112,11 +117,13 @@ static void canonicalisePathMetaData_( assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore)); return; } +#endif inodesSeen.insert(Inode(st.st_dev, st.st_ino)); canonicaliseTimestampAndPermissions(path, st); +#ifndef _WIN32 /* Change ownership to the current uid. If it's a symlink, use lchown if available, otherwise don't bother. Wrong ownership of a symlink doesn't matter, since the owning user can't change @@ -134,22 +141,36 @@ static void canonicalisePathMetaData_( throw SysError("changing owner of '%1%' to %2%", path, geteuid()); } +#endif if (S_ISDIR(st.st_mode)) { std::vector entries = readDirectory(path); for (auto & i : entries) - canonicalisePathMetaData_(i.path().string(), uidRange, inodesSeen); + canonicalisePathMetaData_( + i.path().string(), +#ifndef _WIN32 + uidRange, +#endif + inodesSeen); } } void canonicalisePathMetaData( const Path & path, +#ifndef _WIN32 std::optional> uidRange, +#endif InodesSeen & inodesSeen) { - canonicalisePathMetaData_(path, uidRange, inodesSeen); + canonicalisePathMetaData_( + path, +#ifndef _WIN32 + uidRange, +#endif + inodesSeen); +#ifndef _WIN32 /* On platforms that don't have lchown(), the top-level path can't be a symlink, since we can't change its ownership. */ auto st = lstat(path); @@ -158,14 +179,23 @@ void canonicalisePathMetaData( assert(S_ISLNK(st.st_mode)); throw Error("wrong ownership of top-level store path '%1%'", path); } +#endif } -void canonicalisePathMetaData(const Path & path, - std::optional> uidRange) +void canonicalisePathMetaData(const Path & path +#ifndef _WIN32 + , std::optional> uidRange +#endif + ) { InodesSeen inodesSeen; - canonicalisePathMetaData(path, uidRange, inodesSeen); + canonicalisePathMetaData_( + path, +#ifndef _WIN32 + uidRange, +#endif + inodesSeen); } } diff --git a/src/libstore/unix/posix-fs-canonicalise.hh b/src/libstore/posix-fs-canonicalise.hh similarity index 85% rename from src/libstore/unix/posix-fs-canonicalise.hh rename to src/libstore/posix-fs-canonicalise.hh index 35644af12..45a4f3f20 100644 --- a/src/libstore/unix/posix-fs-canonicalise.hh +++ b/src/libstore/posix-fs-canonicalise.hh @@ -24,7 +24,7 @@ typedef std::set InodesSeen; * without execute permission; setuid bits etc. are cleared) * * - the owner and group are set to the Nix user and group, if we're - * running as root. + * running as root. (Unix only.) * * If uidRange is not empty, this function will throw an error if it * encounters files owned by a user outside of the closed interval @@ -32,11 +32,17 @@ typedef std::set InodesSeen; */ void canonicalisePathMetaData( const Path & path, +#ifndef _WIN32 std::optional> uidRange, +#endif InodesSeen & inodesSeen); + void canonicalisePathMetaData( - const Path & path, - std::optional> uidRange); + const Path & path +#ifndef _WIN32 + , std::optional> uidRange = std::nullopt +#endif + ); void canonicaliseTimestampAndPermissions(const Path & path); diff --git a/src/libstore/unix/schema.sql b/src/libstore/schema.sql similarity index 100% rename from src/libstore/unix/schema.sql rename to src/libstore/schema.sql diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index cefb5befd..419c55e92 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -20,10 +20,6 @@ #include "signals.hh" #include "users.hh" -#ifndef _WIN32 -# include "remote-store.hh" -#endif - #include #include @@ -1265,10 +1261,9 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath) } -#ifndef _WIN32 -# include "local-store.hh" -# include "uds-remote-store.hh" -#endif + +#include "local-store.hh" +#include "uds-remote-store.hh" namespace nix { @@ -1286,9 +1281,6 @@ std::pair splitUriAndParams(const std::string & uri_ return {uri, params}; } -#ifdef _WIN32 // Unused on Windows because the next `#ifndef` -[[maybe_unused]] -#endif static bool isNonUriPath(const std::string & spec) { return @@ -1303,7 +1295,6 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para { // TODO reenable on Windows once we have `LocalStore` and // `UDSRemoteStore`. - #ifndef _WIN32 if (uri == "" || uri == "auto") { auto stateDir = getOr(params, "state", settings.nixStateDir); if (access(stateDir.c_str(), R_OK | W_OK) == 0) @@ -1348,9 +1339,6 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para } else { return nullptr; } - #else - return nullptr; - #endif } // The `parseURL` function supports both IPv6 URIs as defined in diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc index 4d4342996..8d6e35015 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/unix/build/derivation-goal.cc @@ -30,32 +30,6 @@ #include #include -#if HAVE_STATVFS -#include -#endif - -/* Includes required for chroot support. */ -#if __linux__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_SECCOMP -#include -#endif -#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) -#endif - -#if __APPLE__ -#include -#include -#endif - #include #include diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 47251dbd7..39efa19fe 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -94,7 +94,7 @@ Path canonPath(PathView path, bool resolveSymlinks) path, [&followCount, &temp, maxFollow, resolveSymlinks] (std::string & result, std::string_view & remaining) { - if (resolveSymlinks && std::filesystem::is_symlink(result)) { + if (resolveSymlinks && fs::is_symlink(result)) { if (++followCount >= maxFollow) throw Error("infinite symlink recursion in path '%0%'", remaining); remaining = (temp = concatStrings(readLink(result), remaining)); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 5a0688104..4536accc3 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -117,6 +117,11 @@ bool pathAccessible(const Path & path); */ Path readLink(const Path & path); +/** + * Open a `Descriptor` with read-only access to the given directory. + */ +Descriptor openDirectory(const std::filesystem::path & path); + /** * Read the contents of a directory. The entries `.` and `..` are * removed. diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix-domain-socket.hh index a8bbc4b89..ba2baeb13 100644 --- a/src/libutil/unix-domain-socket.hh +++ b/src/libutil/unix-domain-socket.hh @@ -39,6 +39,7 @@ using Socket = * Windows gives this a different name */ # define SHUT_WR SD_SEND +# define SHUT_RDWR SD_BOTH #endif /** diff --git a/src/libutil/unix/file-system.cc b/src/libutil/unix/file-system.cc new file mode 100644 index 000000000..bbbbfa559 --- /dev/null +++ b/src/libutil/unix/file-system.cc @@ -0,0 +1,10 @@ +#include "file-system.hh" + +namespace nix { + +Descriptor openDirectory(const std::filesystem::path & path) +{ + return open(path.c_str(), O_RDONLY | O_DIRECTORY); +} + +} diff --git a/src/libutil/windows/file-system.cc b/src/libutil/windows/file-system.cc new file mode 100644 index 000000000..8002dd75e --- /dev/null +++ b/src/libutil/windows/file-system.cc @@ -0,0 +1,12 @@ +#include "file-system.hh" + +namespace nix { + +Descriptor openDirectory(const std::filesystem::path & path) +{ + return CreateFileW( + path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); +} + +} From 39b2a399ad94f061eea0e0fc639cf1466587959e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 May 2024 13:03:05 -0400 Subject: [PATCH 071/910] Start building the scheduler for Windows Building derivations is a lot harder, but the downloading goals is portable enough. The "common channel" code is due to Volth. I wonder if there is a way we can factor it out into separate functions / files to avoid some within-function CPP. Co-authored-by: volth --- maintainers/flake-module.nix | 22 ++-- .../build/drv-output-substitution-goal.cc | 14 ++- .../build/drv-output-substitution-goal.hh | 15 ++- src/libstore/{unix => }/build/entry-points.cc | 14 ++- src/libstore/{unix => }/build/goal.cc | 0 src/libstore/{unix => }/build/goal.hh | 4 +- .../{unix => }/build/substitution-goal.cc | 16 ++- .../{unix => }/build/substitution-goal.hh | 13 ++- src/libstore/{unix => }/build/worker.cc | 110 ++++++++++++++++-- src/libstore/{unix => }/build/worker.hh | 33 +++++- src/libstore/local.mk | 4 +- src/libstore/unix/build/derivation-goal.cc | 4 +- src/libstore/unix/build/derivation-goal.hh | 6 +- src/libstore/unix/{lock.cc => user-lock.cc} | 2 +- src/libstore/unix/{lock.hh => user-lock.hh} | 0 src/libstore/windows/build.cc | 37 ------ src/libstore/windows/pathlocks.cc | 2 + src/libutil/error.hh | 10 +- src/libutil/serialise.cc | 2 +- src/libutil/windows/file-descriptor.cc | 2 + src/libutil/windows/users.cc | 2 + src/libutil/windows/windows-async-pipe.cc | 43 +++++++ src/libutil/windows/windows-async-pipe.hh | 20 ++++ src/libutil/windows/windows-error.cc | 2 +- src/libutil/windows/windows-error.hh | 2 +- 25 files changed, 285 insertions(+), 94 deletions(-) rename src/libstore/{unix => }/build/drv-output-substitution-goal.cc (93%) rename src/libstore/{unix => }/build/drv-output-substitution-goal.hh (90%) rename src/libstore/{unix => }/build/entry-points.cc (90%) rename src/libstore/{unix => }/build/goal.cc (100%) rename src/libstore/{unix => }/build/goal.hh (97%) rename src/libstore/{unix => }/build/substitution-goal.cc (96%) rename src/libstore/{unix => }/build/substitution-goal.hh (91%) rename src/libstore/{unix => }/build/worker.cc (81%) rename src/libstore/{unix => }/build/worker.hh (92%) rename src/libstore/unix/{lock.cc => user-lock.cc} (99%) rename src/libstore/unix/{lock.hh => user-lock.hh} (100%) delete mode 100644 src/libstore/windows/build.cc create mode 100644 src/libutil/windows/windows-async-pipe.cc create mode 100644 src/libutil/windows/windows-async-pipe.hh diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 351a01fcb..e541aeda8 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -227,18 +227,18 @@ ''^src/libstore/store-dir-config\.hh$'' ''^src/libstore/unix/build/derivation-goal\.cc$'' ''^src/libstore/unix/build/derivation-goal\.hh$'' - ''^src/libstore/unix/build/drv-output-substitution-goal\.cc$'' - ''^src/libstore/unix/build/drv-output-substitution-goal\.hh$'' - ''^src/libstore/unix/build/entry-points\.cc$'' - ''^src/libstore/unix/build/goal\.cc$'' - ''^src/libstore/unix/build/goal\.hh$'' + ''^src/libstore/build/drv-output-substitution-goal\.cc$'' + ''^src/libstore/build/drv-output-substitution-goal\.hh$'' + ''^src/libstore/build/entry-points\.cc$'' + ''^src/libstore/build/goal\.cc$'' + ''^src/libstore/build/goal\.hh$'' ''^src/libstore/unix/build/hook-instance\.cc$'' ''^src/libstore/unix/build/local-derivation-goal\.cc$'' ''^src/libstore/unix/build/local-derivation-goal\.hh$'' - ''^src/libstore/unix/build/substitution-goal\.cc$'' - ''^src/libstore/unix/build/substitution-goal\.hh$'' - ''^src/libstore/unix/build/worker\.cc$'' - ''^src/libstore/unix/build/worker\.hh$'' + ''^src/libstore/build/substitution-goal\.cc$'' + ''^src/libstore/build/substitution-goal\.hh$'' + ''^src/libstore/build/worker\.cc$'' + ''^src/libstore/build/worker\.hh$'' ''^src/libstore/unix/builtins/fetchurl\.cc$'' ''^src/libstore/unix/builtins/unpack-channel\.cc$'' ''^src/libstore/gc\.cc$'' @@ -246,8 +246,8 @@ ''^src/libstore/unix/local-overlay-store\.hh$'' ''^src/libstore/local-store\.cc$'' ''^src/libstore/local-store\.hh$'' - ''^src/libstore/unix/lock\.cc$'' - ''^src/libstore/unix/lock\.hh$'' + ''^src/libstore/unix/user-lock\.cc$'' + ''^src/libstore/unix/user-lock\.hh$'' ''^src/libstore/optimise-store\.cc$'' ''^src/libstore/unix/pathlocks\.cc$'' ''^src/libstore/posix-fs-canonicalise\.cc$'' diff --git a/src/libstore/unix/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc similarity index 93% rename from src/libstore/unix/build/drv-output-substitution-goal.cc rename to src/libstore/build/drv-output-substitution-goal.cc index b30957c84..13a07e4ea 100644 --- a/src/libstore/unix/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -66,7 +66,11 @@ void DrvOutputSubstitutionGoal::tryNext() some other error occurs), so it must not touch `this`. So put the shared state in a separate refcounted object. */ downloadState = std::make_shared(); +#ifndef _WIN32 downloadState->outPipe.create(); +#else + downloadState->outPipe.createAsyncPipe(worker.ioport.get()); +#endif sub->queryRealisation( id, @@ -79,7 +83,13 @@ void DrvOutputSubstitutionGoal::tryNext() } } }); - worker.childStarted(shared_from_this(), {downloadState->outPipe.readSide.get()}, true, false); + worker.childStarted(shared_from_this(), { +#ifndef _WIN32 + downloadState->outPipe.readSide.get() +#else + &downloadState->outPipe +#endif + }, true, false); state = &DrvOutputSubstitutionGoal::realisationFetched; } @@ -158,7 +168,7 @@ void DrvOutputSubstitutionGoal::work() (this->*state)(); } -void DrvOutputSubstitutionGoal::handleEOF(int fd) +void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd) { if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/unix/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh similarity index 90% rename from src/libstore/unix/build/drv-output-substitution-goal.hh rename to src/libstore/build/drv-output-substitution-goal.hh index da2426e5e..4f06a9e9e 100644 --- a/src/libstore/unix/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -1,11 +1,16 @@ #pragma once ///@file +#include +#include + #include "store-api.hh" #include "goal.hh" #include "realisation.hh" -#include -#include + +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif namespace nix { @@ -43,7 +48,11 @@ class DrvOutputSubstitutionGoal : public Goal { struct DownloadState { +#ifndef _WIN32 Pipe outPipe; +#else + windows::AsyncPipe outPipe; +#endif std::promise> promise; }; @@ -71,7 +80,7 @@ public: std::string key() override; void work() override; - void handleEOF(int fd) override; + void handleEOF(Descriptor fd) override; JobCategory jobCategory() const override { return JobCategory::Substitution; diff --git a/src/libstore/unix/build/entry-points.cc b/src/libstore/build/entry-points.cc similarity index 90% rename from src/libstore/unix/build/entry-points.cc rename to src/libstore/build/entry-points.cc index d4bead28e..784f618c1 100644 --- a/src/libstore/unix/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -1,6 +1,8 @@ #include "worker.hh" #include "substitution-goal.hh" -#include "derivation-goal.hh" +#ifndef _WIN32 // TODO Enable building on Windows +# include "derivation-goal.hh" +#endif #include "local-store.hh" namespace nix { @@ -25,9 +27,12 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod ex = std::move(i->ex); } if (i->exitCode != Goal::ecSuccess) { +#ifndef _WIN32 // TODO Enable building on Windows if (auto i2 = dynamic_cast(i.get())) failed.insert(printStorePath(i2->drvPath)); - else if (auto i2 = dynamic_cast(i.get())) + else +#endif + if (auto i2 = dynamic_cast(i.get())) failed.insert(printStorePath(i2->storePath)); } } @@ -74,7 +79,12 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat BuildMode buildMode) { Worker worker(*this, *this); +#ifndef _WIN32 // TODO Enable building on Windows auto goal = worker.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All {}, buildMode); +#else + std::shared_ptr goal; + throw UnimplementedError("Building derivations not yet implemented on windows."); +#endif try { worker.run(Goals{goal}); diff --git a/src/libstore/unix/build/goal.cc b/src/libstore/build/goal.cc similarity index 100% rename from src/libstore/unix/build/goal.cc rename to src/libstore/build/goal.cc diff --git a/src/libstore/unix/build/goal.hh b/src/libstore/build/goal.hh similarity index 97% rename from src/libstore/unix/build/goal.hh rename to src/libstore/build/goal.hh index 9af083230..0d9b828e1 100644 --- a/src/libstore/unix/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -138,12 +138,12 @@ public: virtual void waiteeDone(GoalPtr waitee, ExitCode result); - virtual void handleChildOutput(int fd, std::string_view data) + virtual void handleChildOutput(Descriptor fd, std::string_view data) { abort(); } - virtual void handleEOF(int fd) + virtual void handleEOF(Descriptor fd) { abort(); } diff --git a/src/libstore/unix/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc similarity index 96% rename from src/libstore/unix/build/substitution-goal.cc rename to src/libstore/build/substitution-goal.cc index c7e8e2825..0be3d1e8d 100644 --- a/src/libstore/unix/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -212,7 +212,11 @@ void PathSubstitutionGoal::tryToRun() maintainRunningSubstitutions = std::make_unique>(worker.runningSubstitutions); worker.updateProgress(); +#ifndef _WIN32 outPipe.create(); +#else + outPipe.createAsyncPipe(worker.ioport.get()); +#endif promise = std::promise(); @@ -235,7 +239,13 @@ void PathSubstitutionGoal::tryToRun() } }); - worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false); + worker.childStarted(shared_from_this(), { +#ifndef _WIN32 + outPipe.readSide.get() +#else + &outPipe +#endif + }, true, false); state = &PathSubstitutionGoal::finished; } @@ -294,12 +304,12 @@ void PathSubstitutionGoal::finished() } -void PathSubstitutionGoal::handleChildOutput(int fd, std::string_view data) +void PathSubstitutionGoal::handleChildOutput(Descriptor fd, std::string_view data) { } -void PathSubstitutionGoal::handleEOF(int fd) +void PathSubstitutionGoal::handleEOF(Descriptor fd) { if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/unix/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh similarity index 91% rename from src/libstore/unix/build/substitution-goal.hh rename to src/libstore/build/substitution-goal.hh index 1d389d328..58d309424 100644 --- a/src/libstore/unix/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -1,10 +1,13 @@ #pragma once ///@file -#include "lock.hh" #include "store-api.hh" #include "goal.hh" +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif + namespace nix { class Worker; @@ -45,7 +48,11 @@ struct PathSubstitutionGoal : public Goal /** * Pipe for the substituter's standard output. */ +#ifndef _WIN32 Pipe outPipe; +#else + windows::AsyncPipe outPipe; +#endif /** * The substituter thread. @@ -111,8 +118,8 @@ public: /** * Callback used by the worker to write to the log. */ - void handleChildOutput(int fd, std::string_view data) override; - void handleEOF(int fd) override; + void handleChildOutput(Descriptor fd, std::string_view data) override; + void handleEOF(Descriptor fd) override; /* Called by destructor, can't be overridden */ void cleanup() override final; diff --git a/src/libstore/unix/build/worker.cc b/src/libstore/build/worker.cc similarity index 81% rename from src/libstore/unix/build/worker.cc rename to src/libstore/build/worker.cc index 03fc280a4..ac6f693a2 100644 --- a/src/libstore/unix/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -1,12 +1,20 @@ +#include "local-store.hh" #include "machines.hh" #include "worker.hh" #include "substitution-goal.hh" #include "drv-output-substitution-goal.hh" -#include "local-derivation-goal.hh" -#include "hook-instance.hh" +#ifndef _WIN32 // TODO Enable building on Windows +# include "local-derivation-goal.hh" +# include "hook-instance.hh" +#endif #include "signals.hh" -#include +#ifndef _WIN32 +# include +#else +# include +# include "windows-error.hh" +#endif namespace nix { @@ -41,6 +49,7 @@ Worker::~Worker() assert(expectedNarSize == 0); } +#ifndef _WIN32 // TODO Enable building on Windows std::shared_ptr Worker::makeDerivationGoalCommon( const StorePath & drvPath, @@ -70,7 +79,6 @@ std::shared_ptr Worker::makeDerivationGoal(const StorePath & drv }); } - std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath & drvPath, const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode) { @@ -81,6 +89,8 @@ std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath }); } +#endif + std::shared_ptr Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional ca) { @@ -112,10 +122,14 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode) { return std::visit(overloaded { [&](const DerivedPath::Built & bfd) -> GoalPtr { +#ifndef _WIN32 // TODO Enable building on Windows if (auto bop = std::get_if(&*bfd.drvPath)) return makeDerivationGoal(bop->path, bfd.outputs, buildMode); else throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented."); +#else + throw UnimplementedError("Building derivations not yet implemented on Windows"); +#endif }, [&](const DerivedPath::Opaque & bo) -> GoalPtr { return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair); @@ -141,9 +155,12 @@ static void removeGoal(std::shared_ptr goal, std::map> & void Worker::removeGoal(GoalPtr goal) { +#ifndef _WIN32 // TODO Enable building on Windows if (auto drvGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(drvGoal, derivationGoals); - else if (auto subGoal = std::dynamic_pointer_cast(goal)) + else +#endif + if (auto subGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, substitutionGoals); else if (auto subGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, drvOutputSubstitutionGoals); @@ -187,13 +204,13 @@ unsigned Worker::getNrSubstitutions() } -void Worker::childStarted(GoalPtr goal, const std::set & fds, +void Worker::childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts) { Child child; child.goal = goal; child.goal2 = goal.get(); - child.fds = fds; + child.channels = channels; child.timeStarted = child.lastOutput = steady_time_point::clock::now(); child.inBuildSlot = inBuildSlot; child.respectTimeouts = respectTimeouts; @@ -281,12 +298,15 @@ void Worker::run(const Goals & _topGoals) for (auto & i : _topGoals) { topGoals.insert(i); +#ifndef _WIN32 // TODO Enable building on Windows if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Built { .drvPath = makeConstantStorePathRef(goal->drvPath), .outputs = goal->wantedOutputs, }); - } else if (auto goal = dynamic_cast(i.get())) { + } else +#endif + if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Opaque{goal->storePath}); } } @@ -408,13 +428,14 @@ void Worker::waitForInput() if (useTimeout) vomit("sleeping %d seconds", timeout); +#ifndef _WIN32 /* Use select() to wait for the input side of any logger pipe to become `available'. Note that `available' (i.e., non-blocking) includes EOF. */ std::vector pollStatus; std::map fdToPollStatus; for (auto & i : children) { - for (auto & j : i.fds) { + for (auto & j : i.channels) { pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN }); fdToPollStatus[j] = pollStatus.size() - 1; } @@ -425,6 +446,28 @@ void Worker::waitForInput() if (errno == EINTR) return; throw SysError("waiting for input"); } +#else + OVERLAPPED_ENTRY oentries[0x20] = {0}; + ULONG removed; + bool gotEOF = false; + + // we are on at least Windows Vista / Server 2008 and can get many (countof(oentries)) statuses in one API call + if (!GetQueuedCompletionStatusEx( + ioport.get(), + oentries, + sizeof(oentries) / sizeof(*oentries), + &removed, + useTimeout ? timeout * 1000 : INFINITE, + false)) + { + windows::WinError winError("GetQueuedCompletionStatusEx"); + if (winError.lastError != WAIT_TIMEOUT) + throw winError; + assert(removed == 0); + } else { + assert(0 < removed && removed <= sizeof(oentries)/sizeof(*oentries)); + } +#endif auto after = steady_time_point::clock::now(); @@ -439,20 +482,21 @@ void Worker::waitForInput() GoalPtr goal = j->goal.lock(); assert(goal); - std::set fds2(j->fds); +#ifndef _WIN32 + std::set fds2(j->channels); std::vector buffer(4096); for (auto & k : fds2) { const auto fdPollStatusId = get(fdToPollStatus, k); assert(fdPollStatusId); assert(*fdPollStatusId < pollStatus.size()); if (pollStatus.at(*fdPollStatusId).revents) { - ssize_t rd = ::read(k, buffer.data(), buffer.size()); + ssize_t rd = ::read(fromDescriptorReadOnly(k), buffer.data(), buffer.size()); // FIXME: is there a cleaner way to handle pt close // than EIO? Is this even standard? if (rd == 0 || (rd == -1 && errno == EIO)) { debug("%1%: got EOF", goal->getName()); goal->handleEOF(k); - j->fds.erase(k); + j->channels.erase(k); } else if (rd == -1) { if (errno != EINTR) throw SysError("%s: read failed", goal->getName()); @@ -465,6 +509,48 @@ void Worker::waitForInput() } } } +#else + decltype(j->channels)::iterator p = j->channels.begin(); + while (p != j->channels.end()) { + decltype(p) nextp = p; + ++nextp; + for (ULONG i = 0; i < removed; i++) { + if (oentries[i].lpCompletionKey == ((ULONG_PTR)((*p)->readSide.get()) ^ 0x5555)) { + printMsg(lvlVomit, "%s: read %s bytes", goal->getName(), oentries[i].dwNumberOfBytesTransferred); + if (oentries[i].dwNumberOfBytesTransferred > 0) { + std::string data { + (char *) (*p)->buffer.data(), + oentries[i].dwNumberOfBytesTransferred, + }; + //std::cerr << "read [" << data << "]" << std::endl; + j->lastOutput = after; + goal->handleChildOutput((*p)->readSide.get(), data); + } + + if (gotEOF) { + debug("%s: got EOF", goal->getName()); + goal->handleEOF((*p)->readSide.get()); + nextp = j->channels.erase(p); // no need to maintain `j->channels` ? + } else { + BOOL rc = ReadFile((*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped); + if (rc) { + // here is possible (but not obligatory) to call `goal->handleChildOutput` and repeat ReadFile immediately + } else { + windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get()); + if (winError.lastError == ERROR_BROKEN_PIPE) { + debug("%s: got EOF", goal->getName()); + goal->handleEOF((*p)->readSide.get()); + nextp = j->channels.erase(p); // no need to maintain `j->channels` ? + } else if (winError.lastError != ERROR_IO_PENDING) + throw winError; + } + } + break; + } + } + p = nextp; + } +#endif if (goal->exitCode == Goal::ecBusy && 0 != settings.maxSilentTime && diff --git a/src/libstore/unix/build/worker.hh b/src/libstore/build/worker.hh similarity index 92% rename from src/libstore/unix/build/worker.hh rename to src/libstore/build/worker.hh index ced013ddd..b57734b45 100644 --- a/src/libstore/unix/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -2,18 +2,23 @@ ///@file #include "types.hh" -#include "lock.hh" #include "store-api.hh" #include "goal.hh" #include "realisation.hh" +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif + #include #include namespace nix { /* Forward definition. */ +#ifndef _WIN32 // TODO Enable building on Windows struct DerivationGoal; +#endif struct PathSubstitutionGoal; class DrvOutputSubstitutionGoal; @@ -36,14 +41,22 @@ typedef std::chrono::time_point steady_time_point; /** * A mapping used to remember for each child process to what goal it - * belongs, and file descriptors for receiving log data and output + * belongs, and comm channels for receiving log data and output * path creation commands. */ struct Child { + using CommChannel = +#ifndef _WIN32 + Descriptor +#else + windows::AsyncPipe * +#endif + ; + WeakGoalPtr goal; Goal * goal2; // ugly hackery - std::set fds; + std::set channels; bool respectTimeouts; bool inBuildSlot; /** @@ -53,8 +66,10 @@ struct Child steady_time_point timeStarted; }; +#ifndef _WIN32 // TODO Enable building on Windows /* Forward definition. */ struct HookInstance; +#endif /** * The worker class. @@ -101,7 +116,9 @@ private: * Maps used to prevent multiple instantiations of a goal for the * same derivation / path. */ +#ifndef _WIN32 // TODO Enable building on Windows std::map> derivationGoals; +#endif std::map> substitutionGoals; std::map> drvOutputSubstitutionGoals; @@ -152,10 +169,16 @@ public: */ bool checkMismatch; +#ifdef _WIN32 + AutoCloseFD ioport; +#endif + Store & store; Store & evalStore; +#ifndef _WIN32 // TODO Enable building on Windows std::unique_ptr hook; +#endif uint64_t expectedBuilds = 0; uint64_t doneBuilds = 0; @@ -184,6 +207,7 @@ public: * Make a goal (with caching). */ +#ifndef _WIN32 // TODO Enable building on Windows /** * @ref DerivationGoal "derivation goal" */ @@ -198,6 +222,7 @@ public: std::shared_ptr makeBasicDerivationGoal( const StorePath & drvPath, const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal); +#endif /** * @ref SubstitutionGoal "substitution goal" @@ -238,7 +263,7 @@ public: * Registers a running child process. `inBuildSlot` means that * the process counts towards the jobs limit. */ - void childStarted(GoalPtr goal, const std::set & fds, + void childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts); /** diff --git a/src/libstore/local.mk b/src/libstore/local.mk index cc67da786..60174d009 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -4,7 +4,7 @@ libstore_NAME = libnixstore libstore_DIR := $(d) -libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc) +libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) ifdef HOST_UNIX libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc) endif @@ -43,7 +43,7 @@ endif INCLUDE_libstore := -I $(d) -I $(d)/build ifdef HOST_UNIX - INCLUDE_libstore += -I $(d)/unix + INCLUDE_libstore += -I $(d)/unix -I $(d)/unix/build endif ifdef HOST_LINUX INCLUDE_libstore += -I $(d)/linux diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc index 8d6e35015..004094015 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/unix/build/derivation-goal.cc @@ -1280,7 +1280,7 @@ bool DerivationGoal::isReadDesc(int fd) return fd == hook->builderOut.readSide.get(); } -void DerivationGoal::handleChildOutput(int fd, std::string_view data) +void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) { // local & `ssh://`-builds are dealt with here. auto isWrittenToLog = isReadDesc(fd); @@ -1347,7 +1347,7 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data) } -void DerivationGoal::handleEOF(int fd) +void DerivationGoal::handleEOF(Descriptor fd) { if (!currentLogLine.empty()) flushLine(); worker.wakeUp(shared_from_this()); diff --git a/src/libstore/unix/build/derivation-goal.hh b/src/libstore/unix/build/derivation-goal.hh index ddb5ee1e3..be6eb50c4 100644 --- a/src/libstore/unix/build/derivation-goal.hh +++ b/src/libstore/unix/build/derivation-goal.hh @@ -2,7 +2,7 @@ ///@file #include "parsed-derivations.hh" -#include "lock.hh" +#include "user-lock.hh" #include "outputs-spec.hh" #include "store-api.hh" #include "pathlocks.hh" @@ -292,8 +292,8 @@ struct DerivationGoal : public Goal /** * Callback used by the worker to write to the log. */ - void handleChildOutput(int fd, std::string_view data) override; - void handleEOF(int fd) override; + void handleChildOutput(Descriptor fd, std::string_view data) override; + void handleEOF(Descriptor fd) override; void flushLine(); /** diff --git a/src/libstore/unix/lock.cc b/src/libstore/unix/user-lock.cc similarity index 99% rename from src/libstore/unix/lock.cc rename to src/libstore/unix/user-lock.cc index 023c74e34..8057aa13e 100644 --- a/src/libstore/unix/lock.cc +++ b/src/libstore/unix/user-lock.cc @@ -1,4 +1,4 @@ -#include "lock.hh" +#include "user-lock.hh" #include "file-system.hh" #include "globals.hh" #include "pathlocks.hh" diff --git a/src/libstore/unix/lock.hh b/src/libstore/unix/user-lock.hh similarity index 100% rename from src/libstore/unix/lock.hh rename to src/libstore/unix/user-lock.hh diff --git a/src/libstore/windows/build.cc b/src/libstore/windows/build.cc deleted file mode 100644 index 3eadc5bda..000000000 --- a/src/libstore/windows/build.cc +++ /dev/null @@ -1,37 +0,0 @@ -#include "store-api.hh" -#include "build-result.hh" - -namespace nix { - -void Store::buildPaths(const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) -{ - unsupported("buildPaths"); -} - -std::vector Store::buildPathsWithResults( - const std::vector & reqs, - BuildMode buildMode, - std::shared_ptr evalStore) -{ - unsupported("buildPathsWithResults"); -} - -BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode) -{ - unsupported("buildDerivation"); -} - - -void Store::ensurePath(const StorePath & path) -{ - unsupported("ensurePath"); -} - - -void Store::repairPath(const StorePath & path) -{ - unsupported("repairPath"); -} - -} diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc index 738057f68..8a4d55e00 100644 --- a/src/libstore/windows/pathlocks.cc +++ b/src/libstore/windows/pathlocks.cc @@ -9,6 +9,8 @@ namespace nix { +using namespace nix::windows; + void deleteLockFile(const Path & path, Descriptor desc) { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 0419f36d6..87d181c94 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -206,11 +206,11 @@ MakeError(SystemError, Error); * * Throw this, but prefer not to catch this, and catch `SystemError` * instead. This allows implementations to freely switch between this - * and `WinError` without breaking catch blocks. + * and `windows::WinError` without breaking catch blocks. * * However, it is permissible to catch this and rethrow so long as * certain conditions are not met (e.g. to catch only if `errNo = - * EFooBar`). In that case, try to also catch the equivalent `WinError` + * EFooBar`). In that case, try to also catch the equivalent `windows::WinError` * code. * * @todo Rename this to `PosixError` or similar. At this point Windows @@ -248,7 +248,9 @@ public: }; #ifdef _WIN32 -class WinError; +namespace windows { + class WinError; +} #endif /** @@ -258,7 +260,7 @@ class WinError; */ using NativeSysError = #ifdef _WIN32 - WinError + windows::WinError #else SysError #endif diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 5ea27ccbe..36b99905a 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -136,7 +136,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len) checkInterrupt(); if (!::ReadFile(fd, data, len, &n, NULL)) { _good = false; - throw WinError("ReadFile when FdSource::readUnbuffered"); + throw windows::WinError("ReadFile when FdSource::readUnbuffered"); } #else ssize_t n; diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc index 26f769b66..b5c21ad32 100644 --- a/src/libutil/windows/file-descriptor.cc +++ b/src/libutil/windows/file-descriptor.cc @@ -14,6 +14,8 @@ namespace nix { +using namespace nix::windows; + std::string readFile(HANDLE handle) { LARGE_INTEGER li; diff --git a/src/libutil/windows/users.cc b/src/libutil/windows/users.cc index 1792ff1a1..db6c42df3 100644 --- a/src/libutil/windows/users.cc +++ b/src/libutil/windows/users.cc @@ -9,6 +9,8 @@ namespace nix { +using namespace nix::windows; + std::string getUserName() { // Get the required buffer size diff --git a/src/libutil/windows/windows-async-pipe.cc b/src/libutil/windows/windows-async-pipe.cc new file mode 100644 index 000000000..7aae603bd --- /dev/null +++ b/src/libutil/windows/windows-async-pipe.cc @@ -0,0 +1,43 @@ +#include "windows-async-pipe.hh" +#include "windows-error.hh" + +namespace nix::windows { + +void AsyncPipe::createAsyncPipe(HANDLE iocp) +{ + // std::cerr << (format("-----AsyncPipe::createAsyncPipe(%x)") % iocp) << std::endl; + + buffer.resize(0x1000); + memset(&overlapped, 0, sizeof(overlapped)); + + std::string pipeName = fmt("\\\\.\\pipe\\nix-%d-%p", GetCurrentProcessId(), (void *) this); + + readSide = CreateNamedPipeA( + pipeName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 0, 0, + INFINITE, NULL); + if (!readSide) + throw WinError("CreateNamedPipeA(%s)", pipeName); + + HANDLE hIocp = CreateIoCompletionPort(readSide.get(), iocp, (ULONG_PTR) (readSide.get()) ^ 0x5555, 0); + if (hIocp != iocp) + throw WinError("CreateIoCompletionPort(%x[%s], %x, ...) returned %x", readSide.get(), pipeName, iocp, hIocp); + + if (!ConnectNamedPipe(readSide.get(), &overlapped) && GetLastError() != ERROR_IO_PENDING) + throw WinError("ConnectNamedPipe(%s)", pipeName); + + SECURITY_ATTRIBUTES psa2 = {0}; + psa2.nLength = sizeof(SECURITY_ATTRIBUTES); + psa2.bInheritHandle = TRUE; + + writeSide = CreateFileA(pipeName.c_str(), GENERIC_WRITE, 0, &psa2, OPEN_EXISTING, 0, NULL); + if (!readSide) + throw WinError("CreateFileA(%s)", pipeName); +} + +void AsyncPipe::close() +{ + readSide.close(); + writeSide.close(); +} + +} diff --git a/src/libutil/windows/windows-async-pipe.hh b/src/libutil/windows/windows-async-pipe.hh new file mode 100644 index 000000000..c980201a8 --- /dev/null +++ b/src/libutil/windows/windows-async-pipe.hh @@ -0,0 +1,20 @@ +#pragma once +///@file + +#include "file-descriptor.hh" + +namespace nix::windows { + +class AsyncPipe +{ +public: + AutoCloseFD writeSide, readSide; + OVERLAPPED overlapped; + DWORD got; + std::vector buffer; + + void createAsyncPipe(HANDLE iocp); + void close(); +}; + +} diff --git a/src/libutil/windows/windows-error.cc b/src/libutil/windows/windows-error.cc index 26faaae6d..aead4af23 100644 --- a/src/libutil/windows/windows-error.cc +++ b/src/libutil/windows/windows-error.cc @@ -4,7 +4,7 @@ #define WIN32_LEAN_AND_MEAN #include -namespace nix { +namespace nix::windows { std::string WinError::renderError(DWORD lastError) { diff --git a/src/libutil/windows/windows-error.hh b/src/libutil/windows/windows-error.hh index fdfd0f52c..624b4c4cb 100644 --- a/src/libutil/windows/windows-error.hh +++ b/src/libutil/windows/windows-error.hh @@ -5,7 +5,7 @@ #include "error.hh" -namespace nix { +namespace nix::windows { /** * Windows Error type. From 1db7d1b8404978c4ae00d6ec5d17eca6821d94f6 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 17:42:18 +0530 Subject: [PATCH 072/910] inline the usage of `nix::readDirectory` `nix::readDirectory` is removed. `std::filesystem::directory_iterator` is used directly in places that used this util. --- src/libcmd/repl.cc | 2 +- src/libstore/builtins/buildenv.cc | 4 ++-- src/libstore/gc.cc | 4 ++-- src/libstore/globals.cc | 2 +- src/libstore/local-binary-cache-store.cc | 2 +- src/libstore/local-store.cc | 4 ++-- src/libstore/posix-fs-canonicalise.cc | 3 +-- src/libstore/profiles.cc | 2 +- src/libstore/unix/builtins/unpack-channel.cc | 8 +++++--- src/libutil/file-system.cc | 14 -------------- src/libutil/file-system.hh | 6 ------ src/libutil/linux/cgroup.cc | 2 +- src/libutil/posix-source-accessor.cc | 2 +- src/libutil/unix/file-descriptor.cc | 2 +- src/nix-collect-garbage/nix-collect-garbage.cc | 2 +- src/nix/flake.cc | 2 +- src/nix/prefetch.cc | 7 ++++--- src/nix/run.cc | 2 +- 18 files changed, 26 insertions(+), 44 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 8a9155ab6..a069dd52c 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -259,7 +259,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) try { auto dir = std::string(cur, 0, slash); auto prefix2 = std::string(cur, slash + 1); - for (auto & entry : readDirectory(dir == "" ? "/" : dir)) { + for (auto & entry : std::filesystem::directory_iterator{dir == "" ? "/" : dir}) { auto name = entry.path().filename().string(); if (name[0] != '.' && hasPrefix(name, prefix2)) completions.insert(prev + entry.path().string()); diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 5fcdf6f15..ab35c861d 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -17,10 +17,10 @@ struct State /* For each activated package, create symlinks */ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority) { - std::vector srcFiles; + std::filesystem::directory_iterator srcFiles; try { - srcFiles = readDirectory(srcDir); + srcFiles = std::filesystem::directory_iterator{srcDir}; } catch (std::filesystem::filesystem_error & e) { if (e.code() == std::errc::not_a_directory) { warn("not including '%s' in the user environment because it's not a directory", srcDir); diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 3cd4fb839..59566d010 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -161,7 +161,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) { /* Read the `temproots' directory for per-process temporary root files. */ - for (auto & i : readDirectory(tempRootsDir)) { + for (auto & i : std::filesystem::directory_iterator{tempRootsDir}) { auto name = i.path().filename().string(); if (name[0] == '.') { // Ignore hidden files. Some package managers (notably portage) create @@ -228,7 +228,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R type = getFileType(path); if (type == std::filesystem::file_type::directory) { - for (auto & i : readDirectory(path)) + for (auto & i : std::filesystem::directory_iterator{path}) findRoots(i.path().string(), i.symlink_status().type(), roots); } diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 4df2880e6..d9cab2fb8 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -345,7 +345,7 @@ void initPlugins() for (const auto & pluginFile : settings.pluginFiles.get()) { std::vector pluginFiles; try { - auto ents = readDirectory(pluginFile); + auto ents = std::filesystem::directory_iterator{pluginFile}; for (const auto & ent : ents) pluginFiles.emplace_back(ent.path()); } catch (std::filesystem::filesystem_error & e) { diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 3a48f4480..5cd6af412 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -83,7 +83,7 @@ protected: { StorePathSet paths; - for (auto & entry : readDirectory(binaryCacheDir)) { + for (auto & entry : std::filesystem::directory_iterator{binaryCacheDir}) { auto name = entry.path().filename().string(); if (name.size() != 40 || !hasSuffix(name, ".narinfo")) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 800e69309..106bd9fa1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1406,7 +1406,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) printInfo("checking link hashes..."); - for (auto & link : readDirectory(linksDir)) { + for (auto & link : std::filesystem::directory_iterator{linksDir}) { auto name = link.path().filename(); printMsg(lvlTalkative, "checking contents of '%s'", name); PosixSourceAccessor accessor; @@ -1498,7 +1498,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair database and the filesystem) in the loop below, in order to catch invalid states. */ - for (auto & i : readDirectory(realStoreDir)) { + for (auto & i : std::filesystem::directory_iterator{realStoreDir.to_string()}) { try { storePathsInStoreDir.insert({i.path().filename().string()}); } catch (BadStorePath &) { } diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc index d70ef6fae..d8bae13f5 100644 --- a/src/libstore/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -144,8 +144,7 @@ static void canonicalisePathMetaData_( #endif if (S_ISDIR(st.st_mode)) { - std::vector entries = readDirectory(path); - for (auto & i : entries) + for (auto & i : std::filesystem::directory_iterator{path}) canonicalisePathMetaData_( i.path().string(), #ifndef _WIN32 diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index fa8026703..d0da96262 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -37,7 +37,7 @@ std::pair> findGenerations(Path pro std::filesystem::path profileDir = dirOf(profile); auto profileName = std::string(baseNameOf(profile)); - for (auto & i : readDirectory(profileDir.string())) { + for (auto & i : std::filesystem::directory_iterator{profileDir}) { if (auto n = parseName(profileName, i.path().filename().string())) { auto path = i.path().string(); gens.push_back({ diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index 47bf5d49c..c53604145 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -21,10 +21,12 @@ void builtinUnpackChannel( unpackTarfile(src, out); - auto entries = readDirectory(out); - if (entries.size() != 1) + auto entries = std::filesystem::directory_iterator{out}; + auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); + + if (file_count != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile(entries[0].path().string(), (out + "/" + channelName)); + renameFile(entries->path().string(), (out + "/" + channelName)); } } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 39efa19fe..7032ecdf7 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -222,20 +222,6 @@ Path readLink(const Path & path) } -std::vector readDirectory(const Path & path) -{ - std::vector entries; - entries.reserve(64); - - for (auto & entry : fs::directory_iterator{path}) { - checkInterrupt(); - entries.push_back(std::move(entry)); - } - - return entries; -} - - fs::file_type getFileType(const Path & path) { return fs::symlink_status(path).type(); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 4536accc3..73150239a 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -122,12 +122,6 @@ Path readLink(const Path & path); */ Descriptor openDirectory(const std::filesystem::path & path); -/** - * Read the contents of a directory. The entries `.` and `..` are - * removed. - */ -std::vector readDirectory(const Path & path); - std::filesystem::file_type getFileType(const Path & path); /** diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 619ef7764..ec4077478 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -64,7 +64,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ - for (auto & entry : readDirectory(cgroup)) { + for (auto & entry : std::filesystem::directory_iterator{cgroup}) { if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue; destroyCgroup(cgroup / entry.path().filename(), false); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index aa13f4c56..225fc852c 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -132,7 +132,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & { assertNoSymlinks(path); DirEntries res; - for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { + for (auto & entry : std::filesystem::directory_iterator{makeAbsPath(path)}) { auto type = [&]() -> std::optional { std::filesystem::file_type nativeType; try { diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 222d077e5..84a33af81 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -124,7 +124,7 @@ void closeMostFDs(const std::set & exceptions) { #if __linux__ try { - for (auto & s : readDirectory("/proc/self/fd")) { + for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) { auto fd = std::stoi(s.path().filename()); if (!exceptions.count(fd)) { debug("closing leaked FD %d", fd); diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 02a3a4b83..91209c978 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -27,7 +27,7 @@ void removeOldGenerations(std::string dir) bool canWrite = access(dir.c_str(), W_OK) == 0; - for (auto & i : readDirectory(dir)) { + for (auto & i : std::filesystem::directory_iterator{dir}) { checkInterrupt(); auto path = i.path().string(); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9c1888aa0..78a8a55c3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -866,7 +866,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand { createDirs(to); - for (auto & entry : readDirectory(from)) { + for (auto & entry : std::filesystem::directory_iterator{from}) { auto from2 = entry.path().string(); auto to2 = to + "/" + entry.path().filename().string(); auto st = lstat(from2); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index e932170cf..3ce52acc5 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -115,9 +115,10 @@ std::tuple prefetchFile( /* If the archive unpacks to a single file/directory, then use that as the top-level. */ - auto entries = readDirectory(unpacked); - if (entries.size() == 1) - tmpFile = entries[0].path(); + auto entries = std::filesystem::directory_iterator{unpacked}; + auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); + if (file_count == 1) + tmpFile = entries->path(); else tmpFile = unpacked; } diff --git a/src/nix/run.cc b/src/nix/run.cc index 9c559bdf6..cc999ddf4 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -248,7 +248,7 @@ void chrootHelper(int argc, char * * argv) if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND, 0) == -1) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); - for (auto entry : readDirectory("/")) { + for (auto entry : std::filesystem::directory_iterator{"/"}) { auto src = entry.path().string(); Path dst = tmpDir + "/" + entry.path().filename().string(); if (pathExists(dst)) continue; From 45376637404bbb971fc09e80fe3f7d924cb152ef Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 18:40:16 +0530 Subject: [PATCH 073/910] inline the usage of `nix::renameFile` use `std::filesystem::rename` everywhere and remove `nix::renameFile` --- src/libstore/indirect-root-store.cc | 2 +- src/libstore/local-binary-cache-store.cc | 2 +- src/libstore/local-store.cc | 2 +- src/libstore/unix/build/derivation-goal.cc | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 6 +++--- src/libstore/unix/builtins/unpack-channel.cc | 2 +- src/libutil/file-system.cc | 11 +++-------- src/libutil/file-system.hh | 2 -- 8 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc index 082a458ab..844d0d6ed 100644 --- a/src/libstore/indirect-root-store.cc +++ b/src/libstore/indirect-root-store.cc @@ -12,7 +12,7 @@ void IndirectRootStore::makeSymlink(const Path & link, const Path & target) createSymlink(target, tempLink); /* Atomically replace the old one. */ - renameFile(tempLink, link); + std::filesystem::rename(tempLink, link); } Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 3a48f4480..6743c633e 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -64,7 +64,7 @@ protected: AutoDelete del(tmp, false); StreamToSourceAdapter source(istream); writeFile(tmp, source); - renameFile(tmp, path2); + std::filesystem::rename(tmp, path2); del.cancel(); } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 800e69309..1f31f9bd8 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1779,7 +1779,7 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log) writeFile(tmpFile, compress("bzip2", log)); - renameFile(tmpFile, logPath); + std::filesystem::rename(tmpFile, logPath); } std::optional LocalStore::getVersion() diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc index 8d6e35015..89518b055 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/unix/build/derivation-goal.cc @@ -783,7 +783,7 @@ static void movePath(const Path & src, const Path & dst) if (changePerm) chmod_(src, st.st_mode | S_IWUSR); - renameFile(src, dst); + std::filesystem::rename(src, dst); if (changePerm) chmod_(dst, st.st_mode); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 3b010350d..331794292 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -285,7 +285,7 @@ static void movePath(const Path & src, const Path & dst) if (changePerm) chmod_(src, st.st_mode | S_IWUSR); - renameFile(src, dst); + std::filesystem::rename(src, dst); if (changePerm) chmod_(dst, st.st_mode); @@ -372,7 +372,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull() if (buildMode != bmCheck && status.known->isValid()) continue; auto p = worker.store.toRealPath(status.known->path); if (pathExists(chrootRootDir + p)) - renameFile((chrootRootDir + p), p); + std::filesystem::rename((chrootRootDir + p), p); } return diskFull; @@ -2569,7 +2569,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() // that there's no stale file descriptor pointing to it Path tmpOutput = actualPath + ".tmp"; copyFile(actualPath, tmpOutput, true); - renameFile(tmpOutput, actualPath); + std::filesystem::rename(tmpOutput, actualPath); auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { .method = dof.ca.method, diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index 47bf5d49c..bdc724a70 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -24,7 +24,7 @@ void builtinUnpackChannel( auto entries = readDirectory(out); if (entries.size() != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile(entries[0].path().string(), (out + "/" + channelName)); + std::filesystem::rename(entries[0].path().string(), (out + "/" + channelName)); } } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 39efa19fe..b9f96b88f 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -588,7 +588,7 @@ void replaceSymlink(const Path & target, const Path & link) throw; } - renameFile(tmp, link); + std::filesystem::rename(tmp, link); break; } @@ -651,15 +651,10 @@ void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); } -void renameFile(const Path & oldName, const Path & newName) -{ - fs::rename(oldName, newName); -} - void moveFile(const Path & oldName, const Path & newName) { try { - renameFile(oldName, newName); + std::filesystem::rename(oldName, newName); } catch (fs::filesystem_error & e) { auto oldPath = fs::path(oldName); auto newPath = fs::path(newName); @@ -674,7 +669,7 @@ void moveFile(const Path & oldName, const Path & newName) fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); copy(fs::directory_entry(oldPath), tempCopyTarget, true); - renameFile( + std::filesystem::rename( os_string_to_string(PathViewNG { tempCopyTarget }), os_string_to_string(PathViewNG { newPath })); } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 4536accc3..9f3db05c3 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -177,8 +177,6 @@ void createSymlink(const Path & target, const Path & link); */ void replaceSymlink(const Path & target, const Path & link); -void renameFile(const Path & src, const Path & dst); - /** * Similar to 'renameFile', but fallback to a copy+remove if `src` and `dst` * are on a different filesystem. From d3b7367c802337d5430103bff550b3c447cc5b0a Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 18:58:05 +0530 Subject: [PATCH 074/910] inline usage of `nix::getFileType` and remove it --- src/libstore/gc.cc | 2 +- src/libutil/file-system.cc | 6 ------ src/libutil/file-system.hh | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 3cd4fb839..877608a84 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -225,7 +225,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R try { if (type == std::filesystem::file_type::unknown) - type = getFileType(path); + type = std::filesystem::symlink_status(path).type(); if (type == std::filesystem::file_type::directory) { for (auto & i : readDirectory(path)) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index b9f96b88f..cc93da996 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -236,12 +236,6 @@ std::vector readDirectory(const Path & path) } -fs::file_type getFileType(const Path & path) -{ - return fs::symlink_status(path).type(); -} - - std::string readFile(const Path & path) { AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 9f3db05c3..669de704b 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -128,8 +128,6 @@ Descriptor openDirectory(const std::filesystem::path & path); */ std::vector readDirectory(const Path & path); -std::filesystem::file_type getFileType(const Path & path); - /** * Read the contents of a file into a string. */ From ccf94545db454c6b2b964cee8a72835764f64898 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 19:20:17 +0530 Subject: [PATCH 075/910] rename `copy` -> `copyFile` and remove old copyFile the old `copyFile` was just a wrapper that was calling the `copy` function. This wrapper function is removed and the `copy` function is renamed to `copyFile`. --- src/libstore/unix/build/local-derivation-goal.cc | 9 +++++++-- src/libutil/file-system.cc | 11 +++-------- src/libutil/file-system.hh | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 331794292..9e2b0bd34 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -421,7 +421,9 @@ static void doBind(const Path & source, const Path & target, bool optional = fal } else if (S_ISLNK(st.st_mode)) { // Symlinks can (apparently) not be bind-mounted, so just copy it createDirs(dirOf(target)); - copyFile(source, target, /* andDelete */ false); + copyFile( + std::filesystem::directory_entry(std::filesystem::path(source)), + std::filesystem::path(target), false); } else { createDirs(dirOf(target)); writeFile(target, ""); @@ -2568,7 +2570,10 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() // Replace the output by a fresh copy of itself to make sure // that there's no stale file descriptor pointing to it Path tmpOutput = actualPath + ".tmp"; - copyFile(actualPath, tmpOutput, true); + copyFile( + std::filesystem::directory_entry(std::filesystem::path(actualPath)), + std::filesystem::path(tmpOutput), true); + std::filesystem::rename(tmpOutput, actualPath); auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index cc93da996..294887587 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -605,7 +605,7 @@ static void setWriteTime(const fs::path & p, const struct stat & st) } #endif -void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) +void copyFile(const fs::directory_entry & from, const fs::path & to, bool andDelete) { #ifndef _WIN32 // TODO: Rewrite the `is_*` to use `symlink_status()` @@ -624,7 +624,7 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) } else if (fs::is_directory(fromStatus)) { fs::create_directory(to); for (auto & entry : fs::directory_iterator(from.path())) { - copy(entry, to / entry.path().filename(), andDelete); + copyFile(entry, to / entry.path().filename(), andDelete); } } else { throw Error("file '%s' has an unsupported type", from.path()); @@ -640,11 +640,6 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) } } -void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) -{ - return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); -} - void moveFile(const Path & oldName, const Path & newName) { try { @@ -662,7 +657,7 @@ void moveFile(const Path & oldName, const Path & newName) if (e.code().value() == EXDEV) { fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); - copy(fs::directory_entry(oldPath), tempCopyTarget, true); + copyFile(fs::directory_entry(oldPath), tempCopyTarget, true); std::filesystem::rename( os_string_to_string(PathViewNG { tempCopyTarget }), os_string_to_string(PathViewNG { newPath })); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 669de704b..acc921ebe 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -190,7 +190,7 @@ void moveFile(const Path & src, const Path & dst); * with the guaranty that the destination will be “fresh”, with no stale inode * or file descriptor pointing to it). */ -void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); +void copyFile(const std::filesystem::directory_entry & from, const std::filesystem::path & to, bool andDelete); /** * Automatic cleanup of resources. From 8b5e8f4fba5728f2b3e90fcd1ab15df77e3ea0e8 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Sun, 12 May 2024 16:42:43 -0400 Subject: [PATCH 076/910] git putFile: support flake maximalists Passing the commit message as an argument causes update failures on repositories with lots of flake inputs. In some cases, the commit message is over 250,000 bytes. --- src/libfetchers/unix/git.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index fbdac1fe6..fa7ef3621 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -342,7 +342,8 @@ struct GitInputScheme : InputScheme logger->pause(); Finally restoreLogger([]() { logger->resume(); }); runProgram("git", true, - { "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-m", *commitMsg }); + { "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-F", "-" }, + *commitMsg); } } } From 6bf7edb18b2815ac5758c937d69c1098e021edca Mon Sep 17 00:00:00 2001 From: Hraban Luyat Date: Sun, 12 May 2024 21:36:08 -0400 Subject: [PATCH 077/910] =?UTF-8?q?fix:=20don=E2=80=99t=20expand=20aliases?= =?UTF-8?q?=20in=20develop=20stdenv=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes https://github.com/NixOS/nixpkgs/pull/290775 by not expanding aliases when sourcing the stdenv setup script. The way bash handles aliases is to expand them when a function is defined, not when it is used. I.e.: $ alias echo="echo bar " $ echo foo bar foo $ xyzzy() { echo foo; } $ shopt -u expand_aliases $ xyzzy bar foo $ xyzzy2() { echo foo; } $ xyzzy2 foo The problem is that ~/.bashrc is sourced before the stdenv setup, and bashrc commonly sets aliases for ‘cp’, ‘mv’ and ‘rm’ which you don’t want to take effect in the stdenv derivation builders. The original commit introducing this feature (5fd8cf76676a280ae2b7a86ddabc6b14b41ebfe5) even mentioned this very alias. The only way to avoid this is to disable aliases entirely while sourcing the stdenv setup, and reenable them afterwards. --- src/nix/develop.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 08d44d7aa..0363ca829 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -610,7 +610,7 @@ struct CmdDevelop : Common, MixEnvironment } else { - script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script; + script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\nshopt -u expand_aliases\n" + script + "\nshopt -s expand_aliases\n"; if (developSettings.bashPrompt != "") script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt.get())); From dbe1b51580451bcb08ccc1c768f7f0f4f0e9f421 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 7 May 2024 19:25:44 +0200 Subject: [PATCH 078/910] Add setting to warn about copying/hashing large paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is useful for diagnosing whether an evaluation is copying large paths to the store. Example: $ nix build .#packages.x86_64-linux.default --large-path-warning-threshold 1000000 warning: copied large path '/home/eelco/Dev/nix-master/' to the store (6271792 bytes) warning: copied large path '«github:NixOS/nixpkgs/b550fe4b4776908ac2a861124307045f8e717c8e?narHash=sha256-7kkJQd4rZ%2BvFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo%3D»/' to the store (155263768 bytes) warning: copied large path '«github:libgit2/libgit2/45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5?narHash=sha256-oX4Z3S9WtJlwvj0uH9HlYcWv%2Bx1hqp8mhXl7HsLu2f0%3D»/' to the store (22175416 bytes) warning: copied large path '/nix/store/z985088mcd6w23qwdlirsinnyzayagki-source' to the store (5885872 bytes) --- perl/lib/Nix/Store.xs | 2 +- src/libstore/binary-cache-store.cc | 2 +- src/libstore/globals.hh | 10 ++++++++++ src/libstore/local-store.cc | 4 ++-- src/libstore/store-api.cc | 10 ++++++++-- src/libstore/unix/build/worker.cc | 4 ++-- src/libutil/file-content-address.cc | 10 ++++++---- src/libutil/file-content-address.hh | 7 ++++--- src/libutil/serialise.hh | 20 ++++++++++++++++++++ 9 files changed, 54 insertions(+), 15 deletions(-) diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index ee211ef64..e751c2be1 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -258,7 +258,7 @@ hashPath(char * algo, int base32, char * path) try { Hash h = hashPath( PosixSourceAccessor::createAtRoot(path), - FileIngestionMethod::Recursive, parseHashAlgo(algo)); + FileIngestionMethod::Recursive, parseHashAlgo(algo)).first; auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 5153ca64f..67d00f364 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -453,7 +453,7 @@ StorePath BinaryCacheStore::addToStore( non-recursive+sha256 so we can just use the default implementation of this method in terms of addToStoreFromDump. */ - auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); + auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter).first; auto source = sinkToSource([&](Sink & sink) { path.dumpPath(sink, filter); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 108933422..dc53a07f1 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1262,6 +1262,16 @@ public: store paths of the latest Nix release. )" }; + + Setting largePathWarningThreshold{ + this, + std::numeric_limits::max(), + "large-path-warning-threshold", + R"( + Warn when copying a path larger than this number of bytes to the Nix store + (as determined by its NAR serialisation). + )" + }; }; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 800e69309..33c4d7372 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1272,7 +1272,7 @@ StorePath LocalStore::addToStoreFromDump( ? dumpHash : hashPath( PosixSourceAccessor::createAtRoot(tempPath), - hashMethod.getFileIngestionMethod(), hashAlgo), + hashMethod.getFileIngestionMethod(), hashAlgo).first, { .others = references, // caller is not capable of creating a self-reference, because this is content-addressed without modulus @@ -1412,7 +1412,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) PosixSourceAccessor accessor; std::string hash = hashPath( PosixSourceAccessor::createAtRoot(link.path()), - FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); + FileIngestionMethod::Recursive, HashAlgorithm::SHA256).first.to_string(HashFormat::Nix32, false); if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", link.path(), name, hash); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 419c55e92..a2095e02e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -169,7 +169,9 @@ std::pair StoreDirConfig::computeStorePath( const StorePathSet & references, PathFilter & filter) const { - auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); + auto [h, size] = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); + if (size && *size >= settings.largePathWarningThreshold) + warn("hashed large path '%s' (%d bytes)", path, *size); return { makeFixedOutputPathFromCA( name, @@ -210,7 +212,11 @@ StorePath Store::addToStore( auto source = sinkToSource([&](Sink & sink) { dumpPath(path, sink, fsm, filter); }); - return addToStoreFromDump(*source, name, fsm, method, hashAlgo, references, repair); + LengthSource lengthSource(*source); + auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); + if (lengthSource.total >= settings.largePathWarningThreshold) + warn("copied large path '%s' to the store (%d bytes)", path, lengthSource.total); + return storePath; } void Store::addMultipleToStore( diff --git a/src/libstore/unix/build/worker.cc b/src/libstore/unix/build/worker.cc index 03fc280a4..2cca06213 100644 --- a/src/libstore/unix/build/worker.cc +++ b/src/libstore/unix/build/worker.cc @@ -529,9 +529,9 @@ bool Worker::pathContentsGood(const StorePath & path) if (!pathExists(store.printStorePath(path))) res = false; else { - Hash current = hashPath( + auto current = hashPath( {store.getFSAccessor(), CanonPath(store.printStorePath(path))}, - FileIngestionMethod::Recursive, info->narHash.algo); + FileIngestionMethod::Recursive, info->narHash.algo).first; Hash nullHash(HashAlgorithm::SHA256); res = info->narHash == nullHash || info->narHash == current; } diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 769042d00..8b1e3117a 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -112,17 +112,19 @@ HashResult hashPath( } -Hash hashPath( +std::pair> hashPath( const SourcePath & path, FileIngestionMethod method, HashAlgorithm ht, PathFilter & filter) { switch (method) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: - return hashPath(path, (FileSerialisationMethod) method, ht, filter).first; + case FileIngestionMethod::Recursive: { + auto res = hashPath(path, (FileSerialisationMethod) method, ht, filter); + return {res.first, {res.second}}; + } case FileIngestionMethod::Git: - return git::dumpHash(ht, path, filter).hash; + return {git::dumpHash(ht, path, filter).hash, std::nullopt}; } assert(false); } diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index 145a8fb1f..cd63be551 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -132,14 +132,15 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method); /** * Compute the hash of the given file system object according to the - * given method. + * given method, and for some ingestion methods, the size of the + * serialisation. * * Unlike the other `hashPath`, this works on an arbitrary * `FileIngestionMethod` instead of `FileSerialisationMethod`, but - * doesn't return the size as this is this is not a both simple and + * may not return the size as this is this is not a both simple and * useful defined for a merkle format. */ -Hash hashPath( +std::pair> hashPath( const SourcePath & path, FileIngestionMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter); diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 6249ddaf5..18f4a79c3 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -283,6 +283,26 @@ struct LengthSink : Sink } }; +/** + * A wrapper source that counts the number of bytes read from it. + */ +struct LengthSource : Source +{ + Source & next; + + LengthSource(Source & next) : next(next) + { } + + uint64_t total = 0; + + size_t read(char * data, size_t len) override + { + auto n = next.read(data, len); + total += n; + return n; + } +}; + /** * Convert a function into a sink. */ From 5314430437d117d2e041b87cc568702c66f8702a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 10 May 2024 16:49:40 +0200 Subject: [PATCH 079/910] Move printSize() into libutil Also always include the unit (i.e. "MiB" instead of "M"). --- src/libutil/util.cc | 15 +++++++++++++++ src/libutil/util.hh | 6 ++++++ src/nix/path-info.cc | 17 +++-------------- src/nix/path-info.md | 4 ++-- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 103ce4232..f893fc12d 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -112,6 +112,21 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites) } +std::string renderSize(uint64_t value) +{ + static const std::array prefixes{{ + 'K', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' + }}; + size_t power = 0; + double res = value; + while (res > 1024 && power < prefixes.size()) { + ++power; + res /= 1024; + } + return fmt("%6.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power)); +} + + bool hasPrefix(std::string_view s, std::string_view prefix) { return s.compare(0, prefix.size(), prefix) == 0; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 8b049875a..01e42ce57 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -137,6 +137,12 @@ N string2IntWithUnitPrefix(std::string_view s) throw UsageError("'%s' is not an integer", s); } +/** + * Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6 + * GiB`. + */ +std::string renderSize(uint64_t value); + /** * Parse a string into a float. */ diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 921b25d7f..a1a2c40f4 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -139,21 +139,10 @@ struct CmdPathInfo : StorePathsCommand, MixJSON void printSize(uint64_t value) { - if (!humanReadable) { + if (humanReadable) + std::cout << fmt("\t%s", renderSize(value)); + else std::cout << fmt("\t%11d", value); - return; - } - - static const std::array idents{{ - ' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' - }}; - size_t power = 0; - double res = value; - while (res > 1024 && power < idents.size()) { - ++power; - res /= 1024; - } - std::cout << fmt("\t%6.1f%c", res, idents.at(power)); } void run(ref store, StorePaths && storePaths) override diff --git a/src/nix/path-info.md b/src/nix/path-info.md index 789984559..2e39225b8 100644 --- a/src/nix/path-info.md +++ b/src/nix/path-info.md @@ -26,8 +26,8 @@ R""( ```console # nix path-info --recursive --size --closure-size --human-readable nixpkgs#rustc - /nix/store/01rrgsg5zk3cds0xgdsq40zpk6g51dz9-ncurses-6.2-dev 386.7K 69.1M - /nix/store/0q783wnvixpqz6dxjp16nw296avgczam-libpfm-4.11.0 5.9M 37.4M + /nix/store/01rrgsg5zk3cds0xgdsq40zpk6g51dz9-ncurses-6.2-dev 386.7 KiB 69.1 MiB + /nix/store/0q783wnvixpqz6dxjp16nw296avgczam-libpfm-4.11.0 5.9 MiB 37.4 MiB … ``` From cf3b044b7efb67f82d151da66b339a9ad8c1e5ac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 10 May 2024 16:58:19 +0200 Subject: [PATCH 080/910] Make large path warnings human-readable --- src/libstore/store-api.cc | 4 ++-- src/libutil/util.cc | 4 ++-- src/libutil/util.hh | 5 +++-- src/nix/path-info.cc | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index a2095e02e..0b78f999e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -171,7 +171,7 @@ std::pair StoreDirConfig::computeStorePath( { auto [h, size] = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); if (size && *size >= settings.largePathWarningThreshold) - warn("hashed large path '%s' (%d bytes)", path, *size); + warn("hashed large path '%s' (%s)", path, renderSize(*size)); return { makeFixedOutputPathFromCA( name, @@ -215,7 +215,7 @@ StorePath Store::addToStore( LengthSource lengthSource(*source); auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); if (lengthSource.total >= settings.largePathWarningThreshold) - warn("copied large path '%s' to the store (%d bytes)", path, lengthSource.total); + warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total)); return storePath; } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index f893fc12d..16bca093c 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -112,7 +112,7 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites) } -std::string renderSize(uint64_t value) +std::string renderSize(uint64_t value, bool align) { static const std::array prefixes{{ 'K', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' @@ -123,7 +123,7 @@ std::string renderSize(uint64_t value) ++power; res /= 1024; } - return fmt("%6.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power)); + return fmt(align ? "%6.1f %ciB" : "%.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power)); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 01e42ce57..0c8c82bfd 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -139,9 +139,10 @@ N string2IntWithUnitPrefix(std::string_view s) /** * Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6 - * GiB`. + * GiB`. If `align` is set, the number will be right-justified + * (e.g. `__11.6 GiB`). */ -std::string renderSize(uint64_t value); +std::string renderSize(uint64_t value, bool align = false); /** * Parse a string into a float. diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index a1a2c40f4..47f9baee5 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -140,7 +140,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON void printSize(uint64_t value) { if (humanReadable) - std::cout << fmt("\t%s", renderSize(value)); + std::cout << fmt("\t%s", renderSize(value, true)); else std::cout << fmt("\t%11d", value); } From 4d0777ca69463cbaba3501c10d16012101bb0933 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Mon, 13 May 2024 15:36:00 +0530 Subject: [PATCH 081/910] fix: copy fileName before calling `std::distance` --- src/libstore/unix/builtins/unpack-channel.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index c53604145..bb60688ea 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -22,11 +22,12 @@ void builtinUnpackChannel( unpackTarfile(src, out); auto entries = std::filesystem::directory_iterator{out}; - auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); + auto fileName = entries->path().string(); + auto fileCount = std::distance(std::filesystem::begin(entries), std::filesystem::end(entries)); - if (file_count != 1) + if (fileCount != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile(entries->path().string(), (out + "/" + channelName)); + renameFile(fileName, (out + "/" + channelName)); } } From f0b5628eb2ff90bf6e7009966d1fad39214c303d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 13 May 2024 12:08:45 +0200 Subject: [PATCH 082/910] renderSize(): Add some unit tests --- tests/unit/libutil/tests.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index b66872a6e..9be4a400d 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -421,6 +421,23 @@ namespace nix { ASSERT_EQ(string2Int("-100"), -100); } + /* ---------------------------------------------------------------------------- + * renderSize + * --------------------------------------------------------------------------*/ + + TEST(renderSize, misc) { + ASSERT_EQ(renderSize(0, true), " 0.0 KiB"); + ASSERT_EQ(renderSize(100, true), " 0.1 KiB"); + ASSERT_EQ(renderSize(100), "0.1 KiB"); + ASSERT_EQ(renderSize(972, true), " 0.9 KiB"); + ASSERT_EQ(renderSize(973, true), " 1.0 KiB"); // FIXME: should round down + ASSERT_EQ(renderSize(1024, true), " 1.0 KiB"); + ASSERT_EQ(renderSize(1024 * 1024, true), "1024.0 KiB"); + ASSERT_EQ(renderSize(1100 * 1024, true), " 1.1 MiB"); + ASSERT_EQ(renderSize(2ULL * 1024 * 1024 * 1024, true), " 2.0 GiB"); + ASSERT_EQ(renderSize(2100ULL * 1024 * 1024 * 1024, true), " 2.1 TiB"); + } + #ifndef _WIN32 // TODO re-enable on Windows, once we can start processes /* ---------------------------------------------------------------------------- * statusOk From 553468216636a0037f988a31f95cda40a0e7e3d0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 13 May 2024 10:29:35 +0200 Subject: [PATCH 083/910] Update src/libutil/util.hh Co-authored-by: Robert Hensing --- src/libutil/util.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 0c8c82bfd..6db59ef20 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -139,8 +139,8 @@ N string2IntWithUnitPrefix(std::string_view s) /** * Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6 - * GiB`. If `align` is set, the number will be right-justified - * (e.g. `__11.6 GiB`). + * GiB`. If `align` is set, the number will be right-justified by + * padding with spaces on the left. */ std::string renderSize(uint64_t value, bool align = false); From 62e1ea2f4b563d73fac8d48feae0e9968c9c5bc9 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Mon, 13 May 2024 16:10:21 +0530 Subject: [PATCH 084/910] use `path` for `from` arg in `nix::copyFile` --- .../unix/build/local-derivation-goal.cc | 4 ++-- src/libutil/file-system.cc | 20 +++++++++---------- src/libutil/file-system.hh | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 9e2b0bd34..16095cf5d 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -422,7 +422,7 @@ static void doBind(const Path & source, const Path & target, bool optional = fal // Symlinks can (apparently) not be bind-mounted, so just copy it createDirs(dirOf(target)); copyFile( - std::filesystem::directory_entry(std::filesystem::path(source)), + std::filesystem::path(source), std::filesystem::path(target), false); } else { createDirs(dirOf(target)); @@ -2571,7 +2571,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() // that there's no stale file descriptor pointing to it Path tmpOutput = actualPath + ".tmp"; copyFile( - std::filesystem::directory_entry(std::filesystem::path(actualPath)), + std::filesystem::path(actualPath), std::filesystem::path(tmpOutput), true); std::filesystem::rename(tmpOutput, actualPath); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 294887587..f5628bfdb 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -605,29 +605,29 @@ static void setWriteTime(const fs::path & p, const struct stat & st) } #endif -void copyFile(const fs::directory_entry & from, const fs::path & to, bool andDelete) +void copyFile(const fs::path & from, const fs::path & to, bool andDelete) { #ifndef _WIN32 // TODO: Rewrite the `is_*` to use `symlink_status()` - auto statOfFrom = lstat(from.path().c_str()); + auto statOfFrom = lstat(from.c_str()); #endif - auto fromStatus = from.symlink_status(); + auto fromStatus = fs::symlink_status(from); // Mark the directory as writable so that we can delete its children if (andDelete && fs::is_directory(fromStatus)) { - fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); + fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); } if (fs::is_symlink(fromStatus) || fs::is_regular_file(fromStatus)) { - fs::copy(from.path(), to, fs::copy_options::copy_symlinks | fs::copy_options::overwrite_existing); + fs::copy(from, to, fs::copy_options::copy_symlinks | fs::copy_options::overwrite_existing); } else if (fs::is_directory(fromStatus)) { fs::create_directory(to); - for (auto & entry : fs::directory_iterator(from.path())) { + for (auto & entry : fs::directory_iterator(from)) { copyFile(entry, to / entry.path().filename(), andDelete); } } else { - throw Error("file '%s' has an unsupported type", from.path()); + throw Error("file '%s' has an unsupported type", from); } #ifndef _WIN32 @@ -635,8 +635,8 @@ void copyFile(const fs::directory_entry & from, const fs::path & to, bool andDel #endif if (andDelete) { if (!fs::is_symlink(fromStatus)) - fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); - fs::remove(from.path()); + fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); + fs::remove(from); } } @@ -657,7 +657,7 @@ void moveFile(const Path & oldName, const Path & newName) if (e.code().value() == EXDEV) { fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); - copyFile(fs::directory_entry(oldPath), tempCopyTarget, true); + copyFile(oldPath, tempCopyTarget, true); std::filesystem::rename( os_string_to_string(PathViewNG { tempCopyTarget }), os_string_to_string(PathViewNG { newPath })); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index acc921ebe..a3f412224 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -190,7 +190,7 @@ void moveFile(const Path & src, const Path & dst); * with the guaranty that the destination will be “fresh”, with no stale inode * or file descriptor pointing to it). */ -void copyFile(const std::filesystem::directory_entry & from, const std::filesystem::path & to, bool andDelete); +void copyFile(const std::filesystem::path & from, const std::filesystem::path & to, bool andDelete); /** * Automatic cleanup of resources. From 33ca905cdb4ea52d95f4177bd8c141bc16f7a8d1 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 6 May 2024 15:42:49 +0200 Subject: [PATCH 085/910] tests: simplify initialisation and wiring pararameterisation is not actually needed the way things are currently set up, and it confused me when trying to understand what the code does. all but one test sources vars-and-functions.sh, which nominally only defines variables, but in practice is always coupled with the actual initialisation. while the cleaner way of making this more legible would be to source variables and initialisation separately, this would produce a huge diff. the change requires a few small fixes to keep the tests working: - only create test home directory during initialisation that vars-and-functions.sh wrote to the file system seems not write - fix creation of the test directory due to statefulness, the test home directory was implicitly creating the test root, too. decoupling that made it apparent that this was probably not intentional, and certainly confusing. - only source vars-and-functions.sh if init.sh is not needed there is one test case that only needs a helper function but no initialisation side effects - remove some unnecessary cleanups and split parts of re-used test code there were confusing bits in how initialisation code was repurposed, which break if trying to refactor the outer layers naively... --- doc/manual/src/contributing/testing.md | 6 +- mk/debug-test.sh | 4 - mk/lib.mk | 7 +- mk/run-test.sh | 4 - mk/tests.mk | 4 +- tests/functional/common.sh | 6 +- tests/functional/{ => common}/init.sh | 10 +- .../common/vars-and-functions.sh.in | 3 +- tests/functional/lang.sh | 2 +- .../local-overlay-store/add-lower.sh | 1 + .../local-overlay-store/bad-uris.sh | 1 + tests/functional/local-overlay-store/build.sh | 1 + .../local-overlay-store/check-post-init.sh | 1 + .../functional/local-overlay-store/common.sh | 5 +- .../local-overlay-store/delete-duplicate.sh | 1 + .../local-overlay-store/delete-refs.sh | 1 + tests/functional/local-overlay-store/gc.sh | 1 + .../local-overlay-store/optimise.sh | 1 + .../local-overlay-store/redundant-add.sh | 1 + .../local-overlay-store/stale-file-handle.sh | 1 + .../functional/local-overlay-store/verify.sh | 1 + tests/functional/local.mk | 1 - tests/functional/remote-store.sh | 2 +- tests/functional/user-envs-test-case.sh | 191 +++++++++++++++++ tests/functional/user-envs.sh | 198 +----------------- 25 files changed, 227 insertions(+), 227 deletions(-) rename tests/functional/{ => common}/init.sh (86%) create mode 100644 tests/functional/user-envs-test-case.sh diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 31c39c16c..607914ba3 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -162,14 +162,14 @@ ran test tests/functional/${testName}.sh... [PASS] or without `make`: ```shell-session -$ ./mk/run-test.sh tests/functional/${testName}.sh tests/functional/init.sh +$ ./mk/run-test.sh tests/functional/${testName}.sh ran test tests/functional/${testName}.sh... [PASS] ``` To see the complete output, one can also run: ```shell-session -$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh +$ ./mk/debug-test.sh tests/functional/${testName}.sh +(${testName}.sh:1) foo output from foo +(${testName}.sh:2) bar @@ -204,7 +204,7 @@ edit it like so: Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point: ```shell-session -$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh +$ ./mk/debug-test.sh tests/functional/${testName}.sh ... + gdb blash blub GNU gdb (GDB) 12.1 diff --git a/mk/debug-test.sh b/mk/debug-test.sh index 1cd6f9dce..0dd4406c3 100755 --- a/mk/debug-test.sh +++ b/mk/debug-test.sh @@ -3,12 +3,8 @@ set -eu -o pipefail test=$1 -init=${2-} dir="$(dirname "${BASH_SOURCE[0]}")" source "$dir/common-test.sh" -if [ -n "$init" ]; then - (run "$init" 2>/dev/null > /dev/null) -fi run "$test" diff --git a/mk/lib.mk b/mk/lib.mk index a002d823f..1e7af6ad5 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -87,15 +87,14 @@ $(foreach script, $(bin-scripts), $(eval $(call install-program-in,$(script),$(b $(foreach script, $(bin-scripts), $(eval programs-list += $(script))) $(foreach script, $(noinst-scripts), $(eval programs-list += $(script))) $(foreach template, $(template-files), $(eval $(call instantiate-template,$(template)))) -install_test_init=tests/functional/init.sh $(foreach test, $(install-tests), \ - $(eval $(call run-test,$(test),$(install_test_init))) \ + $(eval $(call run-test,$(test))) \ $(eval installcheck: $(test).test)) $(foreach test-group, $(install-tests-groups), \ - $(eval $(call run-test-group,$(test-group),$(install_test_init))) \ + $(eval $(call run-test-group,$(test-group))) \ $(eval installcheck: $(test-group).test-group) \ $(foreach test, $($(test-group)-tests), \ - $(eval $(call run-test,$(test),$(install_test_init))) \ + $(eval $(call run-test,$(test))) \ $(eval $(test-group).test-group: $(test).test))) # Compilation database. diff --git a/mk/run-test.sh b/mk/run-test.sh index 177a452e8..1256bfcf7 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -8,7 +8,6 @@ yellow="" normal="" test=$1 -init=${2-} dir="$(dirname "${BASH_SOURCE[0]}")" source "$dir/common-test.sh" @@ -22,9 +21,6 @@ if [ -t 1 ]; then fi run_test () { - if [ -n "$init" ]; then - (run "$init" 2>/dev/null > /dev/null) - fi log="$(run "$test" 2>&1)" && status=0 || status=$? } diff --git a/mk/tests.mk b/mk/tests.mk index bac9b704a..0a10f6d3b 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -12,8 +12,8 @@ endef define run-test - $(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1 $2)) - $(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1 $2)) + $(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1)) + $(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1)) endef diff --git a/tests/functional/common.sh b/tests/functional/common.sh index 7b0922c9f..4ec17b706 100644 --- a/tests/functional/common.sh +++ b/tests/functional/common.sh @@ -4,7 +4,11 @@ if [[ -z "${COMMON_SH_SOURCED-}" ]]; then COMMON_SH_SOURCED=1 -source "$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")/common/vars-and-functions.sh" +dir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" + +source "$dir"/common/vars-and-functions.sh +source "$dir"/common/init.sh + if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then startDaemon fi diff --git a/tests/functional/init.sh b/tests/functional/common/init.sh similarity index 86% rename from tests/functional/init.sh rename to tests/functional/common/init.sh index 97b1b0587..9d85dba17 100755 --- a/tests/functional/init.sh +++ b/tests/functional/common/init.sh @@ -1,6 +1,3 @@ -# Don't start the daemon -source common/vars-and-functions.sh - test -n "$TEST_ROOT" if test -d "$TEST_ROOT"; then chmod -R u+rw "$TEST_ROOT" @@ -8,7 +5,8 @@ if test -d "$TEST_ROOT"; then killDaemon rm -rf "$TEST_ROOT" fi -mkdir "$TEST_ROOT" +mkdir -p "$TEST_ROOT" +mkdir "$TEST_HOME" mkdir "$NIX_STORE_DIR" mkdir "$NIX_LOCALSTATE_DIR" @@ -36,7 +34,7 @@ extra-experimental-features = flakes EOF # Initialise the database. +# The flag itself does nothing, but running the command touches the store nix-store --init - -# Did anything happen? +# Sanity check test -e "$NIX_STATE_DIR"/db/db.sqlite diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in index e7e2fc770..cb1f0d566 100644 --- a/tests/functional/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh.in @@ -1,3 +1,5 @@ +# NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk + set -eu -o pipefail if [[ -z "${COMMON_VARS_AND_FUNCTIONS_SH_SOURCED-}" ]]; then @@ -34,7 +36,6 @@ unset XDG_DATA_HOME unset XDG_CONFIG_HOME unset XDG_CONFIG_DIRS unset XDG_CACHE_HOME -mkdir -p $TEST_HOME export PATH=@bindir@:$PATH if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index e35795a7a..c45326473 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -72,7 +72,7 @@ for i in lang/eval-fail-*.nix; do if [[ -e "lang/$i.flags" ]]; then sed -e 's/#.*//' < "lang/$i.flags" else - # note that show-trace is also set by init.sh + # note that show-trace is also set by common/init.sh echo "--eval --strict --show-trace" fi )" diff --git a/tests/functional/local-overlay-store/add-lower.sh b/tests/functional/local-overlay-store/add-lower.sh index f0ac46a91..33bf20ebd 100755 --- a/tests/functional/local-overlay-store/add-lower.sh +++ b/tests/functional/local-overlay-store/add-lower.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/bad-uris.sh b/tests/functional/local-overlay-store/bad-uris.sh index 2517681dd..42a6d47f7 100644 --- a/tests/functional/local-overlay-store/bad-uris.sh +++ b/tests/functional/local-overlay-store/bad-uris.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/build.sh b/tests/functional/local-overlay-store/build.sh index 758585400..2251be7e7 100755 --- a/tests/functional/local-overlay-store/build.sh +++ b/tests/functional/local-overlay-store/build.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/check-post-init.sh b/tests/functional/local-overlay-store/check-post-init.sh index 985bf978e..e0c260276 100755 --- a/tests/functional/local-overlay-store/check-post-init.sh +++ b/tests/functional/local-overlay-store/check-post-init.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 2634f8c8f..0e6097861 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -1,4 +1,4 @@ -source ../common.sh +source ../common/vars-and-functions.sh # The new Linux mount interface does not seem to support remounting # OverlayFS mount points. @@ -37,10 +37,9 @@ addConfig () { setupConfig () { addConfig "require-drop-supplementary-groups = false" addConfig "build-users-group = " + enableFeatures "local-overlay-store" } -enableFeatures "local-overlay-store" - setupStoreDirs () { # Attempt to create store dirs on tmpfs volume. # This ensures lowerdir, upperdir and workdir will be on diff --git a/tests/functional/local-overlay-store/delete-duplicate.sh b/tests/functional/local-overlay-store/delete-duplicate.sh index 0c0b1a3b2..e3b94e1cb 100644 --- a/tests/functional/local-overlay-store/delete-duplicate.sh +++ b/tests/functional/local-overlay-store/delete-duplicate.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/delete-refs.sh b/tests/functional/local-overlay-store/delete-refs.sh index 942d7fbdc..62295aaa1 100755 --- a/tests/functional/local-overlay-store/delete-refs.sh +++ b/tests/functional/local-overlay-store/delete-refs.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/gc.sh b/tests/functional/local-overlay-store/gc.sh index 1e1fb203e..f3420d0b8 100755 --- a/tests/functional/local-overlay-store/gc.sh +++ b/tests/functional/local-overlay-store/gc.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/optimise.sh b/tests/functional/local-overlay-store/optimise.sh index 569afa248..a524a675e 100755 --- a/tests/functional/local-overlay-store/optimise.sh +++ b/tests/functional/local-overlay-store/optimise.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/redundant-add.sh b/tests/functional/local-overlay-store/redundant-add.sh index fbd4799e7..b4f04b2e1 100755 --- a/tests/functional/local-overlay-store/redundant-add.sh +++ b/tests/functional/local-overlay-store/redundant-add.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/stale-file-handle.sh b/tests/functional/local-overlay-store/stale-file-handle.sh index 5e75628ca..684b8ce23 100755 --- a/tests/functional/local-overlay-store/stale-file-handle.sh +++ b/tests/functional/local-overlay-store/stale-file-handle.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/verify.sh b/tests/functional/local-overlay-store/verify.sh index 8b44603ff..d73d1a57d 100755 --- a/tests/functional/local-overlay-store/verify.sh +++ b/tests/functional/local-overlay-store/verify.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 65ab20f9a..021af096c 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -1,6 +1,5 @@ nix_tests = \ test-infra.sh \ - init.sh \ flakes/flakes.sh \ flakes/develop.sh \ flakes/run.sh \ diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh index cc5dd1833..e2c16f18a 100644 --- a/tests/functional/remote-store.sh +++ b/tests/functional/remote-store.sh @@ -23,7 +23,7 @@ fi # Test import-from-derivation through the daemon. [[ $(nix eval --impure --raw --file ./ifd.nix) = hi ]] -storeCleared=1 NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs.sh +NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs-test-case.sh nix-store --gc --max-freed 1K diff --git a/tests/functional/user-envs-test-case.sh b/tests/functional/user-envs-test-case.sh new file mode 100644 index 000000000..f4a90a675 --- /dev/null +++ b/tests/functional/user-envs-test-case.sh @@ -0,0 +1,191 @@ +clearProfiles + +# Query installed: should be empty. +test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0 + +nix-env --switch-profile $profiles/test + +# Query available: should contain several. +test "$(nix-env -f ./user-envs.nix -qa '*' | wc -l)" -eq 6 +outPath10=$(nix-env -f ./user-envs.nix -qa --out-path --no-name '*' | grep foo-1.0) +drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1.0) +[ -n "$outPath10" -a -n "$drvPath10" ] + +# Query with json +nix-env -f ./user-envs.nix -qa --json | jq -e '.[] | select(.name == "bar-0.1") | [ + .outputName == "out", + .outputs.out == null +] | all' +nix-env -f ./user-envs.nix -qa --json --out-path | jq -e '.[] | select(.name == "bar-0.1") | [ + .outputName == "out", + (.outputs.out | test("'$NIX_STORE_DIR'.*-0\\.1")) +] | all' +nix-env -f ./user-envs.nix -qa --json --drv-path | jq -e '.[] | select(.name == "bar-0.1") | (.drvPath | test("'$NIX_STORE_DIR'.*-0\\.1\\.drv"))' + +# Query descriptions. +nix-env -f ./user-envs.nix -qa '*' --description | grepQuiet silly +rm -rf $HOME/.nix-defexpr +ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr +nix-env -qa '*' --description | grepQuiet silly + +# Query the system. +nix-env -qa '*' --system | grepQuiet $system + +# Install "foo-1.0". +nix-env -i foo-1.0 + +# Query installed: should contain foo-1.0 now (which should be +# executable). +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-1.0 +test "$($profiles/test/bin/foo)" = "foo-1.0" + +# Test nix-env -qc to compare installed against available packages, and vice versa. +nix-env -qc '*' | grepQuiet '< 2.0' +nix-env -qac '*' | grepQuiet '> 1.0' + +# Test the -b flag to filter out source-only packages. +[ "$(nix-env -qab | wc -l)" -eq 1 ] + +# Test the -s flag to get package status. +nix-env -qas | grepQuiet 'IP- foo-1.0' +nix-env -qas | grepQuiet -- '--- bar-0.1' + +# Disable foo. +nix-env --set-flag active false foo +(! [ -e "$profiles/test/bin/foo" ]) + +# Enable foo. +nix-env --set-flag active true foo +[ -e "$profiles/test/bin/foo" ] + +# Store the path of foo-1.0. +outPath10_=$(nix-env -q --out-path --no-name '*' | grep foo-1.0) +echo "foo-1.0 = $outPath10" +[ "$outPath10" = "$outPath10_" ] + +# Install "foo-2.0pre1": should remove foo-1.0. +nix-env -i foo-2.0pre1 + +# Query installed: should contain foo-2.0pre1 now. +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0pre1 +test "$($profiles/test/bin/foo)" = "foo-2.0pre1" + +# Upgrade "foo": should install foo-2.0. +NIX_PATH=nixpkgs=./user-envs.nix:${NIX_PATH-} nix-env -f '' -u foo + +# Query installed: should contain foo-2.0 now. +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0 +test "$($profiles/test/bin/foo)" = "foo-2.0" + +# Store the path of foo-2.0. +outPath20=$(nix-env -q --out-path --no-name '*' | grep foo-2.0) +test -n "$outPath20" + +# Install bar-0.1, uninstall foo. +nix-env -i bar-0.1 +nix-env -e foo + +# Query installed: should only contain bar-0.1 now. +if nix-env -q '*' | grepQuiet foo; then false; fi +nix-env -q '*' | grepQuiet bar + +# Rollback: should bring "foo" back. +oldGen="$(nix-store -q --resolve $profiles/test)" +nix-env --rollback +[ "$(nix-store -q --resolve $profiles/test)" != "$oldGen" ] +nix-env -q '*' | grepQuiet foo-2.0 +nix-env -q '*' | grepQuiet bar + +# Rollback again: should remove "bar". +nix-env --rollback +nix-env -q '*' | grepQuiet foo-2.0 +if nix-env -q '*' | grepQuiet bar; then false; fi + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 7 + +# Doing the same operation twice results in the same generation, which triggers +# "lazy" behaviour and does not create a new symlink. + +nix-env -i foo +nix-env -i foo + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 8 + +# Switch to a specified generation. +nix-env --switch-generation 7 +[ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ] + +# Install foo-1.0, now using its store path. +nix-env -i "$outPath10" +nix-env -q '*' | grepQuiet foo-1.0 +nix-store -qR $profiles/test | grep "$outPath10" +nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve $profiles/test)" +[ "$(nix-store -q --deriver "$outPath10")" = $drvPath10 ] + +# Uninstall foo-1.0, using a symlink to its store path. +ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink +nix-env -e $TEST_ROOT/symlink +if nix-env -q '*' | grepQuiet foo; then false; fi +nix-store -qR $profiles/test | grepInverse "$outPath10" + +# Install foo-1.0, now using a symlink to its store path. +nix-env -i $TEST_ROOT/symlink +nix-env -q '*' | grepQuiet foo + +# Delete all old generations. +nix-env --delete-generations old + +# Run the garbage collector. This should get rid of foo-2.0 but not +# foo-1.0. +nix-collect-garbage +test -e "$outPath10" +(! [ -e "$outPath20" ]) + +# Uninstall everything +nix-env -e '*' +test "$(nix-env -q '*' | wc -l)" -eq 0 + +# Installing "foo" should only install the newest foo. +nix-env -i foo +test "$(nix-env -q '*' | grep foo- | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0 + +# On the other hand, this should install both (and should fail due to +# a collision). +nix-env -e '*' +(! nix-env -i foo-1.0 foo-2.0) + +# Installing "*" should install one foo and one bar. +nix-env -e '*' +nix-env -i '*' +test "$(nix-env -q '*' | wc -l)" -eq 2 +nix-env -q '*' | grepQuiet foo-2.0 +nix-env -q '*' | grepQuiet bar-0.1.1 + +# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it +# should be possible to install both without a collision. Also test +# ‘--set-flag priority’ to manually override the declared priorities. +nix-env -e '*' +nix-env -i foo-0.1 foo-1.0 +[ "$($profiles/test/bin/foo)" = "foo-1.0" ] +nix-env --set-flag priority 1 foo-0.1 +[ "$($profiles/test/bin/foo)" = "foo-0.1" ] + +# Test nix-env --set. +nix-env --set $outPath10 +[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] +nix-env --set $drvPath10 +[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] + +# Test the case where $HOME contains a symlink. +mkdir -p $TEST_ROOT/real-home/alice/.nix-defexpr/channels +ln -sfn $TEST_ROOT/real-home $TEST_ROOT/home +ln -sfn $(pwd)/user-envs.nix $TEST_ROOT/home/alice/.nix-defexpr/channels/foo +HOME=$TEST_ROOT/home/alice nix-env -i foo-0.1 diff --git a/tests/functional/user-envs.sh b/tests/functional/user-envs.sh index 7c643f355..a849d5439 100644 --- a/tests/functional/user-envs.sh +++ b/tests/functional/user-envs.sh @@ -1,197 +1,3 @@ -source common.sh +source ./common.sh -if [ -z "${storeCleared-}" ]; then - clearStore -fi - -clearProfiles - -# Query installed: should be empty. -test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0 - -nix-env --switch-profile $profiles/test - -# Query available: should contain several. -test "$(nix-env -f ./user-envs.nix -qa '*' | wc -l)" -eq 6 -outPath10=$(nix-env -f ./user-envs.nix -qa --out-path --no-name '*' | grep foo-1.0) -drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1.0) -[ -n "$outPath10" -a -n "$drvPath10" ] - -# Query with json -nix-env -f ./user-envs.nix -qa --json | jq -e '.[] | select(.name == "bar-0.1") | [ - .outputName == "out", - .outputs.out == null -] | all' -nix-env -f ./user-envs.nix -qa --json --out-path | jq -e '.[] | select(.name == "bar-0.1") | [ - .outputName == "out", - (.outputs.out | test("'$NIX_STORE_DIR'.*-0\\.1")) -] | all' -nix-env -f ./user-envs.nix -qa --json --drv-path | jq -e '.[] | select(.name == "bar-0.1") | (.drvPath | test("'$NIX_STORE_DIR'.*-0\\.1\\.drv"))' - -# Query descriptions. -nix-env -f ./user-envs.nix -qa '*' --description | grepQuiet silly -rm -rf $HOME/.nix-defexpr -ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr -nix-env -qa '*' --description | grepQuiet silly - -# Query the system. -nix-env -qa '*' --system | grepQuiet $system - -# Install "foo-1.0". -nix-env -i foo-1.0 - -# Query installed: should contain foo-1.0 now (which should be -# executable). -test "$(nix-env -q '*' | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-1.0 -test "$($profiles/test/bin/foo)" = "foo-1.0" - -# Test nix-env -qc to compare installed against available packages, and vice versa. -nix-env -qc '*' | grepQuiet '< 2.0' -nix-env -qac '*' | grepQuiet '> 1.0' - -# Test the -b flag to filter out source-only packages. -[ "$(nix-env -qab | wc -l)" -eq 1 ] - -# Test the -s flag to get package status. -nix-env -qas | grepQuiet 'IP- foo-1.0' -nix-env -qas | grepQuiet -- '--- bar-0.1' - -# Disable foo. -nix-env --set-flag active false foo -(! [ -e "$profiles/test/bin/foo" ]) - -# Enable foo. -nix-env --set-flag active true foo -[ -e "$profiles/test/bin/foo" ] - -# Store the path of foo-1.0. -outPath10_=$(nix-env -q --out-path --no-name '*' | grep foo-1.0) -echo "foo-1.0 = $outPath10" -[ "$outPath10" = "$outPath10_" ] - -# Install "foo-2.0pre1": should remove foo-1.0. -nix-env -i foo-2.0pre1 - -# Query installed: should contain foo-2.0pre1 now. -test "$(nix-env -q '*' | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-2.0pre1 -test "$($profiles/test/bin/foo)" = "foo-2.0pre1" - -# Upgrade "foo": should install foo-2.0. -NIX_PATH=nixpkgs=./user-envs.nix:${NIX_PATH-} nix-env -f '' -u foo - -# Query installed: should contain foo-2.0 now. -test "$(nix-env -q '*' | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-2.0 -test "$($profiles/test/bin/foo)" = "foo-2.0" - -# Store the path of foo-2.0. -outPath20=$(nix-env -q --out-path --no-name '*' | grep foo-2.0) -test -n "$outPath20" - -# Install bar-0.1, uninstall foo. -nix-env -i bar-0.1 -nix-env -e foo - -# Query installed: should only contain bar-0.1 now. -if nix-env -q '*' | grepQuiet foo; then false; fi -nix-env -q '*' | grepQuiet bar - -# Rollback: should bring "foo" back. -oldGen="$(nix-store -q --resolve $profiles/test)" -nix-env --rollback -[ "$(nix-store -q --resolve $profiles/test)" != "$oldGen" ] -nix-env -q '*' | grepQuiet foo-2.0 -nix-env -q '*' | grepQuiet bar - -# Rollback again: should remove "bar". -nix-env --rollback -nix-env -q '*' | grepQuiet foo-2.0 -if nix-env -q '*' | grepQuiet bar; then false; fi - -# Count generations. -nix-env --list-generations -test "$(nix-env --list-generations | wc -l)" -eq 7 - -# Doing the same operation twice results in the same generation, which triggers -# "lazy" behaviour and does not create a new symlink. - -nix-env -i foo -nix-env -i foo - -# Count generations. -nix-env --list-generations -test "$(nix-env --list-generations | wc -l)" -eq 8 - -# Switch to a specified generation. -nix-env --switch-generation 7 -[ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ] - -# Install foo-1.0, now using its store path. -nix-env -i "$outPath10" -nix-env -q '*' | grepQuiet foo-1.0 -nix-store -qR $profiles/test | grep "$outPath10" -nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve $profiles/test)" -[ "$(nix-store -q --deriver "$outPath10")" = $drvPath10 ] - -# Uninstall foo-1.0, using a symlink to its store path. -ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink -nix-env -e $TEST_ROOT/symlink -if nix-env -q '*' | grepQuiet foo; then false; fi -nix-store -qR $profiles/test | grepInverse "$outPath10" - -# Install foo-1.0, now using a symlink to its store path. -nix-env -i $TEST_ROOT/symlink -nix-env -q '*' | grepQuiet foo - -# Delete all old generations. -nix-env --delete-generations old - -# Run the garbage collector. This should get rid of foo-2.0 but not -# foo-1.0. -nix-collect-garbage -test -e "$outPath10" -(! [ -e "$outPath20" ]) - -# Uninstall everything -nix-env -e '*' -test "$(nix-env -q '*' | wc -l)" -eq 0 - -# Installing "foo" should only install the newest foo. -nix-env -i foo -test "$(nix-env -q '*' | grep foo- | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-2.0 - -# On the other hand, this should install both (and should fail due to -# a collision). -nix-env -e '*' -(! nix-env -i foo-1.0 foo-2.0) - -# Installing "*" should install one foo and one bar. -nix-env -e '*' -nix-env -i '*' -test "$(nix-env -q '*' | wc -l)" -eq 2 -nix-env -q '*' | grepQuiet foo-2.0 -nix-env -q '*' | grepQuiet bar-0.1.1 - -# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it -# should be possible to install both without a collision. Also test -# ‘--set-flag priority’ to manually override the declared priorities. -nix-env -e '*' -nix-env -i foo-0.1 foo-1.0 -[ "$($profiles/test/bin/foo)" = "foo-1.0" ] -nix-env --set-flag priority 1 foo-0.1 -[ "$($profiles/test/bin/foo)" = "foo-0.1" ] - -# Test nix-env --set. -nix-env --set $outPath10 -[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] -nix-env --set $drvPath10 -[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] - -# Test the case where $HOME contains a symlink. -mkdir -p $TEST_ROOT/real-home/alice/.nix-defexpr/channels -ln -sfn $TEST_ROOT/real-home $TEST_ROOT/home -ln -sfn $(pwd)/user-envs.nix $TEST_ROOT/home/alice/.nix-defexpr/channels/foo -HOME=$TEST_ROOT/home/alice nix-env -i foo-0.1 +source ./user-envs-test-case.sh From 7822ecbadff47fe350a483969e1e307c1c3a3ebe Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 13 May 2024 14:56:14 +0200 Subject: [PATCH 086/910] tests: always clean the test directory previously the test directory could have been left untouched before executing a test when `init.sh` was not run - and sometimes it isn't supposed to be run - which made the test suite highly stateful and thus behaving surprisingly on multiple runs. --- tests/functional/common/init.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 9d85dba17..017179952 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -1,10 +1,8 @@ test -n "$TEST_ROOT" -if test -d "$TEST_ROOT"; then - chmod -R u+rw "$TEST_ROOT" - # We would delete any daemon socket, so let's stop the daemon first. - killDaemon - rm -rf "$TEST_ROOT" -fi +# We would delete any daemon socket, so let's stop the daemon first. +killDaemon +# Destroy the test directory that may have persisted from previous runs +rm -rf "$TEST_ROOT" mkdir -p "$TEST_ROOT" mkdir "$TEST_HOME" From e1e041ed8f99aaff993f0e94bdb64c2f620d100c Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 13 May 2024 09:23:59 -0400 Subject: [PATCH 087/910] Rename commit-lockfile-summary to commit-lock-file-summary for consistency --- src/libexpr/flake/config.cc | 2 +- src/libfetchers/fetch-settings.hh | 4 ++-- src/nix/flake.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index 3c7ed5d8a..e0c5d4512 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -32,7 +32,7 @@ static void writeTrustedList(const TrustedList & trustedList) void ConfigFile::apply() { - std::set whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lockfile-summary"}; + std::set whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lock-file-summary", "commit-lockfile-summary"}; for (auto & [name, value] : settings) { diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index d085f0d82..50cd4d161 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -87,12 +87,12 @@ struct FetchSettings : public Config {}, true, Xp::Flakes}; Setting commitLockFileSummary{ - this, "", "commit-lockfile-summary", + this, "", "commit-lock-file-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}; + {"commit-lockfile-summary"}, true, Xp::Flakes}; Setting trustTarballsFromGitForges{ this, true, "trust-tarballs-from-git-forges", diff --git a/src/nix/flake.md b/src/nix/flake.md index d8b5bf435..661dd2f73 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -439,7 +439,7 @@ The following attributes are supported in `flake.nix`: - [`bash-prompt-prefix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-prefix) - [`bash-prompt-suffix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-suffix) - [`flake-registry`](@docroot@/command-ref/conf-file.md#conf-flake-registry) - - [`commit-lockfile-summary`](@docroot@/command-ref/conf-file.md#conf-commit-lockfile-summary) + - [`commit-lock-file-summary`](@docroot@/command-ref/conf-file.md#conf-commit-lock-file-summary) ## Flake inputs From 9a58d90c73ed9cc90b0b79c2946f0e2601b6cd54 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 14:27:09 +0200 Subject: [PATCH 088/910] tests/nixos/containers/containers.nix: Remove superfluous -v --- tests/nixos/containers/containers.nix | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/nixos/containers/containers.nix b/tests/nixos/containers/containers.nix index c8ee78a4a..6773f5628 100644 --- a/tests/nixos/containers/containers.nix +++ b/tests/nixos/containers/containers.nix @@ -33,30 +33,30 @@ # Test that 'id' gives the expected result in various configurations. # Existing UIDs, sandbox. - host.succeed("nix build -v --no-auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-1") + host.succeed("nix build --no-auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-1") host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") # Existing UIDs, no sandbox. - host.succeed("nix build -v --no-auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-2") + host.succeed("nix build --no-auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-2") host.succeed("[[ $(cat ./result) = 'uid=30001(nixbld1) gid=30000(nixbld) groups=30000(nixbld)' ]]") # Auto-allocated UIDs, sandbox. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-3") + host.succeed("nix build --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-3") host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") # Auto-allocated UIDs, no sandbox. - host.succeed("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-4") + host.succeed("nix build --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-4") host.succeed("[[ $(cat ./result) = 'uid=872415232 gid=30000(nixbld) groups=30000(nixbld)' ]]") # Auto-allocated UIDs, UID range, sandbox. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-5 --arg uidRange true") + host.succeed("nix build --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-5 --arg uidRange true") host.succeed("[[ $(cat ./result) = 'uid=0(root) gid=0(root) groups=0(root)' ]]") # Auto-allocated UIDs, UID range, no sandbox. - host.fail("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true") + host.fail("nix build --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true") # Run systemd-nspawn in a Nix build. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}") + host.succeed("nix build --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}") host.succeed("[[ $(cat ./result/msg) = 'Hello World' ]]") ''; From 1da18e85baa273835c2b0f727374e010f6fd2e89 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 16:23:08 +0200 Subject: [PATCH 089/910] tests/functional/common/init.sh: Make $TEST_ROOT writable before removing it $TEST_ROOT typically contains read-only files/directories (e.g. the Nix store). So we have to make it writable first. --- tests/functional/common/init.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 017179952..74da12651 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -2,7 +2,10 @@ test -n "$TEST_ROOT" # We would delete any daemon socket, so let's stop the daemon first. killDaemon # Destroy the test directory that may have persisted from previous runs -rm -rf "$TEST_ROOT" +if [[ -e "$TEST_ROOT" ]]; then + chmod -R u+w "$TEST_ROOT" + rm -rf "$TEST_ROOT" +fi mkdir -p "$TEST_ROOT" mkdir "$TEST_HOME" From 2f0031aedc4ae254010fe07a5b4452e3fb081b12 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 14 May 2024 21:23:29 +0200 Subject: [PATCH 090/910] Revert "manual: fold sidebar sections" (#10698) The original change arguably reduced ergonomics of navigation, since menu items weren't ctrl+f searchable any more. --- doc/manual/book.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/manual/book.toml b/doc/manual/book.toml index d524dbb13..73fb7e75e 100644 --- a/doc/manual/book.toml +++ b/doc/manual/book.toml @@ -6,8 +6,6 @@ additional-css = ["custom.css"] additional-js = ["redirects.js"] edit-url-template = "https://github.com/NixOS/nix/tree/master/doc/manual/{path}" git-repository-url = "https://github.com/NixOS/nix" -fold.enable = true -fold.level = 1 [preprocessor.anchors] renderers = ["html"] From 05ad4e8806a13fb475250472a0bf677f94cee641 Mon Sep 17 00:00:00 2001 From: Eli Flanagan <163922304+eflanagan0@users.noreply.github.com> Date: Tue, 14 May 2024 16:38:54 -0400 Subject: [PATCH 091/910] doc: convention improvements for copying closure (#10702) * doc: convention improvements for copying closure use -P, which only considers executables but not shell builtins Co-authored-by: Valentin Gagarin --- doc/manual/src/command-ref/nix-copy-closure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index eb1693e1e..46d381f5d 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -78,14 +78,14 @@ authentication, you can avoid typing the passphrase with `ssh-agent`. Copy Firefox with all its dependencies to a remote machine: ```console -$ nix-copy-closure --to alice@itchy.labs $(type -tP firefox) +$ nix-copy-closure --to alice@itchy.example.org $(type -P firefox) ``` Copy Subversion from a remote machine and then install it into a user environment: ```console -$ nix-copy-closure --from alice@itchy.labs \ +$ nix-copy-closure --from alice@itchy.example.org \ /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 $ nix-env --install /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 ``` From 49bd408c10ed8c9e8f5d8f54b27d1027f989da84 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 14 May 2024 23:18:40 +0200 Subject: [PATCH 092/910] remove link to relocated manual page (#10703) fix old anchor redirects to point to the correct location --- doc/manual/redirects.js | 8 ++++---- doc/manual/src/SUMMARY.md.in | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 25648969d..ec5645ea7 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -290,10 +290,10 @@ const redirects = { "ssec-gc-roots": "package-management/garbage-collector-roots.html", "chap-package-management": "package-management/index.html", "sec-profiles": "package-management/profiles.html", - "ssec-s3-substituter": "package-management/s3-substituter.html", - "ssec-s3-substituter-anonymous-reads": "package-management/s3-substituter.html#anonymous-reads-to-your-s3-compatible-binary-cache", - "ssec-s3-substituter-authenticated-reads": "package-management/s3-substituter.html#authenticated-reads-to-your-s3-binary-cache", - "ssec-s3-substituter-authenticated-writes": "package-management/s3-substituter.html#authenticated-writes-to-your-s3-compatible-binary-cache", + "ssec-s3-substituter": "store/types/s3-substituter.html", + "ssec-s3-substituter-anonymous-reads": "store/types/s3-substituter.html#anonymous-reads-to-your-s3-compatible-binary-cache", + "ssec-s3-substituter-authenticated-reads": "store/types/s3-substituter.html#authenticated-reads-to-your-s3-binary-cache", + "ssec-s3-substituter-authenticated-writes": "store/types/s3-substituter.html#authenticated-writes-to-your-s3-compatible-binary-cache", "sec-sharing-packages": "package-management/sharing-packages.html", "ssec-ssh-substituter": "package-management/ssh-substituter.html", "chap-quick-start": "quick-start.html", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index fdfd0a927..7f0fb2e9d 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -43,7 +43,6 @@ - [Serving a Nix store via HTTP](package-management/binary-cache-substituter.md) - [Copying Closures via SSH](package-management/copy-closure.md) - [Serving a Nix store via SSH](package-management/ssh-substituter.md) - - [Serving a Nix store via S3](package-management/s3-substituter.md) - [Remote Builds](advanced-topics/distributed-builds.md) - [Tuning Cores and Jobs](advanced-topics/cores-vs-jobs.md) - [Verifying Build Reproducibility](advanced-topics/diff-hook.md) From dcc2a51bacaf7152ee86b524044e87738f71614e Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:11:14 +0200 Subject: [PATCH 093/910] add example to `nix-store --import` this also features specifying `--store` to give more pointers for discoverability --- doc/manual/src/command-ref/nix-store/import.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/manual/src/command-ref/nix-store/import.md b/doc/manual/src/command-ref/nix-store/import.md index 2711316a7..da28619f4 100644 --- a/doc/manual/src/command-ref/nix-store/import.md +++ b/doc/manual/src/command-ref/nix-store/import.md @@ -19,3 +19,21 @@ Nix store, the import fails. {{#include ../opt-common.md}} {{#include ../env-common.md}} + +# Examples + +> **Example** +> +> Given a closure of GNU Hello as a file: +> +> ```shell-session +> $ storePath="$(nix-build '' -I nixpkgs=channel:nixpkgs-unstable -A hello --no-out-link)" +> $ nix-store --export $(nix-store --query --requisites $storePath) > hello.closure +> ``` +> +> Import the closure into a [remote SSH store](@docroot@/store/types/ssh-store.md) using the [`--store`](@docroot@/command-ref/conf-file.md#conf-store) option: +> +> ```console +> $ nix-store --import --store ssh://alice@itchy.example.org < hello.closure +> ``` + From 7c7aa79ebe69086ac7e2f30bde4af9490b55afd7 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:29:10 +0200 Subject: [PATCH 094/910] redirect "Copying Closures via SSH" guide to `nix-copy-closure` the individual commands' documentation should provide enough examples to make sense of the options and judge what to use and when. proper guides, which would require a more elaborate setup to show off Nix's capabilities are out of scope for the reference manual. --- doc/manual/redirects.js | 2 +- doc/manual/src/SUMMARY.md.in | 1 - doc/manual/src/_redirects | 2 ++ .../src/package-management/copy-closure.md | 34 ------------------- 4 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 doc/manual/src/package-management/copy-closure.md diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 25648969d..9d885e602 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -285,7 +285,7 @@ const redirects = { "ch-basic-package-mgmt": "package-management/basic-package-mgmt.html", "ssec-binary-cache-substituter": "package-management/binary-cache-substituter.html", "sec-channels": "command-ref/nix-channel.html", - "ssec-copy-closure": "package-management/copy-closure.html", + "ssec-copy-closure": "command-ref/nix-copy-closure.html", "sec-garbage-collection": "package-management/garbage-collection.html", "ssec-gc-roots": "package-management/garbage-collector-roots.html", "chap-package-management": "package-management/index.html", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index fdfd0a927..e0ba9ccc8 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -41,7 +41,6 @@ - [Advanced Topics](advanced-topics/index.md) - [Sharing Packages Between Machines](package-management/sharing-packages.md) - [Serving a Nix store via HTTP](package-management/binary-cache-substituter.md) - - [Copying Closures via SSH](package-management/copy-closure.md) - [Serving a Nix store via SSH](package-management/ssh-substituter.md) - [Serving a Nix store via S3](package-management/s3-substituter.md) - [Remote Builds](advanced-topics/distributed-builds.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index 8bf0e854b..a04a36f1e 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -39,3 +39,5 @@ /json/* /protocols/json/:splat 301! /release-notes/release-notes /release-notes 301! + +/package-management/copy-closure /command-ref/nix-copy-closure 301! diff --git a/doc/manual/src/package-management/copy-closure.md b/doc/manual/src/package-management/copy-closure.md deleted file mode 100644 index 14326298b..000000000 --- a/doc/manual/src/package-management/copy-closure.md +++ /dev/null @@ -1,34 +0,0 @@ -# Copying Closures via SSH - -The command `nix-copy-closure` copies a Nix store path along with all -its dependencies to or from another machine via the SSH protocol. It -doesn’t copy store paths that are already present on the target machine. -For example, the following command copies Firefox with all its -dependencies: - - $ nix-copy-closure --to alice@itchy.example.org $(type -p firefox) - -See the [manpage for `nix-copy-closure`](../command-ref/nix-copy-closure.md) for details. - -With `nix-store ---export` and `nix-store --import` you can write the closure of a store -path (that is, the path and all its dependencies) to a file, and then -unpack that file into another Nix store. For example, - - $ nix-store --export $(nix-store --query --requisites $(type -p firefox)) > firefox.closure - -writes the closure of Firefox to a file. You can then copy this file to -another machine and install the closure: - - $ nix-store --import < firefox.closure - -Any store paths in the closure that are already present in the target -store are ignored. It is also possible to pipe the export into another -command, e.g. to copy and install a closure directly to/on another -machine: - - $ nix-store --export $(nix-store --query --requisites $(type -p firefox)) | bzip2 | \ - ssh alice@itchy.example.org "bunzip2 | nix-store --import" - -However, `nix-copy-closure` is generally more efficient because it only -copies paths that are not already present in the target Nix store. From 0c2c26018034b467b7b33d20e6d209bf9dbd0e24 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:40:25 +0200 Subject: [PATCH 095/910] labeler: capture all docs files --- .github/labeler.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index e036eb3c8..0e6fd3e26 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -13,7 +13,7 @@ "documentation": - changed-files: - - any-glob-to-any-file: "doc/manual/*" + - any-glob-to-any-file: "doc/manual/**/*" - any-glob-to-any-file: "src/nix/**/*.md" "store": @@ -40,4 +40,4 @@ - any-glob-to-any-file: "src/*/tests/**/*" # Functional and integration tests - any-glob-to-any-file: "tests/functional/**/*" - + From 6907eaad4f87350b09d844434532428e97d22943 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:21:45 +0200 Subject: [PATCH 096/910] reword documentation on `nix-store --import` - add links to definitions of terms - one sentence per line - be more specific about which store is used for the import - clearly distinguish store paths and store objects - make a recommendation to use `nix-copy-closure` for efficient SSH transfers --- doc/manual/src/command-ref/nix-store/import.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/import.md b/doc/manual/src/command-ref/nix-store/import.md index 2711316a7..0ecde177c 100644 --- a/doc/manual/src/command-ref/nix-store/import.md +++ b/doc/manual/src/command-ref/nix-store/import.md @@ -8,11 +8,13 @@ # Description -The operation `--import` reads a serialisation of a set of store paths -produced by `nix-store --export` from standard input and adds those -store paths to the Nix store. Paths that already exist in the Nix store -are ignored. If a path refers to another path that doesn’t exist in the -Nix store, the import fails. +The operation `--import` reads a serialisation of a set of [store objects](@docroot@/glossary.md#gloss-store-object) produced by [`nix-store --export`](./export.md) from standard input, and adds those store objects to the specified [Nix store](@docroot@/store/index.md). +Paths that already exist in the target Nix store are ignored. +If a path [refers](@docroot@/glossary.md#gloss-reference) to another path that doesn’t exist in the target Nix store, the import fails. + +> **Note** +> +> For efficient transfer of closures to remote machines over SSH, use [`nix-copy-closure`](@docroot@/command-ref/nix-copy-closure.md). {{#include ./opt-common.md}} From 06e13465c55a046d22791942680166f048d11f94 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Tue, 14 May 2024 12:13:14 -0700 Subject: [PATCH 097/910] tests/functional: test both clis warn on unknown settings --- tests/functional/eval.sh | 4 ++++ tests/functional/misc.sh | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index c6a475cd0..502170d14 100644 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -52,3 +52,7 @@ fi # Test --arg-from-stdin. [[ "$(echo bla | nix eval --raw --arg-from-stdin foo --expr '{ foo }: { inherit foo; }' foo)" = bla ]] + +# Test that unknown settings are warned about +out="$(expectStderr 0 nix eval --option foobar baz --expr '""' --raw)" +[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] diff --git a/tests/functional/misc.sh b/tests/functional/misc.sh index af96d20bd..d4379b7ce 100644 --- a/tests/functional/misc.sh +++ b/tests/functional/misc.sh @@ -30,3 +30,12 @@ expectStderr 1 nix-instantiate --eval -E '[]' -A 'x' | grepQuiet "should be a se expectStderr 1 nix-instantiate --eval -E '{}' -A '1' | grepQuiet "should be a list" expectStderr 1 nix-instantiate --eval -E '{}' -A '.' | grepQuiet "empty attribute name" expectStderr 1 nix-instantiate --eval -E '[]' -A '1' | grepQuiet "out of range" + +# Unknown setting warning +# NOTE(cole-h): behavior is different depending on the order, which is why we test an unknown option +# before and after the `'{}'`! +out="$(expectStderr 0 nix-instantiate --option foobar baz --expr '{}')" +[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] + +out="$(expectStderr 0 nix-instantiate '{}' --option foobar baz --expr )" +[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] From 39a269657e766aaa6d6c678f95362a566173e547 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Tue, 14 May 2024 12:13:40 -0700 Subject: [PATCH 098/910] libutil/args: warn on unknown settings after parsing all flags --- doc/manual/rl-next/fix-silent-unknown-options | 28 +++++++++++++++++++ src/libutil/args.cc | 9 +----- 2 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 doc/manual/rl-next/fix-silent-unknown-options diff --git a/doc/manual/rl-next/fix-silent-unknown-options b/doc/manual/rl-next/fix-silent-unknown-options new file mode 100644 index 000000000..0977260ac --- /dev/null +++ b/doc/manual/rl-next/fix-silent-unknown-options @@ -0,0 +1,28 @@ +--- +synopsis: Warn on unknown settings anywhere in the command line +prs: 10701 +--- + +All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line. + +Before: + +```console +$ nix-instantiate --option foobar baz --expr '{}' +warning: unknown setting 'foobar' +$ nix-instantiate '{}' --option foobar baz --expr +$ nix eval --expr '{}' --option foobar baz +{ } +``` + +After: + +```console +$ nix-instantiate --option foobar baz --expr '{}' +warning: unknown setting 'foobar' +$ nix-instantiate '{}' --option foobar baz --expr +warning: unknown setting 'foobar' +$ nix eval --expr '{}' --option foobar baz +warning: unknown setting 'foobar' +{ } +``` diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 243e3a5a6..c202facdf 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -268,8 +268,6 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang) verbosity = lvlError; } - bool argsSeen = false; - // Heuristic to see if we're invoked as a shebang script, namely, // if we have at least one argument, it's the name of an // executable file, and it starts with "#!". @@ -336,10 +334,6 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang) throw UsageError("unrecognised flag '%1%'", arg); } else { - if (!argsSeen) { - argsSeen = true; - initialFlagsProcessed(); - } pos = rewriteArgs(cmdline, pos); pendingArgs.push_back(*pos++); if (processArgs(pendingArgs, false)) @@ -349,8 +343,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang) processArgs(pendingArgs, true); - if (!argsSeen) - initialFlagsProcessed(); + initialFlagsProcessed(); /* Now that we are done parsing, make sure that any experimental * feature required by the flags is enabled */ From 50bbe22a51cec73f5b3b6eff9386f1c70ed10191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ram=C3=ADrez?= Date: Wed, 15 May 2024 15:42:14 -0400 Subject: [PATCH 099/910] reword `nix-env` documentation (#10718) * reword `nix-env` documentation - added links - added an overview of package sources - clarified parsing and matching of package names Co-authored-by: Valentin Gagarin --- doc/manual/src/command-ref/nix-env.md | 94 ++++++++++++++----- doc/manual/src/command-ref/nix-env/install.md | 3 +- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/doc/manual/src/command-ref/nix-env.md b/doc/manual/src/command-ref/nix-env.md index 941723216..c6f627365 100644 --- a/doc/manual/src/command-ref/nix-env.md +++ b/doc/manual/src/command-ref/nix-env.md @@ -47,39 +47,83 @@ These pages can be viewed offline: Example: `nix-env --help --install` +# Package sources + +`nix-env` can obtain packages from multiple sources: + +- An attribute set of derivations from: + - The [default Nix expression](@docroot@/command-ref/files/default-nix-expression.md) (by default) + - A Nix file, specified via `--file` + - A [profile](@docroot@/command-ref/files/profiles.md), specified via `--from-profile` + - A Nix expression that is a function which takes default expression as argument, specified via `--from-expression` +- A [store path](@docroot@/store/store-path.md) + # Selectors -Several commands, such as `nix-env --query ` and `nix-env --install `, take a list of -arguments that specify the packages on which to operate. These are -extended regular expressions that must match the entire name of the -package. (For details on regular expressions, see **regex**(7).) The match is -case-sensitive. The regular expression can optionally be followed by a -dash and a version number; if omitted, any version of the package will -match. Here are some examples: +Several operations, such as [`nix-env --query`](./nix-env/query.md) and [`nix-env --install`](./nix-env/install.md), take a list of *arguments* that specify the packages on which to operate. - - `firefox`\ - Matches the package name `firefox` and any version. +Packages are identified based on a `name` part and a `version` part of a [symbolic derivation name](@docroot@/language/derivations.md#attr-names): - - `firefox-32.0`\ - Matches the package name `firefox` and version `32.0`. +- `name`: Everything up to but not including the first dash (`-`) that is *not* followed by a letter. +- `version`: The rest, excluding the separating dash. - - `gtk\\+`\ - Matches the package name `gtk+`. The `+` character must be escaped - using a backslash to prevent it from being interpreted as a - quantifier, and the backslash must be escaped in turn with another - backslash to ensure that the shell passes it on. +> **Example** +> +> `nix-env` parses the symbolic derivation name `apache-httpd-2.0.48` as: +> +> ```json +> { +> "name": "apache-httpd", +> "version": "2.0.48" +> } +> ``` - - `.\*`\ - Matches any package name. This is the default for most commands. +> **Example** +> +> `nix-env` parses the symbolic derivation name `firefox.*` as: +> +> ```json +> { +> "name": "firefox.*", +> "version": "" +> } +> ``` - - `'.*zip.*'`\ - Matches any package name containing the string `zip`. Note the dots: - `'*zip*'` does not work, because in a regular expression, the - character `*` is interpreted as a quantifier. +The `name` parts of the *arguments* to `nix-env` are treated as extended regular expressions and matched against the `name` parts of derivation names in the package source. +The match is case-sensitive. +The regular expression can optionally be followed by a dash (`-`) and a version number; if omitted, any version of the package will match. +For details on regular expressions, see [**regex**(7)](https://linux.die.net/man/7/regex). - - `'.*(firefox|chromium).*'`\ - Matches any package name containing the strings `firefox` or - `chromium`. +> **Example** +> +> Common patterns for finding package names with `nix-env`: +> +> - `firefox` +> +> Matches the package name `firefox` and any version. +> +> - `firefox-32.0` +> +> Matches the package name `firefox` and version `32.0`. +> +> - `gtk\\+` +> +> Matches the package name `gtk+`. +> The `+` character must be escaped using a backslash (`\`) to prevent it from being interpreted as a quantifier, and the backslash must be escaped in turn with another backslash to ensure that the shell passes it on. +> +> - `.\*` +> +> Matches any package name. +> This is the default for most commands. +> +> - `'.*zip.*'` +> +> Matches any package name containing the string `zip`. +> Note the dots: `'*zip*'` does not work, because in a regular expression, the character `*` is interpreted as a quantifier. +> +> - `'.*(firefox|chromium).*'` +> +> Matches any package name containing the strings `firefox` or `chromium`. # Files diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index d80bcb668..a2cc7f862 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -14,14 +14,13 @@ # Description -The install operation creates a new user environment. +The `--install` operation creates a new user environment. It is based on the current generation of the active [profile](@docroot@/command-ref/files/profiles.md), to which a set of [store paths] described by *args* is added. [store paths]: @docroot@/glossary.md#gloss-store-path The arguments *args* map to store paths in a number of possible ways: - - By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression]. These are [realised], and the resulting output paths are installed. Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified. From 043135a84851b3c33fd8723686a44437eb82e66a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 Apr 2024 17:07:39 -0400 Subject: [PATCH 100/910] Document file system object content addressing In addition: - Take the opportunity to add a bunch more missing hyperlinks, too. - Remove some glossary entries that are now subsumed by dedicated pages. We used to not be able to do this without breaking link fragments, but now we can, so pick up where we left off. Co-authored-by: Robert Hensing --- doc/manual/src/SUMMARY.md.in | 1 + .../src/command-ref/nix-collect-garbage.md | 2 +- .../command-ref/nix-env/delete-generations.md | 2 +- doc/manual/src/command-ref/nix-env/install.md | 2 +- doc/manual/src/command-ref/nix-hash.md | 11 ++- doc/manual/src/command-ref/nix-store/dump.md | 6 +- .../src/command-ref/nix-store/export.md | 6 +- .../src/command-ref/nix-store/import.md | 4 +- .../src/command-ref/nix-store/optimise.md | 3 +- .../src/command-ref/nix-store/realise.md | 4 +- .../src/command-ref/nix-store/restore.md | 4 +- doc/manual/src/contributing/documentation.md | 2 +- doc/manual/src/glossary.md | 23 +++++- .../src/language/advanced-attributes.md | 26 +++--- doc/manual/src/language/derivations.md | 4 +- .../src/language/import-from-derivation.md | 4 +- doc/manual/src/language/operators.md | 2 +- .../src/language/string-interpolation.md | 4 +- doc/manual/src/language/values.md | 2 +- .../src/protocols/json/store-object-info.md | 4 +- doc/manual/src/protocols/nix-archive.md | 3 +- doc/manual/src/protocols/store-path.md | 6 +- doc/manual/src/protocols/tarball-fetcher.md | 4 +- .../file-system-object/content-address.md | 80 +++++++++++++++++++ doc/manual/src/store/store-path.md | 10 +++ src/libcmd/misc-store-flags.cc | 24 ++++-- src/libexpr/primops.cc | 2 +- src/libexpr/primops/fetchTree.cc | 4 +- src/libstore/globals.hh | 2 +- src/libstore/path.hh | 2 +- src/libutil/file-content-address.hh | 39 +++++---- src/nix/derivation-show.md | 2 +- src/nix/unix/daemon.cc | 2 +- 33 files changed, 228 insertions(+), 68 deletions(-) create mode 100644 doc/manual/src/store/file-system-object/content-address.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index fdfd0a927..7ddafef70 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -18,6 +18,7 @@ - [Uninstalling Nix](installation/uninstall.md) - [Nix Store](store/index.md) - [File System Object](store/file-system-object.md) + - [Content-Addressing File System Objects](store/file-system-object/content-address.md) - [Store Object](store/store-object.md) - [Store Path](store/store-path.md) - [Store Types](store/types/index.md) diff --git a/doc/manual/src/command-ref/nix-collect-garbage.md b/doc/manual/src/command-ref/nix-collect-garbage.md index 1bc88d858..8e1307c48 100644 --- a/doc/manual/src/command-ref/nix-collect-garbage.md +++ b/doc/manual/src/command-ref/nix-collect-garbage.md @@ -74,4 +74,4 @@ $ nix-collect-garbage -d ``` [profiles]: @docroot@/command-ref/files/profiles.md -[store objects]: @docroot@/glossary.md#gloss-store-object +[store objects]: @docroot@/store/store-object.md diff --git a/doc/manual/src/command-ref/nix-env/delete-generations.md b/doc/manual/src/command-ref/nix-env/delete-generations.md index 6b6ea798e..ae618b2c6 100644 --- a/doc/manual/src/command-ref/nix-env/delete-generations.md +++ b/doc/manual/src/command-ref/nix-env/delete-generations.md @@ -49,7 +49,7 @@ Periodically deleting old generations is important to make garbage collection effective. The is because profiles are also garbage collection roots — any [store object] reachable from a profile is "alive" and ineligible for deletion. -[store object]: @docroot@/glossary.md#gloss-store-object +[store object]: @docroot@/store/store-object.md {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index d80bcb668..738902041 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -17,7 +17,7 @@ The install operation creates a new user environment. It is based on the current generation of the active [profile](@docroot@/command-ref/files/profiles.md), to which a set of [store paths] described by *args* is added. -[store paths]: @docroot@/glossary.md#gloss-store-path +[store paths]: @docroot@/store/store-path.md The arguments *args* map to store paths in a number of possible ways: diff --git a/doc/manual/src/command-ref/nix-hash.md b/doc/manual/src/command-ref/nix-hash.md index 37c8facec..24e91df12 100644 --- a/doc/manual/src/command-ref/nix-hash.md +++ b/doc/manual/src/command-ref/nix-hash.md @@ -20,16 +20,21 @@ an example. The hash is computed over a *serialisation* of each path: a dump of the file system tree rooted at the path. This allows directories and symlinks to be hashed as well as regular files. The dump is in the -*NAR format* produced by [`nix-store +*[Nix Archive (NAR)][Nix Archive] format* produced by [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md). Thus, `nix-hash path` yields the same cryptographic hash as `nix-store --dump path | md5sum`. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + # Options - `--flat`\ - Print the cryptographic hash of the contents of each regular file - *path*. That is, do not compute the hash over the dump of *path*. + Print the cryptographic hash of the contents of each regular file *path*. + That is, instead of computing + the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, + just [directly hash]((@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is. + This requires *path* to resolve to a regular file rather than directory. The result is identical to that produced by the GNU commands `md5sum` and `sha1sum`. diff --git a/doc/manual/src/command-ref/nix-store/dump.md b/doc/manual/src/command-ref/nix-store/dump.md index c2f3c42ef..b1066fd4c 100644 --- a/doc/manual/src/command-ref/nix-store/dump.md +++ b/doc/manual/src/command-ref/nix-store/dump.md @@ -1,6 +1,6 @@ # Name -`nix-store --dump` - write a single path to a Nix Archive +`nix-store --dump` - write a single path to a [Nix Archive] ## Synopsis @@ -8,7 +8,7 @@ ## Description -The operation `--dump` produces a NAR (Nix ARchive) file containing the +The operation `--dump` produces a [NAR (Nix ARchive)][Nix Archive] file containing the contents of the file system tree rooted at *path*. The archive is written to standard output. @@ -33,6 +33,8 @@ but not other types of files (such as device nodes). A Nix archive can be unpacked using `nix-store --restore`. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + {{#include ./opt-common.md}} {{#include ../opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/export.md b/doc/manual/src/command-ref/nix-store/export.md index 1bc46f53b..09f876865 100644 --- a/doc/manual/src/command-ref/nix-store/export.md +++ b/doc/manual/src/command-ref/nix-store/export.md @@ -1,6 +1,6 @@ # Name -`nix-store --export` - export store paths to a Nix Archive +`nix-store --export` - export store paths to a [Nix Archive] ## Synopsis @@ -11,7 +11,7 @@ The operation `--export` writes a serialisation of the specified store paths to standard output in a format that can be imported into another Nix store with `nix-store --import`. This is like `nix-store ---dump`, except that the NAR archive produced by that command doesn’t +--dump`, except that the [Nix Archive (NAR)][Nix Archive] produced by that command doesn’t contain the necessary meta-information to allow it to be imported into another Nix store (namely, the set of references of the path). @@ -19,6 +19,8 @@ This command does not produce a *closure* of the specified paths, so if a store path references other store paths that are missing in the target Nix store, the import will fail. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + {{#include ./opt-common.md}} {{#include ../opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/import.md b/doc/manual/src/command-ref/nix-store/import.md index 2711316a7..42fae0b22 100644 --- a/doc/manual/src/command-ref/nix-store/import.md +++ b/doc/manual/src/command-ref/nix-store/import.md @@ -1,6 +1,8 @@ # Name -`nix-store --import` - import Nix Archive into the store +`nix-store --import` - import [Nix Archive] into the store + +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive # Synopsis diff --git a/doc/manual/src/command-ref/nix-store/optimise.md b/doc/manual/src/command-ref/nix-store/optimise.md index dc392aeb8..b257466b2 100644 --- a/doc/manual/src/command-ref/nix-store/optimise.md +++ b/doc/manual/src/command-ref/nix-store/optimise.md @@ -12,7 +12,7 @@ The operation `--optimise` reduces Nix store disk space usage by finding identical files in the store and hard-linking them to each other. It typically reduces the size of the store by something like 25-35%. Only regular files and symlinks are hard-linked in this manner. Files are -considered identical when they have the same NAR archive serialisation: +considered identical when they have the same [Nix Archive (NAR)][Nix Archive] serialisation: that is, regular files must have the same contents and permission (executable or non-executable), and symlinks must have the same contents. @@ -38,3 +38,4 @@ hashing files in `/nix/store/qhqx7l2f1kmwihc9bnxs7rc159hsxnf3-gcc-4.1.1' there are 114486 files with equal contents out of 215894 files in total ``` +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index 5428d57fa..6e56387eb 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -25,11 +25,11 @@ Each of *paths* is processed as follows: If no substitutes are available and no store derivation is given, realisation fails. -[store paths]: @docroot@/glossary.md#gloss-store-path +[store paths]: @docroot@/store/store-path.md [valid]: @docroot@/glossary.md#gloss-validity [store derivation]: @docroot@/glossary.md#gloss-store-derivation [output paths]: @docroot@/glossary.md#gloss-output-path -[store objects]: @docroot@/glossary.md#gloss-store-object +[store objects]: @docroot@/store/store-object.md [closure]: @docroot@/glossary.md#gloss-closure [substituters]: @docroot@/command-ref/conf-file.md#conf-substituters [content-addressed derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations diff --git a/doc/manual/src/command-ref/nix-store/restore.md b/doc/manual/src/command-ref/nix-store/restore.md index fcba43df4..2d0aa3127 100644 --- a/doc/manual/src/command-ref/nix-store/restore.md +++ b/doc/manual/src/command-ref/nix-store/restore.md @@ -8,9 +8,11 @@ ## Description -The operation `--restore` unpacks a NAR archive to *path*, which must +The operation `--restore` unpacks a [Nix Archive (NAR)][Nix Archive] to *path*, which must not already exist. The archive is read from standard input. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + {{#include ./opt-common.md}} {{#include ../opt-common.md}} diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index 359fdb556..6e7c0a967 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -147,7 +147,7 @@ Please observe these guidelines to ease reviews: ``` A [store object] contains a [file system object] and [references] to other store objects. - [store object]: @docroot@/glossary.md#gloss-store-object + [store object]: @docroot@/store/store-object.md [file system object]: @docroot@/architecture/file-system-object.md [references]: @docroot@/glossary.md#gloss-reference ``` diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index cbffda187..080b25d30 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -1,5 +1,24 @@ # Glossary +- [content address]{#gloss-content-address} + + A + [*content address*](https://en.wikipedia.org/wiki/Content-addressable_storage) + is a secure way to reference immutable data. + The reference is calculated directly from the content of the data being referenced, which means the reference is + [*tamper proof*](https://en.wikipedia.org/wiki/Tamperproofing) + --- variations of the data should always calculate to distinct content addresses. + + For how Nix uses content addresses, see: + + - [Content-Addressing File System Objects](@docroot@/store/file-system-object/content-address.md) + - [content-addressed store object](#gloss-content-addressed-store-object) + - [content-addressed derivation](#gloss-content-addressed-derivation) + + Software Heritage's writing on [*Intrinsic and Extrinsic identifiers*](https://www.softwareheritage.org/2020/07/09/intrinsic-vs-extrinsic-identifiers) is also a good introduction to the value of content-addressing over other referencing schemes. + + Besides content addressing, the Nix store also uses [input addressing](#gloss-input-addressed-store-object). + - [derivation]{#gloss-derivation} A description of a build task. The result of a derivation is a @@ -266,13 +285,15 @@ See [installables](./command-ref/new-cli/nix.md#installables) for [`nix` commands](./command-ref/new-cli/nix.md) (experimental) for details. -- [NAR]{#gloss-nar} +- [Nix Archive (NAR)]{#gloss-nar} A *N*ix *AR*chive. This is a serialisation of a path in the Nix store. It can contain regular files, directories and symbolic links. NARs are generated and unpacked using `nix-store --dump` and `nix-store --restore`. + See [Nix Archive](store/file-system-object/content-address.html#serial-nix-archive) for details. + - [`∅`]{#gloss-emtpy-set} The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile. diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 1fcc5a95b..3b8e48554 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -199,19 +199,23 @@ Derivations can declare some infrequently used optional attributes. The `outputHashMode` attribute determines how the hash is computed. It must be one of the following two values: - - `"flat"`\ - The output must be a non-executable regular file. If it isn’t, - the build fails. The hash is simply computed over the contents - of that file (so it’s equal to what Unix commands like - `sha256sum` or `sha1sum` produce). + + + - `"flat"` + + The output must be a non-executable regular file; if it isn’t, the build fails. + The hash is + [simply computed over the contents of that file](@docroot@/store/file-system-object/content-address.md#serial-flat) + (so it’s equal to what Unix commands like `sha256sum` or `sha1sum` produce). This is the default. - - `"recursive"` or `"nar"`\ - The hash is computed over the [NAR archive](@docroot@/glossary.md#gloss-nar) dump of the output - (i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). In - this case, the output can be anything, including a directory - tree. + - `"recursive"` or `"nar"` + + The hash is computed over the + [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) + dump of the output (i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). + In this case, the output is allowed to be any [file system object], including directories and more. `"recursive"` is the traditional way of indicating this, and is supported since 2005 (virtually the entire history of Nix). @@ -303,7 +307,7 @@ Derivations can declare some infrequently used optional attributes. [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), the following attributes are available: - - `maxSize` defines the maximum size of the resulting [store object](@docroot@/glossary.md#gloss-store-object). + - `maxSize` defines the maximum size of the resulting [store object](@docroot@/store/store-object.md). - `maxClosureSize` defines the maximum size of the output's closure. - `ignoreSelfRefs` controls whether self-references should be considered when checking for allowed references/requisites. diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 75f824a34..b95900cdd 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -17,7 +17,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect A symbolic name for the derivation. It is added to the [store path] of the corresponding [store derivation] as well as to its [output paths](@docroot@/glossary.md#gloss-output-path). - [store path]: @docroot@/glossary.md#gloss-store-path + [store path]: @docroot@/store/store-path.md > **Example** > @@ -141,7 +141,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect By default, a derivation produces a single output called `out`. However, derivations can produce multiple outputs. - This allows the associated [store objects](@docroot@/glossary.md#gloss-store-object) and their [closures](@docroot@/glossary.md#gloss-closure) to be copied or garbage-collected separately. + This allows the associated [store objects](@docroot@/store/store-object.md) and their [closures](@docroot@/glossary.md#gloss-closure) to be copied or garbage-collected separately. > **Example** > diff --git a/doc/manual/src/language/import-from-derivation.md b/doc/manual/src/language/import-from-derivation.md index fb12ba51a..e901f5bcf 100644 --- a/doc/manual/src/language/import-from-derivation.md +++ b/doc/manual/src/language/import-from-derivation.md @@ -2,9 +2,9 @@ The value of a Nix expression can depend on the contents of a [store object]. -[store object]: @docroot@/glossary.md#gloss-store-object +[store object]: @docroot@/store/store-object.md -Passing an expression `expr` that evaluates to a [store path](@docroot@/glossary.md#gloss-store-path) to any built-in function which reads from the filesystem constitutes Import From Derivation (IFD): +Passing an expression `expr` that evaluates to a [store path](@docroot@/store/store-path.md) to any built-in function which reads from the filesystem constitutes Import From Derivation (IFD): - [`import`](./builtins.md#builtins-import)` expr` - [`builtins.readFile`](./builtins.md#builtins-readFile)` expr` diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 698fed47e..311887e96 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -128,7 +128,7 @@ The result is a string. > The file or directory at *path* must exist and is copied to the [store]. > The path appears in the result as the corresponding [store path]. -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md [store]: @docroot@/glossary.md#gloss-store [String and path concatenation]: #string-and-path-concatenation diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 1f8fecca8..1e2c4ad95 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -107,9 +107,9 @@ An expression that is interpolated must evaluate to one of the following: A string interpolates to itself. -A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](@docroot@/glossary.md#gloss-store-object). +A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](@docroot@/store/store-object.md). -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md > **Example** > diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 2dd52b379..4eb1887fa 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -124,7 +124,7 @@ For example, assume you used a file path in an interpolated string during a `nix repl` session. Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. - [store path]: @docroot@/glossary.md#gloss-store-path + [store path]: @docroot@/store/store-path.md Path literals can also include [string interpolation], besides being [interpolated into other expressions]. diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index 179cafbb4..22a14715f 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -28,9 +28,9 @@ Info about a [store object]. Content address of this store object's file system object, used to compute its store path. -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md [file system object]: @docroot@/store/file-system-object.md -[Nix Archive]: @docroot@/glossary.md#gloss-nar +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive ## Impure fields diff --git a/doc/manual/src/protocols/nix-archive.md b/doc/manual/src/protocols/nix-archive.md index 4fb6282ee..bfc523b3d 100644 --- a/doc/manual/src/protocols/nix-archive.md +++ b/doc/manual/src/protocols/nix-archive.md @@ -1,9 +1,10 @@ # Nix Archive (NAR) format -This is the complete specification of the Nix Archive format. +This is the complete specification of the [Nix Archive] format. The Nix Archive format closely follows the abstract specification of a [file system object] tree, because it is designed to serialize exactly that data structure. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#nix-archive [file system object]: @docroot@/store/file-system-object.md The format of this specification is close to [Extended Backus–Naur form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form), with the exception of the `str(..)` function / parameterized rule, which length-prefixes and pads strings. diff --git a/doc/manual/src/protocols/store-path.md b/doc/manual/src/protocols/store-path.md index 565c4fa75..657774238 100644 --- a/doc/manual/src/protocols/store-path.md +++ b/doc/manual/src/protocols/store-path.md @@ -1,12 +1,14 @@ # Complete Store Path Calculation -This is the complete specification for how store paths are calculated. +This is the complete specification for how [store path]s are calculated. The format of this specification is close to [Extended Backus–Naur form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form), but must deviate for a few things such as hash functions which we treat as bidirectional for specification purposes. Regular users do *not* need to know this information --- store paths can be treated as black boxes computed from the properties of the store objects they refer to. But for those interested in exactly how Nix works, e.g. if they are reimplementing it, this information can be useful. +[store path](@docroot@/store/store-path.md) + ## Store path proper ```ebnf @@ -113,7 +115,7 @@ where Note that `id` = `"out"`, regardless of the name part of the store path. Also note that NAR + SHA-256 must not use this case, and instead must use the `type` = `"source:" ...` case. -[Nix Archive (NAR)]: @docroot@/glossary.md#gloss-NAR +[Nix Archive (NAR)]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive [sha-256]: https://en.m.wikipedia.org/wiki/SHA-256 ### Historical Note diff --git a/doc/manual/src/protocols/tarball-fetcher.md b/doc/manual/src/protocols/tarball-fetcher.md index 274fa6d63..24ec7ae14 100644 --- a/doc/manual/src/protocols/tarball-fetcher.md +++ b/doc/manual/src/protocols/tarball-fetcher.md @@ -22,7 +22,7 @@ Link: ; rel="immutable" *flakeref* must be a tarball flakeref. It can contain the tarball flake attributes `narHash`, `rev`, `revCount` and `lastModified`. If `narHash` is included, its -value must be the NAR hash of the unpacked tarball (as computed via +value must be the [NAR hash][Nix Archive] of the unpacked tarball (as computed via `nix hash path`). Nix checks the contents of the returned tarball against the `narHash` attribute. The `rev` and `revCount` attributes are useful when the tarball flake is a mirror of a fetcher type that @@ -40,3 +40,5 @@ Link: **Warning** +> +> This method is part of the [`git-hashing`][xp-feature-git-hashing] experimental feature. + +Git's file system model is very close to Nix's, and so Git's content addressing method is a pretty good fit. +Just as with regular Git, files and symlinks are hashed as git "blobs", and directories are hashed as git "trees". + +However, one difference between Nix's and Git's file system model needs special treatment. +Plain files, executable files, and symlinks are not differentiated as distinctly addressable objects, but by their context: by the directory entry that refers to them. +That means so long as the root object is a directory, there is no problem: +every non-directory object is owned by a parent directory, and the entry that refers to it provides the missing information. +However, if the root object is not a directory, then we have no way of knowing which one of an executable file, non-executable file, or symlink it is supposed to be. + +In response to this, we have decided to treat a bare file as non-executable file. +This is similar to do what we do with [flat serialisation](#flat), which also lacks this information. +To avoid an address collision, attempts to hash a bare executable file or symlink will result in an error (just as would happen for flat serialisation also). +Thus, Git can encode some, but not all of Nix's "File System Objects", and this sort of content-addressing is likewise partial. + +In the future, we may support a Git-like hash for such file system objects, or we may adopt another Merkle DAG format which is capable of representing all Nix file system objects. + +[file system object]: ../file-system-object.md +[store object]: ../store-object.md +[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/store/store-path.md b/doc/manual/src/store/store-path.md index 085aead51..beec2389b 100644 --- a/doc/manual/src/store/store-path.md +++ b/doc/manual/src/store/store-path.md @@ -1,5 +1,11 @@ # Store Path +> **Example** +> +> `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1` +> +> A rendered store path + Nix implements references to [store objects](./index.md#store-object) as *store paths*. Think of a store path as an [opaque], [unique identifier]: @@ -37,6 +43,10 @@ A store path is rendered to a file system path as the concatenation of > store directory digest name > ``` +Exactly how the digest is calculated depends on the type of store path. +Store path digests are *supposed* to be opaque, and so for most operations, it is not necessary to know the details. +That said, the manual has a full [specification of store path digests](@docroot@/protocols/store-path.md). + ## Store Directory Every [Nix store](./index.md) has a store directory. diff --git a/src/libcmd/misc-store-flags.cc b/src/libcmd/misc-store-flags.cc index e66d3f63b..063a9dd9e 100644 --- a/src/libcmd/misc-store-flags.cc +++ b/src/libcmd/misc-store-flags.cc @@ -81,9 +81,15 @@ Args::Flag fileIngestionMethod(FileIngestionMethod * method) How to compute the hash of the input. One of: - - `nar` (the default): Serialises the input as an archive (following the [_Nix Archive Format_](https://edolstra.github.io/pubs/phd-thesis.pdf#page=101)) and passes that to the hash function. + - `nar` (the default): + Serialises the input as a + [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) + and passes that to the hash function. - - `flat`: Assumes that the input is a single file and directly passes it to the hash function; + - `flat`: + Assumes that the input is a single file and + [directly passes](@docroot@/store/file-system-object/content-address.md#serial-flat) + it to the hash function. )", .labels = {"file-ingestion-method"}, .handler = {[method](std::string s) { @@ -97,16 +103,24 @@ Args::Flag contentAddressMethod(ContentAddressMethod * method) return Args::Flag { .longName = "mode", // FIXME indentation carefully made for context, this is messed up. + /* FIXME link to store object content-addressing not file system + object content addressing once we have that page. */ .description = R"( How to compute the content-address of the store object. One of: - - `nar` (the default): Serialises the input as an archive (following the [_Nix Archive Format_](https://edolstra.github.io/pubs/phd-thesis.pdf#page=101)) and passes that to the hash function. + - `nar` (the default): + Serialises the input as a + [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) + and passes that to the hash function. - - `flat`: Assumes that the input is a single file and directly passes it to the hash function; + - `flat`: + Assumes that the input is a single file and + [directly passes](@docroot@/store/file-system-object/content-address.md#serial-flat) + it to the hash function. - `text`: Like `flat`, but used for - [derivations](@docroot@/glossary.md#store-derivation) serialized in store object and + [derivations](@docroot@/glossary.md#store-derivation) serialized in store object and [`builtins.toFile`](@docroot@/language/builtins.html#builtins-toFile). For advanced use-cases only; for regular usage prefer `nar` and `flat. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 109127d1d..1ad1e3fc0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4515,7 +4515,7 @@ void EvalState::createBaseEnv() 1683705525 ``` - The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation, unless both evaluate `builtins.currentTime` in the same second. + The [store path](@docroot@/store/store-path.md) of a derivation depending on `currentTime` will differ for each evaluation, unless both evaluate `builtins.currentTime` in the same second. )", .impureOnly = true, }); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index e27f30512..fa462dc33 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -200,8 +200,8 @@ static RegisterPrimOp primop_fetchTree({ .doc = R"( Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with: - - the resulting fixed-output [store path](@docroot@/glossary.md#gloss-store-path) - - the corresponding [NAR](@docroot@/glossary.md#gloss-nar) hash + - the resulting fixed-output [store path](@docroot@/store/store-path.md) + - the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash - backend-specific metadata (currently not documented). *input* must be an attribute set with the following attributes: diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 108933422..dc18a11fc 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -910,7 +910,7 @@ public: "substituters", R"( A list of [URLs of Nix stores](@docroot@/store/types/index.md#store-url-format) to be used as substituters, separated by whitespace. - A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Nix can obtain [store objects](@docroot@/glossary.md#gloss-store-object) instead of building them. + A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Nix can obtain [store objects](@docroot@/store/store-object.md) instead of building them. Substituters are tried based on their priority value, which each substituter can set independently. Lower value means higher priority. diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 4ca6747b3..3c26fc515 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -13,7 +13,7 @@ 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 + * See store/store-path.html for more information on a * conceptual level. */ class StorePath diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index 145a8fb1f..c19de27ed 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -12,16 +12,28 @@ struct SourcePath; /** * An enumeration of the ways we can serialize file system * objects. + * + * See `file-system-object/content-address.md#serial` in the manual for + * a user-facing description of this concept, but note that this type is also + * used for storing or sending copies; not just for addressing. + * Note also that there are other content addressing methods that don't + * correspond to a serialisation method. */ enum struct FileSerialisationMethod : uint8_t { /** * Flat-file. The contents of a single file exactly. + * + * See `file-system-object/content-address.md#serial-flat` in the + * manual. */ Flat, /** * Nix Archive. Serializes the file-system object in * Nix Archive format. + * + * See `file-system-object/content-address.md#serial-nix-archive` in + * the manual. */ Recursive, }; @@ -81,33 +93,32 @@ HashResult hashPath( /** * An enumeration of the ways we can ingest file system * objects, producing a hash or digest. + * + * See `file-system-object/content-address.md` in the manual for a + * user-facing description of this concept. */ enum struct FileIngestionMethod : uint8_t { /** * Hash `FileSerialisationMethod::Flat` serialisation. + * + * See `file-system-object/content-address.md#serial-flat` in the + * manual. */ Flat, /** - * Hash `FileSerialisationMethod::Git` serialisation. + * Hash `FileSerialisationMethod::Recursive` serialisation. + * + * See `file-system-object/content-address.md#serial-flat` in the + * manual. */ Recursive, /** - * Git hashing. In particular files are hashed as git "blobs", and - * directories are hashed as git "trees". + * Git hashing. * - * Unlike `Flat` and `Recursive`, this is not a hash of a single - * serialisation but a [Merkle - * DAG](https://en.wikipedia.org/wiki/Merkle_tree) of multiple - * rounds of serialisation and hashing. - * - * @note Git's data model is slightly different, in that a plain - * file doesn't have an executable bit, directory entries do - * instead. We decide treat a bare file as non-executable by fiat, - * as we do with `FileIngestionMethod::Flat` which also lacks this - * information. Thus, Git can encode some but all of Nix's "File - * System Objects", and this sort of hashing is likewise partial. + * See `file-system-object/content-address.md#serial-git` in the + * manual. */ Git, }; diff --git a/src/nix/derivation-show.md b/src/nix/derivation-show.md index 2437ea08f..9fff58ef9 100644 --- a/src/nix/derivation-show.md +++ b/src/nix/derivation-show.md @@ -50,7 +50,7 @@ By default, this command only shows top-level derivations, but with `nix derivation show` outputs a JSON map of [store path]s to derivations in the following format: -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md {{#include ../../protocols/json/derivation.md}} diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index 8afcbe982..de77a7b6b 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -58,7 +58,7 @@ struct AuthorizationSettings : Config { this, {"root"}, "trusted-users", R"( A list of user names, separated by whitespace. - These users will have additional rights when connecting to the Nix daemon, such as the ability to specify additional [substituters](#conf-substituters), or to import unsigned [NARs](@docroot@/glossary.md#gloss-nar). + These users will have additional rights when connecting to the Nix daemon, such as the ability to specify additional [substituters](#conf-substituters), or to import unsigned realisations or unsigned input-addressed store objects. You can also specify groups by prefixing names with `@`. For instance, `@wheel` means all users in the `wheel` group. From d50ce2df14138fc22e8528a37e5460e2f397420b Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:14:06 +0200 Subject: [PATCH 101/910] make a more relevant example for `nix-store --export` given `nix-copy-closure` exists, it doesn't make much sense to do nix-store --export $paths | nix-store --import --store ssh://foo@bar since that dumps everything rather than granularly transferring store objects as needed. therefore, pick an example where dumping the entire closure into a file actually makes a difference, such as when deploying to airgapped systems. --- .../src/command-ref/nix-store/export.md | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/export.md b/doc/manual/src/command-ref/nix-store/export.md index 1bc46f53b..4f6dd5ffa 100644 --- a/doc/manual/src/command-ref/nix-store/export.md +++ b/doc/manual/src/command-ref/nix-store/export.md @@ -27,15 +27,21 @@ Nix store, the import will fail. # Examples -To copy a whole closure, do something -like: - -```console -$ nix-store --export $(nix-store --query --requisites paths) > out -``` - -To import the whole closure again, run: - -```console -$ nix-store --import < out -``` +> **Example** +> +> Deploy GNU Hello to an airgapped machine via USB stick. +> +> Write the closure to the block device on a machine with internet connection: +> +> ```shell-session +> [alice@itchy]$ storePath=$(nix-build '' -I nixpkgs=channel:nixpkgs-unstable -A hello --no-out-link) +> [alice@itchy]$ nix-store --export $(nix-store --query --requisites $storePath) | sudo dd of=/dev/usb +> ``` +> +> Read the closure from the block device on the machine without internet connection: +> +> ```shell-session +> [bob@scratchy]$ hello=$(sudo dd if=/dev/usb | nix-store --import | tail -1) +> [bob@scratchy]$ $hello/bin/hello +> Hello, world! +> ``` From 449404531db5fd18a43456bd45517dbf91ec3197 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Thu, 16 May 2024 00:39:39 +0200 Subject: [PATCH 102/910] fix "Embedding the Nix Evaluator" c api example --- doc/external-api/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 167c02199..d3581d207 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -46,9 +46,9 @@ Nix expression `builtins.nixVersion`. // NOTE: This example lacks all error handling. Production code must check for // errors, as some return values will be undefined. -void my_get_string_cb(const char * start, unsigned int n, char ** user_data) +void my_get_string_cb(const char * start, unsigned int n, void * user_data) { - *user_data = strdup(start); + *((char **) user_data) = strdup(start); } int main() @@ -63,7 +63,7 @@ int main() nix_value_force(NULL, state, value); char * version; - nix_get_string(NULL, value, my_get_string_cb, version); + nix_get_string(NULL, value, my_get_string_cb, &version); printf("Nix version: %s\n", version); free(version); From 359043ed0db8ba55284a62b370dc44157a4fe723 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Tue, 30 Apr 2024 18:11:01 +0200 Subject: [PATCH 103/910] add missing c api parameter names to documentation --- src/libexpr-c/nix_api_external.h | 4 ++-- src/libstore-c/nix_api_store.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/nix_api_external.h b/src/libexpr-c/nix_api_external.h index 12ea00407..a52346da9 100644 --- a/src/libexpr-c/nix_api_external.h +++ b/src/libexpr-c/nix_api_external.h @@ -136,7 +136,7 @@ typedef struct NixCExternalValueDesc * or setting it to the empty string, will make the conversion throw an error. */ void (*printValueAsJSON)( - void * self, EvalState *, bool strict, nix_string_context * c, bool copyToStore, nix_string_return * res); + void * self, EvalState * state, bool strict, nix_string_context * c, bool copyToStore, nix_string_return * res); /** * @brief Convert the external value to XML * @@ -155,7 +155,7 @@ typedef struct NixCExternalValueDesc */ void (*printValueAsXML)( void * self, - EvalState *, + EvalState * state, int strict, int location, void * doc, diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index 209f91f0d..baa53ef09 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -63,7 +63,7 @@ nix_err nix_init_plugins(nix_c_context * context); * @return a Store pointer, NULL in case of errors * @see nix_store_free */ -Store * nix_store_open(nix_c_context *, const char * uri, const char *** params); +Store * nix_store_open(nix_c_context * context, const char * uri, const char *** params); /** * @brief Deallocate a nix store and free any resources if not also held by other Store instances. From f63292462c5cfd89d718c71a2a345bc0413a3de8 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Tue, 30 Apr 2024 18:13:52 +0200 Subject: [PATCH 104/910] document `nix_external_print`'s `printer` parameter to be an out parameter --- src/libexpr-c/nix_api_external.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_external.h b/src/libexpr-c/nix_api_external.h index a52346da9..6c524b975 100644 --- a/src/libexpr-c/nix_api_external.h +++ b/src/libexpr-c/nix_api_external.h @@ -48,7 +48,7 @@ void nix_set_string_return(nix_string_return * str, const char * c); * Print to the nix_printer * * @param[out] context Optional, stores error information - * @param printer The nix_printer to print to + * @param[out] printer The nix_printer to print to * @param[in] str The string to print * @returns NIX_OK if everything worked */ From c66796d4604ba97cb6a7f63286ffd38d223936ac Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Fri, 17 May 2024 08:38:57 +0800 Subject: [PATCH 105/910] fix typo in testing documentation --- doc/manual/src/contributing/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 607914ba3..88b3c5cd9 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -60,7 +60,7 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks. > ``` The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `tests/unit/${library_name_without-nix}`. -Given a interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `tests/unit/libexpr/tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `tests/unit/libexpr-support/tests/value/context.{hh,cc}`. +Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `tests/unit/libexpr/tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `tests/unit/libexpr-support/tests/value/context.{hh,cc}`. Data for unit tests is stored in a `data` subdir of the directory for each unit test executable. For example, `libnixstore` code is in `src/libstore`, and its test data is in `tests/unit/libstore/data`. From a2cb90caba0d3e428bfb778f059e6ea866565ba9 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Fri, 17 May 2024 17:26:09 +0200 Subject: [PATCH 106/910] chore: add `CITATION.cff` file --- CITATION.cff | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..c3366101a --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,31 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: Nix +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Eelco + family-names: Dolstra + email: edolstra@gmail.com + - name: The Nix contributors + website: 'https://github.com/NixOS/nix' +identifiers: + - type: url + value: 'https://edolstra.github.io/pubs/phd-thesis.pdf' + description: PHD Thesis +repository-code: 'https://github.com/NixOS/nix' +url: 'https://nixos.org/' +abstract: >- + Nix, a purely functional package manager, is a powerful + package manager for Linux and other Unix systems that + makes package management reliable and reproducible. +keywords: + - reproducibility + - open-source + - c++ + - functional +license: LGPL-2.1 From 979a019014569eee7d0071605f6ff500b544f6ac Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 17 May 2024 18:18:38 +0200 Subject: [PATCH 107/910] Improve nix-store --delete failure message On several occasions I've found myself confused when trying to delete a store path, because I am told it's still alive, but nix-store --query --roots doesn't show anything. Let's save future users this confusion by mentioning that a path might be alive due to having referrers, not just roots. --- src/libstore/gc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 8286dff27..d3fa88f59 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -781,7 +781,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) throw Error( "Cannot delete path '%1%' since it is still alive. " "To find out why, use: " - "nix-store --query --roots", + "nix-store --query --roots and nix-store --query --referrers", printStorePath(i)); } From 19720d733fb8c8a34f05ccf83cc4b67bf8ed4fe1 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Mon, 13 May 2024 20:58:54 +0200 Subject: [PATCH 108/910] nix3-build: show all FOD errors with `--keep-going` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basically I'd expect the same behavior as with `nix-build`, i.e. with `--keep-going` the hash-mismatch error of each failing fixed-output derivation is shown. The approach is derived from `Store::buildPaths` (`entry-point.cc`): instead of throwing the first build-result, check if there are any build errors and if so, display all of them and throw after that. Unfortunately, the BuildResult struct doesn't have an `ErrorInfo` (there's a FIXME for that at least), so I have to construct my own here. This is a rather cheap bugfix and I decided against touching too many parts of libstore for that (also I don't know if that's in line with the ongoing refactoring work). Closes https://git.lix.systems/lix-project/lix/issues/302 Change-Id: I378ab984fa271e6808c6897c45e0f070eb4c6fac Signed-off-by: Jörg Thalheim --- doc/manual/rl-next/consistent-nix-build.md | 6 ++++ src/libcmd/installables.cc | 38 ++++++++++++++++++--- tests/functional/build.sh | 32 ++++++++++++++++++ tests/functional/fod-failing.nix | 39 ++++++++++++++++++++++ 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 doc/manual/rl-next/consistent-nix-build.md create mode 100644 tests/functional/fod-failing.nix diff --git a/doc/manual/rl-next/consistent-nix-build.md b/doc/manual/rl-next/consistent-nix-build.md new file mode 100644 index 000000000..d5929dc8e --- /dev/null +++ b/doc/manual/rl-next/consistent-nix-build.md @@ -0,0 +1,6 @@ +--- +synopsis: Show all FOD errors with `nix build --keep-going` +--- + +`nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means +that if e.g. multiple FODs fail to build, all hash mismatches are displayed. diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 43e312540..6835c512c 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -601,6 +601,37 @@ std::vector Installable::build( return res; } +static void throwBuildErrors( + std::vector & buildResults, + const Store & store) +{ + std::vector failed; + for (auto & buildResult : buildResults) { + if (!buildResult.success()) { + failed.push_back(buildResult); + } + } + + auto failedResult = failed.begin(); + if (failedResult != failed.end()) { + if (failed.size() == 1) { + failedResult->rethrow(); + } else { + StringSet failedPaths; + for (; failedResult != failed.end(); failedResult++) { + if (!failedResult->errorMsg.empty()) { + logError(ErrorInfo{ + .level = lvlError, + .msg = failedResult->errorMsg, + }); + } + failedPaths.insert(failedResult->path.to_string(store)); + } + throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); + } + } +} + std::vector, BuiltPathWithResult>> Installable::build2( ref evalStore, ref store, @@ -662,10 +693,9 @@ std::vector, BuiltPathWithResult>> Installable::build if (settings.printMissing) printMissing(store, pathsToBuild, lvlInfo); - for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) { - if (!buildResult.success()) - buildResult.rethrow(); - + auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore); + throwBuildErrors(buildResults, *store); + for (auto & buildResult : buildResults) { for (auto & aux : backmap[buildResult.path]) { std::visit(overloaded { [&](const DerivedPath::Built & bfd) { diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 7fbdb0f07..95a20dc6a 100644 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -133,3 +133,35 @@ nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status # Make sure that `--stdin` works and does not apply any defaults printf "" | nix build --no-link --stdin --json | jq --exit-status '. == []' printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status '.[0]|has("drvPath")' + +# --keep-going and FOD +out="$(nix build -f fod-failing.nix -L 2>&1)" && status=0 || status=$? +test "$status" = 1 +# one "hash mismatch" error, one "build of ... failed" +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 2 +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x1\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'" +<<<"$out" grepQuiet -E "error: build of '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out' failed" + +out="$(nix build -f fod-failing.nix -L x1 x2 x3 --keep-going 2>&1)" && status=0 || status=$? +test "$status" = 1 +# three "hash mismatch" errors - for each failing fod, one "build of ... failed" +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 4 +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x1\\.drv'" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x2\\.drv'" +<<<"$out" grepQuiet -E "error: build of '.*-x[1-3]\\.drv\\^out', '.*-x[1-3]\\.drv\\^out', '.*-x[1-3]\\.drv\\^out' failed" + +out="$(nix build -f fod-failing.nix -L x4 2>&1)" && status=0 || status=$? +test "$status" = 1 +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 2 +<<<"$out" grepQuiet -E "error: 1 dependencies of derivation '.*-x4\\.drv' failed to build" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x2\\.drv'" + +out="$(nix build -f fod-failing.nix -L x4 --keep-going 2>&1)" && status=0 || status=$? +test "$status" = 1 +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 3 +<<<"$out" grepQuiet -E "error: 2 dependencies of derivation '.*-x4\\.drv' failed to build" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'" diff --git a/tests/functional/fod-failing.nix b/tests/functional/fod-failing.nix new file mode 100644 index 000000000..37c04fe12 --- /dev/null +++ b/tests/functional/fod-failing.nix @@ -0,0 +1,39 @@ +with import ./config.nix; +rec { + x1 = mkDerivation { + name = "x1"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x2 = mkDerivation { + name = "x2"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x3 = mkDerivation { + name = "x3"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x4 = mkDerivation { + name = "x4"; + inherit x2 x3; + builder = builtins.toFile "builder.sh" + '' + echo $x2 $x3 + exit 1 + ''; + }; +} From 52a16b7e5981fbb3e1b8eac8404b17350b2623c0 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 8 May 2024 18:50:03 -0600 Subject: [PATCH 109/910] add and fix -Wdeprecated-copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *so* many warnings, from only two definitions Change-Id: If2561cd500c05a1e33cce984faf9f3e42a8a95ac Signed-off-by: Jörg Thalheim --- Makefile | 2 +- src/libutil/fmt.hh | 2 ++ src/libutil/ref.hh | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ea0754fa5..cb6eeb573 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index c178257d4..ef44a8409 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -182,6 +182,8 @@ public: return *this; } + HintFmt & operator=(HintFmt const & rhs) = default; + std::string str() const { return fmt.str(); diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 5d0c3696d..03aa64273 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -77,6 +77,8 @@ public: return ref((std::shared_ptr) p); } + ref & operator=(ref const & rhs) = default; + bool operator == (const ref & other) const { return p == other.p; From 05bc889b893c7c37994a3c7d869e222994bcdc73 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Thu, 9 May 2024 07:00:51 -0600 Subject: [PATCH 110/910] add and fix -Wignored-qualifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4bffa766ae04dd80355f9b8c10e59700e4b406da Signed-off-by: Jörg Thalheim --- Makefile | 2 +- src/libfetchers/tarball.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cb6eeb573..6c96ef5db 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index d03ff82ce..5de367052 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -202,7 +202,7 @@ struct CurlInputScheme : InputScheme { const std::set transportUrlSchemes = {"file", "http", "https"}; - const bool hasTarballExtension(std::string_view path) const + bool hasTarballExtension(std::string_view path) const { return hasSuffix(path, ".zip") || hasSuffix(path, ".tar") || hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz") From a5de384cffe5bedb0a83361242de9a6480a1e6c0 Mon Sep 17 00:00:00 2001 From: Lorenz Leutgeb Date: Sat, 18 May 2024 19:47:18 +0200 Subject: [PATCH 111/910] chore: PhD thesis as reference in `CITATION.cff` --- CITATION.cff | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index c3366101a..0105fb823 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,6 +1,3 @@ -# This CITATION.cff file was generated with cffinit. -# Visit https://bit.ly/cffinit to generate yours today! - cff-version: 1.2.0 title: Nix message: >- @@ -13,10 +10,24 @@ authors: email: edolstra@gmail.com - name: The Nix contributors website: 'https://github.com/NixOS/nix' -identifiers: - - type: url - value: 'https://edolstra.github.io/pubs/phd-thesis.pdf' - description: PHD Thesis +references: + - title: The Purely Functional Software Deployment Model + authors: + - family-names: Dolstra + given-names: Eelco + year: 2006 + type: thesis + thesis-type: PhD thesis + isbn: 90-393-4130-3 + url: https://dspace.library.uu.nl/handle/1874/7540 + database-provider: Utrecht University Repository + institution: + name: Utrecht University + keywords: + - configuration management + - software deployment + - purely functional + - component-based software engineering repository-code: 'https://github.com/NixOS/nix' url: 'https://nixos.org/' abstract: >- From 53f0c44d6c4cacd0444c9f0d72e97eb2a76251f6 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 16:05:14 -0700 Subject: [PATCH 112/910] Implement `updateWindowSize` for Windows --- src/libutil/terminal.cc | 15 +++++++++++++-- src/libutil/terminal.hh | 4 ---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index 4dc280f8c..5d5ff7dcb 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -4,6 +4,8 @@ #if _WIN32 # include +# define WIN32_LEAN_AND_MEAN +# include # define isatty _isatty #else # include @@ -97,17 +99,26 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w static Sync> windowSize{{0, 0}}; -#ifndef _WIN32 void updateWindowSize() { + #ifndef _WIN32 struct winsize ws; if (ioctl(2, TIOCGWINSZ, &ws) == 0) { auto windowSize_(windowSize.lock()); windowSize_->first = ws.ws_row; windowSize_->second = ws.ws_col; } + #else + CONSOLE_SCREEN_BUFFER_INFO info; + // From https://stackoverflow.com/a/12642749 + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) != 0) { + auto windowSize_(windowSize.lock()); + // From https://github.com/libuv/libuv/blob/v1.48.0/src/win/tty.c#L1130 + windowSize_->first = info.srWindow.Bottom - info.srWindow.Top + 1; + windowSize_->second = info.dwSize.X; + } + #endif } -#endif std::pair getWindowSize() diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 628833283..9d8d0c743 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -21,16 +21,12 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll = false, unsigned int width = std::numeric_limits::max()); -#ifndef _WIN32 - /** * Recalculate the window size, updating a global variable. Used in the * `SIGWINCH` signal handler. */ void updateWindowSize(); -#endif - /** * @return the number of rows and columns of the terminal. * From e42d00c961ad948c80c27f49d58316a7eae5e728 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 04:09:56 -0700 Subject: [PATCH 113/910] Change `rlim_t` to `size_t` in `setStackSize` in preparation of Windows impl --- src/libutil/current-process.cc | 6 +++--- src/libutil/current-process.hh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index c88013b3c..b252bd4d0 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -60,14 +60,14 @@ unsigned int getMaxCPU() #ifndef _WIN32 -rlim_t savedStackSize = 0; +size_t savedStackSize = 0; -void setStackSize(rlim_t stackSize) +void setStackSize(size_t stackSize) { struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) { savedStackSize = limit.rlim_cur; - limit.rlim_cur = std::min(stackSize, limit.rlim_max); + limit.rlim_cur = std::min(static_cast(stackSize), limit.rlim_max); if (setrlimit(RLIMIT_STACK, &limit) != 0) { logger->log( lvlError, diff --git a/src/libutil/current-process.hh b/src/libutil/current-process.hh index a5adb70cf..8db4ada0d 100644 --- a/src/libutil/current-process.hh +++ b/src/libutil/current-process.hh @@ -21,7 +21,7 @@ unsigned int getMaxCPU(); /** * Change the stack size. */ -void setStackSize(rlim_t stackSize); +void setStackSize(size_t stackSize); #endif /** From 6a3f906382342f05cfe9254f8e0d1c4b01ad9ad5 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 04:27:09 -0700 Subject: [PATCH 114/910] Implement `setStackSize` for Windows --- src/libutil/current-process.cc | 23 +++++++++++++++++++++-- src/libutil/current-process.hh | 2 -- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index b252bd4d0..db3111560 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -59,11 +59,11 @@ unsigned int getMaxCPU() ////////////////////////////////////////////////////////////////////// -#ifndef _WIN32 size_t savedStackSize = 0; void setStackSize(size_t stackSize) { + #ifndef _WIN32 struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) { savedStackSize = limit.rlim_cur; @@ -81,8 +81,27 @@ void setStackSize(size_t stackSize) ); } } + #else + ULONG currStackSize = 0; + // This retrieves the current promised stack size + SetThreadStackGuarantee(&currStackSize); + if (currStackSize < stackSize) { + savedStackSize = currStackSize; + ULONG newStackSize = stackSize; + if (SetThreadStackGuarantee(&newStackSize) == 0) { + logger->log( + lvlError, + HintFmt( + "Failed to increase stack size from %1% to %2%: %3%", + savedStackSize, + stackSize, + std::to_string(GetLastError()) + ).str() + ); + } + } + #endif } -#endif void restoreProcessContext(bool restoreMounts) { diff --git a/src/libutil/current-process.hh b/src/libutil/current-process.hh index 8db4ada0d..8286bf89d 100644 --- a/src/libutil/current-process.hh +++ b/src/libutil/current-process.hh @@ -17,12 +17,10 @@ namespace nix { */ unsigned int getMaxCPU(); -#ifndef _WIN32 // TODO implement on Windows, if needed. /** * Change the stack size. */ void setStackSize(size_t stackSize); -#endif /** * Restore the original inherited Unix process context (such as signal From a41f4223de5123d90264f2eb9843f218153c59fb Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 06:44:52 -0700 Subject: [PATCH 115/910] Use setStackSize on Windows --- src/nix/main.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index bc13a4df5..d3f3324ac 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -522,10 +522,13 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { -#ifndef _WIN32 // TODO implement on Windows +#ifndef _WIN32 // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); +#else + // Windows' default stack reservation is 1 MB, going over will fail + nix::setStackSize(1 * 1024 * 1024); #endif return nix::handleExceptions(argv[0], [&]() { From e0bfa6c55fbf1e275419b0ca98902781ffa9fa0d Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 20 May 2024 08:27:33 +0200 Subject: [PATCH 116/910] small additions to the documentation of `nix_store_open` and `nix_state_create` (#10728) --- src/libexpr-c/nix_api_expr.h | 2 +- src/libstore-c/nix_api_store.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 04fc92f0f..ab7f50384 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -140,7 +140,7 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * * @brief Create a new Nix language evaluator state. * * @param[out] context Optional, stores error information - * @param[in] lookupPath Array of strings corresponding to entries in NIX_PATH. + * @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH. * @param[in] store The Nix store to use. * @return A new Nix state or NULL on failure. */ diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index baa53ef09..e9441be1a 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -57,9 +57,11 @@ nix_err nix_init_plugins(nix_c_context * context); * @brief Open a nix store * Store instances may share state and resources behind the scenes. * @param[out] context Optional, stores error information - * @param[in] uri URI of the nix store, copied - * @param[in] params optional, array of key-value pairs, {{"endpoint", - * "https://s3.local"}} + * @param[in] uri URI of the Nix store, copied. See [*Store URL format* in the Nix Reference + * Manual](https://nixos.org/manual/nix/stable/store/types/#store-url-format). + * @param[in] params optional, null-terminated array of key-value pairs, e.g. {{"endpoint", + * "https://s3.local"}}. See [*Store Types* in the Nix Reference + * Manual](https://nixos.org/manual/nix/stable/store/types). * @return a Store pointer, NULL in case of errors * @see nix_store_free */ From 209d75529caaaf82a760056fc60156b9153af5c8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 20 May 2024 08:28:35 +0200 Subject: [PATCH 117/910] reword documentation on `nix-store --export` (#10713) - add links to definitions of terms - one sentence per line - be more specific about which store is used for the import - clearly distinguish store paths and store objects - make a recommendation to use `nix-copy-closure` for efficient SSH transfers --- .../src/command-ref/nix-store/export.md | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/export.md b/doc/manual/src/command-ref/nix-store/export.md index ba51ff5f6..ba772eb43 100644 --- a/doc/manual/src/command-ref/nix-store/export.md +++ b/doc/manual/src/command-ref/nix-store/export.md @@ -8,16 +8,20 @@ ## Description -The operation `--export` writes a serialisation of the specified store -paths to standard output in a format that can be imported into another -Nix store with `nix-store --import`. This is like `nix-store ---dump`, except that the [Nix Archive (NAR)][Nix Archive] produced by that command doesn’t -contain the necessary meta-information to allow it to be imported into -another Nix store (namely, the set of references of the path). +The operation `--export` writes a serialisation of the given [store objects](@docroot@/glossary.md#gloss-store-object) to standard output in a format that can be imported into another [Nix store](@docroot@/store/index.md) with [`nix-store --import`](./import.md). -This command does not produce a *closure* of the specified paths, so if -a store path references other store paths that are missing in the target -Nix store, the import will fail. +> **Warning** +> +> This command *does not* produce a [closure](@docroot@/glossary.md#gloss-closure) of the specified store paths. +> Trying to import a store object that refers to store paths not available in the target Nix store will fail. +> +> Use [`nix-store --query`](@docroot@/command-ref/nix-store/query.md) to obtain the closure of a store path. + +This command is different from [`nix-store --dump`](./dump.md), which produces a [Nix archive](@docroot@/glossary.md#gloss-nar) that *does not* contain the set of [references](@docroot@/glossary.md#gloss-reference) of a given store path. + +> **Note** +> +> For efficient transfer of closures to remote machines over SSH, use [`nix-copy-closure`](@docroot@/command-ref/nix-copy-closure.md). [Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive From 927034e7acd3dd993c5cea189922f5d8050a65fe Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 May 2024 10:25:04 +0200 Subject: [PATCH 118/910] value.hh: Shut up warning about useless const qualifier --- src/libexpr/value.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 61cf2d310..208cab21d 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -449,7 +449,7 @@ public: return std::string_view(payload.string.c_str); } - const char * const c_str() const + const char * c_str() const { assert(internalType == tString); return payload.string.c_str; From d48bbda2e702879b2296b897ec805167af178ebc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 20 May 2024 08:34:49 -0400 Subject: [PATCH 119/910] Update the `updateWindowSize` documentation --- src/libutil/terminal.hh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 9d8d0c743..902e75945 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -22,8 +22,9 @@ std::string filterANSIEscapes(std::string_view s, unsigned int width = std::numeric_limits::max()); /** - * Recalculate the window size, updating a global variable. Used in the - * `SIGWINCH` signal handler. + * Recalculate the window size, updating a global variable. + * + * Used in the `SIGWINCH` signal handler on Unix, for example. */ void updateWindowSize(); From 1c75af969a1ed0df63885c691863b8fee798cd4e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 Apr 2024 17:07:39 -0400 Subject: [PATCH 120/910] Document store object content addressing & improve JSON format The JSON format no longer uses the legacy ATerm `r:` prefixing nonsese, but separate fields. Progress on #9866 Co-authored-by: Robert Hensing --- doc/manual/rl-next/derivation-json-change.md | 12 ++ doc/manual/src/SUMMARY.md.in | 1 + .../src/language/advanced-attributes.md | 45 ++++--- doc/manual/src/protocols/json/derivation.md | 27 +++- doc/manual/src/protocols/store-path.md | 25 ++-- .../file-system-object/content-address.md | 9 +- .../src/store/store-object/content-address.md | 123 ++++++++++++++++++ src/libcmd/misc-store-flags.cc | 12 +- src/libstore/derivations.cc | 24 ++-- src/nix/flake.md | 10 +- src/nix/nar-cat.md | 3 +- src/nix/nar-dump-path.md | 6 +- src/nix/nar-ls.md | 6 +- src/nix/nar.md | 9 +- src/nix/store-dump-path.md | 4 +- src/nix/verify.md | 2 + .../data/derivation/output-caFixedFlat.json | 1 + .../data/derivation/output-caFixedNAR.json | 3 +- .../data/derivation/output-caFixedText.json | 3 +- .../data/derivation/output-caFloating.json | 3 +- .../data/derivation/output-impure.json | 5 +- 21 files changed, 268 insertions(+), 65 deletions(-) create mode 100644 doc/manual/rl-next/derivation-json-change.md create mode 100644 doc/manual/src/store/store-object/content-address.md diff --git a/doc/manual/rl-next/derivation-json-change.md b/doc/manual/rl-next/derivation-json-change.md new file mode 100644 index 000000000..2a1d40e83 --- /dev/null +++ b/doc/manual/rl-next/derivation-json-change.md @@ -0,0 +1,12 @@ +--- +synopsis: Modify `nix derivation {add,show}` JSON format +issues: 9866 +prs: 10722 +--- + +The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). +In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, +rather than one field with an arcane `:`-separated format. + +This JSON format is only used by the experimental `nix derivation` family of commands, at this time. +Future revisions are expected as the JSON format is still not entirely in compliance even after these changes. diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index d1c0ad12a..52e6354c6 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -20,6 +20,7 @@ - [File System Object](store/file-system-object.md) - [Content-Addressing File System Objects](store/file-system-object/content-address.md) - [Store Object](store/store-object.md) + - [Content-Addressing Store Objects](store/store-object/content-address.md) - [Store Path](store/store-path.md) - [Store Types](store/types/index.md) {{#include ./store/types/SUMMARY.md}} diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 3b8e48554..113062db1 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -197,37 +197,40 @@ Derivations can declare some infrequently used optional attributes. `outputHashAlgo` can only be `null` when `outputHash` follows the SRI format. The `outputHashMode` attribute determines how the hash is computed. - It must be one of the following two values: + It must be one of the following values: - - - - `"flat"` - - The output must be a non-executable regular file; if it isn’t, the build fails. - The hash is - [simply computed over the contents of that file](@docroot@/store/file-system-object/content-address.md#serial-flat) - (so it’s equal to what Unix commands like `sha256sum` or `sha1sum` produce). + - [`"flat"`](@docroot@/store/store-object/content-address.md#method-flat) This is the default. - - `"recursive"` or `"nar"` + - [`"recursive"` or `"nar"`](@docroot@/store/store-object/content-address.md#method-nix-archive) - The hash is computed over the - [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) - dump of the output (i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). - In this case, the output is allowed to be any [file system object], including directories and more. + > **Compatibility** + > + > `"recursive"` is the traditional way of indicating this, + > and is supported since 2005 (virtually the entire history of Nix). + > `"nar"` is more clear, and consistent with other parts of Nix (such as the CLI), + > however support for it is only added in Nix version 2.21. - `"recursive"` is the traditional way of indicating this, - and is supported since 2005 (virtually the entire history of Nix). - `"nar"` is more clear, and consistent with other parts of Nix (such as the CLI), - however support for it is only added in Nix version 2.21. + - [`"text"`](@docroot@/store/store-object/content-address.md#method-text) + + > **Warning** + > + > The use of this method for derivation outputs is part of the [`dynamic-derivations`][xp-feature-dynamic-derivations] experimental feature. + + - [`"git"`](@docroot@/store/store-object/content-address.md#method-git) + + > **Warning** + > + > This method is part of the [`git-hashing`][xp-feature-git-hashing] experimental feature. - [`__contentAddressed`]{#adv-attr-__contentAddressed} + > **Warning** > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md). > > To use this attribute, you must enable the - > [`ca-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) experimental feature. + > [`ca-derivations`][xp-feature-ca-derivations] experimental feature. > For example, in [nix.conf](../command-ref/conf-file.md) you could add: > > ``` @@ -359,3 +362,7 @@ Derivations can declare some infrequently used optional attributes. ``` ensures that the derivation can only be built on a machine with the `kvm` feature. + +[xp-feature-ca-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations +[xp-feature-dynamic-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations +[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/protocols/json/derivation.md b/doc/manual/src/protocols/json/derivation.md index 649d543cc..f881dd703 100644 --- a/doc/manual/src/protocols/json/derivation.md +++ b/doc/manual/src/protocols/json/derivation.md @@ -18,10 +18,30 @@ is a JSON object with the following fields: Information about the output paths of the derivation. This is a JSON object with one member per output, where the key is the output name and the value is a JSON object with these fields: - * `path`: The output path. + * `path`: + The output path, if it is known in advanced. + Otherwise, `null`. + + + * `method`: + For an output which will be [content addresed], a string representing the [method](@docroot@/store/store-object/content-address.md) of content addressing that is chosen. + Valid method strings are: + + - [`flat`](@docroot@/store/store-object/content-address.md#method-flat) + - [`nar`](@docroot@/store/store-object/content-address.md#method-nix-archive) + - [`text`](@docroot@/store/store-object/content-address.md#method-text) + - [`git`](@docroot@/store/store-object/content-address.md#method-git) + + Otherwise, `null`. * `hashAlgo`: - For fixed-output derivations, the hashing algorithm (e.g. `sha256`), optionally prefixed by `r:` if `hash` denotes a NAR hash rather than a flat file hash. + For an output which will be [content addresed], the name of the hash algorithm used. + Valid algorithm strings are: + + - `md5` + - `sha1` + - `sha256` + - `sha512` * `hash`: For fixed-output derivations, the expected content hash in base-16. @@ -32,7 +52,8 @@ is a JSON object with the following fields: > "outputs": { > "out": { > "path": "/nix/store/2543j7c6jn75blc3drf4g5vhb1rhdq29-source", - > "hashAlgo": "r:sha256", + > "method": "nar", + > "hashAlgo": "sha256", > "hash": "6fc80dcc62179dbc12fc0b5881275898f93444833d21b89dfe5f7fbcbb1d0d62" > } > } diff --git a/doc/manual/src/protocols/store-path.md b/doc/manual/src/protocols/store-path.md index 657774238..52352d358 100644 --- a/doc/manual/src/protocols/store-path.md +++ b/doc/manual/src/protocols/store-path.md @@ -36,18 +36,23 @@ where - `type` = one of: - ```ebnf - | "text" ( ":" store-path )* + | "text" { ":" store-path } ``` - for encoded derivations written to the store. + This is for the + ["Text"](@docroot@/store/store-object/content-address.md#method-text) + method of content addressing store objects. The optional trailing store paths are the references of the store object. - ```ebnf - | "source" ( ":" store-path )* + | "source" { ":" store-path } [ ":self" ] ``` - For paths copied to the store and hashed via a [Nix Archive (NAR)] and [SHA-256][sha-256]. - Just like in the text case, we can have the store objects referenced by their paths. + This is for the + ["Nix Archive"](@docroot@/store/store-object/content-address.md#method-nix-archive) + method of content addressing store objects, + if the hash algorithm is [SHA-256]. + Just like in the "Text" case, we can have the store objects referenced by their paths. Additionally, we can have an optional `:self` label to denote self reference. - ```ebnf @@ -55,8 +60,12 @@ where ``` For either the outputs built from derivations, - paths copied to the store hashed that area single file hashed directly, or the via a hash algorithm other than [SHA-256][sha-256]. - (in that case "source" is used; this is only necessary for compatibility). + or content-addressed store objects that are not using one of the two above cases. + To be explicit about the latter, that is currently these methods: + + - ["Flat"](@docroot@/store/store-object/content-address.md#method-flat) + - ["Git"](@docroot@/store/store-object/content-address.md#method-git) + - ["Nix Archive"](@docroot@/store/store-object/content-address.md#method-nix-archive) if the hash algorithm is not [SHA-256]. `id` is the name of the output (usually, "out"). For content-addressed store objects, `id`, is always "out". @@ -116,7 +125,7 @@ where Also note that NAR + SHA-256 must not use this case, and instead must use the `type` = `"source:" ...` case. [Nix Archive (NAR)]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive -[sha-256]: https://en.m.wikipedia.org/wiki/SHA-256 +[SHA-256]: https://en.m.wikipedia.org/wiki/SHA-256 ### Historical Note diff --git a/doc/manual/src/store/file-system-object/content-address.md b/doc/manual/src/store/file-system-object/content-address.md index fc5be8c67..1c63c52eb 100644 --- a/doc/manual/src/store/file-system-object/content-address.md +++ b/doc/manual/src/store/file-system-object/content-address.md @@ -1,7 +1,9 @@ # Content-Addressing File System Objects For many operations, Nix needs to calculate [a content addresses](@docroot@/glossary.md#gloss-content-address) of [a file system object][file system object]. -Usually this is needed as part of content addressing [store objects], since store objects always have a root file system object. +Usually this is needed as part of +[content addressing store objects](../store-object/content-address.md), +since store objects always have a root file system object. But some command-line utilities also just work on "raw" file system objects, not part of any store object. Every content addressing scheme Nix uses ultimately involves feeding data into a [hash function](https://en.wikipedia.org/wiki/Hash_function), and getting back an opaque fixed-size digest which is deemed a content address. @@ -18,6 +20,9 @@ A single file object can just be hashed by its contents. This is not enough information to encode the fact that the file system object is a file, but if we *already* know that the FSO is a single non-executable file by other means, it is sufficient. +Because the hashed data is just the raw file, as is, this choice is good for compatibility with other systems. +For example, Unix commands like `sha256sum` or `sha1sum` will produce hashes for single files that match this. + ### Nix Archive (NAR) { #serial-nix-archive } For the other cases of [file system objects][file system object], especially directories with arbitrary descendents, we need a more complex serialisation format. @@ -69,7 +74,7 @@ every non-directory object is owned by a parent directory, and the entry that re However, if the root object is not a directory, then we have no way of knowing which one of an executable file, non-executable file, or symlink it is supposed to be. In response to this, we have decided to treat a bare file as non-executable file. -This is similar to do what we do with [flat serialisation](#flat), which also lacks this information. +This is similar to do what we do with [flat serialisation](#serial-flat), which also lacks this information. To avoid an address collision, attempts to hash a bare executable file or symlink will result in an error (just as would happen for flat serialisation also). Thus, Git can encode some, but not all of Nix's "File System Objects", and this sort of content-addressing is likewise partial. diff --git a/doc/manual/src/store/store-object/content-address.md b/doc/manual/src/store/store-object/content-address.md new file mode 100644 index 000000000..e9f085ad5 --- /dev/null +++ b/doc/manual/src/store/store-object/content-address.md @@ -0,0 +1,123 @@ +# Content-Addressing Store Objects + +Just [like][fso-ca] [File System Objects][File System Object], +[Store Objects][Store Object] can also be [content-addressed](@docroot@/glossary.md#gloss-content-addressed), +unless they are [input-addressed](@docroot@/glossary.md#gloss-input-addressed-store-object). + +For store objects, the content address we produce will take the form of a [Store Path] rather than regular hash. +In particular, the content-addressing scheme will ensure that the digest of the store path is solely computed from the + +- file system object graph (the root one and its children, if it has any) +- references +- [store directory](../store-path.md#store-directory) +- name + +of the store object, and not any other information, which would not be an intrinsic property of that store object. + +For the full specification of the algorithms involved, see the [specification of store path digests][sp-spec]. + +[File System Object]: ../file-system-object.md +[Store Object]: ../store-object.md +[Store Path]: ../store-path.md + +## Content addressing each part of a store object + +### File System Objects + +With all currently supported store object content addressing methods, the file system object is always [content-addressed][fso-ca] first, and then that hash is incorporated into content address computation for the store object. + +### References + +With all currently supported store object content addressing methods, +other objects are referred to by their regular (string-encoded-) [store paths][Store Path]. + +Self-references however cannot be referred to by their path, because we are in the midst of describing how to compute that path! + +> The alternative would require finding as hash function fixed point, i.e. the solution to an equation in the form +> ``` +> digest = hash(..... || digest || ....) +> ``` +> which is computationally infeasible. +> As far as we know, this is equivalent to finding a hash collision. + +Instead we just have a "has self reference" boolean, which will end up affecting the digest. + +### Name and Store Directory + +These two items affect the digest in a way that is standard for store path digest computations and not specific to content-addressing. +Consult the [specification of store path digests][sp-spec] for further details. + +## Content addressing Methods + +For historical reasons, we don't support all features in all combinations. +Each currently supported method of content addressing chooses a single method of file system object hashing, and may offer some restrictions on references. +The names and store directories are unrestricted however. + +### Flat { #method-flat } + +This uses the corresponding [Flat](../file-system-object/content-address.md#serial-flat) method of file system object content addressing. + +References are not supported: store objects with flat hashing *and* references can not be created. + +### Text { #method-text } + +This also uses the corresponding [Flat](../file-system-object/content-address.md#serial-flat) method of file system object content addressing. + +References to other store objects are supported, but self references are not. + +This is the only store-object content-addressing method that is not named identically with a corresponding file system object method. +It is somewhat obscure, mainly used for "drv files" +(derivations serialized as store objects in their ["ATerm" file format](@docroot@/protocols/derivation-aterm.md)). +Prefer another method if possible. + +### Nix Archive { #method-nix-archive } + +This uses the corresponding [Nix Archive](../file-system-object/content-address.md#serial-nix-archive) method of file system object content addressing. + +References (to other store objects and self references alike) are supported so long as the hash algorithm is SHA-256, but not (neither kind) otherwise. + +### Git { #method-git } + +> **Warning** +> +> This method is part of the [`git-hashing`][xp-feature-git-hashing] experimental feature. + +This uses the corresponding [Git](../file-system-object/content-address.md#serial-git) method of file system object content addressing. + +References are not supported. + +Only SHA-1 is supported at this time. +If [SHA-256-based Git](https://git-scm.com/docs/hash-function-transition) +becomes more widespread, this restriction will be revisited. + +### Reproducibility + +The above system is more complex than it needs to be to support all types of file system objects and references, owing to accretion of features over time. +However, there's a lot of value in supporting old expressions and reproducing the same hashes with any version of Nix. +Still, the fundamental property remains that if one knows how a store object is supposed to be hashed +--- all the non-Hash, non-references information above +--- one can recompute a store object's store path just from that metadata and its content proper (its references and file system objects). +Collectively, we can call this information the "content address method". + +By storing the "Content address method" extra information as part of store object +--- making it data not metadata +--- we achieve the key property of making content-addressed store objects *trustless*. + +What this is means is that they are just plain old data, not containing any "claim" that could be false. +All this information is free to vary, and if any of it varies one gets (ignoring the possibility of hash collisions, as usual) a different store path. +Store paths referring to content-addressed store objects uniquely identify a store object, and given that object, one can recompute the store path. +Any content-addressed store object purporting to be the referee of a store object can be readily verified to see whether it in fact does without any extra information. +No other party claiming a store object corresponds to a store path need be trusted because this verification can be done instead. + +Content addressing currently is used when adding data like source code to the store. +Such data are "basal inputs", not produced from any other derivation (to our knowledge). +Content addressing is thus the only way to address them of our two options. +([Input addressing](@docroot@/glossary.md#gloss-input-addressed-store-object), is only valid for store paths produced from derivations.) + +Additionally, content addressing is also used for the outputs of certain sorts of derivations. +It is very nice to be able to uniformly content-address all data rather than rely on a mix of content addressing and input addressing. +This however, is in some cases still experimental, so in practice input addressing is still (as of 2022) widely used. + +[fso-ca]: ../file-system-object/content-address.md +[sp-spec]: @docroot@/protocols/store-path.md +[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing diff --git a/src/libcmd/misc-store-flags.cc b/src/libcmd/misc-store-flags.cc index 063a9dd9e..06552c032 100644 --- a/src/libcmd/misc-store-flags.cc +++ b/src/libcmd/misc-store-flags.cc @@ -103,27 +103,27 @@ Args::Flag contentAddressMethod(ContentAddressMethod * method) return Args::Flag { .longName = "mode", // FIXME indentation carefully made for context, this is messed up. - /* FIXME link to store object content-addressing not file system - object content addressing once we have that page. */ .description = R"( How to compute the content-address of the store object. One of: - - `nar` (the default): + - [`nar`](@docroot@/store/store-object/content-address.md#method-nix-archive) + (the default): Serialises the input as a [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) and passes that to the hash function. - - `flat`: + - [`flat`](@docroot@/store/store-object/content-address.md#method-flat): Assumes that the input is a single file and [directly passes](@docroot@/store/file-system-object/content-address.md#serial-flat) it to the hash function. - - `text`: Like `flat`, but used for + - [`text`](@docroot@/store/store-object/content-address.md#method-text): + Like `flat`, but used for [derivations](@docroot@/glossary.md#store-derivation) serialized in store object and [`builtins.toFile`](@docroot@/language/builtins.html#builtins-toFile). For advanced use-cases only; - for regular usage prefer `nar` and `flat. + for regular usage prefer `nar` and `flat`. )", .labels = {"content-address-method"}, .handler = {[method](std::string s) { diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index fcf813a37..e13705911 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -1216,16 +1216,19 @@ nlohmann::json DerivationOutput::toJSON( }, [&](const DerivationOutput::CAFixed & dof) { res["path"] = store.printStorePath(dof.path(store, drvName, outputName)); - res["hashAlgo"] = dof.ca.printMethodAlgo(); + res["method"] = std::string { dof.ca.method.render() }; + res["hashAlgo"] = printHashAlgo(dof.ca.hash.algo); res["hash"] = dof.ca.hash.to_string(HashFormat::Base16, false); // FIXME print refs? }, [&](const DerivationOutput::CAFloating & dof) { - res["hashAlgo"] = std::string { dof.method.renderPrefix() } + printHashAlgo(dof.hashAlgo); + res["method"] = std::string { dof.method.render() }; + res["hashAlgo"] = printHashAlgo(dof.hashAlgo); }, [&](const DerivationOutput::Deferred &) {}, [&](const DerivationOutput::Impure & doi) { - res["hashAlgo"] = std::string { doi.method.renderPrefix() } + printHashAlgo(doi.hashAlgo); + res["method"] = std::string { doi.method.render() }; + res["hashAlgo"] = printHashAlgo(doi.hashAlgo); res["impure"] = true; }, }, raw); @@ -1245,12 +1248,13 @@ DerivationOutput DerivationOutput::fromJSON( keys.insert(key); auto methodAlgo = [&]() -> std::pair { - auto & str = getString(valueAt(json, "hashAlgo")); - std::string_view s = str; - ContentAddressMethod method = ContentAddressMethod::parsePrefix(s); + auto & method_ = getString(valueAt(json, "method")); + ContentAddressMethod method = ContentAddressMethod::parse(method_); if (method == TextIngestionMethod {}) xpSettings.require(Xp::DynamicDerivations); - auto hashAlgo = parseHashAlgo(s); + + auto & hashAlgo_ = getString(valueAt(json, "hashAlgo")); + auto hashAlgo = parseHashAlgo(hashAlgo_); return { std::move(method), std::move(hashAlgo) }; }; @@ -1260,7 +1264,7 @@ DerivationOutput DerivationOutput::fromJSON( }; } - else if (keys == (std::set { "path", "hashAlgo", "hash" })) { + else if (keys == (std::set { "path", "method", "hashAlgo", "hash" })) { auto [method, hashAlgo] = methodAlgo(); auto dof = DerivationOutput::CAFixed { .ca = ContentAddress { @@ -1273,7 +1277,7 @@ DerivationOutput DerivationOutput::fromJSON( return dof; } - else if (keys == (std::set { "hashAlgo" })) { + else if (keys == (std::set { "method", "hashAlgo" })) { xpSettings.require(Xp::CaDerivations); auto [method, hashAlgo] = methodAlgo(); return DerivationOutput::CAFloating { @@ -1286,7 +1290,7 @@ DerivationOutput DerivationOutput::fromJSON( return DerivationOutput::Deferred {}; } - else if (keys == (std::set { "hashAlgo", "impure" })) { + else if (keys == (std::set { "method", "hashAlgo", "impure" })) { xpSettings.require(Xp::ImpureDerivations); auto [method, hashAlgo] = methodAlgo(); return DerivationOutput::Impure { diff --git a/src/nix/flake.md b/src/nix/flake.md index 661dd2f73..2f43d0264 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -134,7 +134,9 @@ The following generic flake reference attributes are supported: repository or tarball. The default is the root directory of the flake. -* `narHash`: The hash of the NAR serialisation (in SRI format) of the +* `narHash`: The hash of the + [Nix Archive (NAR) serialisation][Nix Archive] + (in SRI format) of the contents of the flake. This is useful for flake types such as tarballs that lack a unique content identifier such as a Git commit hash. @@ -423,8 +425,9 @@ The following attributes are supported in `flake.nix`: * `lastModified`: The commit time of the revision `rev` as an integer denoting the number of seconds since 1970. - * `narHash`: The SHA-256 (in SRI format) of the NAR serialization of - the flake's source tree. + * `narHash`: The SHA-256 (in SRI format) of the + [Nix Archive (NAR) serialisation][Nix Archive] + NAR serialization of the flake's source tree. The value returned by the `outputs` function must be an attribute set. The attributes can have arbitrary values; however, various @@ -703,4 +706,5 @@ will not look at the lock files of dependencies. However, lock file generation itself *does* use the lock files of dependencies by default. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/nar-cat.md b/src/nix/nar-cat.md index 55c481a28..1131eb2bf 100644 --- a/src/nix/nar-cat.md +++ b/src/nix/nar-cat.md @@ -2,7 +2,7 @@ R""( # Examples -* List a file in a NAR and pipe it through `gunzip`: +* List a file in a [Nix Archive (NAR)][Nix Archive] and pipe it through `gunzip`: ```console # nix nar cat ./hello.nar /share/man/man1/hello.1.gz | gunzip @@ -16,4 +16,5 @@ R""( This command prints on standard output the contents of the regular file *path* inside the NAR file *nar*. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/nar-dump-path.md b/src/nix/nar-dump-path.md index de82202de..4676e4fef 100644 --- a/src/nix/nar-dump-path.md +++ b/src/nix/nar-dump-path.md @@ -2,7 +2,7 @@ R""( # Examples -* To serialise directory `foo` as a NAR: +* To serialise directory `foo` as a [Nix Archive (NAR)][Nix Archive]: ```console # nix nar pack ./foo > foo.nar @@ -10,8 +10,10 @@ R""( # Description -This command generates a NAR file containing the serialisation of +This command generates a [Nix Archive (NAR)][Nix Archive] file containing the serialisation of *path*, which must contain only regular files, directories and symbolic links. The NAR is written to standard output. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + )"" diff --git a/src/nix/nar-ls.md b/src/nix/nar-ls.md index 5a03c5d82..27c4b97e6 100644 --- a/src/nix/nar-ls.md +++ b/src/nix/nar-ls.md @@ -2,7 +2,7 @@ R""( # Examples -* To list a specific file in a NAR: +* To list a specific file in a [NAR][Nix Archive]: ```console # nix nar ls --long ./hello.nar /bin/hello @@ -19,6 +19,8 @@ R""( # Description -This command shows information about a *path* inside NAR file *nar*. +This command shows information about a *path* inside [Nix Archive (NAR)][Nix Archive] file *nar*. + +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/nar.md b/src/nix/nar.md index a83b5c764..b0f70ce93 100644 --- a/src/nix/nar.md +++ b/src/nix/nar.md @@ -3,11 +3,14 @@ R""( # Description `nix nar` provides several subcommands for creating and inspecting -*Nix Archives* (NARs). +[*Nix Archives* (NARs)][Nix Archive]. # File format -For the definition of the NAR file format, see Figure 5.2 in -https://edolstra.github.io/pubs/phd-thesis.pdf. +For the definition of the Nix Archive file format, see +[within the protocols chapter](@docroot@/protocols/nix-archive.md) +of the manual. + +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/store-dump-path.md b/src/nix/store-dump-path.md index 56e2174b6..21467ff32 100644 --- a/src/nix/store-dump-path.md +++ b/src/nix/store-dump-path.md @@ -17,7 +17,9 @@ R""( # Description -This command generates a NAR file containing the serialisation of the +This command generates a [Nix Archive (NAR)][Nix Archive] file containing the serialisation of the store path [*installable*](./nix.md#installables). The NAR is written to standard output. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + )"" diff --git a/src/nix/verify.md b/src/nix/verify.md index e1d55eab4..ae0b0acd6 100644 --- a/src/nix/verify.md +++ b/src/nix/verify.md @@ -46,4 +46,6 @@ The exit status of this command is the sum of the following values: * **4** if any path couldn't be verified for any other reason (such as an I/O error). +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + )"" diff --git a/tests/unit/libstore/data/derivation/output-caFixedFlat.json b/tests/unit/libstore/data/derivation/output-caFixedFlat.json index fe000ea36..7001ea0a9 100644 --- a/tests/unit/libstore/data/derivation/output-caFixedFlat.json +++ b/tests/unit/libstore/data/derivation/output-caFixedFlat.json @@ -1,5 +1,6 @@ { "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", "hashAlgo": "sha256", + "method": "flat", "path": "/nix/store/rhcg9h16sqvlbpsa6dqm57sbr2al6nzg-drv-name-output-name" } diff --git a/tests/unit/libstore/data/derivation/output-caFixedNAR.json b/tests/unit/libstore/data/derivation/output-caFixedNAR.json index 1afd60223..54eb306e6 100644 --- a/tests/unit/libstore/data/derivation/output-caFixedNAR.json +++ b/tests/unit/libstore/data/derivation/output-caFixedNAR.json @@ -1,5 +1,6 @@ { "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", - "hashAlgo": "r:sha256", + "hashAlgo": "sha256", + "method": "nar", "path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name" } diff --git a/tests/unit/libstore/data/derivation/output-caFixedText.json b/tests/unit/libstore/data/derivation/output-caFixedText.json index 0b2cc8bbc..e8a651860 100644 --- a/tests/unit/libstore/data/derivation/output-caFixedText.json +++ b/tests/unit/libstore/data/derivation/output-caFixedText.json @@ -1,5 +1,6 @@ { "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", - "hashAlgo": "text:sha256", + "hashAlgo": "sha256", + "method": "text", "path": "/nix/store/6s1zwabh956jvhv4w9xcdb5jiyanyxg1-drv-name-output-name" } diff --git a/tests/unit/libstore/data/derivation/output-caFloating.json b/tests/unit/libstore/data/derivation/output-caFloating.json index 9115de851..8b9b5f681 100644 --- a/tests/unit/libstore/data/derivation/output-caFloating.json +++ b/tests/unit/libstore/data/derivation/output-caFloating.json @@ -1,3 +1,4 @@ { - "hashAlgo": "r:sha256" + "hashAlgo": "sha256", + "method": "nar" } diff --git a/tests/unit/libstore/data/derivation/output-impure.json b/tests/unit/libstore/data/derivation/output-impure.json index 62b61cdca..bec03702b 100644 --- a/tests/unit/libstore/data/derivation/output-impure.json +++ b/tests/unit/libstore/data/derivation/output-impure.json @@ -1,4 +1,5 @@ { - "hashAlgo": "r:sha256", - "impure": true + "hashAlgo": "sha256", + "impure": true, + "method": "nar" } From 4c91bc543c06cc3cd34b76f13f2e40005735539c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 20 May 2024 09:04:15 -0400 Subject: [PATCH 121/910] Remove store object content address reproducibility section --- .../src/store/store-object/content-address.md | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/doc/manual/src/store/store-object/content-address.md b/doc/manual/src/store/store-object/content-address.md index e9f085ad5..f6f982035 100644 --- a/doc/manual/src/store/store-object/content-address.md +++ b/doc/manual/src/store/store-object/content-address.md @@ -90,34 +90,6 @@ Only SHA-1 is supported at this time. If [SHA-256-based Git](https://git-scm.com/docs/hash-function-transition) becomes more widespread, this restriction will be revisited. -### Reproducibility - -The above system is more complex than it needs to be to support all types of file system objects and references, owing to accretion of features over time. -However, there's a lot of value in supporting old expressions and reproducing the same hashes with any version of Nix. -Still, the fundamental property remains that if one knows how a store object is supposed to be hashed ---- all the non-Hash, non-references information above ---- one can recompute a store object's store path just from that metadata and its content proper (its references and file system objects). -Collectively, we can call this information the "content address method". - -By storing the "Content address method" extra information as part of store object ---- making it data not metadata ---- we achieve the key property of making content-addressed store objects *trustless*. - -What this is means is that they are just plain old data, not containing any "claim" that could be false. -All this information is free to vary, and if any of it varies one gets (ignoring the possibility of hash collisions, as usual) a different store path. -Store paths referring to content-addressed store objects uniquely identify a store object, and given that object, one can recompute the store path. -Any content-addressed store object purporting to be the referee of a store object can be readily verified to see whether it in fact does without any extra information. -No other party claiming a store object corresponds to a store path need be trusted because this verification can be done instead. - -Content addressing currently is used when adding data like source code to the store. -Such data are "basal inputs", not produced from any other derivation (to our knowledge). -Content addressing is thus the only way to address them of our two options. -([Input addressing](@docroot@/glossary.md#gloss-input-addressed-store-object), is only valid for store paths produced from derivations.) - -Additionally, content addressing is also used for the outputs of certain sorts of derivations. -It is very nice to be able to uniformly content-address all data rather than rely on a mix of content addressing and input addressing. -This however, is in some cases still experimental, so in practice input addressing is still (as of 2022) widely used. - [fso-ca]: ../file-system-object/content-address.md [sp-spec]: @docroot@/protocols/store-path.md [xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing From fc14378ae34a8c8474d77cfcacb35e5fc35a1fda Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 20 May 2024 22:39:34 +0200 Subject: [PATCH 122/910] add cross-references for discoverability (#10714) --- doc/manual/src/command-ref/nix-store/dump.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/dump.md b/doc/manual/src/command-ref/nix-store/dump.md index b1066fd4c..3de0e27b0 100644 --- a/doc/manual/src/command-ref/nix-store/dump.md +++ b/doc/manual/src/command-ref/nix-store/dump.md @@ -8,7 +8,7 @@ ## Description -The operation `--dump` produces a [NAR (Nix ARchive)][Nix Archive] file containing the +The operation `--dump` produces a [Nix archive](@docroot@/glossary.md#gloss-nar) (NAR) file containing the contents of the file system tree rooted at *path*. The archive is written to standard output. @@ -30,8 +30,7 @@ NAR archives support filenames of unlimited length and 64-bit file sizes. They can contain regular files, directories, and symbolic links, but not other types of files (such as device nodes). -A Nix archive can be unpacked using `nix-store ---restore`. +A Nix archive can be unpacked using [`nix-store --restore`](@docroot@/command-ref/nix-store/restore.md). [Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive From 40b7fb4f1156195043856b24ab2341f4957b27d6 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 20 May 2024 22:45:45 +0200 Subject: [PATCH 123/910] expand example on `nix-copy-closure` (#10708) * expand example on nix-copy-closure Co-authored-by: Robert Hensing --- .../src/command-ref/nix-copy-closure.md | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index 46d381f5d..3c62191f1 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -75,17 +75,28 @@ authentication, you can avoid typing the passphrase with `ssh-agent`. # Examples -Copy Firefox with all its dependencies to a remote machine: +> **Example** +> +> Copy GNU Hello with all its dependencies to a remote machine: +> +> ```shell-session +> $ storePath="$(nix-build '' -I nixpkgs=channel:nixpkgs-unstable -A hello --no-out-link)" +> $ nix-copy-closure --to alice@itchy.example.org "$storePath" +> copying 5 paths... +> copying path '/nix/store/nrwkk6ak3rgkrxbqhsscb01jpzmslf2r-xgcc-13.2.0-libgcc' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/gm61h1y42pqyl6178g90x8zm22n6pyy5-libunistring-1.1' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/ddfzjdykw67s20c35i7a6624by3iz5jv-libidn2-2.3.7' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/g1n2vryg06amvcc1avb2mcq36faly0mh-hello-2.12.1' to 'ssh://alice@itchy.example.org'... +> ``` -```console -$ nix-copy-closure --to alice@itchy.example.org $(type -P firefox) -``` - -Copy Subversion from a remote machine and then install it into a user -environment: - -```console -$ nix-copy-closure --from alice@itchy.example.org \ - /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 -$ nix-env --install /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 -``` +> **Example** +> +> Copy GNU Hello from a remote machine using a known store path, and run it: +> +> ```shell-session +> $ storePath="$(nix-instantiate --eval '' -I nixpkgs=channel:nixpkgs-unstable -A hello.outPath | tr -d '"')" +> $ nix-copy-closure --from alice@itchy.example.org "$storePath" +> $ "$storePath"/bin/hello +> Hello, world! +> ``` From 8b369f90fd2422a03310a0d3c623668e86e00e01 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 20 May 2024 17:41:12 -0400 Subject: [PATCH 124/910] Query path infos (plural) and handshake version minimum for hydra 1. Hydra currently queries for multiple path infos at once, so let us make a connection item for that. 2. The minimum of the two versions should always be used, see #9584. (The issue remains open because the daemon protocol needs to be likewise updated.) --- src/libstore/legacy-ssh-store.cc | 32 ++++++++++++++------------- src/libstore/serve-protocol-impl.cc | 29 ++++++++++++++++++++++-- src/libstore/serve-protocol-impl.hh | 4 ++++ tests/unit/libstore/serve-protocol.cc | 3 ++- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index e422adeec..9bc986bfa 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -105,24 +105,26 @@ void LegacySSHStore::queryPathInfoUncached(const StorePath & path, debug("querying remote host '%s' for info on '%s'", host, printStorePath(path)); - conn->to << ServeProto::Command::QueryPathInfos << PathSet{printStorePath(path)}; - conn->to.flush(); + auto infos = conn->queryPathInfos(*this, {path}); - auto p = readString(conn->from); - if (p.empty()) return callback(nullptr); - auto path2 = parseStorePath(p); - assert(path == path2); - auto info = std::make_shared( - path, - ServeProto::Serialise::read(*this, *conn)); + switch (infos.size()) { + case 0: + return callback(nullptr); + case 1: { + auto & [path2, info] = *infos.begin(); - if (info->narHash == Hash::dummy) - throw Error("NAR hash is now mandatory"); + if (info.narHash == Hash::dummy) + throw Error("NAR hash is now mandatory"); - auto s = readString(conn->from); - assert(s == ""); - - callback(std::move(info)); + assert(path == path2); + return callback(std::make_shared( + std::move(path), + std::move(info) + )); + } + default: + throw Error("More path infos returned than queried"); + } } catch (...) { callback.rethrow(); } } diff --git a/src/libstore/serve-protocol-impl.cc b/src/libstore/serve-protocol-impl.cc index b39212884..5e1d3f1e8 100644 --- a/src/libstore/serve-protocol-impl.cc +++ b/src/libstore/serve-protocol-impl.cc @@ -19,7 +19,7 @@ ServeProto::Version ServeProto::BasicClientConnection::handshake( auto remoteVersion = readInt(from); if (GET_PROTOCOL_MAJOR(remoteVersion) != 0x200) throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host); - return remoteVersion; + return std::min(remoteVersion, localVersion); } ServeProto::Version ServeProto::BasicServerConnection::handshake( @@ -31,7 +31,8 @@ ServeProto::Version ServeProto::BasicServerConnection::handshake( if (magic != SERVE_MAGIC_1) throw Error("protocol mismatch"); to << SERVE_MAGIC_2 << localVersion; to.flush(); - return readInt(from); + auto remoteVersion = readInt(from); + return std::min(remoteVersion, localVersion); } @@ -51,6 +52,30 @@ StorePathSet ServeProto::BasicClientConnection::queryValidPaths( } +std::map ServeProto::BasicClientConnection::queryPathInfos( + const Store & store, + const StorePathSet & paths) +{ + std::map infos; + + to << ServeProto::Command::QueryPathInfos; + ServeProto::write(store, *this, paths); + to.flush(); + + while (true) { + auto storePathS = readString(from); + if (storePathS == "") break; + + auto storePath = store.parseStorePath(storePathS); + assert(paths.count(storePath) == 1); + auto info = ServeProto::Serialise::read(store, *this); + infos.insert_or_assign(std::move(storePath), std::move(info)); + } + + return infos; +} + + void ServeProto::BasicClientConnection::putBuildDerivationRequest( const Store & store, const StorePath & drvPath, const BasicDerivation & drv, diff --git a/src/libstore/serve-protocol-impl.hh b/src/libstore/serve-protocol-impl.hh index fd8d94697..1d9c79ef0 100644 --- a/src/libstore/serve-protocol-impl.hh +++ b/src/libstore/serve-protocol-impl.hh @@ -122,6 +122,10 @@ struct ServeProto::BasicClientConnection bool lock, const StorePathSet & paths, SubstituteFlag maybeSubstitute); + std::map queryPathInfos( + const Store & store, + const StorePathSet & paths); + /** * Just the request half, because Hydra may do other things between * issuing the request and reading the `BuildResult` response. diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index b2fd0fb82..61dc15528 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -505,7 +505,8 @@ TEST_F(ServeProtoTest, handshake_client_corrupted_throws) } else { auto ver = ServeProto::BasicClientConnection::handshake( nullSink, in, defaultVersion, "blah"); - EXPECT_NE(ver, defaultVersion); + // `std::min` of this and the other version saves us + EXPECT_EQ(ver, defaultVersion); } } }); From 7577597cc4f6bad5fc6e64467ef6cce048c95569 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 22:51:53 +0000 Subject: [PATCH 125/910] --- updated-dependencies: - dependency-name: cachix/cachix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8eac49d..2a55c7fd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: # The sandbox would otherwise be disabled by default on Darwin extra_nix_config: "sandbox = true" - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v15 if: needs.check_secrets.outputs.cachix == 'true' with: name: '${{ env.CACHIX_NAME }}' @@ -65,7 +65,7 @@ jobs: - uses: cachix/install-nix-action@v26 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v15 with: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' @@ -119,7 +119,7 @@ jobs: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v15 if: needs.check_secrets.outputs.cachix == 'true' with: name: '${{ env.CACHIX_NAME }}' From 041fec0b5c6c9fe2e75b0069ed346cca9db6adc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 22:51:54 +0000 Subject: [PATCH 126/910] --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8eac49d..7374e1819 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: # The sandbox would otherwise be disabled by default on Darwin extra_nix_config: "sandbox = true" @@ -62,7 +62,7 @@ jobs: with: fetch-depth: 0 - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - uses: cachix/cachix-action@v14 @@ -84,7 +84,7 @@ jobs: steps: - uses: actions/checkout@v4 - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: install_url: '${{needs.installer.outputs.installerURL}}' install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve" @@ -114,7 +114,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV From 56afe228df1f1b6c9e69137871b9f0c322278ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20R=C3=BCckert?= Date: Tue, 21 May 2024 00:52:11 +0200 Subject: [PATCH 127/910] Use CFLAGS for libseccomp from pkg-config also for the CFLAGS Otherwise the configure check for fchmodat2 afterwards fails because it can not find the header files. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b2a5794b5..90a6d45d5 100644 --- a/configure.ac +++ b/configure.ac @@ -313,7 +313,7 @@ case "$host_os" in ])) if test "x$enable_seccomp_sandboxing" != "xno"; then PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp], - [CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"]) + [CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS" CFLAGS="$LIBSECCOMP_CFLAGS $CFLAGS"]) have_seccomp=1 AC_DEFINE([HAVE_SECCOMP], [1], [Whether seccomp is available and should be used for sandboxing.]) AC_COMPILE_IFELSE([ From 142222030c823286939bcc317fbd5824067319cc Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Thu, 16 May 2024 02:33:58 +0200 Subject: [PATCH 128/910] remove redundant and outdated example from `libexpr-c` documentation --- doc/external-api/README.md | 2 +- src/libexpr-c/nix_api_expr.h | 20 +------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 167c02199..e6612221b 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -27,7 +27,7 @@ appreciated. The following examples, for simplicity, don't include error handling. See the [Handling errors](@ref errors) section for more information. -# Embedding the Nix Evaluator +# Embedding the Nix Evaluator{#nix_evaluator_example} In this example we programmatically start the Nix language evaluator with a dummy store (that has no store paths and cannot be written to), and evaluate the diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 04fc92f0f..ce0062ee1 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -3,25 +3,7 @@ /** @defgroup libexpr libexpr * @brief Bindings to the Nix language evaluator * - * Example (without error handling): - * @code{.c} - * int main() { - * nix_libexpr_init(NULL); - * - * Store* store = nix_store_open(NULL, "dummy", NULL); - * EvalState* state = nix_state_create(NULL, NULL, store); // empty nix path - * Value *value = nix_alloc_value(NULL, state); - * - * nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); - * nix_value_force(NULL, state, value); - * printf("nix version: %s\n", nix_get_string(NULL, value)); - * - * nix_gc_decref(NULL, value); - * nix_state_free(state); - * nix_store_free(store); - * return 0; - * } - * @endcode + * See *[Embedding the Nix Evaluator](@ref nix_evaluator_example)* for an example. * @{ */ /** @file From bb1a4ea21a6af3c37c7d1c948e36678c96c3f499 Mon Sep 17 00:00:00 2001 From: eihqnh Date: Mon, 13 May 2024 21:12:58 +0800 Subject: [PATCH 129/910] nix repl: make runNix() isInteractive is true by default --- src/libcmd/repl.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 8a9155ab6..2aa674e2f 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -137,12 +137,13 @@ void runNix(Path program, const Strings & args, { auto subprocessEnv = getEnv(); subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue(); - + //isInteractive avoid grabling interactive commands runProgram2(RunOptions { .program = settings.nixBinDir+ "/" + program, .args = args, .environment = subprocessEnv, .input = input, + .isInteractive = true, }); return; @@ -508,13 +509,9 @@ ProcessLineResult NixRepl::processLine(std::string line) auto editor = args.front(); args.pop_front(); - // avoid garbling the editor with the progress bar - logger->pause(); - Finally resume([&]() { logger->resume(); }); - // runProgram redirects stdout to a StringSink, // using runProgram2 to allow editors to display their UI - runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args }); + runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args , .isInteractive = true }); // Reload right after exiting the editor state->resetFileCache(); From 117dbc2c46de41d81385a70149ed5dbf331e5f00 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 21 May 2024 16:42:59 +0200 Subject: [PATCH 130/910] add examples of comments make a suggestion for what to do if one wants to write nested comments --- doc/manual/src/language/constructs.md | 59 ++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md index 4d75ea82c..491d221b3 100644 --- a/doc/manual/src/language/constructs.md +++ b/doc/manual/src/language/constructs.md @@ -414,12 +414,62 @@ Does evaluate to `"inner"`. ## Comments -Comments can be single-line, started with a `#` character, or -inline/multi-line, enclosed within `/* ... */`. +- Inline comments start with `#` and run until the end of the line. -`#` comments last until the end of the line. + > **Example** + > + > ```nix + > # A number + > 2 # Equals 1 + 1 + > ``` + > + > ```console + > 2 + > ``` -`/*` comments run until the next occurrence of `*/`; this cannot be escaped. +- Block comments start with `/*` and run until the next occurrence of `*/`. + + > **Example** + > + > ```nix + > /* + > Block comments + > can span multiple lines. + > */ "hello" + > ``` + > + > ```console + > "hello" + > ``` + + This means that block comments cannot be nested. + + > **Example** + > + > ```nix + > /* /* nope */ */ 1 + > ``` + > + > ```console + > error: syntax error, unexpected '*' + > + > at «string»:1:15: + > + > 1| /* /* nope */ * + > | ^ + > ``` + + Consider escaping nested comments and unescaping them in post-processing. + + > **Example** + > + > ```nix + > /* /* nested *\/ */ 1 + > ``` + > + > ```console + > 1 + > ``` ## Scoping rules @@ -432,6 +482,5 @@ Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science * secondary scope --- implicitly-bound variables * [`with`](#with-expressions) - Primary scope takes precedence over secondary scope. See [`with`](#with-expressions) for a detailed example. From 470c0501eb994c8a31f03561d4d38114236ab8b3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Jan 2024 10:31:52 -0500 Subject: [PATCH 131/910] Ensure all store types support "real" URIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In particular `local://` and `unix://` (without any path) now work, and mean the same things as `local` and `daemon`, respectively. We thus now have the opportunity to desguar `local` and `daemon` early. This will allow me to make a change to https://github.com/NixOS/nix/pull/9839 requested during review to desugar those earlier. Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libstore/dummy-store.cc | 7 ++-- src/libstore/http-binary-cache-store.cc | 11 +++++-- src/libstore/legacy-ssh-store.cc | 16 +++++++-- src/libstore/legacy-ssh-store.hh | 12 ++++++- src/libstore/local-binary-cache-store.cc | 8 +++-- src/libstore/local-store.cc | 16 +++++++-- src/libstore/local-store.hh | 7 ++-- src/libstore/s3-binary-cache-store.cc | 10 +++--- src/libstore/ssh-store-config.cc | 24 ++++++++++++++ src/libstore/ssh-store-config.hh | 23 +++++++++++++ src/libstore/ssh-store.cc | 26 +++++++++++---- src/libstore/ssh.cc | 6 +++- src/libstore/ssh.hh | 6 +++- src/libstore/store-api.cc | 42 ++---------------------- src/libstore/store-api.hh | 11 +++++-- src/libstore/uds-remote-store.cc | 8 +++-- src/libstore/uds-remote-store.hh | 5 ++- src/libstore/unix/local-overlay-store.hh | 2 +- tests/functional/read-only-store.sh | 5 ++- 19 files changed, 170 insertions(+), 75 deletions(-) create mode 100644 src/libstore/ssh-store-config.cc diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index 30f23cff9..0d5d03091 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -18,9 +18,12 @@ struct DummyStoreConfig : virtual StoreConfig { struct DummyStore : public virtual DummyStoreConfig, public virtual Store { - DummyStore(const std::string scheme, const std::string uri, const Params & params) + DummyStore(std::string_view scheme, std::string_view authority, const Params & params) : DummyStore(params) - { } + { + if (!authority.empty()) + throw UsageError("`%s` store URIs must not contain an authority part %s", scheme, authority); + } DummyStore(const Params & params) : StoreConfig(params) diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 5da87e935..3328caef9 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -39,15 +39,20 @@ private: public: HttpBinaryCacheStore( - const std::string & scheme, - const Path & _cacheUri, + std::string_view scheme, + PathView _cacheUri, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) , HttpBinaryCacheStoreConfig(params) , Store(params) , BinaryCacheStore(params) - , cacheUri(scheme + "://" + _cacheUri) + , cacheUri( + std::string { scheme } + + "://" + + (!_cacheUri.empty() + ? _cacheUri + : throw UsageError("`%s` Store requires a non-empty authority in Store URL", scheme))) { while (!cacheUri.empty() && cacheUri.back() == '/') cacheUri.pop_back(); diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 9bc986bfa..c75d50ade 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -28,8 +28,18 @@ struct LegacySSHStore::Connection : public ServeProto::BasicClientConnection bool good = true; }; +LegacySSHStore::LegacySSHStore( + std::string_view scheme, + std::string_view host, + const Params & params) + : LegacySSHStore{scheme, LegacySSHStoreConfig::extractConnStr(scheme, host), params} +{ +} -LegacySSHStore::LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params) +LegacySSHStore::LegacySSHStore( + std::string_view scheme, + std::string host, + const Params & params) : StoreConfig(params) , CommonSSHStoreConfig(params) , LegacySSHStoreConfig(params) @@ -42,8 +52,8 @@ LegacySSHStore::LegacySSHStore(const std::string & scheme, const std::string & h )) , master( host, - sshKey, - sshPublicHostKey, + sshKey.get(), + sshPublicHostKey.get(), // Use SSH master only if using more than 1 connection. connections->capacity() > 1, compress, diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index 343823693..81872996a 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -41,7 +41,17 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor static std::set uriSchemes() { return {"ssh"}; } - LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params); + LegacySSHStore( + std::string_view scheme, + std::string_view host, + const Params & params); + +private: + LegacySSHStore( + std::string_view scheme, + std::string host, + const Params & params); +public: ref openConnection(); diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 87a6026f1..3e25ab8a4 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -28,9 +28,13 @@ private: public: + /** + * @param binaryCacheDir `file://` is a short-hand for `file:///` + * for now. + */ LocalBinaryCacheStore( - const std::string scheme, - const Path & binaryCacheDir, + std::string_view scheme, + PathView binaryCacheDir, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index dd06e5b65..c39060ba7 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -463,10 +463,20 @@ LocalStore::LocalStore(const Params & params) } -LocalStore::LocalStore(std::string scheme, std::string path, const Params & params) - : LocalStore(params) +LocalStore::LocalStore( + std::string_view scheme, + PathView path, + const Params & _params) + : LocalStore([&]{ + // Default `?root` from `path` if non set + if (!path.empty() && _params.count("root") == 0) { + auto params = _params; + params.insert_or_assign("root", std::string { path }); + return params; + } + return _params; + }()) { - throw UnimplementedError("LocalStore"); } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index b3d7bd6d0..b0a0def9a 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -137,12 +137,15 @@ public: * necessary. */ LocalStore(const Params & params); - LocalStore(std::string scheme, std::string path, const Params & params); + LocalStore( + std::string_view scheme, + PathView path, + const Params & params); ~LocalStore(); static std::set uriSchemes() - { return {}; } + { return {"local"}; } /** * Implementations of abstract store API methods. diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 1a62d92d4..e9850dce6 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -213,7 +213,7 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig support it. > **Note** - > + > > HTTPS should be used if the cache might contain sensitive > information. )"}; @@ -224,7 +224,7 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig 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. )"}; @@ -269,8 +269,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3Helper s3Helper; S3BinaryCacheStoreImpl( - const std::string & uriScheme, - const std::string & bucketName, + std::string_view uriScheme, + std::string_view bucketName, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) @@ -281,6 +281,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual , bucketName(bucketName) , s3Helper(profile, region, scheme, endpoint) { + if (bucketName.empty()) + throw UsageError("`%s` store requires a bucket name in its Store URI", uriScheme); diskCache = getNarInfoDiskCache(); } diff --git a/src/libstore/ssh-store-config.cc b/src/libstore/ssh-store-config.cc new file mode 100644 index 000000000..742354cce --- /dev/null +++ b/src/libstore/ssh-store-config.cc @@ -0,0 +1,24 @@ +#include + +#include "ssh-store-config.hh" + +namespace nix { + +std::string CommonSSHStoreConfig::extractConnStr(std::string_view scheme, std::string_view _connStr) +{ + if (_connStr.empty()) + throw UsageError("`%s` store requires a valid SSH host as the authority part in Store URI", scheme); + + std::string connStr{_connStr}; + + std::smatch result; + static std::regex v6AddrRegex("^((.*)@)?\\[(.*)\\]$"); + + if (std::regex_match(connStr, result, v6AddrRegex)) { + connStr = result[1].matched ? result.str(1) + result.str(3) : result.str(3); + } + + return connStr; +} + +} diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/ssh-store-config.hh index 4ce4ffc4c..09f9f23a7 100644 --- a/src/libstore/ssh-store-config.hh +++ b/src/libstore/ssh-store-config.hh @@ -24,6 +24,29 @@ struct CommonSSHStoreConfig : virtual StoreConfig to be used on the remote machine. The default is `auto` (i.e. use the Nix daemon or `/nix/store` directly). )"}; + + /** + * The `parseURL` function supports both IPv6 URIs as defined in + * RFC2732, but also pure addresses. The latter one is needed here to + * connect to a remote store via SSH (it's possible to do e.g. `ssh root@::1`). + * + * This function now ensures that a usable connection string is available: + * + * - If the store to be opened is not an SSH store, nothing will be done. + * + * - If the URL looks like `root@[::1]` (which is allowed by the URL parser and probably + * needed to pass further flags), it + * will be transformed into `root@::1` for SSH (same for `[::1]` -> `::1`). + * + * - If the URL looks like `root@::1` it will be left as-is. + * + * - In any other case, the string will be left as-is. + * + * Will throw an error if `connStr` is empty too. + */ + static std::string extractConnStr( + std::string_view scheme, + std::string_view connStr); }; } diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 220d5d31b..246abaac2 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -32,9 +32,10 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore { -public: - - SSHStore(const std::string & scheme, const std::string & host, const Params & params) + SSHStore( + std::string_view scheme, + std::string host, + const Params & params) : StoreConfig(params) , RemoteStoreConfig(params) , CommonSSHStoreConfig(params) @@ -44,14 +45,24 @@ public: , host(host) , master( host, - sshKey, - sshPublicHostKey, + sshKey.get(), + sshPublicHostKey.get(), // Use SSH master only if using more than 1 connection. connections->capacity() > 1, compress) { } +public: + + SSHStore( + std::string_view scheme, + std::string_view host, + const Params & params) + : SSHStore{scheme, SSHStoreConfig::extractConnStr(scheme, host), params} + { + } + static std::set uriSchemes() { return {"ssh-ng"}; } std::string getUri() override @@ -141,7 +152,10 @@ class MountedSSHStore : public virtual MountedSSHStoreConfig, public virtual SSH { public: - MountedSSHStore(const std::string & scheme, const std::string & host, const Params & params) + MountedSSHStore( + std::string_view scheme, + std::string_view host, + const Params & params) : StoreConfig(params) , RemoteStoreConfig(params) , CommonSSHStoreConfig(params) diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 7e730299a..5eb63fa67 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -6,7 +6,11 @@ namespace nix { -SSHMaster::SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD) +SSHMaster::SSHMaster( + std::string_view host, + std::string_view keyFile, + std::string_view sshPublicHostKey, + bool useMaster, bool compress, int logFD) : host(host) , fakeSSH(host == "localhost") , keyFile(keyFile) diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index 3b1a0827a..be0a32287 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -39,7 +39,11 @@ private: public: - SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1); + SSHMaster( + std::string_view host, + std::string_view keyFile, + std::string_view sshPublicHostKey, + bool useMaster, bool compress, int logFD = -1); struct Connection { diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 419c55e92..6eba3a77d 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -21,7 +21,6 @@ #include "users.hh" #include -#include using json = nlohmann::json; @@ -1321,9 +1320,7 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); } else debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); - Store::Params params2; - params2["root"] = chrootStore; - return std::make_shared(params2); + return std::make_shared("local", chrootStore, params); } #endif else @@ -1333,42 +1330,12 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para } else if (uri == "local") { return std::make_shared(params); } else if (isNonUriPath(uri)) { - Store::Params params2 = params; - params2["root"] = absPath(uri); - return std::make_shared(params2); + return std::make_shared("local", absPath(uri), params); } else { return nullptr; } } -// The `parseURL` function supports both IPv6 URIs as defined in -// RFC2732, but also pure addresses. The latter one is needed here to -// connect to a remote store via SSH (it's possible to do e.g. `ssh root@::1`). -// -// This function now ensures that a usable connection string is available: -// * If the store to be opened is not an SSH store, nothing will be done. -// * If the URL looks like `root@[::1]` (which is allowed by the URL parser and probably -// needed to pass further flags), it -// will be transformed into `root@::1` for SSH (same for `[::1]` -> `::1`). -// * If the URL looks like `root@::1` it will be left as-is. -// * In any other case, the string will be left as-is. -static std::string extractConnStr(const std::string &proto, const std::string &connStr) -{ - if (proto.rfind("ssh") != std::string::npos) { - std::smatch result; - std::regex v6AddrRegex("^((.*)@)?\\[(.*)\\]$"); - - if (std::regex_match(connStr, result, v6AddrRegex)) { - if (result[1].matched) { - return result.str(1) + result.str(3); - } - return result.str(3); - } - } - - return connStr; -} - ref openStore(const std::string & uri_, const Store::Params & extraParams) { @@ -1377,10 +1344,7 @@ ref openStore(const std::string & uri_, auto parsedUri = parseURL(uri_); params.insert(parsedUri.query.begin(), parsedUri.query.end()); - auto baseURI = extractConnStr( - parsedUri.scheme, - parsedUri.authority.value_or("") + parsedUri.path - ); + auto baseURI = parsedUri.authority.value_or("") + parsedUri.path; for (auto implem : *Implementations::registered) { if (implem.uriSchemes.count(parsedUri.scheme)) { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index ae8c22437..a508e5a00 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -901,7 +901,14 @@ std::list> getDefaultSubstituters(); struct StoreFactory { std::set uriSchemes; - std::function (const std::string & scheme, const std::string & uri, const Store::Params & params)> create; + /** + * The `authorityPath` parameter is `/`, or really + * whatever comes after `://` and before `?`. + */ + std::function ( + std::string_view scheme, + std::string_view authorityPath, + const Store::Params & params)> create; std::function ()> getConfig; }; @@ -916,7 +923,7 @@ struct Implementations StoreFactory factory{ .uriSchemes = T::uriSchemes(), .create = - ([](const std::string & scheme, const std::string & uri, const Store::Params & params) + ([](auto scheme, auto uri, auto & params) -> std::shared_ptr { return std::make_shared(scheme, uri, params); }), .getConfig = diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index 649644146..499f76967 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -40,12 +40,13 @@ UDSRemoteStore::UDSRemoteStore(const Params & params) UDSRemoteStore::UDSRemoteStore( - const std::string scheme, - std::string socket_path, + std::string_view scheme, + PathView socket_path, const Params & params) : UDSRemoteStore(params) { - path.emplace(socket_path); + if (!socket_path.empty()) + path.emplace(socket_path); } @@ -54,6 +55,7 @@ std::string UDSRemoteStore::getUri() if (path) { return std::string("unix://") + *path; } else { + // unix:// with no path also works. Change what we return? return "daemon"; } } diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index 8bce8994a..6f0494bb6 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -28,7 +28,10 @@ class UDSRemoteStore : public virtual UDSRemoteStoreConfig public: UDSRemoteStore(const Params & params); - UDSRemoteStore(const std::string scheme, std::string path, const Params & params); + UDSRemoteStore( + std::string_view scheme, + PathView path, + const Params & params); std::string getUri() override; diff --git a/src/libstore/unix/local-overlay-store.hh b/src/libstore/unix/local-overlay-store.hh index 2c24285dd..35a301013 100644 --- a/src/libstore/unix/local-overlay-store.hh +++ b/src/libstore/unix/local-overlay-store.hh @@ -92,7 +92,7 @@ class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual public: LocalOverlayStore(const Params & params); - LocalOverlayStore(std::string scheme, std::string path, const Params & params) + LocalOverlayStore(std::string_view scheme, PathView path, const Params & params) : LocalOverlayStore(params) { if (!path.empty()) diff --git a/tests/functional/read-only-store.sh b/tests/functional/read-only-store.sh index d63920c19..834ac1b51 100644 --- a/tests/functional/read-only-store.sh +++ b/tests/functional/read-only-store.sh @@ -9,7 +9,10 @@ clearStore happy () { # We can do a read-only query just fine with a read-only store nix --store local?read-only=true path-info $dummyPath - + + # `local://` also works. + nix --store local://?read-only=true path-info $dummyPath + # We can "write" an already-present store-path a read-only store, because no IO is actually required nix-store --store local?read-only=true --add dummy } From 77cb02b7390ca13142c85af1cbe6ba1a6fbde2bf Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 21 May 2024 18:06:16 +0200 Subject: [PATCH 132/910] reword documentation on `nix-copy-closure` (#10709) * reword documentation on `nix-copy-closure` - one sentence per line - be more precise with respect to which Nix stores are being accessed - make a clear distinction between store paths and store objects - add links to definitions of terms - clarify which machine is which - --to and --from don't take arguments Co-authored-by: Robert Hensing --- .../src/command-ref/nix-copy-closure.md | 69 ++++++++----------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index 3c62191f1..d94bde3a3 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -1,75 +1,64 @@ # Name -`nix-copy-closure` - copy a closure to or from a remote machine via SSH +`nix-copy-closure` - copy store objects to or from a remote machine via SSH # Synopsis `nix-copy-closure` - [`--to` | `--from`] + [`--to` | `--from` ] [`--gzip`] [`--include-outputs`] [`--use-substitutes` | `-s`] [`-v`] - _user@machine_ _paths_ + [_user_@]_machine_[:_port_] _paths_ # Description -`nix-copy-closure` gives you an easy and efficient way to exchange -software between machines. Given one or more Nix store _paths_ on the -local machine, `nix-copy-closure` computes the closure of those paths -(i.e. all their dependencies in the Nix store), and copies all paths -in the closure to the remote machine via the `ssh` (Secure Shell) -command. With the `--from` option, the direction is reversed: the -closure of _paths_ on a remote machine is copied to the Nix store on -the local machine. +Given _paths_ from one machine, `nix-copy-closure` computes the [closure](@docroot@/glossary.md#gloss-closure) of those paths (i.e. all their dependencies in the Nix store), and copies [store objects](@docroot@/glossary.md#gloss-store-object) in that closure to another machine via SSH. +It doesn’t copy store objects that are already present on the other machine. -This command is efficient because it only sends the store paths -that are missing on the target machine. +> **Note** +> +> While the Nix store to use on the local machine can be specified on the command line with the [`--store`](@docroot@/command-ref/conf-file.md#conf-store) option, the Nix store to be accessed on the remote machine can only be [configured statically](@docroot@/command-ref/conf-file.md#configuration-file) on that remote machine. -Since `nix-copy-closure` calls `ssh`, you may be asked to type in the -appropriate password or passphrase. In fact, you may be asked _twice_ -because `nix-copy-closure` currently connects twice to the remote -machine, first to get the set of paths missing on the target machine, -and second to send the dump of those paths. When using public key -authentication, you can avoid typing the passphrase with `ssh-agent`. +Since `nix-copy-closure` calls `ssh`, you may need to authenticate with the remote machine. +In fact, you may be asked for authentication _twice_ because `nix-copy-closure` currently connects twice to the remote machine: first to get the set of paths missing on the target machine, and second to send the dump of those paths. +When using public key authentication, you can avoid typing the passphrase with `ssh-agent`. # Options - - `--to`\ - Copy the closure of _paths_ from the local Nix store to the Nix - store on _machine_. This is the default. + - `--to` - - `--from`\ - Copy the closure of _paths_ from the Nix store on _machine_ to the - local Nix store. + Copy the closure of _paths_ from a Nix store accessible from the local machine to the Nix store on the remote _machine_. + This is the default behavior. + + - `--from` + + Copy the closure of _paths_ from the Nix store on the remote _machine_ to the local machine's specified Nix store. + + - `--gzip` - - `--gzip`\ Enable compression of the SSH connection. - - `--include-outputs`\ + - `--include-outputs` + Also copy the outputs of [store derivation]s included in the closure. [store derivation]: @docroot@/glossary.md#gloss-store-derivation - - `--use-substitutes` / `-s`\ - Attempt to download missing paths on the target machine using Nix’s - substitute mechanism. Any paths that cannot be substituted on the - target are still copied normally from the source. This is useful, - for instance, if the connection between the source and target - machine is slow, but the connection between the target machine and - `nixos.org` (the default binary cache server) is - fast. + - `--use-substitutes` / `-s` - - `-v`\ - Show verbose output. + Attempt to download missing store objects on the target from [substituters](@docroot@/command-ref/conf-file.md#conf-substituters). + Any store objects that cannot be substituted on the target are still copied normally from the source. + This is useful, for instance, if the connection between the source and target machine is slow, but the connection between the target machine and `cache.nixos.org` (the default binary cache server) is fast. {{#include ./opt-common.md}} # Environment variables - - `NIX_SSHOPTS`\ - Additional options to be passed to `ssh` on the command - line. + - `NIX_SSHOPTS` + + Additional options to be passed to `ssh` on the command line. {{#include ./env-common.md}} From 1d6c2316a988a97b2c4d214f582580f70c7d9586 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 21 May 2024 17:52:49 -0400 Subject: [PATCH 133/910] Slightly change formatting style For long expressions, one argument or parameter per line is just easier. --- .clang-format | 2 ++ src/libcmd/network-proxy.cc | 5 ++++- src/libstore/windows/pathlocks.cc | 9 +++++++-- src/libutil/compression.cc | 13 +++++++++---- src/libutil/windows/file-system.cc | 9 +++++++-- tests/unit/libexpr/nix_api_value.cc | 21 +++++++++++++++------ 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/.clang-format b/.clang-format index 73eac7ef6..07a5ef5bc 100644 --- a/.clang-format +++ b/.clang-format @@ -30,3 +30,5 @@ BreakBeforeBinaryOperators: NonAssignment AlwaysBreakBeforeMultilineStrings: true IndentPPDirectives: AfterHash PPIndentWidth: 2 +BinPackArguments: false +BinPackParameters: false diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc index 633b2c005..4b7d2441f 100644 --- a/src/libcmd/network-proxy.cc +++ b/src/libcmd/network-proxy.cc @@ -25,7 +25,10 @@ static StringSet getExcludingNoProxyVariables() static const StringSet excludeVariables{"no_proxy", "NO_PROXY"}; StringSet variables; std::set_difference( - networkProxyVariables.begin(), networkProxyVariables.end(), excludeVariables.begin(), excludeVariables.end(), + networkProxyVariables.begin(), + networkProxyVariables.end(), + excludeVariables.begin(), + excludeVariables.end(), std::inserter(variables, variables.begin())); return variables; } diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc index 738057f68..1199878e9 100644 --- a/src/libstore/windows/pathlocks.cc +++ b/src/libstore/windows/pathlocks.cc @@ -35,8 +35,13 @@ void PathLocks::unlock() AutoCloseFD openLockFile(const Path & path, bool create) { AutoCloseFD desc = CreateFileA( - path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - create ? OPEN_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS, NULL); + path.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + create ? OPEN_ALWAYS : OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS, + NULL); if (desc.get() == INVALID_HANDLE_VALUE) warn("%s: %s", path, std::to_string(GetLastError())); diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index d17401f27..d27028565 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -263,8 +263,13 @@ struct BrotliCompressionSink : ChunkedCompressionSink checkInterrupt(); if (!BrotliEncoderCompressStream( - state, data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, &avail_in, &next_in, - &avail_out, &next_out, nullptr)) + state, + data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, + &avail_in, + &next_in, + &avail_out, + &next_out, + nullptr)) throw CompressionError("error while compressing brotli compression"); if (avail_out < sizeof(outbuf) || avail_in == 0) { @@ -280,8 +285,8 @@ struct BrotliCompressionSink : ChunkedCompressionSink ref makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel, int level) { - std::vector la_supports = {"bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", - "lzip", "lzma", "lzop", "xz", "zstd"}; + std::vector la_supports = { + "bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", "lzip", "lzma", "lzop", "xz", "zstd"}; if (std::find(la_supports.begin(), la_supports.end(), method) != la_supports.end()) { return make_ref(nextSink, method, parallel, level); } diff --git a/src/libutil/windows/file-system.cc b/src/libutil/windows/file-system.cc index 8002dd75e..b15355efe 100644 --- a/src/libutil/windows/file-system.cc +++ b/src/libutil/windows/file-system.cc @@ -5,8 +5,13 @@ namespace nix { Descriptor openDirectory(const std::filesystem::path & path) { return CreateFileW( - path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); + path.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); } } diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 6e1131e10..c71593c85 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -256,10 +256,13 @@ TEST_F(nix_api_expr_test, nix_value_init) Value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( - ctx, state, R"( + ctx, + state, + R"( a: a * a )", - "", f); + "", + f); // Test @@ -325,20 +328,26 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) Value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( - ctx, state, R"( + ctx, + state, + R"( a: { foo = a; } )", - "", f); + "", + f); assert_ctx_ok(); Value * e = nix_alloc_value(ctx, state); { Value * g = nix_alloc_value(ctx, state); nix_expr_eval_from_string( - ctx, state, R"( + ctx, + state, + R"( _ignore: throw "error message for test case nix_value_init_apply_lazy_arg" )", - "", g); + "", + g); assert_ctx_ok(); nix_init_apply(ctx, e, g, g); From c036d75f9e15baea68056de601fb089e38d36c5c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 23 Jan 2024 14:23:03 -0500 Subject: [PATCH 134/910] Factor out abstract syntax for Store URIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Need to decouple parsing from actually opening a store for Machine configs. Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> Co-authored-by: Robert Hensing --- src/libstore/store-api.cc | 147 +++++++++++--------------------- src/libstore/store-api.hh | 45 +++------- src/libstore/store-reference.cc | 92 ++++++++++++++++++++ src/libstore/store-reference.hh | 84 ++++++++++++++++++ 4 files changed, 236 insertions(+), 132 deletions(-) create mode 100644 src/libstore/store-reference.cc create mode 100644 src/libstore/store-reference.hh diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 6eba3a77d..7c2b3815f 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -8,7 +8,6 @@ #include "util.hh" #include "nar-info-disk-cache.hh" #include "thread-pool.hh" -#include "url.hh" #include "references.hh" #include "archive.hh" #include "callback.hh" @@ -1267,109 +1266,63 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath) namespace nix { -/* Split URI into protocol+hierarchy part and its parameter set. */ -std::pair splitUriAndParams(const std::string & uri_) -{ - auto uri(uri_); - Store::Params params; - auto q = uri.find('?'); - if (q != std::string::npos) { - params = decodeQuery(uri.substr(q + 1)); - uri = uri_.substr(0, q); - } - return {uri, params}; -} - -static bool isNonUriPath(const std::string & spec) -{ - return - // is not a URL - spec.find("://") == std::string::npos - // Has at least one path separator, and so isn't a single word that - // might be special like "auto" - && spec.find("/") != std::string::npos; -} - -std::shared_ptr openFromNonUri(const std::string & uri, const Store::Params & params) -{ - // TODO reenable on Windows once we have `LocalStore` and - // `UDSRemoteStore`. - if (uri == "" || uri == "auto") { - auto stateDir = getOr(params, "state", settings.nixStateDir); - if (access(stateDir.c_str(), R_OK | W_OK) == 0) - return std::make_shared(params); - else if (pathExists(settings.nixDaemonSocketFile)) - return std::make_shared(params); - #if __linux__ - else if (!pathExists(stateDir) - && params.empty() - && !isRootUser() - && !getEnv("NIX_STORE_DIR").has_value() - && !getEnv("NIX_STATE_DIR").has_value()) - { - /* If /nix doesn't exist, there is no daemon socket, and - we're not root, then automatically set up a chroot - store in ~/.local/share/nix/root. */ - auto chrootStore = getDataDir() + "/nix/root"; - if (!pathExists(chrootStore)) { - try { - createDirs(chrootStore); - } catch (Error & e) { - return std::make_shared(params); - } - warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); - } else - debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); - return std::make_shared("local", chrootStore, params); - } - #endif - else - return std::make_shared(params); - } else if (uri == "daemon") { - return std::make_shared(params); - } else if (uri == "local") { - return std::make_shared(params); - } else if (isNonUriPath(uri)) { - return std::make_shared("local", absPath(uri), params); - } else { - return nullptr; - } -} - -ref openStore(const std::string & uri_, +ref openStore(const std::string & uri, const Store::Params & extraParams) { - auto params = extraParams; - try { - auto parsedUri = parseURL(uri_); - params.insert(parsedUri.query.begin(), parsedUri.query.end()); + return openStore(StoreReference::parse(uri, extraParams)); +} - auto baseURI = parsedUri.authority.value_or("") + parsedUri.path; +ref openStore(StoreReference && storeURI) +{ + auto & params = storeURI.params; - for (auto implem : *Implementations::registered) { - if (implem.uriSchemes.count(parsedUri.scheme)) { - auto store = implem.create(parsedUri.scheme, baseURI, params); - if (store) { - experimentalFeatureSettings.require(store->experimentalFeature()); - store->init(); - store->warnUnknownSettings(); - return ref(store); - } + auto store = std::visit(overloaded { + [&](const StoreReference::Auto &) -> std::shared_ptr { + auto stateDir = getOr(params, "state", settings.nixStateDir); + if (access(stateDir.c_str(), R_OK | W_OK) == 0) + return std::make_shared(params); + else if (pathExists(settings.nixDaemonSocketFile)) + return std::make_shared(params); + #if __linux__ + else if (!pathExists(stateDir) + && params.empty() + && !isRootUser() + && !getEnv("NIX_STORE_DIR").has_value() + && !getEnv("NIX_STATE_DIR").has_value()) + { + /* If /nix doesn't exist, there is no daemon socket, and + we're not root, then automatically set up a chroot + store in ~/.local/share/nix/root. */ + auto chrootStore = getDataDir() + "/nix/root"; + if (!pathExists(chrootStore)) { + try { + createDirs(chrootStore); + } catch (Error & e) { + return std::make_shared(params); + } + warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); + } else + debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); + return std::make_shared("local", chrootStore, params); } - } - } - catch (BadURL &) { - auto [uri, uriParams] = splitUriAndParams(uri_); - params.insert(uriParams.begin(), uriParams.end()); + #endif + else + return std::make_shared(params); + }, + [&](const StoreReference::Specified & g) { + for (auto implem : *Implementations::registered) + if (implem.uriSchemes.count(g.scheme)) + return implem.create(g.scheme, g.authority, params); - if (auto store = openFromNonUri(uri, params)) { - experimentalFeatureSettings.require(store->experimentalFeature()); - store->warnUnknownSettings(); - return ref(store); - } - } + throw Error("don't know how to open Nix store with scheme '%s'", g.scheme); + }, + }, storeURI.variant); - throw Error("don't know how to open Nix store '%s'", uri_); + experimentalFeatureSettings.require(store->experimentalFeature()); + store->warnUnknownSettings(); + store->init(); + + return ref { store }; } std::list> getDefaultSubstituters() diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index a508e5a00..430d9a5ab 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -13,6 +13,7 @@ #include "path-info.hh" #include "repair-flag.hh" #include "store-dir-config.hh" +#include "store-reference.hh" #include "source-path.hh" #include @@ -65,7 +66,7 @@ MakeError(Unsupported, Error); MakeError(SubstituteGone, Error); MakeError(SubstituterDisabled, Error); -MakeError(InvalidStoreURI, Error); +MakeError(InvalidStoreReference, Error); struct Realisation; struct RealisedPath; @@ -102,7 +103,7 @@ typedef std::map> StorePathCAMap; struct StoreConfig : public StoreDirConfig { - typedef std::map Params; + using Params = StoreReference::Params; using StoreDirConfig::StoreDirConfig; @@ -859,34 +860,13 @@ OutputPathMap resolveDerivedPath(Store &, const DerivedPath::Built &, Store * ev /** * @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 type by appending - * ‘?key=value&key=value&...’ to the URI. + */ +ref openStore(StoreReference && storeURI); + + +/** + * Opens the store at `uri`, where `uri` is in the format expected by `StoreReference::parse` + */ ref openStore(const std::string & uri = settings.storeUri.get(), const Store::Params & extraParams = Store::Params()); @@ -957,11 +937,6 @@ std::optional decodeValidPathInfo( std::istream & str, std::optional hashGiven = std::nullopt); -/** - * Split URI into protocol+hierarchy part and its parameter set. - */ -std::pair splitUriAndParams(const std::string & uri); - const ContentAddress * getDerivationCA(const BasicDerivation & drv); std::map drvOutputReferences( diff --git a/src/libstore/store-reference.cc b/src/libstore/store-reference.cc new file mode 100644 index 000000000..9e5dafd99 --- /dev/null +++ b/src/libstore/store-reference.cc @@ -0,0 +1,92 @@ +#include + +#include "error.hh" +#include "url.hh" +#include "store-reference.hh" +#include "file-system.hh" + +namespace nix { + +static bool isNonUriPath(const std::string & spec) +{ + return + // is not a URL + spec.find("://") == std::string::npos + // Has at least one path separator, and so isn't a single word that + // might be special like "auto" + && spec.find("/") != std::string::npos; +} + +StoreReference StoreReference::parse(const std::string & uri, const StoreReference::Params & extraParams) +{ + auto params = extraParams; + try { + auto parsedUri = parseURL(uri); + params.insert(parsedUri.query.begin(), parsedUri.query.end()); + + auto baseURI = parsedUri.authority.value_or("") + parsedUri.path; + + return { + .variant = + Specified{ + .scheme = std::move(parsedUri.scheme), + .authority = std::move(baseURI), + }, + .params = std::move(params), + }; + } catch (BadURL &) { + auto [baseURI, uriParams] = splitUriAndParams(uri); + params.insert(uriParams.begin(), uriParams.end()); + + if (baseURI == "" || baseURI == "auto") { + return { + .variant = Auto{}, + .params = std::move(params), + }; + } else if (baseURI == "daemon") { + return { + .variant = + Specified{ + .scheme = "unix", + .authority = "", + }, + .params = std::move(params), + }; + } else if (baseURI == "local") { + return { + .variant = + Specified{ + .scheme = "local", + .authority = "", + }, + .params = std::move(params), + }; + } else if (isNonUriPath(baseURI)) { + return { + .variant = + Specified{ + .scheme = "local", + .authority = absPath(baseURI), + }, + .params = std::move(params), + }; + } + } + + throw UsageError("Cannot parse Nix store '%s'", uri); +} + +/* Split URI into protocol+hierarchy part and its parameter set. */ +std::pair splitUriAndParams(const std::string & uri_) +{ + auto uri(uri_); + StoreReference::Params params; + auto q = uri.find('?'); + if (q != std::string::npos) { + params = decodeQuery(uri.substr(q + 1)); + uri = uri_.substr(0, q); + } + return {uri, params}; +} + +} diff --git a/src/libstore/store-reference.hh b/src/libstore/store-reference.hh new file mode 100644 index 000000000..c94a87d7d --- /dev/null +++ b/src/libstore/store-reference.hh @@ -0,0 +1,84 @@ +#pragma once +///@file + +#include + +#include "types.hh" + +namespace nix { + +/** + * A parsed Store URI (URI is a slight misnomer...), parsed but not yet + * resolved to a specific instance and query parms validated. + * + * 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 type by appending + * ‘?key=value&key=value&...’ to the URI. + */ +struct StoreReference +{ + using Params = std::map; + + /** + * Special store reference `""` or `"auto"` + */ + struct Auto + { + inline bool operator==(const Auto & rhs) const = default; + inline auto operator<=>(const Auto & rhs) const = default; + }; + + /** + * General case, a regular `scheme://authority` URL. + */ + struct Specified + { + std::string scheme; + std::string authority; + + bool operator==(const Specified & rhs) const = default; + auto operator<=>(const Specified & rhs) const = default; + }; + + typedef std::variant Variant; + + Variant variant; + + Params params; + + bool operator==(const StoreReference & rhs) const = default; + auto operator<=>(const StoreReference & rhs) const = default; + + static StoreReference parse(const std::string & uri, const Params & extraParams = Params{}); +}; + +/** + * Split URI into protocol+hierarchy part and its parameter set. + */ +std::pair splitUriAndParams(const std::string & uri); + +} From b59a7a14c4fd48835f3b36c3a4b942de77e5d9a1 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 23 Jan 2024 15:36:44 -0500 Subject: [PATCH 135/910] Add `StoreReference::render` This will be needed for the next step. Also allows us to write round trip tests. --- .clang-format | 2 +- maintainers/flake-module.nix | 66 +++++++++- src/libstore/store-reference.cc | 24 ++++ src/libstore/store-reference.hh | 10 +- src/libutil/url.hh | 2 + .../libstore/data/store-reference/auto.txt | 1 + .../data/store-reference/auto_param.txt | 1 + .../libstore/data/store-reference/local_1.txt | 1 + .../libstore/data/store-reference/local_2.txt | 1 + .../store-reference/local_shorthand_1.txt | 1 + .../store-reference/local_shorthand_2.txt | 1 + .../libstore/data/store-reference/ssh.txt | 1 + .../libstore/data/store-reference/unix.txt | 1 + .../data/store-reference/unix_shorthand.txt | 1 + tests/unit/libstore/store-reference.cc | 123 ++++++++++++++++++ 15 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 tests/unit/libstore/data/store-reference/auto.txt create mode 100644 tests/unit/libstore/data/store-reference/auto_param.txt create mode 100644 tests/unit/libstore/data/store-reference/local_1.txt create mode 100644 tests/unit/libstore/data/store-reference/local_2.txt create mode 100644 tests/unit/libstore/data/store-reference/local_shorthand_1.txt create mode 100644 tests/unit/libstore/data/store-reference/local_shorthand_2.txt create mode 100644 tests/unit/libstore/data/store-reference/ssh.txt create mode 100644 tests/unit/libstore/data/store-reference/unix.txt create mode 100644 tests/unit/libstore/data/store-reference/unix_shorthand.txt create mode 100644 tests/unit/libstore/store-reference.cc diff --git a/.clang-format b/.clang-format index 07a5ef5bc..f5d7fb711 100644 --- a/.clang-format +++ b/.clang-format @@ -15,7 +15,7 @@ SpaceAfterCStyleCast: true SpaceAfterTemplateKeyword: false AccessModifierOffset: -4 AlignAfterOpenBracket: AlwaysBreak -AlignEscapedNewlines: DontAlign +AlignEscapedNewlines: Left ColumnLimit: 120 BreakStringLiterals: false BitFieldColonSpacing: None diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 351a01fcb..f7f05d38e 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -17,7 +17,8 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^tests/.*'' + ''^tests/functional/.*$'' + ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code ''^src/toml11/.*'' @@ -426,6 +427,69 @@ ''^src/nix/upgrade-nix\.cc$'' ''^src/nix/verify\.cc$'' ''^src/nix/why-depends\.cc$'' + + ''^tests/nixos/ca-fd-leak/sender\.c'' + ''^tests/nixos/ca-fd-leak/smuggler\.c'' + ''^tests/unit/libexpr-support/tests/libexpr\.hh'' + ''^tests/unit/libexpr-support/tests/value/context\.cc'' + ''^tests/unit/libexpr-support/tests/value/context\.hh'' + ''^tests/unit/libexpr/derived-path\.cc'' + ''^tests/unit/libexpr/error_traces\.cc'' + ''^tests/unit/libexpr/eval\.cc'' + ''^tests/unit/libexpr/flake/flakeref\.cc'' + ''^tests/unit/libexpr/flake/url-name\.cc'' + ''^tests/unit/libexpr/json\.cc'' + ''^tests/unit/libexpr/main\.cc'' + ''^tests/unit/libexpr/primops\.cc'' + ''^tests/unit/libexpr/search-path\.cc'' + ''^tests/unit/libexpr/trivial\.cc'' + ''^tests/unit/libexpr/value/context\.cc'' + ''^tests/unit/libexpr/value/print\.cc'' + ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.hh'' + ''^tests/unit/libstore-support/tests/libstore\.hh'' + ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' + ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' + ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' + ''^tests/unit/libstore-support/tests/path\.cc'' + ''^tests/unit/libstore-support/tests/path\.hh'' + ''^tests/unit/libstore-support/tests/protocol\.hh'' + ''^tests/unit/libstore/common-protocol\.cc'' + ''^tests/unit/libstore/content-address\.cc'' + ''^tests/unit/libstore/derivation\.cc'' + ''^tests/unit/libstore/derived-path\.cc'' + ''^tests/unit/libstore/downstream-placeholder\.cc'' + ''^tests/unit/libstore/machines\.cc'' + ''^tests/unit/libstore/nar-info-disk-cache\.cc'' + ''^tests/unit/libstore/nar-info\.cc'' + ''^tests/unit/libstore/outputs-spec\.cc'' + ''^tests/unit/libstore/path-info\.cc'' + ''^tests/unit/libstore/path\.cc'' + ''^tests/unit/libstore/serve-protocol\.cc'' + ''^tests/unit/libstore/worker-protocol\.cc'' + ''^tests/unit/libutil-support/tests/characterization\.hh'' + ''^tests/unit/libutil-support/tests/hash\.cc'' + ''^tests/unit/libutil-support/tests/hash\.hh'' + ''^tests/unit/libutil/args\.cc'' + ''^tests/unit/libutil/canon-path\.cc'' + ''^tests/unit/libutil/chunked-vector\.cc'' + ''^tests/unit/libutil/closure\.cc'' + ''^tests/unit/libutil/compression\.cc'' + ''^tests/unit/libutil/config\.cc'' + ''^tests/unit/libutil/file-content-address\.cc'' + ''^tests/unit/libutil/git\.cc'' + ''^tests/unit/libutil/hash\.cc'' + ''^tests/unit/libutil/hilite\.cc'' + ''^tests/unit/libutil/json-utils\.cc'' + ''^tests/unit/libutil/logging\.cc'' + ''^tests/unit/libutil/lru-cache\.cc'' + ''^tests/unit/libutil/pool\.cc'' + ''^tests/unit/libutil/references\.cc'' + ''^tests/unit/libutil/suggestions\.cc'' + ''^tests/unit/libutil/tests\.cc'' + ''^tests/unit/libutil/url\.cc'' + ''^tests/unit/libutil/xml-writer\.cc'' ]; }; diff --git a/src/libstore/store-reference.cc b/src/libstore/store-reference.cc index 9e5dafd99..b4968dfad 100644 --- a/src/libstore/store-reference.cc +++ b/src/libstore/store-reference.cc @@ -4,6 +4,7 @@ #include "url.hh" #include "store-reference.hh" #include "file-system.hh" +#include "util.hh" namespace nix { @@ -17,6 +18,29 @@ static bool isNonUriPath(const std::string & spec) && spec.find("/") != std::string::npos; } +std::string StoreReference::render() const +{ + std::string res; + + std::visit( + overloaded{ + [&](const StoreReference::Auto &) { res = "auto"; }, + [&](const StoreReference::Specified & g) { + res = g.scheme; + res += "://"; + res += g.authority; + }, + }, + variant); + + if (!params.empty()) { + res += "?"; + res += encodeQuery(params); + } + + return res; +} + StoreReference StoreReference::parse(const std::string & uri, const StoreReference::Params & extraParams) { auto params = extraParams; diff --git a/src/libstore/store-reference.hh b/src/libstore/store-reference.hh index c94a87d7d..e99335c0d 100644 --- a/src/libstore/store-reference.hh +++ b/src/libstore/store-reference.hh @@ -58,7 +58,7 @@ struct StoreReference struct Specified { std::string scheme; - std::string authority; + std::string authority = ""; bool operator==(const Specified & rhs) const = default; auto operator<=>(const Specified & rhs) const = default; @@ -73,6 +73,14 @@ struct StoreReference bool operator==(const StoreReference & rhs) const = default; auto operator<=>(const StoreReference & rhs) const = default; + /** + * Render the whole store reference as a URI, including parameters. + */ + std::string render() const; + + /** + * Parse a URI into a store reference. + */ static StoreReference parse(const std::string & uri, const Params & extraParams = Params{}); }; diff --git a/src/libutil/url.hh b/src/libutil/url.hh index 24806bbff..6cd06e53d 100644 --- a/src/libutil/url.hh +++ b/src/libutil/url.hh @@ -33,6 +33,8 @@ std::string percentEncode(std::string_view s, std::string_view keep=""); std::map decodeQuery(const std::string & query); +std::string encodeQuery(const std::map & query); + ParsedURL parseURL(const std::string & url); /** diff --git a/tests/unit/libstore/data/store-reference/auto.txt b/tests/unit/libstore/data/store-reference/auto.txt new file mode 100644 index 000000000..4d18c3e59 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/auto.txt @@ -0,0 +1 @@ +auto \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/auto_param.txt b/tests/unit/libstore/data/store-reference/auto_param.txt new file mode 100644 index 000000000..54adabb25 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/auto_param.txt @@ -0,0 +1 @@ +auto?root=/foo/bar/baz \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_1.txt b/tests/unit/libstore/data/store-reference/local_1.txt new file mode 100644 index 000000000..74b1b9677 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_1.txt @@ -0,0 +1 @@ +local://?root=/foo/bar/baz \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_2.txt b/tests/unit/libstore/data/store-reference/local_2.txt new file mode 100644 index 000000000..8b5593fb1 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_2.txt @@ -0,0 +1 @@ +local:///foo/bar/baz?trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_1.txt b/tests/unit/libstore/data/store-reference/local_shorthand_1.txt new file mode 100644 index 000000000..896189be9 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_shorthand_1.txt @@ -0,0 +1 @@ +local?root=/foo/bar/baz \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_2.txt b/tests/unit/libstore/data/store-reference/local_shorthand_2.txt new file mode 100644 index 000000000..7a9dad3b3 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_shorthand_2.txt @@ -0,0 +1 @@ +/foo/bar/baz?trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/ssh.txt b/tests/unit/libstore/data/store-reference/ssh.txt new file mode 100644 index 000000000..8c61010ec --- /dev/null +++ b/tests/unit/libstore/data/store-reference/ssh.txt @@ -0,0 +1 @@ +ssh://localhost \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/unix.txt b/tests/unit/libstore/data/store-reference/unix.txt new file mode 100644 index 000000000..195489048 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/unix.txt @@ -0,0 +1 @@ +unix://?max-connections=7&trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/unix_shorthand.txt b/tests/unit/libstore/data/store-reference/unix_shorthand.txt new file mode 100644 index 000000000..0300337e9 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/unix_shorthand.txt @@ -0,0 +1 @@ +daemon?max-connections=7&trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/store-reference.cc b/tests/unit/libstore/store-reference.cc new file mode 100644 index 000000000..16e033ec4 --- /dev/null +++ b/tests/unit/libstore/store-reference.cc @@ -0,0 +1,123 @@ +#include +#include + +#include "file-system.hh" +#include "store-reference.hh" + +#include "tests/characterization.hh" +#include "tests/libstore.hh" + +namespace nix { + +using nlohmann::json; + +class StoreReferenceTest : public CharacterizationTest, public LibStoreTest +{ + Path unitTestData = getUnitTestData() + "/store-reference"; + + Path goldenMaster(PathView testStem) const override + { + return unitTestData + "/" + testStem + ".txt"; + } +}; + +#define URI_TEST_READ(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_from_uri) \ + { \ + readTest(#STEM, ([&](const auto & encoded) { \ + StoreReference expected = OBJ; \ + auto got = StoreReference::parse(encoded); \ + ASSERT_EQ(got, expected); \ + })); \ + } + +#define URI_TEST_WRITE(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_to_uri) \ + { \ + writeTest( \ + #STEM, \ + [&]() -> StoreReference { return OBJ; }, \ + [](const auto & file) { return StoreReference::parse(readFile(file)); }, \ + [](const auto & file, const auto & got) { return writeFile(file, got.render()); }); \ + } + +#define URI_TEST(STEM, OBJ) \ + URI_TEST_READ(STEM, OBJ) \ + URI_TEST_WRITE(STEM, OBJ) + +URI_TEST( + auto, + (StoreReference{ + .variant = StoreReference::Auto{}, + .params = {}, + })) + +URI_TEST( + auto_param, + (StoreReference{ + .variant = StoreReference::Auto{}, + .params = + { + {"root", "/foo/bar/baz"}, + }, + })) + +static StoreReference localExample_1{ + .variant = + StoreReference::Specified{ + .scheme = "local", + }, + .params = + { + {"root", "/foo/bar/baz"}, + }, +}; + +static StoreReference localExample_2{ + .variant = + StoreReference::Specified{ + .scheme = "local", + .authority = "/foo/bar/baz", + }, + .params = + { + {"trusted", "true"}, + }, +}; + +URI_TEST(local_1, localExample_1) + +URI_TEST(local_2, localExample_2) + +URI_TEST_READ(local_shorthand_1, localExample_1) + +URI_TEST_READ(local_shorthand_2, localExample_2) + +static StoreReference unixExample{ + .variant = + StoreReference::Specified{ + .scheme = "unix", + }, + .params = + { + {"max-connections", "7"}, + {"trusted", "true"}, + }, +}; + +URI_TEST(unix, unixExample) + +URI_TEST_READ(unix_shorthand, unixExample) + +URI_TEST( + ssh, + (StoreReference{ + .variant = + StoreReference::Specified{ + .scheme = "ssh", + .authority = "localhost", + }, + .params = {}, + })) + +} From b3ebcc5aad1f3c8f56895efc0632af47a5dca532 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 23 Jan 2024 15:36:03 -0500 Subject: [PATCH 136/910] Use the new `StoreReference` in `Machine` This makes the remote builder abstract syntax more robust. --- src/build-remote/build-remote.cc | 22 +++++++-------- src/libstore/machines.cc | 35 +++++++++++++++--------- src/libstore/machines.hh | 21 ++++++++++++-- tests/unit/libstore/machines.cc | 47 +++++++++++++++++--------------- 4 files changed, 77 insertions(+), 48 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 18eee830b..582e6d623 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -37,7 +37,7 @@ static std::string currentLoad; static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot) { - return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true); + return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri.render()), slot), true); } static bool allSupportedLocally(Store & store, const std::set& requiredFeatures) { @@ -99,7 +99,7 @@ static int main_build_remote(int argc, char * * argv) } std::optional drvPath; - std::string storeUri; + StoreReference storeUri; while (true) { @@ -135,7 +135,7 @@ static int main_build_remote(int argc, char * * argv) Machine * bestMachine = nullptr; uint64_t bestLoad = 0; for (auto & m : machines) { - debug("considering building on remote machine '%s'", m.storeUri); + debug("considering building on remote machine '%s'", m.storeUri.render()); if (m.enabled && m.systemSupported(neededSystem) && @@ -233,7 +233,7 @@ static int main_build_remote(int argc, char * * argv) try { - Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri.render())); sshStore = bestMachine->openStore(); sshStore->connect(); @@ -242,7 +242,7 @@ static int main_build_remote(int argc, char * * argv) } catch (std::exception & e) { auto msg = chomp(drainFD(5, false)); printError("cannot build on '%s': %s%s", - bestMachine->storeUri, e.what(), + bestMachine->storeUri.render(), e.what(), msg.empty() ? "" : ": " + msg); bestMachine->enabled = false; continue; @@ -257,15 +257,15 @@ connected: assert(sshStore); - std::cerr << "# accept\n" << storeUri << "\n"; + std::cerr << "# accept\n" << storeUri.render() << "\n"; auto inputs = readStrings(source); auto wantedOutputs = readStrings(source); - AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true); + AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri.render()) + ".upload-lock", true); { - Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri.render())); auto old = signal(SIGALRM, handleAlarm); alarm(15 * 60); @@ -278,7 +278,7 @@ connected: auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute; { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri.render())); copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute); } @@ -316,7 +316,7 @@ connected: optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv); auto & result = *optResult; if (!result.success()) - throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg); + throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri.render(), result.errorMsg); } else { copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute); auto res = sshStore->buildPathsWithResults({ @@ -359,7 +359,7 @@ connected: } if (!missingPaths.empty()) { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri.render())); if (auto localStore = store.dynamic_pointer_cast()) for (auto & path : missingPaths) localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */ diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index 2d461c63a..644762242 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -6,7 +6,8 @@ namespace nix { -Machine::Machine(decltype(storeUri) storeUri, +Machine::Machine( + const std::string & storeUri, decltype(systemTypes) systemTypes, decltype(sshKey) sshKey, decltype(maxJobs) maxJobs, @@ -14,7 +15,7 @@ Machine::Machine(decltype(storeUri) storeUri, decltype(supportedFeatures) supportedFeatures, decltype(mandatoryFeatures) mandatoryFeatures, decltype(sshPublicHostKey) sshPublicHostKey) : - storeUri( + storeUri(StoreReference::parse( // Backwards compatibility: if the URI is schemeless, is not a path, // and is not one of the special store connection words, prepend // ssh://. @@ -28,7 +29,7 @@ Machine::Machine(decltype(storeUri) storeUri, || hasPrefix(storeUri, "local?") || hasPrefix(storeUri, "?") ? storeUri - : "ssh://" + storeUri), + : "ssh://" + storeUri)), systemTypes(systemTypes), sshKey(sshKey), maxJobs(maxJobs), @@ -63,23 +64,26 @@ bool Machine::mandatoryMet(const std::set & features) const }); } -ref Machine::openStore() const +StoreReference Machine::completeStoreReference() const { - Store::Params storeParams; - if (hasPrefix(storeUri, "ssh://")) { - storeParams["max-connections"] = "1"; - storeParams["log-fd"] = "4"; + auto storeUri = this->storeUri; + + auto * generic = std::get_if(&storeUri.variant); + + if (generic && generic->scheme == "ssh") { + storeUri.params["max-connections"] = "1"; + storeUri.params["log-fd"] = "4"; } - if (hasPrefix(storeUri, "ssh://") || hasPrefix(storeUri, "ssh-ng://")) { + if (generic && (generic->scheme == "ssh" || generic->scheme == "ssh-ng")) { if (sshKey != "") - storeParams["ssh-key"] = sshKey; + storeUri.params["ssh-key"] = sshKey; if (sshPublicHostKey != "") - storeParams["base64-ssh-public-host-key"] = sshPublicHostKey; + storeUri.params["base64-ssh-public-host-key"] = sshPublicHostKey; } { - auto & fs = storeParams["system-features"]; + auto & fs = storeUri.params["system-features"]; auto append = [&](auto feats) { for (auto & f : feats) { if (fs.size() > 0) fs += ' '; @@ -90,7 +94,12 @@ ref Machine::openStore() const append(mandatoryFeatures); } - return nix::openStore(storeUri, storeParams); + return storeUri; +} + +ref Machine::openStore() const +{ + return nix::openStore(completeStoreReference()); } static std::vector expandBuilderLines(const std::string & builders) diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh index 8516409d4..97980df8d 100644 --- a/src/libstore/machines.hh +++ b/src/libstore/machines.hh @@ -2,6 +2,7 @@ ///@file #include "types.hh" +#include "store-reference.hh" namespace nix { @@ -9,7 +10,7 @@ class Store; struct Machine { - const std::string storeUri; + const StoreReference storeUri; const std::set systemTypes; const std::string sshKey; const unsigned int maxJobs; @@ -36,7 +37,8 @@ struct Machine { */ bool mandatoryMet(const std::set & features) const; - Machine(decltype(storeUri) storeUri, + Machine( + const std::string & storeUri, decltype(systemTypes) systemTypes, decltype(sshKey) sshKey, decltype(maxJobs) maxJobs, @@ -45,6 +47,21 @@ struct Machine { decltype(mandatoryFeatures) mandatoryFeatures, decltype(sshPublicHostKey) sshPublicHostKey); + /** + * Elaborate `storeUri` into a complete store reference, + * incorporating information from the other fields of the `Machine` + * as applicable. + */ + StoreReference completeStoreReference() const; + + /** + * Open a `Store` for this machine. + * + * Just a simple function composition: + * ```c++ + * nix::openStore(completeStoreReference()) + * ``` + */ ref openStore() const; }; diff --git a/tests/unit/libstore/machines.cc b/tests/unit/libstore/machines.cc index 9fd7fda54..4107ba655 100644 --- a/tests/unit/libstore/machines.cc +++ b/tests/unit/libstore/machines.cc @@ -3,24 +3,16 @@ #include "file-system.hh" #include "util.hh" +#include #include using testing::Contains; using testing::ElementsAre; -using testing::EndsWith; using testing::Eq; using testing::Field; using testing::SizeIs; -using nix::absPath; -using nix::FormatError; -using nix::UsageError; -using nix::getMachines; -using nix::Machine; -using nix::Machines; -using nix::pathExists; -using nix::Settings; -using nix::settings; +using namespace nix; class Environment : public ::testing::Environment { public: @@ -40,7 +32,7 @@ TEST(machines, getMachinesUriOnly) { settings.builders = "nix@scratchy.labs.cs.uu.nl"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1))); @@ -54,7 +46,7 @@ TEST(machines, getMachinesDefaults) { settings.builders = "nix@scratchy.labs.cs.uu.nl - - - - - - -"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1))); @@ -64,20 +56,31 @@ TEST(machines, getMachinesDefaults) { EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, SizeIs(0))); } +MATCHER_P(AuthorityMatches, authority, "") { + *result_listener + << "where the authority of " + << arg.render() + << " is " + << authority; + auto * generic = std::get_if(&arg.variant); + if (!generic) return false; + return generic->authority == authority; +} + TEST(machines, getMachinesWithNewLineSeparator) { settings.builders = "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(2)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithSemicolonSeparator) { settings.builders = "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl"; Machines actual = getMachines(); EXPECT_THAT(actual, SizeIs(2)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { @@ -86,7 +89,7 @@ TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8))); @@ -104,7 +107,7 @@ TEST(machines, "KEY+BASE64+ENCODED=="; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8))); @@ -120,7 +123,7 @@ TEST(machines, getMachinesWithMultiOptions) { "MandatoryFeature1,MandatoryFeature2"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("Arch1", "Arch2"))); EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("SupportedFeature1", "SupportedFeature2"))); EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("MandatoryFeature1", "MandatoryFeature2"))); @@ -146,9 +149,9 @@ TEST(machines, getMachinesWithCorrectFileReference) { settings.builders = std::string("@") + path; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(3)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@poochie.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@poochie.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) { From f923ed6b6a7318e8fc77e8d3aeda6796671f67cb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 12:35:44 -0400 Subject: [PATCH 137/910] Require `drvPath` attribute to end with `.drv` Fixes #4977 --- src/libexpr/eval-cache.cc | 1 + src/libexpr/eval-error.cc | 3 +-- src/libexpr/get-drvs.cc | 18 +++++++++---- src/libexpr/print.cc | 23 +++++++++++----- src/libstore/derivations.cc | 5 ++-- src/libstore/path.cc | 8 +++++- src/libstore/path.hh | 27 +++++++------------ src/nix-env/user-env.cc | 1 + src/nix/bundle.cc | 2 ++ tests/functional/lang.sh | 3 +++ .../lang/non-eval-fail-bad-drvPath.nix | 14 ++++++++++ 11 files changed, 71 insertions(+), 34 deletions(-) create mode 100644 tests/functional/lang/non-eval-fail-bad-drvPath.nix diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index d60967a14..a8222b985 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -753,6 +753,7 @@ StorePath AttrCursor::forceDerivation() { auto aDrvPath = getAttr(root->state.sDrvPath, true); auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); + drvPath.requireDerivation(); if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { /* The eval cache contains 'drvPath', but the actual path has been garbage-collected. So force it to be regenerated. */ diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 8db03610b..a9409468c 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -27,8 +27,7 @@ EvalErrorBuilder & EvalErrorBuilder::atPos(Value & value, PosIdx fallback) template EvalErrorBuilder & EvalErrorBuilder::withTrace(PosIdx pos, const std::string_view text) { - error.err.traces.push_front( - Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text))}); + error.addTrace(error.state.positions[pos], text); return *this; } diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index cf10ed84a..ed16a51a1 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -69,13 +69,21 @@ std::string PackageInfo::querySystem() const std::optional PackageInfo::queryDrvPath() const { if (!drvPath && attrs) { - NixStringContext context; - if (auto i = attrs->get(state->sDrvPath)) - drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")}; - else + if (auto i = attrs->get(state->sDrvPath)) { + NixStringContext context; + auto found = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation"); + try { + found.requireDerivation(); + } catch (Error & e) { + e.addTrace(state->positions[i->pos], "while evaluating the 'drvPath' attribute of a derivation"); + throw; + } + drvPath = {std::move(found)}; + } else drvPath = {std::nullopt}; } - return drvPath.value_or(std::nullopt); + drvPath.value_or(std::nullopt); + return *drvPath; } diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 7799a0bbe..d53fadac7 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -271,16 +271,27 @@ private: void printDerivation(Value & v) { - NixStringContext context; - std::string storePath; - if (auto i = v.attrs()->get(state.sDrvPath)) - storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); + std::optional storePath; + if (auto i = v.attrs()->get(state.sDrvPath)) { + NixStringContext context; + storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"); + } + + /* This unforutately breaks printing nested values because of + how the pretty printer is used (when pretting printing and warning + to same terminal / std stream). */ +#if 0 + if (storePath && !storePath->isDerivation()) + warn( + "drvPath attribute '%s' is not a valid store path to a derivation, this value not work properly", + state.store->printStorePath(*storePath)); +#endif if (options.ansiColors) output << ANSI_GREEN; output << "«derivation"; - if (!storePath.empty()) { - output << " " << storePath; + if (storePath) { + output << " " << state.store->printStorePath(*storePath); } output << "»"; if (options.ansiColors) diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index e13705911..869880112 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -930,10 +930,9 @@ DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const StoreDirC std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath) { + drvPath.requireDerivation(); auto nameWithSuffix = drvPath.name(); - constexpr std::string_view extension = ".drv"; - assert(hasSuffix(nameWithSuffix, extension)); - nameWithSuffix.remove_suffix(extension.size()); + nameWithSuffix.remove_suffix(drvExtension.size()); return nameWithSuffix; } diff --git a/src/libstore/path.cc b/src/libstore/path.cc index 4b806e408..8d9726722 100644 --- a/src/libstore/path.cc +++ b/src/libstore/path.cc @@ -49,11 +49,17 @@ StorePath::StorePath(const Hash & hash, std::string_view _name) checkName(baseName, name()); } -bool StorePath::isDerivation() const +bool StorePath::isDerivation() const noexcept { return hasSuffix(name(), drvExtension); } +void StorePath::requireDerivation() const +{ + if (!isDerivation()) + throw FormatError("store path '%s' is not a valid derivation path", to_string()); +} + StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x"); StorePath StorePath::random(std::string_view name) diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 3c26fc515..4abbfcd7c 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -35,30 +35,23 @@ public: StorePath(const Hash & hash, std::string_view name); - std::string_view to_string() const + std::string_view to_string() const noexcept { return baseName; } - bool operator < (const StorePath & other) const - { - return baseName < other.baseName; - } - - bool operator == (const StorePath & other) const - { - return baseName == other.baseName; - } - - bool operator != (const StorePath & other) const - { - return baseName != other.baseName; - } + bool operator == (const StorePath & other) const noexcept = default; + auto operator <=> (const StorePath & other) const noexcept = default; /** * Check whether a file name ends with the extension for derivations. */ - bool isDerivation() const; + bool isDerivation() const noexcept; + + /** + * Throw an exception if `isDerivation` is false. + */ + void requireDerivation() const; std::string_view name() const { @@ -82,7 +75,7 @@ typedef std::vector StorePaths; * The file extension of \ref Derivation derivations when serialized * into store objects. */ -const std::string drvExtension = ".drv"; +constexpr std::string_view drvExtension = ".drv"; } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 6cbbacb15..f7b091f8f 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -140,6 +140,7 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, NixStringContext context; auto & aDrvPath(*topLevel.attrs()->find(state.sDrvPath)); auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, ""); + topLevelDrv.requireDerivation(); auto & aOutPath(*topLevel.attrs()->find(state.sOutPath)); auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, ""); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 2e50392f7..554c36540 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -100,6 +100,8 @@ struct CmdBundle : InstallableValueCommand NixStringContext context2; auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, ""); + drvPath.requireDerivation(); + auto attr2 = vRes->attrs()->get(evalState->sOutPath); if (!attr2) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index c45326473..60603cebe 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -24,6 +24,9 @@ nix-instantiate --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuietIn nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grepQuietInverse Hello expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' | grepQuiet Hello expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello %" (throw "Foo")' | grepQuiet 'Hello %' +# Relies on parsing the expression derivation as a derivation, can't use --eval +expectStderr 1 nix-instantiate --show-trace lang/non-eval-fail-bad-drvPath.nix | grepQuiet "store path '8qlfcic10lw5304gqm8q45nr7g7jl62b-cachix-1.7.3-bin' is not a valid derivation path" + nix-instantiate --eval -E 'let x = builtins.trace { x = x; } true; in x' \ 2>&1 | grepQuiet -E 'trace: { x = «potential infinite recursion»; }' diff --git a/tests/functional/lang/non-eval-fail-bad-drvPath.nix b/tests/functional/lang/non-eval-fail-bad-drvPath.nix new file mode 100644 index 000000000..23639bc54 --- /dev/null +++ b/tests/functional/lang/non-eval-fail-bad-drvPath.nix @@ -0,0 +1,14 @@ +let + package = { + type = "derivation"; + name = "cachix-1.7.3"; + system = builtins.currentSystem; + outputs = [ "out" ]; + # Illegal, because does not end in `.drv` + drvPath = "${builtins.storeDir}/8qlfcic10lw5304gqm8q45nr7g7jl62b-cachix-1.7.3-bin"; + outputName = "out"; + outPath = "${builtins.storeDir}/8qlfcic10lw5304gqm8q45nr7g7jl62b-cachix-1.7.3-bin"; + out = package; + }; +in +package From d5fdfdc59294bbbf9c27479fb2ca3ecee71dc659 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 16:04:14 -0400 Subject: [PATCH 138/910] `unshareFilesystem`: Do not assume caller --- src/libstore/filetransfer.cc | 7 ++++++- src/libutil/linux/namespaces.cc | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 219b60c44..e28dd43ec 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -580,7 +580,12 @@ struct curlFileTransfer : public FileTransfer #endif #if __linux__ - unshareFilesystem(); + try { + unshareFilesystem(); + } catch (nix::Error & e) { + e.addTrace({}, "in download thread"); + throw; + } #endif std::map> items; diff --git a/src/libutil/linux/namespaces.cc b/src/libutil/linux/namespaces.cc index f8289ef39..cb7a0d6e7 100644 --- a/src/libutil/linux/namespaces.cc +++ b/src/libutil/linux/namespaces.cc @@ -140,7 +140,7 @@ void restoreMountNamespace() void unshareFilesystem() { if (unshare(CLONE_FS) != 0 && errno != EPERM) - throw SysError("unsharing filesystem state in download thread"); + throw SysError("unsharing filesystem state"); } } From dc7615dbbb38bd949e3aa2198ca7e3a12c09db8f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 16:04:40 -0400 Subject: [PATCH 139/910] `tryUnshareFilesystem`: Ignore `ENOSYS` too Fixes #10747 --- src/libstore/filetransfer.cc | 2 +- src/libutil/linux/namespaces.cc | 4 ++-- src/libutil/linux/namespaces.hh | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index e28dd43ec..a54ebdcf3 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -581,7 +581,7 @@ struct curlFileTransfer : public FileTransfer #if __linux__ try { - unshareFilesystem(); + tryUnshareFilesystem(); } catch (nix::Error & e) { e.addTrace({}, "in download thread"); throw; diff --git a/src/libutil/linux/namespaces.cc b/src/libutil/linux/namespaces.cc index cb7a0d6e7..d4766cbba 100644 --- a/src/libutil/linux/namespaces.cc +++ b/src/libutil/linux/namespaces.cc @@ -137,9 +137,9 @@ void restoreMountNamespace() } } -void unshareFilesystem() +void tryUnshareFilesystem() { - if (unshare(CLONE_FS) != 0 && errno != EPERM) + if (unshare(CLONE_FS) != 0 && errno != EPERM && errno != ENOSYS) throw SysError("unsharing filesystem state"); } diff --git a/src/libutil/linux/namespaces.hh b/src/libutil/linux/namespaces.hh index ef3c9123f..208920b80 100644 --- a/src/libutil/linux/namespaces.hh +++ b/src/libutil/linux/namespaces.hh @@ -20,11 +20,13 @@ void saveMountNamespace(); void restoreMountNamespace(); /** - * Cause this thread to not share any FS attributes with the main + * Cause this thread to try to not share any FS attributes with the main * thread, because this causes setns() in restoreMountNamespace() to * fail. + * + * This is best effort -- EPERM and ENOSYS failures are just ignored. */ -void unshareFilesystem(); +void tryUnshareFilesystem(); bool userNamespacesSupported(); From 5845fd59c34198ad52a7f7bcb6d3ea7176ca437b Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 23 May 2024 01:24:15 +0200 Subject: [PATCH 140/910] CODEOWNERS: add fricklerhandwerk for documentation (#10759) --- .github/CODEOWNERS | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 59db217d9..99ca670e0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,7 +11,16 @@ .github/CODEOWNERS @edolstra # Documentation of built-in functions -src/libexpr/primops.cc @roberth +src/libexpr/primops.cc @roberth @fricklerhandwerk + +# Documentation of settings +src/libexpr/eval-settings.hh @fricklerhandwerk +src/libstore/globals.hh @fricklerhandwerk + +# Documentation +doc/manual @fricklerhandwerk +maintainers/*.md @fricklerhandwerk +src/**/*.md @fricklerhandwerk # Libstore layer /src/libstore @thufschmitt @ericson2314 From f2bcebc4503c72ba8ad0406058db31a4f883ad0b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 23:12:23 -0400 Subject: [PATCH 141/910] Restore exposing machine file parsing This was accidentally removed in e989c83b44d7c4d8ffe2e5f4231e4861c5f7732f. I restored it and also did a few other cleanups: - Make a static method for namespacing purposes - Put the test files in the data dir with the other test data - Avoid mutating globals in the machine config tests This will be used by Hydra. --- src/libstore/machines.cc | 19 ++-- src/libstore/machines.hh | 22 ++++- .../machines/bad_format} | 0 .../machines.valid => data/machines/valid} | 0 tests/unit/libstore/machines.cc | 88 ++++++++----------- .../libutil-support/tests/characterization.hh | 4 +- 6 files changed, 72 insertions(+), 61 deletions(-) rename tests/unit/libstore/{test-data/machines.bad_format => data/machines/bad_format} (100%) rename tests/unit/libstore/{test-data/machines.valid => data/machines/valid} (100%) diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index 644762242..b4df658b1 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -131,7 +131,7 @@ static std::vector expandBuilderLines(const std::string & builders) return result; } -static Machine parseBuilderLine(const std::string & line) +static Machine parseBuilderLine(const std::set & defaultSystems, const std::string & line) { const auto tokens = tokenizeString>(line); @@ -170,7 +170,7 @@ static Machine parseBuilderLine(const std::string & line) return { tokens[0], - isSet(1) ? tokenizeString>(tokens[1], ",") : std::set{settings.thisSystem}, + isSet(1) ? tokenizeString>(tokens[1], ",") : defaultSystems, isSet(2) ? tokens[2] : "", isSet(3) ? parseUnsignedIntField(3) : 1U, isSet(4) ? parseFloatField(4) : 1.0f, @@ -180,17 +180,24 @@ static Machine parseBuilderLine(const std::string & line) }; } -static Machines parseBuilderLines(const std::vector & builders) +static Machines parseBuilderLines(const std::set & defaultSystems, const std::vector & builders) { Machines result; - std::transform(builders.begin(), builders.end(), std::back_inserter(result), parseBuilderLine); + std::transform( + builders.begin(), builders.end(), std::back_inserter(result), + [&](auto && line) { return parseBuilderLine(defaultSystems, line); }); return result; } +Machines Machine::parseConfig(const std::set & defaultSystems, const std::string & s) +{ + const auto builderLines = expandBuilderLines(s); + return parseBuilderLines(defaultSystems, builderLines); +} + Machines getMachines() { - const auto builderLines = expandBuilderLines(settings.builders); - return parseBuilderLines(builderLines); + return Machine::parseConfig({settings.thisSystem}, settings.builders); } } diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh index 97980df8d..2a55c4456 100644 --- a/src/libstore/machines.hh +++ b/src/libstore/machines.hh @@ -8,6 +8,10 @@ namespace nix { class Store; +struct Machine; + +typedef std::vector Machines; + struct Machine { const StoreReference storeUri; @@ -63,12 +67,22 @@ struct Machine { * ``` */ ref openStore() const; + + /** + * Parse a machine configuration. + * + * Every machine is specified on its own line, and lines beginning + * with `@` are interpreted as paths to other configuration files in + * the same format. + */ + static Machines parseConfig(const std::set & defaultSystems, const std::string & config); }; -typedef std::vector Machines; - -void parseMachines(const std::string & s, Machines & machines); - +/** + * Parse machines from the global config + * + * @todo Remove, globals are bad. + */ Machines getMachines(); } diff --git a/tests/unit/libstore/test-data/machines.bad_format b/tests/unit/libstore/data/machines/bad_format similarity index 100% rename from tests/unit/libstore/test-data/machines.bad_format rename to tests/unit/libstore/data/machines/bad_format diff --git a/tests/unit/libstore/test-data/machines.valid b/tests/unit/libstore/data/machines/valid similarity index 100% rename from tests/unit/libstore/test-data/machines.valid rename to tests/unit/libstore/data/machines/valid diff --git a/tests/unit/libstore/machines.cc b/tests/unit/libstore/machines.cc index 4107ba655..2307f4d62 100644 --- a/tests/unit/libstore/machines.cc +++ b/tests/unit/libstore/machines.cc @@ -1,8 +1,9 @@ #include "machines.hh" -#include "globals.hh" #include "file-system.hh" #include "util.hh" +#include "tests/characterization.hh" + #include #include @@ -14,23 +15,13 @@ using testing::SizeIs; using namespace nix; -class Environment : public ::testing::Environment { - public: - void SetUp() override { settings.thisSystem = "TEST_ARCH-TEST_OS"; } -}; - -testing::Environment* const foo_env = - testing::AddGlobalTestEnvironment(new Environment); - TEST(machines, getMachinesWithEmptyBuilders) { - settings.builders = ""; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, ""); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesUriOnly) { - settings.builders = "nix@scratchy.labs.cs.uu.nl"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({"TEST_ARCH-TEST_OS"}, "nix@scratchy.labs.cs.uu.nl"); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); @@ -43,8 +34,7 @@ TEST(machines, getMachinesUriOnly) { } TEST(machines, getMachinesDefaults) { - settings.builders = "nix@scratchy.labs.cs.uu.nl - - - - - - -"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({"TEST_ARCH-TEST_OS"}, "nix@scratchy.labs.cs.uu.nl - - - - - - -"); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); @@ -68,26 +58,24 @@ MATCHER_P(AuthorityMatches, authority, "") { } TEST(machines, getMachinesWithNewLineSeparator) { - settings.builders = "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl"); ASSERT_THAT(actual, SizeIs(2)); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithSemicolonSeparator) { - settings.builders = "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl"); EXPECT_THAT(actual, SizeIs(2)); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { - settings.builders = "nix@scratchy.labs.cs.uu.nl i686-linux " - "/home/nix/.ssh/id_scratchy_auto 8 3 kvm " - "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, + "nix@scratchy.labs.cs.uu.nl i686-linux " + "/home/nix/.ssh/id_scratchy_auto 8 3 kvm " + "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); @@ -101,11 +89,10 @@ TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { TEST(machines, getMachinesWithCorrectCompleteSingleBuilderWithTabColumnDelimiter) { - settings.builders = + auto actual = Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl\ti686-linux\t/home/nix/.ssh/" "id_scratchy_auto\t8\t3\tkvm\tbenchmark\tSSH+HOST+PUBLIC+" - "KEY+BASE64+ENCODED=="; - Machines actual = getMachines(); + "KEY+BASE64+ENCODED=="); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); @@ -118,10 +105,10 @@ TEST(machines, } TEST(machines, getMachinesWithMultiOptions) { - settings.builders = "nix@scratchy.labs.cs.uu.nl Arch1,Arch2 - - - " - "SupportedFeature1,SupportedFeature2 " - "MandatoryFeature1,MandatoryFeature2"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, + "nix@scratchy.labs.cs.uu.nl Arch1,Arch2 - - - " + "SupportedFeature1,SupportedFeature2 " + "MandatoryFeature1,MandatoryFeature2"); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("Arch1", "Arch2"))); @@ -130,24 +117,28 @@ TEST(machines, getMachinesWithMultiOptions) { } TEST(machines, getMachinesWithIncorrectFormat) { - settings.builders = "nix@scratchy.labs.cs.uu.nl - - eight"; - EXPECT_THROW(getMachines(), FormatError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - -1"; - EXPECT_THROW(getMachines(), FormatError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 three"; - EXPECT_THROW(getMachines(), FormatError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 -3"; - EXPECT_THROW(getMachines(), UsageError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 3 - - BAD_BASE64"; - EXPECT_THROW(getMachines(), FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - eight"), + FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - -1"), + FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - 8 three"), + FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - 8 -3"), + UsageError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - 8 3 - - BAD_BASE64"), + FormatError); } TEST(machines, getMachinesWithCorrectFileReference) { - auto path = absPath("tests/unit/libstore/test-data/machines.valid"); + auto path = absPath(getUnitTestData() + "/machines/valid"); ASSERT_TRUE(pathExists(path)); - settings.builders = std::string("@") + path; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "@" + path); ASSERT_THAT(actual, SizeIs(3)); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); @@ -158,18 +149,17 @@ TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) { auto path = "/dev/null"; ASSERT_TRUE(pathExists(path)); - settings.builders = std::string("@") + path; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, std::string{"@"} + path); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesWithIncorrectFileReference) { - settings.builders = std::string("@") + absPath("/not/a/file"); - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "@" + absPath("/not/a/file")); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesWithCorrectFileReferenceToIncorrectFile) { - settings.builders = std::string("@") + absPath("tests/unit/libstore/test-data/machines.bad_format"); - EXPECT_THROW(getMachines(), FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "@" + absPath(getUnitTestData() + "/machines/bad_format")), + FormatError); } diff --git a/tests/unit/libutil-support/tests/characterization.hh b/tests/unit/libutil-support/tests/characterization.hh index 9d6c850f0..c2f686dbf 100644 --- a/tests/unit/libutil-support/tests/characterization.hh +++ b/tests/unit/libutil-support/tests/characterization.hh @@ -12,7 +12,7 @@ namespace nix { * The path to the unit test data directory. See the contributing guide * in the manual for further details. */ -static Path getUnitTestData() { +static inline Path getUnitTestData() { return getEnv("_NIX_TEST_UNIT_DATA").value(); } @@ -21,7 +21,7 @@ static Path getUnitTestData() { * against them. See the contributing guide in the manual for further * details. */ -static bool testAccept() { +static inline bool testAccept() { return getEnv("_NIX_TEST_ACCEPT") == "1"; } From 5f68e6d69fe734565cf64705d93a99546a88f97c Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Thu, 23 May 2024 03:54:35 -0700 Subject: [PATCH 142/910] Get max stack size in `setStackSize` to match Linux --- src/libutil/current-process.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index db3111560..9efb68d47 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -82,19 +82,23 @@ void setStackSize(size_t stackSize) } } #else + ULONG_PTR stackLow, stackHigh; + GetCurrentThreadStackLimits(&stackLow, &stackHigh); + ULONG maxStackSize = stackHigh - stackLow; ULONG currStackSize = 0; // This retrieves the current promised stack size SetThreadStackGuarantee(&currStackSize); if (currStackSize < stackSize) { savedStackSize = currStackSize; - ULONG newStackSize = stackSize; + ULONG newStackSize = std::min(static_cast(stackSize), maxStackSize); if (SetThreadStackGuarantee(&newStackSize) == 0) { logger->log( lvlError, HintFmt( - "Failed to increase stack size from %1% to %2%: %3%", + "Failed to increase stack size from %1% to %2% (maximum allowed stack size: %3%): %4%", savedStackSize, stackSize, + maxStackSize, std::to_string(GetLastError()) ).str() ); From 5384ceacc3fa2609d5b64ee005dee554656f8836 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 23 May 2024 11:28:25 -0400 Subject: [PATCH 143/910] Document field being initialized in `Machine` constructor --- src/libstore/machines.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index b4df658b1..20f24e883 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -168,14 +168,24 @@ static Machine parseBuilderLine(const std::set & defaultSystems, co if (!isSet(0)) throw FormatError("bad machine specification: store URL was not found at the first column of a row: '%s'", line); + // TODO use designated initializers, once C++ supports those with + // custom constructors. return { + // `storeUri` tokens[0], + // `systemTypes` isSet(1) ? tokenizeString>(tokens[1], ",") : defaultSystems, + // `sshKey` isSet(2) ? tokens[2] : "", + // `maxJobs` isSet(3) ? parseUnsignedIntField(3) : 1U, + // `speedFactor` isSet(4) ? parseFloatField(4) : 1.0f, + // `supportedFeatures` isSet(5) ? tokenizeString>(tokens[5], ",") : std::set{}, + // `mandatoryFeatures` isSet(6) ? tokenizeString>(tokens[6], ",") : std::set{}, + // `sshPublicHostKey` isSet(7) ? ensureBase64(7) : "" }; } From a942a34469b22491d381a517e86a17f3999a140a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:07:33 +0200 Subject: [PATCH 144/910] C API: Fix nix_c_primop_wrapper for strict initializers https://github.com/NixOS/nix/pull/10555 added a check requiring that output parameters always have an uninitialized Value as argument. Unfortunately the output parameter of the primop callback received a thunk instead. See the comment for implementation considerations. --- src/libexpr-c/nix_api_value.cc | 24 +++++++++++++++++--- tests/unit/libexpr/nix_api_expr.cc | 35 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 0366e5020..37e0012d1 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -73,10 +73,28 @@ static void nix_c_primop_wrapper( PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) { nix_c_context ctx; - f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &v); - /* TODO: In the future, this should throw different errors depending on the error code */ - if (ctx.last_err_code != NIX_OK) + + // v currently has a thunk, but the C API initializers require an uninitialized value. + // + // We can't destroy the thunk, because that makes it impossible to retry, + // which is needed for tryEval and for evaluation drivers that evaluate more + // than one value (e.g. an attrset with two derivations, both of which + // reference v). + // + // Instead we create a temporary value, and then assign the result to v. + // This does not give the primop definition access to the thunk, but that's + // ok because we don't see a need for this yet (e.g. inspecting thunks, + // or maybe something to make blackholes work better; we don't know). + nix::Value vTmp; + + f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &vTmp); + + if (ctx.last_err_code != NIX_OK) { + /* TODO: Throw different errors depending on the error code */ state.error("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow(); + } + + v = vTmp; } PrimOp * nix_alloc_primop( diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 0818f1cab..babc8c6a4 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -191,4 +191,39 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context) nix_realised_string_free(r); } +const char * SAMPLE_USER_DATA = "whatever"; + +static void primop_square(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ + assert(context); + assert(state); + assert(user_data == SAMPLE_USER_DATA); + auto i = nix_get_int(context, args[0]); + nix_init_int(context, ret, i * i); +} + +TEST_F(nix_api_expr_test, nix_expr_primop) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_square, 1, "square", nullptr, "square an integer", (void *) SAMPLE_USER_DATA); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, three, result); + assert_ctx_ok(); + + auto r = nix_get_int(ctx, result); + ASSERT_EQ(9, r); +} + } // namespace nixC From 88842270454281c3541296bea87b46d553b42c3a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:31:20 +0200 Subject: [PATCH 145/910] C API: Require initialized value from primop definition --- src/libexpr-c/nix_api_value.cc | 6 ++++++ tests/unit/libexpr/nix_api_expr.cc | 31 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 37e0012d1..043c6a568 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -94,6 +94,12 @@ static void nix_c_primop_wrapper( state.error("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow(); } + if (!vTmp.isValid()) { + state.error("Implementation error in custom function: return value was not initialized") + .atPos(pos) + .debugThrow(); + } + v = vTmp; } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index babc8c6a4..9d629b463 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -226,4 +226,35 @@ TEST_F(nix_api_expr_test, nix_expr_primop) ASSERT_EQ(9, r); } +static void +primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ +} + +TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_bad_no_return, 1, "badNoReturn", nullptr, "a broken primop", nullptr); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, three, result); + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_THAT( + ctx->last_err, + testing::Optional( + testing::HasSubstr("Implementation error in custom function: return value was not initialized"))); + ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badNoReturn"))); +} + } // namespace nixC From 8ef6efc1844a9782dbe265271020f6ffee571af0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:36:40 +0200 Subject: [PATCH 146/910] C API: Require non-thunk value from primop definition --- src/libexpr-c/nix_api_value.cc | 9 +++++++ tests/unit/libexpr/nix_api_expr.cc | 41 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 043c6a568..fa24b4494 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -100,6 +100,15 @@ static void nix_c_primop_wrapper( .debugThrow(); } + if (vTmp.type() == nix::nThunk) { + // We might allow this in the future if it makes sense for the evaluator + // e.g. implementing tail recursion by returning a thunk to the next + // "iteration". Until then, this is most likely a mistake or misunderstanding. + state.error("Implementation error in custom function: return value must not be a thunk") + .atPos(pos) + .debugThrow(); + } + v = vTmp; } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 9d629b463..e78f2bf61 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -257,4 +257,45 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badNoReturn"))); } +static void +primop_bad_return_thunk(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ + nix_init_apply(context, ret, args[0], args[1]); +} +TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_bad_return_thunk, 2, "badReturnThunk", nullptr, "a broken primop", nullptr); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * toString = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_expr_eval_from_string(ctx, state, "builtins.toString", ".", toString); + assert_ctx_ok(); + + Value * four = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, four, 4); + assert_ctx_ok(); + + Value * partial = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, toString, partial); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, partial, four, result); + + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_THAT( + ctx->last_err, + testing::Optional( + testing::HasSubstr("Implementation error in custom function: return value must not be a thunk"))); + ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badReturnThunk"))); +} } // namespace nixC From 4bc4fb40eac1f571ef94694a96a3bf4bf26ad22d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:53:33 +0200 Subject: [PATCH 147/910] C API: builtin -> custom function Not all primops will be in `builtins`. --- src/libexpr-c/nix_api_value.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index fa24b4494..978cf7f43 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -91,7 +91,7 @@ static void nix_c_primop_wrapper( if (ctx.last_err_code != NIX_OK) { /* TODO: Throw different errors depending on the error code */ - state.error("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow(); + state.error("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow(); } if (!vTmp.isValid()) { From ab106c5ca3326c939467a1334aeb78f78485503e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 19:20:04 +0200 Subject: [PATCH 148/910] C API: Test arity 2 primop --- tests/unit/libexpr/nix_api_expr.cc | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index e78f2bf61..17ba610e4 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -226,6 +226,65 @@ TEST_F(nix_api_expr_test, nix_expr_primop) ASSERT_EQ(9, r); } +static void primop_repeat(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ + assert(context); + assert(state); + assert(user_data == SAMPLE_USER_DATA); + + // Get the string to repeat + std::string s; + if (nix_get_string(context, args[0], OBSERVE_STRING(s)) != NIX_OK) + return; + + // Get the number of times to repeat + auto n = nix_get_int(context, args[1]); + if (nix_err_code(context) != NIX_OK) + return; + + // Repeat the string + std::string result; + for (int i = 0; i < n; ++i) + result += s; + + nix_init_string(context, ret, result.c_str()); +} + +TEST_F(nix_api_expr_test, nix_expr_primop_arity_2) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * hello = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_string(ctx, hello, "hello"); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * partial = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, hello, partial); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, partial, three, result); + assert_ctx_ok(); + + std::string r; + nix_get_string(ctx, result, OBSERVE_STRING(r)); + ASSERT_STREQ("hellohellohello", r.c_str()); +} + static void primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) { From 2497d1035182d0d9b209aee6cfcd1a2b2b2be55b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 19:55:32 +0200 Subject: [PATCH 149/910] C API: Add nix_value_call_multi, NIX_VALUE_CALL _multi can be implemented more efficiently. NIX_VALUE_CALL is a convenient way to invoke it. --- src/libexpr-c/nix_api_expr.cc | 11 +++++++ src/libexpr-c/nix_api_expr.h | 41 +++++++++++++++++++++++++ tests/unit/libexpr/nix_api_expr.cc | 49 +++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index a29c3425e..b86d745db 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -65,6 +65,17 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, V NIXC_CATCH_ERRS } +nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + state->state.callFunction(*(nix::Value *) fn, nargs, (nix::Value * *)args, *(nix::Value *) value, nix::noPos); + state->state.forceValue(*(nix::Value *) value, nix::noPos); + } + NIXC_CATCH_ERRS +} + nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value) { if (context) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 647b23569..b026ea70b 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -12,6 +12,7 @@ #include "nix_api_store.h" #include "nix_api_util.h" +#include #ifdef __cplusplus extern "C" { @@ -80,6 +81,46 @@ nix_err nix_expr_eval_from_string( */ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value); +/** + * @brief Calls a Nix function with multiple arguments. + * + * Technically these are functions that return functions. It is common for Nix + * functions to be curried, so this function is useful for calling them. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[in] fn The Nix function to call. + * @param[in] nargs The number of arguments. + * @param[in] args The arguments to pass to the function. + * @param[out] value The result of the function call. + * + * @see nix_value_call For the single argument primitive. + * @see NIX_VALUE_CALL For a macro that wraps this function for convenience. + */ +nix_err nix_value_call_multi( + nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value); + +/** + * @brief Calls a Nix function with multiple arguments. + * + * Technically these are functions that return functions. It is common for Nix + * functions to be curried, so this function is useful for calling them. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[out] value The result of the function call. + * @param[in] fn The Nix function to call. + * @param[in] args The arguments to pass to the function. + * + * @see nix_value_call_multi + */ +#define NIX_VALUE_CALL(context, state, value, fn, ...) \ + do { \ + Value * args_array[] = {__VA_ARGS__}; \ + size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ + nix_value_call_multi(context, state, fn, nargs, args_array, value); \ + } while (0) + /** * @brief Forces the evaluation of a Nix value. * diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 17ba610e4..fd7d043c0 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -250,7 +250,7 @@ static void primop_repeat(void * user_data, nix_c_context * context, EvalState * nix_init_string(context, ret, result.c_str()); } -TEST_F(nix_api_expr_test, nix_expr_primop_arity_2) +TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_multiple_calls) { PrimOp * primop = nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); @@ -285,6 +285,38 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2) ASSERT_STREQ("hellohellohello", r.c_str()); } +TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * hello = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_string(ctx, hello, "hello"); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + NIX_VALUE_CALL(ctx, state, result, primopValue, hello, three); + assert_ctx_ok(); + + std::string r; + nix_get_string(ctx, result, OBSERVE_STRING(r)); + assert_ctx_ok(); + + ASSERT_STREQ("hellohellohello", r.c_str()); +} + static void primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) { @@ -357,4 +389,19 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) testing::HasSubstr("Implementation error in custom function: return value must not be a thunk"))); ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badReturnThunk"))); } + +TEST_F(nix_api_expr_test, nix_value_call_multi_no_args) +{ + Value * n = nix_alloc_value(ctx, state); + nix_init_int(ctx, n, 3); + assert_ctx_ok(); + + Value * r = nix_alloc_value(ctx, state); + nix_value_call_multi(ctx, state, n, 0, nullptr, r); + assert_ctx_ok(); + + auto rInt = nix_get_int(ctx, r); + assert_ctx_ok(); + ASSERT_EQ(3, rInt); +} } // namespace nixC From 97c346329151916727a3dd154e4d088b2e16fd2d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 19:56:32 +0200 Subject: [PATCH 150/910] C API: Refactor: use NIX_VALUE_CALL --- tests/unit/libexpr/nix_api_expr.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index fd7d043c0..92a6a1175 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -373,14 +373,9 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) nix_init_int(ctx, four, 4); assert_ctx_ok(); - Value * partial = nix_alloc_value(ctx, state); - assert_ctx_ok(); - nix_value_call(ctx, state, primopValue, toString, partial); - assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); - nix_value_call(ctx, state, partial, four, result); + NIX_VALUE_CALL(ctx, state, result, primopValue, toString, four); ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); ASSERT_THAT( From 0b7da099d112cf4bebc49844405ab5556c1a1ea4 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Thu, 23 May 2024 03:55:25 -0700 Subject: [PATCH 151/910] Commit more stack size in some windows binaries This way we can commit the same amount of stack size (64 MB) without a conditional. Includes nix, libnixexpr-tests, libnixfetchers-tests, libnixstore-tests, libnixutil-tests. --- src/nix/local.mk | 5 +++++ src/nix/main.cc | 5 ----- tests/unit/libexpr/local.mk | 5 +++++ tests/unit/libfetchers/local.mk | 5 +++++ tests/unit/libstore/local.mk | 5 +++++ tests/unit/libutil/local.mk | 5 +++++ 6 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/nix/local.mk b/src/nix/local.mk index 305b0e9df..9883509fb 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -30,6 +30,11 @@ nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd nix_LDFLAGS = $(THREAD_LDFLAGS) $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) $(LOWDOWN_LIBS) +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + nix_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif + $(foreach name, \ nix-build nix-channel nix-collect-garbage nix-copy-closure nix-daemon nix-env nix-hash nix-instantiate nix-prefetch-url nix-shell nix-store, \ $(eval $(call install-symlink, nix, $(bindir)/$(name)))) diff --git a/src/nix/main.cc b/src/nix/main.cc index d3f3324ac..541d1a1b3 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -522,14 +522,9 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { -#ifndef _WIN32 // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); -#else - // Windows' default stack reservation is 1 MB, going over will fail - nix::setStackSize(1 * 1024 * 1024); -#endif return nix::handleExceptions(argv[0], [&]() { nix::mainWrapped(argc, argv); diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index c59191db4..09a7dfca1 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -38,3 +38,8 @@ libexpr-tests_LIBS = \ libexpr libexprc libfetchers libstore libstorec libutil libutilc libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk index e9f659fd7..d576d28f3 100644 --- a/tests/unit/libfetchers/local.mk +++ b/tests/unit/libfetchers/local.mk @@ -30,3 +30,8 @@ libfetchers-tests_LIBS = \ libfetchers libstore libutil libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk index b8f895fad..0af1d2622 100644 --- a/tests/unit/libstore/local.mk +++ b/tests/unit/libstore/local.mk @@ -31,3 +31,8 @@ libstore-tests_LIBS = \ libstore libstorec libutil libutilc libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk index 39b4c0782..b9bddc24d 100644 --- a/tests/unit/libutil/local.mk +++ b/tests/unit/libutil/local.mk @@ -27,6 +27,11 @@ libutil-tests_LIBS = libutil-test-support libutil libutilc libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif + check: $(d)/data/git/check-data.sh.test $(eval $(call run-test,$(d)/data/git/check-data.sh)) From 8b86f415c1a8565085e70475933e459a29d67283 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 24 May 2024 16:16:38 +0200 Subject: [PATCH 152/910] Add test for the evaluation cache --- tests/functional/flakes/eval-cache.sh | 22 ++++++++++++++++++++++ tests/functional/local.mk | 1 + 2 files changed, 23 insertions(+) create mode 100644 tests/functional/flakes/eval-cache.sh diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh new file mode 100644 index 000000000..90c7abd3c --- /dev/null +++ b/tests/functional/flakes/eval-cache.sh @@ -0,0 +1,22 @@ +source ./common.sh + +requireGit + +flake1Dir="$TEST_ROOT/eval-cache-flake" + +createGitRepo "$flake1Dir" "" + +cat >"$flake1Dir/flake.nix" <&1 | grepQuiet 'error: breaks' +expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 18b920f7d..9c279b49b 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -16,6 +16,7 @@ nix_tests = \ flakes/build-paths.sh \ flakes/flake-in-submodule.sh \ flakes/prefetch.sh \ + flakes/eval-cache.sh \ gc.sh \ nix-collect-garbage-d.sh \ remote-store.sh \ From 2c88930ef298f6804eb9c064ca918aef01fcd2e3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2024 19:34:07 +0200 Subject: [PATCH 153/910] AttrCursor: Remove forceErrors Instead, force evaluation of the original value only if we need to show the exception to the user. --- src/libexpr/eval-cache.cc | 40 +++++++++++++++++++++++++++------------ src/libexpr/eval-cache.hh | 25 +++++++++++++++++++----- src/libexpr/eval-error.cc | 1 - src/libexpr/eval-error.hh | 1 - src/nix/main.cc | 11 ++++++++++- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index d60967a14..a4080819c 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -7,6 +7,25 @@ namespace nix::eval_cache { +CachedEvalError::CachedEvalError(ref cursor, Symbol attr) + : EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr)) + , cursor(cursor), attr(attr) +{ } + +void CachedEvalError::force() +{ + auto & v = cursor->forceValue(); + + if (v.type() == nAttrs) { + auto a = v.attrs()->get(this->attr); + + state.forceValue(*a->value, a->pos); + } + + // Shouldn't happen. + throw EvalError(state, "evaluation of cached failed attribute '%s' unexpectedly succeeded", cursor->getAttrPathStr(attr)); +} + static const char * schema = R"sql( create table if not exists Attributes ( parent integer not null, @@ -470,7 +489,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]); } -std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErrors) +std::shared_ptr AttrCursor::maybeGetAttr(Symbol name) { if (root->db) { if (!cachedValue) @@ -487,12 +506,9 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro if (attr) { if (std::get_if(&attr->second)) return nullptr; - else if (std::get_if(&attr->second)) { - if (forceErrors) - debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name)); - else - throw CachedEvalError(root->state, "cached failure of attribute '%s'", getAttrPathStr(name)); - } else + else if (std::get_if(&attr->second)) + throw CachedEvalError(ref(shared_from_this()), name); + else return std::make_shared(root, std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); } @@ -537,9 +553,9 @@ std::shared_ptr AttrCursor::maybeGetAttr(std::string_view name) return maybeGetAttr(root->state.symbols.create(name)); } -ref AttrCursor::getAttr(Symbol name, bool forceErrors) +ref AttrCursor::getAttr(Symbol name) { - auto p = maybeGetAttr(name, forceErrors); + auto p = maybeGetAttr(name); if (!p) throw Error("attribute '%s' does not exist", getAttrPathStr(name)); return ref(p); @@ -550,11 +566,11 @@ ref AttrCursor::getAttr(std::string_view name) return getAttr(root->state.symbols.create(name)); } -OrSuggestions> AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force) +OrSuggestions> AttrCursor::findAlongAttrPath(const std::vector & attrPath) { auto res = shared_from_this(); for (auto & attr : attrPath) { - auto child = res->maybeGetAttr(attr, force); + auto child = res->maybeGetAttr(attr); if (!child) { auto suggestions = res->getSuggestionsForAttr(attr); return OrSuggestions>::failed(suggestions); @@ -751,7 +767,7 @@ bool AttrCursor::isDerivation() StorePath AttrCursor::forceDerivation() { - auto aDrvPath = getAttr(root->state.sDrvPath, true); + auto aDrvPath = getAttr(root->state.sDrvPath); auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { /* The eval cache contains 'drvPath', but the actual path has diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 46c4999c8..cac985829 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -10,14 +10,28 @@ namespace nix::eval_cache { -MakeError(CachedEvalError, EvalError); - struct AttrDb; class AttrCursor; +struct CachedEvalError : EvalError +{ + const ref cursor; + const Symbol attr; + + CachedEvalError(ref cursor, Symbol attr); + + /** + * Evaluate this attribute, which should result in a regular + * `EvalError` exception being thrown. + */ + [[noreturn]] + void force(); +}; + class EvalCache : public std::enable_shared_from_this { friend class AttrCursor; + friend class CachedEvalError; std::shared_ptr db; EvalState & state; @@ -73,6 +87,7 @@ typedef std::variant< class AttrCursor : public std::enable_shared_from_this { friend class EvalCache; + friend class CachedEvalError; ref root; typedef std::optional, Symbol>> Parent; @@ -102,11 +117,11 @@ public: Suggestions getSuggestionsForAttr(Symbol name); - std::shared_ptr maybeGetAttr(Symbol name, bool forceErrors = false); + std::shared_ptr maybeGetAttr(Symbol name); std::shared_ptr maybeGetAttr(std::string_view name); - ref getAttr(Symbol name, bool forceErrors = false); + ref getAttr(Symbol name); ref getAttr(std::string_view name); @@ -114,7 +129,7 @@ public: * Get an attribute along a chain of attrsets. Note that this does * not auto-call functors or functions. */ - OrSuggestions> findAlongAttrPath(const std::vector & attrPath, bool force = false); + OrSuggestions> findAlongAttrPath(const std::vector & attrPath); std::string getString(); diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 8db03610b..9e7f50093 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -99,7 +99,6 @@ template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; -template class EvalErrorBuilder; template class EvalErrorBuilder; } diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index 7e0cbe982..27407eb6e 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -43,7 +43,6 @@ MakeError(Abort, EvalError); MakeError(TypeError, EvalError); MakeError(UndefinedVarError, EvalError); MakeError(MissingArgumentError, EvalError); -MakeError(CachedEvalError, EvalError); MakeError(InfiniteRecursionError, EvalError); struct InvalidPathError : public EvalError diff --git a/src/nix/main.cc b/src/nix/main.cc index bc13a4df5..f20042948 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -18,6 +18,7 @@ #include "terminal.hh" #include "users.hh" #include "network-proxy.hh" +#include "eval-cache.hh" #include #include @@ -515,7 +516,15 @@ void mainWrapped(int argc, char * * argv) if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) { evalSettings.pureEval = false; } - args.command->second->run(); + + try { + args.command->second->run(); + } catch (eval_cache::CachedEvalError & e) { + /* Evaluate the original attribute that resulted in this + cached error so that we can show the original error to the + user. */ + e.force(); + } } } From eeb4c408670a6037bd4085fde654bf4a5945c4c7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2024 19:42:05 +0200 Subject: [PATCH 154/910] Typo --- src/nix/flake.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 78a8a55c3..6bf694e08 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1176,7 +1176,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON // If we don't recognize it, it's probably content return true; } catch (EvalError & e) { - // Some attrs may contain errors, eg. legacyPackages of + // Some attrs may contain errors, e.g. legacyPackages of // nixpkgs. We still want to recurse into it, instead of // skipping it at all. return true; From 2de4589e465f0ad7e78bed8fc15d065a99006be1 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Wed, 22 May 2024 18:26:01 +0200 Subject: [PATCH 155/910] libstore: remove unused copyPath function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source: https://git.lix.systems/lix-project/lix/commit/47523944c5f13250dd0eb9d56d5c6621f4722bea Signed-off-by: Jörg Thalheim --- src/libutil/archive.cc | 9 --------- src/libutil/archive.hh | 2 -- 2 files changed, 11 deletions(-) diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 04f777d00..d20936de4 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -315,13 +315,4 @@ void copyNAR(Source & source, Sink & sink) } -void copyPath(const Path & from, const Path & to) -{ - auto source = sinkToSource([&](Sink & sink) { - dumpPath(from, sink); - }); - restorePath(to, *source); -} - - } diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index 28c63bb85..bd70072ce 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -82,8 +82,6 @@ void restorePath(const Path & path, Source & source); */ void copyNAR(Source & source, Sink & sink); -void copyPath(const Path & from, const Path & to); - inline constexpr std::string_view narVersionMagic1 = "nix-archive-1"; From f97da4b11c8d38f3bb002407076bf56acba8675b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sat, 25 May 2024 23:06:56 +0200 Subject: [PATCH 156/910] libutil/source-accessor: custom error if source does not exist This allows better error handling by catching this error in particular. --- src/libutil/source-accessor.cc | 2 +- src/libutil/source-accessor.hh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libutil/source-accessor.cc b/src/libutil/source-accessor.cc index 66093d2cc..e797951c7 100644 --- a/src/libutil/source-accessor.cc +++ b/src/libutil/source-accessor.cc @@ -53,7 +53,7 @@ SourceAccessor::Stat SourceAccessor::lstat(const CanonPath & path) if (auto st = maybeLstat(path)) return *st; else - throw Error("path '%s' does not exist", showPath(path)); + throw FileNotFound("path '%s' does not exist", showPath(path)); } void SourceAccessor::setPathDisplay(std::string displayPrefix, std::string displaySuffix) diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index d7fb0af5f..cc8db01f5 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -29,6 +29,8 @@ enum class SymlinkResolution { Full, }; +MakeError(FileNotFound, Error); + /** * A read-only filesystem abstraction. This is used by the Nix * evaluator and elsewhere for accessing sources in various From ffe6ba69d6a3a290e56a2891d05c91059f4b9e3a Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 23 May 2024 02:52:54 +0200 Subject: [PATCH 157/910] repl: do not crash when tab-completing import errors File not found while importing is not currently caught by the tab-completion handler. Original bug report: https://git.lix.systems/lix-project/lix/issues/340 Fix has been adapted from https://gerrit.lix.systems/c/lix/+/1189 Example crash: $ cat /tmp/foo.nix { someImport = import ./this_file_does_not_exist; } $ ./src/nix/nix repl --file /tmp/foo.nix warning: unknown experimental feature 'repl-flake' Nix 2.23.0pre20240517_dirty Type :? for help. Loading installable ''... Added 1 variables. nix-repl> someImport. --- src/libcmd/repl.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a069dd52c..e5d3f6f15 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -304,6 +304,8 @@ StringSet NixRepl::completePrefix(const std::string & prefix) // Quietly ignore evaluation errors. } catch (BadURL & e) { // Quietly ignore BadURL flake-related errors. + } catch (FileNotFound & e) { + // Quietly ignore non-existent file beeing `import`-ed. } } From eeb89c28b026ed96baf2479491c071e527b92b58 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 00:18:58 -0400 Subject: [PATCH 158/910] Worker proto use proper serialiser for `BuildMode` Do this instead of an unchecked cast I redid this to use the serialisation framework (including a unit test), but I am keeping the reference to credit Jade for spotting the issue. Change-Id: Icf6af7935e8f139bef36b40ad475e973aa48855c (adapted from commit 2a7a824d83dc5fb33326b8b89625685f283a743b) Co-Authored-By: Jade Lovelace --- src/libstore/daemon.cc | 6 ++-- src/libstore/store-api.hh | 2 +- src/libstore/worker-protocol.cc | 28 ++++++++++++++++++ src/libstore/worker-protocol.hh | 3 ++ .../data/worker-protocol/build-mode.bin | Bin 0 -> 24 bytes tests/unit/libstore/worker-protocol.cc | 11 +++++++ 6 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 tests/unit/libstore/data/worker-protocol/build-mode.bin diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 47d6d5541..eb6a4e690 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -531,7 +531,7 @@ static void performOp(TunnelLogger * logger, ref store, auto drvs = WorkerProto::Serialise::read(*store, rconn); BuildMode mode = bmNormal; if (GET_PROTOCOL_MINOR(clientVersion) >= 15) { - mode = (BuildMode) readInt(from); + mode = WorkerProto::Serialise::read(*store, rconn); /* Repairing is not atomic, so disallowed for "untrusted" clients. @@ -555,7 +555,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::BuildPathsWithResults: { auto drvs = WorkerProto::Serialise::read(*store, rconn); BuildMode mode = bmNormal; - mode = (BuildMode) readInt(from); + mode = WorkerProto::Serialise::read(*store, rconn); /* Repairing is not atomic, so disallowed for "untrusted" clients. @@ -586,7 +586,7 @@ static void performOp(TunnelLogger * logger, ref store, * correctly. */ readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath)); - BuildMode buildMode = (BuildMode) readInt(from); + auto buildMode = WorkerProto::Serialise::read(*store, rconn); logger->startWork(); auto drvType = drv.type(); diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 430d9a5ab..15712458c 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -92,7 +92,7 @@ enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true }; const uint32_t exportMagic = 0x4558494e; -enum BuildMode { bmNormal, bmRepair, bmCheck }; +enum BuildMode : uint8_t { bmNormal, bmRepair, bmCheck }; enum TrustedFlag : bool { NotTrusted = false, Trusted = true }; struct BuildResult; diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index a50259d24..ed6367c38 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -14,6 +14,34 @@ namespace nix { /* protocol-specific definitions */ +BuildMode WorkerProto::Serialise::read(const StoreDirConfig & store, WorkerProto::ReadConn conn) +{ + auto temp = readNum(conn.from); + switch (temp) { + case bmNormal: return bmNormal; + case bmRepair: return bmRepair; + case bmCheck: return bmCheck; + default: throw Error("Invalid build mode"); + } +} + +void WorkerProto::Serialise::write(const StoreDirConfig & store, WorkerProto::WriteConn conn, const BuildMode & buildMode) +{ + switch (buildMode) { + case bmNormal: + conn.to << uint8_t{bmNormal}; + break; + case bmRepair: + conn.to << uint8_t{bmRepair}; + break; + case bmCheck: + conn.to << uint8_t{bmCheck}; + break; + default: + assert(false); + }; +} + std::optional WorkerProto::Serialise>::read(const StoreDirConfig & store, WorkerProto::ReadConn conn) { auto temp = readNum(conn.from); diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 91d277b77..34607b7af 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -35,6 +35,7 @@ struct BuildResult; struct KeyedBuildResult; struct ValidPathInfo; struct UnkeyedValidPathInfo; +enum BuildMode : uint8_t; enum TrustedFlag : bool; @@ -215,6 +216,8 @@ DECLARE_WORKER_SERIALISER(ValidPathInfo); template<> DECLARE_WORKER_SERIALISER(UnkeyedValidPathInfo); template<> +DECLARE_WORKER_SERIALISER(BuildMode); +template<> DECLARE_WORKER_SERIALISER(std::optional); template<> DECLARE_WORKER_SERIALISER(std::optional); diff --git a/tests/unit/libstore/data/worker-protocol/build-mode.bin b/tests/unit/libstore/data/worker-protocol/build-mode.bin new file mode 100644 index 0000000000000000000000000000000000000000..51b239409bc815c19b00cb97f62996fb782f24c3 GIT binary patch literal 24 PcmZQzfB;4)%>< { + bmNormal, + bmRepair, + bmCheck, + })) + VERSIONED_CHARACTERIZATION_TEST( WorkerProtoTest, optionalTrustedFlag, From 8ebd99c74ee7e69be7cbe4b83f845ad51b0a8e61 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 00:21:34 -0400 Subject: [PATCH 159/910] Back in enum values for `BuildMode` serializer We don't want to rely on how C assigns numbers for enums in the wire format. Sure, this is totally determined by the ABI, but it obscures the code and makes it harder to safely change the enum definition (should we need to) without accidentally breaking the wire format. --- src/libstore/worker-protocol.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index ed6367c38..4d397ed21 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -18,9 +18,9 @@ BuildMode WorkerProto::Serialise::read(const StoreDirConfig & store, { auto temp = readNum(conn.from); switch (temp) { - case bmNormal: return bmNormal; - case bmRepair: return bmRepair; - case bmCheck: return bmCheck; + case 0: return bmNormal; + case 1: return bmRepair; + case 2: return bmCheck; default: throw Error("Invalid build mode"); } } @@ -29,13 +29,13 @@ void WorkerProto::Serialise::write(const StoreDirConfig & store, Work { switch (buildMode) { case bmNormal: - conn.to << uint8_t{bmNormal}; + conn.to << uint8_t{0}; break; case bmRepair: - conn.to << uint8_t{bmRepair}; + conn.to << uint8_t{1}; break; case bmCheck: - conn.to << uint8_t{bmCheck}; + conn.to << uint8_t{2}; break; default: assert(false); From f71b4da0b3ed994f2bfc3764df6f524ebe72c4da Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 23 May 2024 16:40:05 -0400 Subject: [PATCH 160/910] Factor our connection code for worker proto like serve proto This increases test coverage, and gets the worker protocol ready to be used by Hydra. Why don't we just try to use the store interface in Hydra? Well, the problem is that the store interface works on connection pools, with each opreation getting potentially a different connection, but the way temp roots work requires that we keep one logical "transaction" (temp root session) using the same connection. The longer-term solution probably is making connections themselves implement the store interface, but that is something that builds on this, so I feel OK that this is not churn in the wrong direction. Fixes #9584 --- src/libstore/daemon.cc | 38 +-- src/libstore/legacy-ssh-store.cc | 52 ++-- src/libstore/remote-store-connection.hh | 82 +---- src/libstore/remote-store.cc | 214 ++----------- ...l-impl.cc => serve-protocol-connection.cc} | 68 +++-- src/libstore/serve-protocol-connection.hh | 108 +++++++ src/libstore/serve-protocol-impl.hh | 101 ------- src/libstore/worker-protocol-connection.cc | 280 ++++++++++++++++++ src/libstore/worker-protocol-connection.hh | 187 ++++++++++++ src/libstore/worker-protocol.cc | 31 ++ src/libstore/worker-protocol.hh | 42 +++ src/nix-store/nix-store.cc | 1 + .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin 0 -> 32 bytes .../client-handshake-info_1_35.bin | Bin 0 -> 48 bytes .../worker-protocol/handshake-to-client.bin | Bin 0 -> 16 bytes tests/unit/libstore/serve-protocol.cc | 1 + tests/unit/libstore/worker-protocol.cc | 153 +++++++++- 18 files changed, 906 insertions(+), 452 deletions(-) rename src/libstore/{serve-protocol-impl.cc => serve-protocol-connection.cc} (51%) create mode 100644 src/libstore/serve-protocol-connection.hh create mode 100644 src/libstore/worker-protocol-connection.cc create mode 100644 src/libstore/worker-protocol-connection.hh create mode 100644 tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin create mode 100644 tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin create mode 100644 tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin create mode 100644 tests/unit/libstore/data/worker-protocol/handshake-to-client.bin diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 47d6d5541..2add9ae12 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1,6 +1,7 @@ #include "daemon.hh" #include "signals.hh" #include "worker-protocol.hh" +#include "worker-protocol-connection.hh" #include "worker-protocol-impl.hh" #include "build-result.hh" #include "store-api.hh" @@ -1026,11 +1027,9 @@ void processConnection( #endif /* Exchange the greeting. */ - unsigned int magic = readInt(from); - if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch"); - to << WORKER_MAGIC_2 << PROTOCOL_VERSION; - to.flush(); - WorkerProto::Version clientVersion = readInt(from); + WorkerProto::Version clientVersion = + WorkerProto::BasicServerConnection::handshake( + to, from, PROTOCOL_VERSION); if (clientVersion < 0x10a) throw Error("the Nix client version is too old"); @@ -1048,29 +1047,20 @@ void processConnection( printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); }); - if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) { - // Obsolete CPU affinity. - readInt(from); - } + WorkerProto::BasicServerConnection conn { + .to = to, + .from = from, + .clientVersion = clientVersion, + }; - if (GET_PROTOCOL_MINOR(clientVersion) >= 11) - readInt(from); // obsolete reserveSpace - - if (GET_PROTOCOL_MINOR(clientVersion) >= 33) - to << nixVersion; - - if (GET_PROTOCOL_MINOR(clientVersion) >= 35) { + conn.postHandshake(*store, { + .daemonNixVersion = nixVersion, // We and the underlying store both need to trust the client for // it to be trusted. - auto temp = trusted + .remoteTrustsUs = trusted ? store->isTrustedClient() - : std::optional { NotTrusted }; - WorkerProto::WriteConn wconn { - .to = to, - .version = clientVersion, - }; - WorkerProto::write(*store, wconn, temp); - } + : std::optional { NotTrusted }, + }); /* Send startup error messages to the client. */ tunnelLogger->startWork(); diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index c75d50ade..146049922 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -4,6 +4,7 @@ #include "pool.hh" #include "remote-store.hh" #include "serve-protocol.hh" +#include "serve-protocol-connection.hh" #include "serve-protocol-impl.hh" #include "build-result.hh" #include "store-api.hh" @@ -86,7 +87,7 @@ ref LegacySSHStore::openConnection() conn->sshConn->in.close(); { NullSink nullSink; - conn->from.drainInto(nullSink); + tee.drainInto(nullSink); } throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'", host, chomp(saved.s)); @@ -168,41 +169,38 @@ void LegacySSHStore::addToStore(const ValidPathInfo & info, Source & source, } conn->to.flush(); + if (readInt(conn->from) != 1) + throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), host); + } else { - conn->to - << ServeProto::Command::ImportPaths - << 1; - try { - copyNAR(source, conn->to); - } catch (...) { - conn->good = false; - throw; - } - conn->to - << exportMagic - << printStorePath(info.path); - ServeProto::write(*this, *conn, info.references); - conn->to - << (info.deriver ? printStorePath(*info.deriver) : "") - << 0 - << 0; - conn->to.flush(); + conn->importPaths(*this, [&](Sink & sink) { + try { + copyNAR(source, sink); + } catch (...) { + conn->good = false; + throw; + } + sink + << exportMagic + << printStorePath(info.path); + ServeProto::write(*this, *conn, info.references); + sink + << (info.deriver ? printStorePath(*info.deriver) : "") + << 0 + << 0; + }); } - - if (readInt(conn->from) != 1) - throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), host); } void LegacySSHStore::narFromPath(const StorePath & path, Sink & sink) { auto conn(connections->get()); - - conn->to << ServeProto::Command::DumpStorePath << printStorePath(path); - conn->to.flush(); - copyNAR(conn->from, sink); + conn->narFromPath(*this, path, [&](auto & source) { + copyNAR(source, sink); + }); } @@ -226,7 +224,7 @@ BuildResult LegacySSHStore::buildDerivation(const StorePath & drvPath, const Bas conn->putBuildDerivationRequest(*this, drvPath, drv, buildSettings()); - return ServeProto::Serialise::read(*this, *conn); + return conn->getBuildDerivationResponse(*this); } diff --git a/src/libstore/remote-store-connection.hh b/src/libstore/remote-store-connection.hh index 44328b06b..405120ee9 100644 --- a/src/libstore/remote-store-connection.hh +++ b/src/libstore/remote-store-connection.hh @@ -3,6 +3,7 @@ #include "remote-store.hh" #include "worker-protocol.hh" +#include "worker-protocol-connection.hh" #include "pool.hh" namespace nix { @@ -14,90 +15,13 @@ namespace nix { * Contains `Source` and `Sink` for actual communication, along with * other information learned when negotiating the connection. */ -struct RemoteStore::Connection +struct RemoteStore::Connection : WorkerProto::BasicClientConnection, + WorkerProto::ClientHandshakeInfo { - /** - * Send with this. - */ - FdSink to; - - /** - * Receive with this. - */ - FdSource from; - - /** - * Worker protocol version used for the connection. - * - * Despite its name, I think it is actually the maximum version both - * sides support. (If the maximum doesn't exist, we would fail to - * establish a connection and produce a value of this type.) - */ - WorkerProto::Version daemonVersion; - - /** - * Whether the remote side trusts us or not. - * - * 3 values: "yes", "no", or `std::nullopt` for "unknown". - * - * Note that the "remote side" might not be just the end daemon, but - * also an intermediary forwarder that can make its own trusting - * decisions. This would be the intersection of all their trust - * decisions, since it takes only one link in the chain to start - * denying operations. - */ - std::optional remoteTrustsUs; - - /** - * The version of the Nix daemon that is processing our requests. - * - * Do note, it may or may not communicating with another daemon, - * rather than being an "end" `LocalStore` or similar. - */ - std::optional daemonNixVersion; - /** * Time this connection was established. */ std::chrono::time_point startTime; - - /** - * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the - * factored out worker protocol searlizers with a - * `RemoteStore::Connection`. - * - * The worker protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::ReadConn () - { - return WorkerProto::ReadConn { - .from = from, - .version = daemonVersion, - }; - } - - /** - * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the - * factored out worker protocol searlizers with a - * `RemoteStore::Connection`. - * - * The worker protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::WriteConn () - { - return WorkerProto::WriteConn { - .to = to, - .version = daemonVersion, - }; - } - - virtual ~Connection(); - - virtual void closeWrite() = 0; - - std::exception_ptr processStderr(Sink * sink = 0, Source * source = 0, bool flush = true); }; /** diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 09196481b..d6efc14f9 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -69,50 +69,26 @@ void RemoteStore::initConnection(Connection & conn) /* Send the magic greeting, check for the reply. */ try { conn.from.endOfFileError = "Nix daemon disconnected unexpectedly (maybe it crashed?)"; - conn.to << WORKER_MAGIC_1; - conn.to.flush(); + StringSink saved; + TeeSource tee(conn.from, saved); try { - TeeSource tee(conn.from, saved); - unsigned int magic = readInt(tee); - if (magic != WORKER_MAGIC_2) - throw Error("protocol mismatch"); + conn.daemonVersion = WorkerProto::BasicClientConnection::handshake( + conn.to, tee, PROTOCOL_VERSION); } catch (SerialisationError & e) { /* In case the other side is waiting for our input, close it. */ conn.closeWrite(); - auto msg = conn.from.drain(); - throw Error("protocol mismatch, got '%s'", chomp(saved.s + msg)); + { + NullSink nullSink; + tee.drainInto(nullSink); + } + throw Error("protocol mismatch, got '%s'", chomp(saved.s)); } - conn.from >> conn.daemonVersion; - if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) - throw Error("Nix daemon protocol version not supported"); - if (GET_PROTOCOL_MINOR(conn.daemonVersion) < 10) - throw Error("the Nix daemon version is too old"); - conn.to << PROTOCOL_VERSION; + static_cast(conn) = conn.postHandshake(*this); - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 14) { - // Obsolete CPU affinity. - conn.to << 0; - } - - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 11) - conn.to << false; // obsolete reserveSpace - - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 33) { - conn.to.flush(); - conn.daemonNixVersion = readString(conn.from); - } - - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 35) { - conn.remoteTrustsUs = WorkerProto::Serialise>::read(*this, conn); - } else { - // We don't know the answer; protocol to old. - conn.remoteTrustsUs = std::nullopt; - } - - auto ex = conn.processStderr(); + auto ex = conn.processStderrReturn(); if (ex) std::rethrow_exception(ex); } catch (Error & e) { @@ -158,7 +134,7 @@ void RemoteStore::setOptions(Connection & conn) conn.to << i.first << i.second.value; } - auto ex = conn.processStderr(); + auto ex = conn.processStderrReturn(); if (ex) std::rethrow_exception(ex); } @@ -173,28 +149,7 @@ RemoteStore::ConnectionHandle::~ConnectionHandle() void RemoteStore::ConnectionHandle::processStderr(Sink * sink, Source * source, bool flush) { - auto ex = handle->processStderr(sink, source, flush); - if (ex) { - daemonException = true; - try { - std::rethrow_exception(ex); - } catch (const Error & e) { - // Nix versions before #4628 did not have an adequate behavior for reporting that the derivation format was upgraded. - // To avoid having to add compatibility logic in many places, we expect to catch almost all occurrences of the - // old incomprehensible error here, so that we can explain to users what's going on when their daemon is - // older than #4628 (2023). - if (experimentalFeatureSettings.isEnabled(Xp::DynamicDerivations) && - GET_PROTOCOL_MINOR(handle->daemonVersion) <= 35) - { - auto m = e.msg(); - if (m.find("parsing derivation") != std::string::npos && - m.find("expected string") != std::string::npos && - m.find("Derive([") != std::string::npos) - throw Error("%s, this might be because the daemon is too old to understand dependencies on dynamic derivations. Check to see if the raw derivation is in the form '%s'", std::move(m), "DrvWithVersion(..)"); - } - throw; - } - } + handle->processStderr(&daemonException, sink, source, flush); } @@ -226,13 +181,7 @@ StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, Substitute if (isValidPath(i)) res.insert(i); return res; } else { - conn->to << WorkerProto::Op::QueryValidPaths; - WorkerProto::write(*this, *conn, paths); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 27) { - conn->to << maybeSubstitute; - } - conn.processStderr(); - return WorkerProto::Serialise::read(*this, *conn); + return conn->queryValidPaths(*this, &conn.daemonException, paths, maybeSubstitute); } } @@ -322,22 +271,10 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path, std::shared_ptr info; { auto conn(getConnection()); - conn->to << WorkerProto::Op::QueryPathInfo << printStorePath(path); - try { - conn.processStderr(); - } catch (Error & e) { - // Ugly backwards compatibility hack. - if (e.msg().find("is not valid") != std::string::npos) - throw InvalidPath(std::move(e.info())); - throw; - } - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) { - bool valid; conn->from >> valid; - if (!valid) throw InvalidPath("path '%s' is not valid", printStorePath(path)); - } info = std::make_shared( StorePath{path}, - WorkerProto::Serialise::read(*this, *conn)); + conn->queryPathInfo(*this, &conn.daemonException, path)); + } callback(std::move(info)); } catch (...) { callback.rethrow(); } @@ -542,8 +479,6 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, auto conn(getConnection()); if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 18) { - conn->to << WorkerProto::Op::ImportPaths; - auto source2 = sinkToSource([&](Sink & sink) { sink << 1 // == path follows ; @@ -558,11 +493,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, << 0 // == no path follows ; }); - - conn.processStderr(0, source2.get()); - - auto importedPaths = WorkerProto::Serialise::read(*this, *conn); - assert(importedPaths.size() <= 1); + conn->importPaths(*this, &conn.daemonException, *source2); } else { @@ -807,9 +738,7 @@ BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicD BuildMode buildMode) { auto conn(getConnection()); - conn->to << WorkerProto::Op::BuildDerivation << printStorePath(drvPath); - writeDerivation(conn->to, *this, drv); - conn->to << buildMode; + conn->putBuildDerivationRequest(*this, &conn.daemonException, drvPath, drv, buildMode); conn.processStderr(); return WorkerProto::Serialise::read(*this, *conn); } @@ -827,9 +756,7 @@ void RemoteStore::ensurePath(const StorePath & path) void RemoteStore::addTempRoot(const StorePath & path) { auto conn(getConnection()); - conn->to << WorkerProto::Op::AddTempRoot << printStorePath(path); - conn.processStderr(); - readInt(conn->from); + conn->addTempRoot(*this, &conn.daemonException, path); } @@ -969,22 +896,12 @@ void RemoteStore::flushBadConnections() connections->flushBad(); } - -RemoteStore::Connection::~Connection() -{ - try { - to.flush(); - } catch (...) { - ignoreException(); - } -} - void RemoteStore::narFromPath(const StorePath & path, Sink & sink) { - auto conn(connections->get()); - conn->to << WorkerProto::Op::NarFromPath << printStorePath(path); - conn->processStderr(); - copyNAR(conn->from, sink); + auto conn(getConnection()); + conn->narFromPath(*this, &conn.daemonException, path, [&](Source & source) { + copyNAR(conn->from, sink); + }); } ref RemoteStore::getFSAccessor(bool requireValidPath) @@ -992,91 +909,6 @@ ref RemoteStore::getFSAccessor(bool requireValidPath) return make_ref(ref(shared_from_this())); } -static Logger::Fields readFields(Source & from) -{ - Logger::Fields fields; - size_t size = readInt(from); - for (size_t n = 0; n < size; n++) { - auto type = (decltype(Logger::Field::type)) readInt(from); - if (type == Logger::Field::tInt) - fields.push_back(readNum(from)); - else if (type == Logger::Field::tString) - fields.push_back(readString(from)); - else - throw Error("got unsupported field type %x from Nix daemon", (int) type); - } - return fields; -} - - -std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source * source, bool flush) -{ - if (flush) - to.flush(); - - while (true) { - - auto msg = readNum(from); - - if (msg == STDERR_WRITE) { - auto s = readString(from); - if (!sink) throw Error("no sink"); - (*sink)(s); - } - - else if (msg == STDERR_READ) { - if (!source) throw Error("no source"); - size_t len = readNum(from); - auto buf = std::make_unique(len); - writeString({(const char *) buf.get(), source->read(buf.get(), len)}, to); - to.flush(); - } - - else if (msg == STDERR_ERROR) { - if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) { - return std::make_exception_ptr(readError(from)); - } else { - auto error = readString(from); - unsigned int status = readInt(from); - return std::make_exception_ptr(Error(status, error)); - } - } - - else if (msg == STDERR_NEXT) - printError(chomp(readString(from))); - - else if (msg == STDERR_START_ACTIVITY) { - auto act = readNum(from); - auto lvl = (Verbosity) readInt(from); - auto type = (ActivityType) readInt(from); - auto s = readString(from); - auto fields = readFields(from); - auto parent = readNum(from); - logger->startActivity(act, lvl, type, s, fields, parent); - } - - else if (msg == STDERR_STOP_ACTIVITY) { - auto act = readNum(from); - logger->stopActivity(act); - } - - else if (msg == STDERR_RESULT) { - auto act = readNum(from); - auto type = (ResultType) readInt(from); - auto fields = readFields(from); - logger->result(act, type, fields); - } - - else if (msg == STDERR_LAST) - break; - - else - throw Error("got unknown message type %x from Nix daemon", msg); - } - - return nullptr; -} - void RemoteStore::ConnectionHandle::withFramedSink(std::function fun) { (*this)->to.flush(); diff --git a/src/libstore/serve-protocol-impl.cc b/src/libstore/serve-protocol-connection.cc similarity index 51% rename from src/libstore/serve-protocol-impl.cc rename to src/libstore/serve-protocol-connection.cc index 5e1d3f1e8..07379999b 100644 --- a/src/libstore/serve-protocol-impl.cc +++ b/src/libstore/serve-protocol-connection.cc @@ -1,3 +1,4 @@ +#include "serve-protocol-connection.hh" #include "serve-protocol-impl.hh" #include "build-result.hh" #include "derivations.hh" @@ -5,10 +6,7 @@ namespace nix { ServeProto::Version ServeProto::BasicClientConnection::handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion, - std::string_view host) + BufferedSink & to, Source & from, ServeProto::Version localVersion, std::string_view host) { to << SERVE_MAGIC_1 << localVersion; to.flush(); @@ -22,39 +20,30 @@ ServeProto::Version ServeProto::BasicClientConnection::handshake( return std::min(remoteVersion, localVersion); } -ServeProto::Version ServeProto::BasicServerConnection::handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion) +ServeProto::Version +ServeProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, ServeProto::Version localVersion) { unsigned int magic = readInt(from); - if (magic != SERVE_MAGIC_1) throw Error("protocol mismatch"); + if (magic != SERVE_MAGIC_1) + throw Error("protocol mismatch"); to << SERVE_MAGIC_2 << localVersion; to.flush(); auto remoteVersion = readInt(from); return std::min(remoteVersion, localVersion); } - StorePathSet ServeProto::BasicClientConnection::queryValidPaths( - const Store & store, - bool lock, const StorePathSet & paths, - SubstituteFlag maybeSubstitute) + const StoreDirConfig & store, bool lock, const StorePathSet & paths, SubstituteFlag maybeSubstitute) { - to - << ServeProto::Command::QueryValidPaths - << lock - << maybeSubstitute; + to << ServeProto::Command::QueryValidPaths << lock << maybeSubstitute; write(store, *this, paths); to.flush(); return Serialise::read(store, *this); } - -std::map ServeProto::BasicClientConnection::queryPathInfos( - const Store & store, - const StorePathSet & paths) +std::map +ServeProto::BasicClientConnection::queryPathInfos(const StoreDirConfig & store, const StorePathSet & paths) { std::map infos; @@ -64,7 +53,8 @@ std::map ServeProto::BasicClientConnection::que while (true) { auto storePathS = readString(from); - if (storePathS == "") break; + if (storePathS == "") + break; auto storePath = store.parseStorePath(storePathS); assert(paths.count(storePath) == 1); @@ -75,15 +65,13 @@ std::map ServeProto::BasicClientConnection::que return infos; } - void ServeProto::BasicClientConnection::putBuildDerivationRequest( - const Store & store, - const StorePath & drvPath, const BasicDerivation & drv, + const StoreDirConfig & store, + const StorePath & drvPath, + const BasicDerivation & drv, const ServeProto::BuildOptions & options) { - to - << ServeProto::Command::BuildDerivation - << store.printStorePath(drvPath); + to << ServeProto::Command::BuildDerivation << store.printStorePath(drvPath); writeDerivation(to, store, drv); ServeProto::write(store, *this, options); @@ -91,4 +79,28 @@ void ServeProto::BasicClientConnection::putBuildDerivationRequest( to.flush(); } +BuildResult ServeProto::BasicClientConnection::getBuildDerivationResponse(const StoreDirConfig & store) +{ + return ServeProto::Serialise::read(store, *this); +} + +void ServeProto::BasicClientConnection::narFromPath( + const StoreDirConfig & store, const StorePath & path, std::function fun) +{ + to << ServeProto::Command::DumpStorePath << store.printStorePath(path); + to.flush(); + + fun(from); +} + +void ServeProto::BasicClientConnection::importPaths(const StoreDirConfig & store, std::function fun) +{ + to << ServeProto::Command::ImportPaths; + fun(to); + to.flush(); + + if (readInt(from) != 1) + throw Error("remote machine failed to import closure"); +} + } diff --git a/src/libstore/serve-protocol-connection.hh b/src/libstore/serve-protocol-connection.hh new file mode 100644 index 000000000..73bf71443 --- /dev/null +++ b/src/libstore/serve-protocol-connection.hh @@ -0,0 +1,108 @@ +#pragma once +///@file + +#include "serve-protocol.hh" +#include "store-api.hh" + +namespace nix { + +struct ServeProto::BasicClientConnection +{ + FdSink to; + FdSource from; + ServeProto::Version remoteVersion; + + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + * + * @param host Just used to add context to thrown exceptions. + */ + static ServeProto::Version + handshake(BufferedSink & to, Source & from, ServeProto::Version localVersion, std::string_view host); + + /** + * Coercion to `ServeProto::ReadConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator ServeProto::ReadConn() + { + return ServeProto::ReadConn{ + .from = from, + .version = remoteVersion, + }; + } + + /** + * Coercion to `ServeProto::WriteConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator ServeProto::WriteConn() + { + return ServeProto::WriteConn{ + .to = to, + .version = remoteVersion, + }; + } + + StorePathSet queryValidPaths( + const StoreDirConfig & remoteStore, bool lock, const StorePathSet & paths, SubstituteFlag maybeSubstitute); + + std::map queryPathInfos(const StoreDirConfig & store, const StorePathSet & paths); + ; + + void putBuildDerivationRequest( + const StoreDirConfig & store, + const StorePath & drvPath, + const BasicDerivation & drv, + const ServeProto::BuildOptions & options); + + /** + * Get the response, must be paired with + * `putBuildDerivationRequest`. + */ + BuildResult getBuildDerivationResponse(const StoreDirConfig & store); + + void narFromPath(const StoreDirConfig & store, const StorePath & path, std::function fun); + + void importPaths(const StoreDirConfig & store, std::function fun); +}; + +struct ServeProto::BasicServerConnection +{ + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + */ + static ServeProto::Version handshake(BufferedSink & to, Source & from, ServeProto::Version localVersion); +}; + +} diff --git a/src/libstore/serve-protocol-impl.hh b/src/libstore/serve-protocol-impl.hh index 1d9c79ef0..67bc5dc6e 100644 --- a/src/libstore/serve-protocol-impl.hh +++ b/src/libstore/serve-protocol-impl.hh @@ -57,105 +57,4 @@ struct ServeProto::Serialise /* protocol-specific templates */ -struct ServeProto::BasicClientConnection -{ - FdSink to; - FdSource from; - ServeProto::Version remoteVersion; - - /** - * Establishes connection, negotiating version. - * - * @return the version provided by the other side of the - * connection. - * - * @param to Taken by reference to allow for various error handling - * mechanisms. - * - * @param from Taken by reference to allow for various error - * handling mechanisms. - * - * @param localVersion Our version which is sent over - * - * @param host Just used to add context to thrown exceptions. - */ - static ServeProto::Version handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion, - std::string_view host); - - /** - * Coercion to `ServeProto::ReadConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator ServeProto::ReadConn () - { - return ServeProto::ReadConn { - .from = from, - .version = remoteVersion, - }; - } - - /** - * Coercion to `ServeProto::WriteConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator ServeProto::WriteConn () - { - return ServeProto::WriteConn { - .to = to, - .version = remoteVersion, - }; - } - - StorePathSet queryValidPaths( - const Store & remoteStore, - bool lock, const StorePathSet & paths, - SubstituteFlag maybeSubstitute); - - std::map queryPathInfos( - const Store & store, - const StorePathSet & paths); - - /** - * Just the request half, because Hydra may do other things between - * issuing the request and reading the `BuildResult` response. - */ - void putBuildDerivationRequest( - const Store & store, - const StorePath & drvPath, const BasicDerivation & drv, - const ServeProto::BuildOptions & options); -}; - -struct ServeProto::BasicServerConnection -{ - /** - * Establishes connection, negotiating version. - * - * @return the version provided by the other side of the - * connection. - * - * @param to Taken by reference to allow for various error handling - * mechanisms. - * - * @param from Taken by reference to allow for various error - * handling mechanisms. - * - * @param localVersion Our version which is sent over - */ - static ServeProto::Version handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion); -}; - } diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc new file mode 100644 index 000000000..072bae8da --- /dev/null +++ b/src/libstore/worker-protocol-connection.cc @@ -0,0 +1,280 @@ +#include "worker-protocol-connection.hh" +#include "worker-protocol-impl.hh" +#include "build-result.hh" +#include "derivations.hh" + +namespace nix { + +WorkerProto::BasicClientConnection::~BasicClientConnection() +{ + try { + to.flush(); + } catch (...) { + ignoreException(); + } +} + +static Logger::Fields readFields(Source & from) +{ + Logger::Fields fields; + size_t size = readInt(from); + for (size_t n = 0; n < size; n++) { + auto type = (decltype(Logger::Field::type)) readInt(from); + if (type == Logger::Field::tInt) + fields.push_back(readNum(from)); + else if (type == Logger::Field::tString) + fields.push_back(readString(from)); + else + throw Error("got unsupported field type %x from Nix daemon", (int) type); + } + return fields; +} + +std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink * sink, Source * source, bool flush) +{ + if (flush) + to.flush(); + + std::exception_ptr ex; + + while (true) { + + auto msg = readNum(from); + + if (msg == STDERR_WRITE) { + auto s = readString(from); + if (!sink) + throw Error("no sink"); + (*sink)(s); + } + + else if (msg == STDERR_READ) { + if (!source) + throw Error("no source"); + size_t len = readNum(from); + auto buf = std::make_unique(len); + writeString({(const char *) buf.get(), source->read(buf.get(), len)}, to); + to.flush(); + } + + else if (msg == STDERR_ERROR) { + if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) { + ex = std::make_exception_ptr(readError(from)); + } else { + auto error = readString(from); + unsigned int status = readInt(from); + ex = std::make_exception_ptr(Error(status, error)); + } + break; + } + + else if (msg == STDERR_NEXT) + printError(chomp(readString(from))); + + else if (msg == STDERR_START_ACTIVITY) { + auto act = readNum(from); + auto lvl = (Verbosity) readInt(from); + auto type = (ActivityType) readInt(from); + auto s = readString(from); + auto fields = readFields(from); + auto parent = readNum(from); + logger->startActivity(act, lvl, type, s, fields, parent); + } + + else if (msg == STDERR_STOP_ACTIVITY) { + auto act = readNum(from); + logger->stopActivity(act); + } + + else if (msg == STDERR_RESULT) { + auto act = readNum(from); + auto type = (ResultType) readInt(from); + auto fields = readFields(from); + logger->result(act, type, fields); + } + + else if (msg == STDERR_LAST) + break; + + else + throw Error("got unknown message type %x from Nix daemon", msg); + } + + if (!ex) { + return ex; + } else { + try { + std::rethrow_exception(ex); + } catch (const Error & e) { + // Nix versions before #4628 did not have an adequate + // behavior for reporting that the derivation format was + // upgraded. To avoid having to add compatibility logic in + // many places, we expect to catch almost all occurrences of + // the old incomprehensible error here, so that we can + // explain to users what's going on when their daemon is + // older than #4628 (2023). + if (experimentalFeatureSettings.isEnabled(Xp::DynamicDerivations) + && GET_PROTOCOL_MINOR(daemonVersion) <= 35) { + auto m = e.msg(); + if (m.find("parsing derivation") != std::string::npos && m.find("expected string") != std::string::npos + && m.find("Derive([") != std::string::npos) + return std::make_exception_ptr(Error( + "%s, this might be because the daemon is too old to understand dependencies on dynamic derivations. Check to see if the raw derivation is in the form '%s'", + std::move(m), + "Drv WithVersion(..)")); + } + return std::current_exception(); + } + } +} + +void WorkerProto::BasicClientConnection::processStderr(bool * daemonException, Sink * sink, Source * source, bool flush) +{ + auto ex = processStderrReturn(sink, source, flush); + if (ex) { + *daemonException = true; + std::rethrow_exception(ex); + } +} + +WorkerProto::Version +WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +{ + to << WORKER_MAGIC_1 << localVersion; + to.flush(); + + unsigned int magic = readInt(from); + if (magic != WORKER_MAGIC_2) + throw Error("nix-daemon protocol mismatch from"); + auto daemonVersion = readInt(from); + + if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) + throw Error("Nix daemon protocol version not supported"); + if (GET_PROTOCOL_MINOR(daemonVersion) < 10) + throw Error("the Nix daemon version is too old"); + to << localVersion; + + return std::min(daemonVersion, localVersion); +} + +WorkerProto::Version +WorkerProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +{ + unsigned int magic = readInt(from); + if (magic != WORKER_MAGIC_1) + throw Error("protocol mismatch"); + to << WORKER_MAGIC_2 << localVersion; + to.flush(); + auto clientVersion = readInt(from); + return std::min(clientVersion, localVersion); +} + +WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandshake(const StoreDirConfig & store) +{ + WorkerProto::ClientHandshakeInfo res; + + if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) { + // Obsolete CPU affinity. + to << 0; + } + + if (GET_PROTOCOL_MINOR(daemonVersion) >= 11) + to << false; // obsolete reserveSpace + + if (GET_PROTOCOL_MINOR(daemonVersion) >= 33) + to.flush(); + + return WorkerProto::Serialise::read(store, *this); +} + +void WorkerProto::BasicServerConnection::postHandshake(const StoreDirConfig & store, const ClientHandshakeInfo & info) +{ + if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) { + // Obsolete CPU affinity. + readInt(from); + } + + if (GET_PROTOCOL_MINOR(clientVersion) >= 11) + readInt(from); // obsolete reserveSpace + + WorkerProto::write(store, *this, info); +} + +UnkeyedValidPathInfo WorkerProto::BasicClientConnection::queryPathInfo( + const StoreDirConfig & store, bool * daemonException, const StorePath & path) +{ + to << WorkerProto::Op::QueryPathInfo << store.printStorePath(path); + try { + processStderr(daemonException); + } catch (Error & e) { + // Ugly backwards compatibility hack. + if (e.msg().find("is not valid") != std::string::npos) + throw InvalidPath(std::move(e.info())); + throw; + } + if (GET_PROTOCOL_MINOR(daemonVersion) >= 17) { + bool valid; + from >> valid; + if (!valid) + throw InvalidPath("path '%s' is not valid", store.printStorePath(path)); + } + return WorkerProto::Serialise::read(store, *this); +} + +StorePathSet WorkerProto::BasicClientConnection::queryValidPaths( + const StoreDirConfig & store, bool * daemonException, const StorePathSet & paths, SubstituteFlag maybeSubstitute) +{ + assert(GET_PROTOCOL_MINOR(daemonVersion) >= 12); + to << WorkerProto::Op::QueryValidPaths; + WorkerProto::write(store, *this, paths); + if (GET_PROTOCOL_MINOR(daemonVersion) >= 27) { + to << maybeSubstitute; + } + processStderr(daemonException); + return WorkerProto::Serialise::read(store, *this); +} + +void WorkerProto::BasicClientConnection::addTempRoot( + const StoreDirConfig & store, bool * daemonException, const StorePath & path) +{ + to << WorkerProto::Op::AddTempRoot << store.printStorePath(path); + processStderr(daemonException); + readInt(from); +} + +void WorkerProto::BasicClientConnection::putBuildDerivationRequest( + const StoreDirConfig & store, + bool * daemonException, + const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode) +{ + to << WorkerProto::Op::BuildDerivation << store.printStorePath(drvPath); + writeDerivation(to, store, drv); + to << buildMode; +} + +BuildResult +WorkerProto::BasicClientConnection::getBuildDerivationResponse(const StoreDirConfig & store, bool * daemonException) +{ + return WorkerProto::Serialise::read(store, *this); +} + +void WorkerProto::BasicClientConnection::narFromPath( + const StoreDirConfig & store, bool * daemonException, const StorePath & path, std::function fun) +{ + to << WorkerProto::Op::NarFromPath << store.printStorePath(path); + processStderr(daemonException); + + fun(from); +} + +void WorkerProto::BasicClientConnection::importPaths( + const StoreDirConfig & store, bool * daemonException, Source & source) +{ + to << WorkerProto::Op::ImportPaths; + processStderr(daemonException, 0, &source); + auto importedPaths = WorkerProto::Serialise::read(store, *this); + assert(importedPaths.size() <= importedPaths.size()); +} +} diff --git a/src/libstore/worker-protocol-connection.hh b/src/libstore/worker-protocol-connection.hh new file mode 100644 index 000000000..9dd723fd0 --- /dev/null +++ b/src/libstore/worker-protocol-connection.hh @@ -0,0 +1,187 @@ +#pragma once +///@file + +#include "worker-protocol.hh" +#include "store-api.hh" + +namespace nix { + +struct WorkerProto::BasicClientConnection +{ + /** + * Send with this. + */ + FdSink to; + + /** + * Receive with this. + */ + FdSource from; + + /** + * Worker protocol version used for the connection. + * + * Despite its name, it is actually the maximum version both + * sides support. (If the maximum doesn't exist, we would fail to + * establish a connection and produce a value of this type.) + */ + WorkerProto::Version daemonVersion; + + /** + * Flush to direction + */ + virtual ~BasicClientConnection(); + + virtual void closeWrite() = 0; + + std::exception_ptr processStderrReturn(Sink * sink = 0, Source * source = 0, bool flush = true); + + void processStderr(bool * daemonException, Sink * sink = 0, Source * source = 0, bool flush = true); + + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + */ + static Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + + /** + * After calling handshake, must call this to exchange some basic + * information abou the connection. + */ + ClientHandshakeInfo postHandshake(const StoreDirConfig & store); + + /** + * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::ReadConn() + { + return WorkerProto::ReadConn{ + .from = from, + .version = daemonVersion, + }; + } + + /** + * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::WriteConn() + { + return WorkerProto::WriteConn{ + .to = to, + .version = daemonVersion, + }; + } + + void addTempRoot(const StoreDirConfig & remoteStore, bool * daemonException, const StorePath & path); + + StorePathSet queryValidPaths( + const StoreDirConfig & remoteStore, + bool * daemonException, + const StorePathSet & paths, + SubstituteFlag maybeSubstitute); + + UnkeyedValidPathInfo queryPathInfo(const StoreDirConfig & store, bool * daemonException, const StorePath & path); + + void putBuildDerivationRequest( + const StoreDirConfig & store, + bool * daemonException, + const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode); + + /** + * Get the response, must be paired with + * `putBuildDerivationRequest`. + */ + BuildResult getBuildDerivationResponse(const StoreDirConfig & store, bool * daemonException); + + void narFromPath( + const StoreDirConfig & store, + bool * daemonException, + const StorePath & path, + std::function fun); + + void importPaths(const StoreDirConfig & store, bool * daemonException, Source & source); +}; + +struct WorkerProto::BasicServerConnection +{ + /** + * Send with this. + */ + FdSink & to; + + /** + * Receive with this. + */ + FdSource & from; + + /** + * Worker protocol version used for the connection. + * + * Despite its name, it is actually the maximum version both + * sides support. (If the maximum doesn't exist, we would fail to + * establish a connection and produce a value of this type.) + */ + WorkerProto::Version clientVersion; + + operator WorkerProto::ReadConn() + { + return WorkerProto::ReadConn{ + .from = from, + .version = clientVersion, + }; + } + + operator WorkerProto::WriteConn() + { + return WorkerProto::WriteConn{ + .to = to, + .version = clientVersion, + }; + } + + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + */ + static WorkerProto::Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + + /** + * After calling handshake, must call this to exchange some basic + * information abou the connection. + */ + void postHandshake(const StoreDirConfig & store, const ClientHandshakeInfo & info); +}; + +} diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index a50259d24..c61540bd6 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -222,4 +222,35 @@ void WorkerProto::Serialise::write(const StoreDirConfig & } } + +WorkerProto::ClientHandshakeInfo WorkerProto::Serialise::read(const StoreDirConfig & store, ReadConn conn) +{ + WorkerProto::ClientHandshakeInfo res; + + if (GET_PROTOCOL_MINOR(conn.version) >= 33) { + res.daemonNixVersion = readString(conn.from); + } + + if (GET_PROTOCOL_MINOR(conn.version) >= 35) { + res.remoteTrustsUs = WorkerProto::Serialise>::read(store, conn); + } else { + // We don't know the answer; protocol to old. + res.remoteTrustsUs = std::nullopt; + } + + return res; +} + +void WorkerProto::Serialise::write(const StoreDirConfig & store, WriteConn conn, const WorkerProto::ClientHandshakeInfo & info) +{ + if (GET_PROTOCOL_MINOR(conn.version) >= 33) { + assert(info.daemonNixVersion); + conn.to << *info.daemonNixVersion; + } + + if (GET_PROTOCOL_MINOR(conn.version) >= 35) { + WorkerProto::write(store, conn, info.remoteTrustsUs); + } +} + } diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 91d277b77..db61574aa 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -76,6 +76,19 @@ struct WorkerProto Version version; }; + /** + * Stripped down serialization logic suitable for sharing with Hydra. + * + * @todo remove once Hydra uses Store abstraction consistently. + */ + struct BasicClientConnection; + struct BasicServerConnection; + + /** + * Extra information provided as part of protocol negotation. + */ + struct ClientHandshakeInfo; + /** * Data type for canonical pairs of serialisers for the worker protocol. * @@ -166,6 +179,33 @@ enum struct WorkerProto::Op : uint64_t AddPermRoot = 47, }; +struct WorkerProto::ClientHandshakeInfo +{ + /** + * The version of the Nix daemon that is processing our requests +. + * + * Do note, it may or may not communicating with another daemon, + * rather than being an "end" `LocalStore` or similar. + */ + std::optional daemonNixVersion; + + /** + * Whether the remote side trusts us or not. + * + * 3 values: "yes", "no", or `std::nullopt` for "unknown". + * + * Note that the "remote side" might not be just the end daemon, but + * also an intermediary forwarder that can make its own trusting + * decisions. This would be the intersection of all their trust + * decisions, since it takes only one link in the chain to start + * denying operations. + */ + std::optional remoteTrustsUs; + + bool operator == (const ClientHandshakeInfo &) const = default; +}; + /** * Convenience for sending operation codes. * @@ -218,6 +258,8 @@ template<> DECLARE_WORKER_SERIALISER(std::optional); template<> DECLARE_WORKER_SERIALISER(std::optional); +template<> +DECLARE_WORKER_SERIALISER(WorkerProto::ClientHandshakeInfo); template DECLARE_WORKER_SERIALISER(std::vector); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index b23d99ad6..6d028e0a7 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -7,6 +7,7 @@ #include "local-fs-store.hh" #include "log-store.hh" #include "serve-protocol.hh" +#include "serve-protocol-connection.hh" #include "serve-protocol-impl.hh" #include "shared.hh" #include "graphml.hh" diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin new file mode 100644 index 0000000000000000000000000000000000000000..96c6efafc26106c56194eb973f82406c0e66c297 GIT binary patch literal 32 WcmZQ(fPl38d@zF<%1=rx0Z79*4o07R|=g8%>k literal 0 HcmV?d00001 diff --git a/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin b/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin new file mode 100644 index 0000000000000000000000000000000000000000..bee94fbe53ae24cb60bfa2d506031389c71b43ac GIT binary patch literal 16 Tcmd1LtVm%10xm`n$-n>r86p9| literal 0 HcmV?d00001 diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index 61dc15528..ebf0c52b0 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -6,6 +6,7 @@ #include "serve-protocol.hh" #include "serve-protocol-impl.hh" +#include "serve-protocol-connection.hh" #include "build-result.hh" #include "file-descriptor.hh" #include "tests/protocol.hh" diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 2b2e559a9..853d56b6a 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -4,6 +4,7 @@ #include #include "worker-protocol.hh" +#include "worker-protocol-connection.hh" #include "worker-protocol-impl.hh" #include "derived-path.hh" #include "build-result.hh" @@ -18,9 +19,9 @@ struct WorkerProtoTest : VersionedProtoTest { /** * For serializers that don't care about the minimum version, we - * used the oldest one: 1.0. + * used the oldest one: 1.10. */ - WorkerProto::Version defaultVersion = 1 << 8 | 0; + WorkerProto::Version defaultVersion = 1 << 8 | 10; }; @@ -591,4 +592,152 @@ VERSIONED_CHARACTERIZATION_TEST( }, })) +VERSIONED_CHARACTERIZATION_TEST( + WorkerProtoTest, + clientHandshakeInfo_1_30, + "client-handshake-info_1_30", + 1 << 8 | 30, + (std::tuple { + {}, + })) + +VERSIONED_CHARACTERIZATION_TEST( + WorkerProtoTest, + clientHandshakeInfo_1_33, + "client-handshake-info_1_33", + 1 << 8 | 33, + (std::tuple { + { + .daemonNixVersion = std::optional { "foo" }, + }, + { + .daemonNixVersion = std::optional { "bar" }, + }, + })) + +VERSIONED_CHARACTERIZATION_TEST( + WorkerProtoTest, + clientHandshakeInfo_1_35, + "client-handshake-info_1_35", + 1 << 8 | 35, + (std::tuple { + { + .daemonNixVersion = std::optional { "foo" }, + .remoteTrustsUs = std::optional { NotTrusted }, + }, + { + .daemonNixVersion = std::optional { "bar" }, + .remoteTrustsUs = std::optional { Trusted }, + }, + })) + +TEST_F(WorkerProtoTest, handshake_log) +{ + CharacterizationTest::writeTest("handshake-to-client", [&]() -> std::string { + StringSink toClientLog; + + Pipe toClient, toServer; + toClient.create(); + toServer.create(); + + WorkerProto::Version clientResult; + + auto thread = std::thread([&]() { + FdSink out { toServer.writeSide.get() }; + FdSource in0 { toClient.readSide.get() }; + TeeSource in { in0, toClientLog }; + clientResult = WorkerProto::BasicClientConnection::handshake( + out, in, defaultVersion); + }); + + { + FdSink out { toClient.writeSide.get() }; + FdSource in { toServer.readSide.get() }; + WorkerProto::BasicServerConnection::handshake( + out, in, defaultVersion); + }; + + thread.join(); + + return std::move(toClientLog.s); + }); +} + +/// Has to be a `BufferedSink` for handshake. +struct NullBufferedSink : BufferedSink { + void writeUnbuffered(std::string_view data) override { } +}; + +TEST_F(WorkerProtoTest, handshake_client_replay) +{ + CharacterizationTest::readTest("handshake-to-client", [&](std::string toClientLog) { + NullBufferedSink nullSink; + + StringSource in { toClientLog }; + auto clientResult = WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion); + + EXPECT_EQ(clientResult, defaultVersion); + }); +} + +TEST_F(WorkerProtoTest, handshake_client_truncated_replay_throws) +{ + CharacterizationTest::readTest("handshake-to-client", [&](std::string toClientLog) { + for (size_t len = 0; len < toClientLog.size(); ++len) { + NullBufferedSink nullSink; + StringSource in { + // truncate + toClientLog.substr(0, len) + }; + if (len < 8) { + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + EndOfFile); + } else { + // Not sure why cannot keep on checking for `EndOfFile`. + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + Error); + } + } + }); +} + +TEST_F(WorkerProtoTest, handshake_client_corrupted_throws) +{ + CharacterizationTest::readTest("handshake-to-client", [&](const std::string toClientLog) { + for (size_t idx = 0; idx < toClientLog.size(); ++idx) { + // corrupt a copy + std::string toClientLogCorrupt = toClientLog; + toClientLogCorrupt[idx] *= 4; + ++toClientLogCorrupt[idx]; + + NullBufferedSink nullSink; + StringSource in { toClientLogCorrupt }; + + if (idx < 4 || idx == 9) { + // magic bytes don't match + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + Error); + } else if (idx < 8 || idx >= 12) { + // Number out of bounds + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + SerialisationError); + } else { + auto ver = WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion); + // `std::min` of this and the other version saves us + EXPECT_EQ(ver, defaultVersion); + } + } + }); +} + } From ebfada36a1d07159141bcb0fb60e7a4f934d448b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 27 May 2024 09:58:49 +0200 Subject: [PATCH 161/910] Add repl completion test --- flake.nix | 1 + tests/repl-completion.nix | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/repl-completion.nix diff --git a/flake.nix b/flake.nix index 987f25305..6356eedc0 100644 --- a/flake.nix +++ b/flake.nix @@ -393,6 +393,7 @@ in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } '' LANG=C.UTF-8 ${pkgs.changelog-d-nix}/bin/changelog-d ${./doc/manual/rl-next} >$out ''; + repl-completion = nixpkgsFor.${system}.native.callPackage ./tests/repl-completion.nix { }; } // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) { dockerImage = self.hydraJobs.dockerImage.${system}; } // (lib.optionalAttrs (!(builtins.elem system linux32BitSystems))) { diff --git a/tests/repl-completion.nix b/tests/repl-completion.nix new file mode 100644 index 000000000..3ba198a98 --- /dev/null +++ b/tests/repl-completion.nix @@ -0,0 +1,40 @@ +{ runCommand, nix, expect }: + +# We only use expect when necessary, e.g. for testing tab completion in nix repl. +# See also tests/functional/repl.sh + +runCommand "repl-completion" { + nativeBuildInputs = [ + expect + nix + ]; + expectScript = '' + # Regression https://github.com/NixOS/nix/pull/10778 + spawn nix repl --offline --extra-experimental-features nix-command + expect "nix-repl>" + send "foo = import ./does-not-exist.nix\n" + expect "nix-repl>" + send "foo.\t" + expect { + "nix-repl>" { + puts "Got another prompt. Good." + } + eof { + puts "Got EOF. Bad." + exit 1 + } + } + exit 0 + ''; + passAsFile = [ "expectScript" ]; +} +'' + export NIX_STORE=$TMPDIR/store + export NIX_STATE_DIR=$TMPDIR/state + export HOME=$TMPDIR/home + mkdir $HOME + + nix-store --init + expect $expectScriptPath + touch $out +'' \ No newline at end of file From e7ea5591a25f2f28be15ca5bf8768fb460b4a663 Mon Sep 17 00:00:00 2001 From: Martin Joerg Date: Mon, 27 May 2024 15:56:52 +0200 Subject: [PATCH 162/910] fix typos --- doc/manual/src/language/operators.md | 2 +- src/nix/flake-metadata.md | 4 ++-- src/nix/hash.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 311887e96..9e5ab52a2 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -141,7 +141,7 @@ The result is a string. Update [attribute set] *attrset1* with names and values from *attrset2*. -The returned attribute set will have of all the attributes in *attrset1* and *attrset2*. +The returned attribute set will have all of the attributes in *attrset1* and *attrset2*. If an attribute name is present in both, the attribute value from the latter is taken. [Update]: #update diff --git a/src/nix/flake-metadata.md b/src/nix/flake-metadata.md index 5a009409b..adfd3dc96 100644 --- a/src/nix/flake-metadata.md +++ b/src/nix/flake-metadata.md @@ -2,10 +2,10 @@ R""( # Examples -* Show what `nixpkgs` resolves to: +* Show what `dwarffs` resolves to: ```console - # nix flake metadata nixpkgs + # nix flake metadata dwarffs Resolved URL: github:edolstra/dwarffs Locked URL: github:edolstra/dwarffs/f691e2c991e75edb22836f1dbe632c40324215c5 Description: A filesystem that fetches DWARF debug info from the Internet on demand diff --git a/src/nix/hash.cc b/src/nix/hash.cc index f969886ea..f57b224d2 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -181,7 +181,7 @@ struct CmdToBase : Command void run() override { - warn("The old format conversion sub commands of `nix hash` where deprecated in favor of `nix hash convert`."); + warn("The old format conversion sub commands of `nix hash` were deprecated in favor of `nix hash convert`."); for (auto s : args) logger->cout(Hash::parseAny(s, hashAlgo).to_string(hashFormat, hashFormat == HashFormat::SRI)); } From 3e9c3738d32bb4b9fbea0ad067b095b94cd4f907 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 23 May 2024 12:14:53 -0400 Subject: [PATCH 163/910] Create `CommonSSHStoreConfig::createSSHMaster` By moving `host` to the config, we can do a lot further cleanups and dedups. This anticipates a world where we always go `StoreReference` -> `*StoreConfig` -> `Store*` rather than skipping the middle step too. Progress on #10766 Progress on https://github.com/NixOS/hydra/issues/1164 --- src/libstore/legacy-ssh-store.cc | 19 +++------------ src/libstore/legacy-ssh-store.hh | 9 ------- src/libstore/ssh-store-config.cc | 21 +++++++++++++++- src/libstore/ssh-store-config.hh | 22 ++++++++++++----- src/libstore/ssh-store.cc | 42 +++++++++++++++----------------- src/libstore/ssh.cc | 2 +- src/libstore/ssh.hh | 4 +-- 7 files changed, 61 insertions(+), 58 deletions(-) diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index c75d50ade..9db5ce532 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -32,32 +32,19 @@ LegacySSHStore::LegacySSHStore( std::string_view scheme, std::string_view host, const Params & params) - : LegacySSHStore{scheme, LegacySSHStoreConfig::extractConnStr(scheme, host), params} -{ -} - -LegacySSHStore::LegacySSHStore( - std::string_view scheme, - std::string host, - const Params & params) : StoreConfig(params) - , CommonSSHStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) , LegacySSHStoreConfig(params) , Store(params) - , host(host) , connections(make_ref>( std::max(1, (int) maxConnections), [this]() { return openConnection(); }, [](const ref & r) { return r->good; } )) - , master( - host, - sshKey.get(), - sshPublicHostKey.get(), + , master(createSSHMaster( // Use SSH master only if using more than 1 connection. connections->capacity() > 1, - compress, - logFD) + logFD)) { } diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index 81872996a..6c3c95080 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -33,8 +33,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor struct Connection; - std::string host; - ref> connections; SSHMaster master; @@ -46,13 +44,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor std::string_view host, const Params & params); -private: - LegacySSHStore( - std::string_view scheme, - std::string host, - const Params & params); -public: - ref openConnection(); std::string getUri() override; diff --git a/src/libstore/ssh-store-config.cc b/src/libstore/ssh-store-config.cc index 742354cce..e81a94874 100644 --- a/src/libstore/ssh-store-config.cc +++ b/src/libstore/ssh-store-config.cc @@ -1,10 +1,11 @@ #include #include "ssh-store-config.hh" +#include "ssh.hh" namespace nix { -std::string CommonSSHStoreConfig::extractConnStr(std::string_view scheme, std::string_view _connStr) +static std::string extractConnStr(std::string_view scheme, std::string_view _connStr) { if (_connStr.empty()) throw UsageError("`%s` store requires a valid SSH host as the authority part in Store URI", scheme); @@ -21,4 +22,22 @@ std::string CommonSSHStoreConfig::extractConnStr(std::string_view scheme, std::s return connStr; } +CommonSSHStoreConfig::CommonSSHStoreConfig(std::string_view scheme, std::string_view host, const Params & params) + : StoreConfig(params) + , host(extractConnStr(scheme, host)) +{ +} + +SSHMaster CommonSSHStoreConfig::createSSHMaster(bool useMaster, Descriptor logFD) +{ + return { + host, + sshKey.get(), + sshPublicHostKey.get(), + useMaster, + compress, + logFD, + }; +} + } diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/ssh-store-config.hh index 09f9f23a7..5deb6f4c9 100644 --- a/src/libstore/ssh-store-config.hh +++ b/src/libstore/ssh-store-config.hh @@ -5,10 +5,14 @@ namespace nix { +class SSHMaster; + struct CommonSSHStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + CommonSSHStoreConfig(std::string_view scheme, std::string_view host, const Params & params); + const Setting sshKey{this, "", "ssh-key", "Path to the SSH private key used to authenticate to the remote machine."}; @@ -30,9 +34,7 @@ struct CommonSSHStoreConfig : virtual StoreConfig * RFC2732, but also pure addresses. The latter one is needed here to * connect to a remote store via SSH (it's possible to do e.g. `ssh root@::1`). * - * This function now ensures that a usable connection string is available: - * - * - If the store to be opened is not an SSH store, nothing will be done. + * When initialized, the following adjustments are made: * * - If the URL looks like `root@[::1]` (which is allowed by the URL parser and probably * needed to pass further flags), it @@ -44,9 +46,17 @@ struct CommonSSHStoreConfig : virtual StoreConfig * * Will throw an error if `connStr` is empty too. */ - static std::string extractConnStr( - std::string_view scheme, - std::string_view connStr); + std::string host; + + /** + * Small wrapper around `SSHMaster::SSHMaster` that gets most + * arguments from this configuration. + * + * See that constructor for details on the remaining two arguments. + */ + SSHMaster createSSHMaster( + bool useMaster, + Descriptor logFD = INVALID_DESCRIPTOR); }; } diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 246abaac2..7ad934b73 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -32,34 +32,21 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore { - SSHStore( - std::string_view scheme, - std::string host, - const Params & params) - : StoreConfig(params) - , RemoteStoreConfig(params) - , CommonSSHStoreConfig(params) - , SSHStoreConfig(params) - , Store(params) - , RemoteStore(params) - , host(host) - , master( - host, - sshKey.get(), - sshPublicHostKey.get(), - // Use SSH master only if using more than 1 connection. - connections->capacity() > 1, - compress) - { - } - public: SSHStore( std::string_view scheme, std::string_view host, const Params & params) - : SSHStore{scheme, SSHStoreConfig::extractConnStr(scheme, host), params} + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) + , SSHStoreConfig(params) + , Store(params) + , RemoteStore(params) + , master(createSSHMaster( + // Use SSH master only if using more than 1 connection. + connections->capacity() > 1)) { } @@ -119,6 +106,15 @@ struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfi { } + MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) + , SSHStoreConfig(params) + , LocalFSStoreConfig(params) + { + } + const std::string name() override { return "Experimental SSH Store with filesystem mounted"; } std::string doc() override @@ -158,7 +154,7 @@ public: const Params & params) : StoreConfig(params) , RemoteStoreConfig(params) - , CommonSSHStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) , SSHStoreConfig(params) , LocalFSStoreConfig(params) , MountedSSHStoreConfig(params) diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 5eb63fa67..e5d623adf 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -10,7 +10,7 @@ SSHMaster::SSHMaster( std::string_view host, std::string_view keyFile, std::string_view sshPublicHostKey, - bool useMaster, bool compress, int logFD) + bool useMaster, bool compress, Descriptor logFD) : host(host) , fakeSSH(host == "localhost") , keyFile(keyFile) diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index be0a32287..19b30e883 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -17,7 +17,7 @@ private: const std::string sshPublicHostKey; const bool useMaster; const bool compress; - const int logFD; + const Descriptor logFD; struct State { @@ -43,7 +43,7 @@ public: std::string_view host, std::string_view keyFile, std::string_view sshPublicHostKey, - bool useMaster, bool compress, int logFD = -1); + bool useMaster, bool compress, Descriptor logFD = INVALID_DESCRIPTOR); struct Connection { From 1d5d748fe403df884a23ca6ed87e0575ae619193 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 22:32:52 -0400 Subject: [PATCH 164/910] Fix format 39b2a399ad94f061eea0e0fc639cf1466587959e passed CI but was landed after the formatting change in 1d6c2316a988a97b2c4d214f582580f70c7d9586. --- src/libutil/windows/windows-async-pipe.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libutil/windows/windows-async-pipe.cc b/src/libutil/windows/windows-async-pipe.cc index 7aae603bd..4fa57ca36 100644 --- a/src/libutil/windows/windows-async-pipe.cc +++ b/src/libutil/windows/windows-async-pipe.cc @@ -13,8 +13,14 @@ void AsyncPipe::createAsyncPipe(HANDLE iocp) std::string pipeName = fmt("\\\\.\\pipe\\nix-%d-%p", GetCurrentProcessId(), (void *) this); readSide = CreateNamedPipeA( - pipeName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 0, 0, - INFINITE, NULL); + pipeName.c_str(), + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, + 0, + 0, + INFINITE, + NULL); if (!readSide) throw WinError("CreateNamedPipeA(%s)", pipeName); From 567265ae67d0de0d4273376dffd33ea8fb141c5c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 09:40:49 -0400 Subject: [PATCH 165/910] Start getting all shell scripts passing shellcheck Like with the formatter, we are blacklisting most files by default. Do a few files to get us started, and get a sense of what this looks like. --- .shellcheckrc | 2 + maintainers/flake-module.nix | 235 +++++++++++++++++++++++- mk/common-test.sh | 9 +- mk/run-test.sh | 5 +- scripts/bigsur-nixbld-user-migration.sh | 16 +- tests/functional/repl.sh | 33 ++-- 6 files changed, 274 insertions(+), 26 deletions(-) create mode 100644 .shellcheckrc diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 000000000..662e2045c --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +external-sources=true +source-path=SCRIPTDIR diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 5dbafabaa..dd986e686 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -11,13 +11,13 @@ pre-commit.settings = { hooks = { clang-format.enable = true; + shellcheck.enable = true; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 }; excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^tests/functional/.*$'' ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code @@ -25,7 +25,7 @@ ''^doc/manual/redirects\.js$'' ''^doc/manual/theme/highlight\.js$'' - # We haven't applied formatting to these files yet + # We haven't applied formatting to these files yet (C++) ''^doc/manual/redirects\.js$'' ''^doc/manual/theme/highlight\.js$'' ''^precompiled-headers\.h$'' @@ -428,6 +428,8 @@ ''^src/nix/verify\.cc$'' ''^src/nix/why-depends\.cc$'' + ''^tests/functional/plugins/plugintest\.cc'' + ''^tests/functional/test-libstoreconsumer/main\.cc'' ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/unit/libexpr-support/tests/libexpr\.hh'' @@ -490,6 +492,235 @@ ''^tests/unit/libutil/tests\.cc'' ''^tests/unit/libutil/url\.cc'' ''^tests/unit/libutil/xml-writer\.cc'' + + # We haven't lintted these files yet (shell) + ''^config/install-sh$'' + ''^misc/systemv/nix-daemon$'' + ''^misc/bash/completion\.sh$'' + ''^misc/fish/completion\.fish$'' + ''^misc/zsh/completion\.zsh$'' + ''^scripts/check-hydra-status\.sh$'' + ''^scripts/create-darwin-volume\.sh$'' + ''^scripts/install-darwin-multi-user\.sh$'' + ''^scripts/install-multi-user\.sh$'' + ''^scripts/install-nix-from-closure\.sh$'' + ''^scripts/install-systemd-multi-user\.sh$'' + ''^src/nix/get-env\.sh$'' + ''^tests/functional/add\.sh$'' + ''^tests/functional/bash-profile\.sh$'' + ''^tests/functional/binary-cache-build-remote\.sh$'' + ''^tests/functional/binary-cache\.sh$'' + ''^tests/functional/brotli\.sh$'' + ''^tests/functional/build-delete\.sh$'' + ''^tests/functional/build-dry\.sh$'' + ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' + ''^tests/functional/build-remote-content-addressed-floating\.sh$'' + ''^tests/functional/build-remote-input-addressed\.sh$'' + ''^tests/functional/build-remote-trustless-after\.sh$'' + ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' + ''^tests/functional/build-remote-trustless\.sh$'' + ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' + ''^tests/functional/build-remote\.sh$'' + ''^tests/functional/build\.sh$'' + ''^tests/functional/ca/build-cache\.sh$'' + ''^tests/functional/ca/build-dry\.sh$'' + ''^tests/functional/ca/build-with-garbage-path\.sh$'' + ''^tests/functional/ca/build\.sh$'' + ''^tests/functional/ca/common\.sh$'' + ''^tests/functional/ca/concurrent-builds\.sh$'' + ''^tests/functional/ca/derivation-json\.sh$'' + ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' + ''^tests/functional/ca/eval-store\.sh$'' + ''^tests/functional/ca/gc\.sh$'' + ''^tests/functional/ca/import-derivation\.sh$'' + ''^tests/functional/ca/new-build-cmd\.sh$'' + ''^tests/functional/ca/nix-copy\.sh$'' + ''^tests/functional/ca/nix-run\.sh$'' + ''^tests/functional/ca/nix-shell\.sh$'' + ''^tests/functional/ca/post-hook\.sh$'' + ''^tests/functional/ca/recursive\.sh$'' + ''^tests/functional/ca/repl\.sh$'' + ''^tests/functional/ca/selfref-gc\.sh$'' + ''^tests/functional/ca/signatures\.sh$'' + ''^tests/functional/ca/substitute\.sh$'' + ''^tests/functional/ca/why-depends\.sh$'' + ''^tests/functional/case-hack\.sh$'' + ''^tests/functional/check-refs\.sh$'' + ''^tests/functional/check-reqs\.sh$'' + ''^tests/functional/check\.sh$'' + ''^tests/functional/chroot-store\.sh$'' + ''^tests/functional/common\.sh$'' + ''^tests/functional/common/init\.sh$'' + ''^tests/functional/completions\.sh$'' + ''^tests/functional/compression-levels\.sh$'' + ''^tests/functional/compute-levels\.sh$'' + ''^tests/functional/config\.sh$'' + ''^tests/functional/db-migration\.sh$'' + ''^tests/functional/debugger\.sh$'' + ''^tests/functional/dependencies\.builder0\.sh$'' + ''^tests/functional/dependencies\.sh$'' + ''^tests/functional/derivation-json\.sh$'' + ''^tests/functional/dump-db\.sh$'' + ''^tests/functional/dyn-drv/build-built-drv\.sh$'' + ''^tests/functional/dyn-drv/common\.sh$'' + ''^tests/functional/dyn-drv/dep-built-drv\.sh$'' + ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' + ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' + ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' + ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' + ''^tests/functional/eval-store\.sh$'' + ''^tests/functional/eval\.sh$'' + ''^tests/functional/experimental-features\.sh$'' + ''^tests/functional/export-graph\.sh$'' + ''^tests/functional/export\.sh$'' + ''^tests/functional/extra-sandbox-profile\.sh$'' + ''^tests/functional/fetchClosure\.sh$'' + ''^tests/functional/fetchGit\.sh$'' + ''^tests/functional/fetchGitRefs\.sh$'' + ''^tests/functional/fetchGitSubmodules\.sh$'' + ''^tests/functional/fetchGitVerification\.sh$'' + ''^tests/functional/fetchMercurial\.sh$'' + ''^tests/functional/fetchPath\.sh$'' + ''^tests/functional/fetchTree-file\.sh$'' + ''^tests/functional/fetchurl\.sh$'' + ''^tests/functional/filter-source\.sh$'' + ''^tests/functional/fixed\.builder1\.sh$'' + ''^tests/functional/fixed\.builder2\.sh$'' + ''^tests/functional/fixed\.sh$'' + ''^tests/functional/flakes/absolute-attr-paths\.sh$'' + ''^tests/functional/flakes/absolute-paths\.sh$'' + ''^tests/functional/flakes/build-paths\.sh$'' + ''^tests/functional/flakes/bundle\.sh$'' + ''^tests/functional/flakes/check\.sh$'' + ''^tests/functional/flakes/circular\.sh$'' + ''^tests/functional/flakes/common\.sh$'' + ''^tests/functional/flakes/config\.sh$'' + ''^tests/functional/flakes/develop\.sh$'' + ''^tests/functional/flakes/flake-in-submodule\.sh$'' + ''^tests/functional/flakes/flakes\.sh$'' + ''^tests/functional/flakes/follow-paths\.sh$'' + ''^tests/functional/flakes/init\.sh$'' + ''^tests/functional/flakes/inputs\.sh$'' + ''^tests/functional/flakes/mercurial\.sh$'' + ''^tests/functional/flakes/prefetch\.sh$'' + ''^tests/functional/flakes/run\.sh$'' + ''^tests/functional/flakes/search-root\.sh$'' + ''^tests/functional/flakes/show\.sh$'' + ''^tests/functional/flakes/unlocked-override\.sh$'' + ''^tests/functional/fmt\.sh$'' + ''^tests/functional/fmt\.simple\.sh$'' + ''^tests/functional/function-trace\.sh$'' + ''^tests/functional/gc-auto\.sh$'' + ''^tests/functional/gc-concurrent\.builder\.sh$'' + ''^tests/functional/gc-concurrent\.sh$'' + ''^tests/functional/gc-concurrent2\.builder\.sh$'' + ''^tests/functional/gc-non-blocking\.sh$'' + ''^tests/functional/gc-runtime\.sh$'' + ''^tests/functional/gc\.sh$'' + ''^tests/functional/git-hashing/common\.sh$'' + ''^tests/functional/git-hashing/simple\.sh$'' + ''^tests/functional/hash-convert\.sh$'' + ''^tests/functional/hash-path\.sh$'' + ''^tests/functional/help\.sh$'' + ''^tests/functional/import-derivation\.sh$'' + ''^tests/functional/impure-derivations\.sh$'' + ''^tests/functional/impure-env\.sh$'' + ''^tests/functional/impure-eval\.sh$'' + ''^tests/functional/install-darwin\.sh$'' + ''^tests/functional/lang-test-infra\.sh$'' + ''^tests/functional/lang\.sh$'' + ''^tests/functional/lang/framework\.sh$'' + ''^tests/functional/legacy-ssh-store\.sh$'' + ''^tests/functional/linux-sandbox\.sh$'' + ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' + ''^tests/functional/local-overlay-store/add-lower\.sh$'' + ''^tests/functional/local-overlay-store/bad-uris\.sh$'' + ''^tests/functional/local-overlay-store/build-inner\.sh$'' + ''^tests/functional/local-overlay-store/build\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init-inner\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init\.sh$'' + ''^tests/functional/local-overlay-store/common\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs\.sh$'' + ''^tests/functional/local-overlay-store/gc-inner\.sh$'' + ''^tests/functional/local-overlay-store/gc\.sh$'' + ''^tests/functional/local-overlay-store/optimise-inner\.sh$'' + ''^tests/functional/local-overlay-store/optimise\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add-inner\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add\.sh$'' + ''^tests/functional/local-overlay-store/remount\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle-inner\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle\.sh$'' + ''^tests/functional/local-overlay-store/verify-inner\.sh$'' + ''^tests/functional/local-overlay-store/verify\.sh$'' + ''^tests/functional/logging\.sh$'' + ''^tests/functional/misc\.sh$'' + ''^tests/functional/multiple-outputs\.sh$'' + ''^tests/functional/nar-access\.sh$'' + ''^tests/functional/nested-sandboxing\.sh$'' + ''^tests/functional/nested-sandboxing/command\.sh$'' + ''^tests/functional/nix-build\.sh$'' + ''^tests/functional/nix-channel\.sh$'' + ''^tests/functional/nix-collect-garbage-d\.sh$'' + ''^tests/functional/nix-copy-ssh-common\.sh$'' + ''^tests/functional/nix-copy-ssh-ng\.sh$'' + ''^tests/functional/nix-copy-ssh\.sh$'' + ''^tests/functional/nix-daemon-untrusting\.sh$'' + ''^tests/functional/nix-profile\.sh$'' + ''^tests/functional/nix-shell\.sh$'' + ''^tests/functional/nix_path\.sh$'' + ''^tests/functional/optimise-store\.sh$'' + ''^tests/functional/output-normalization\.sh$'' + ''^tests/functional/parallel\.builder\.sh$'' + ''^tests/functional/parallel\.sh$'' + ''^tests/functional/pass-as-file\.sh$'' + ''^tests/functional/path-from-hash-part\.sh$'' + ''^tests/functional/path-info\.sh$'' + ''^tests/functional/placeholders\.sh$'' + ''^tests/functional/plugins\.sh$'' + ''^tests/functional/post-hook\.sh$'' + ''^tests/functional/pure-eval\.sh$'' + ''^tests/functional/push-to-store-old\.sh$'' + ''^tests/functional/push-to-store\.sh$'' + ''^tests/functional/read-only-store\.sh$'' + ''^tests/functional/readfile-context\.sh$'' + ''^tests/functional/recursive\.sh$'' + ''^tests/functional/referrers\.sh$'' + ''^tests/functional/remote-store\.sh$'' + ''^tests/functional/repair\.sh$'' + ''^tests/functional/restricted\.sh$'' + ''^tests/functional/search\.sh$'' + ''^tests/functional/secure-drv-outputs\.sh$'' + ''^tests/functional/selfref-gc\.sh$'' + ''^tests/functional/shell\.sh$'' + ''^tests/functional/shell\.shebang\.sh$'' + ''^tests/functional/signing\.sh$'' + ''^tests/functional/simple\.builder\.sh$'' + ''^tests/functional/simple\.sh$'' + ''^tests/functional/ssh-relay\.sh$'' + ''^tests/functional/store-info\.sh$'' + ''^tests/functional/structured-attrs\.sh$'' + ''^tests/functional/substitute-with-invalid-ca\.sh$'' + ''^tests/functional/suggestions\.sh$'' + ''^tests/functional/supplementary-groups\.sh$'' + ''^tests/functional/tarball\.sh$'' + ''^tests/functional/test-infra\.sh$'' + ''^tests/functional/test-libstoreconsumer\.sh$'' + ''^tests/functional/timeout\.sh$'' + ''^tests/functional/toString-path\.sh$'' + ''^tests/functional/user-envs-migration\.sh$'' + ''^tests/functional/user-envs-test-case\.sh$'' + ''^tests/functional/user-envs\.builder\.sh$'' + ''^tests/functional/user-envs\.sh$'' + ''^tests/functional/why-depends\.sh$'' + ''^tests/functional/zstd\.sh$'' + ''^tests/unit/libutil/data/git/check-data\.sh$'' ]; }; diff --git a/mk/common-test.sh b/mk/common-test.sh index 2abea7887..de24b6fcc 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -1,19 +1,24 @@ +# shellcheck shell=bash + # Remove overall test dir (at most one of the two should match) and # remove file extension. + +# shellcheck disable=SC2154 test_name=$(echo -n "$test" | sed \ -e "s|^tests/unit/[^/]*/data/||" \ -e "s|^tests/functional/||" \ -e "s|\.sh$||" \ ) +# shellcheck disable=SC2016 TESTS_ENVIRONMENT=( "TEST_NAME=$test_name" 'NIX_REMOTE=' 'PS4=+(${BASH_SOURCE[0]-$0}:$LINENO) ' ) -: ${BASH:=/usr/bin/env bash} +read -r -a bash <<< "${BASH:-/usr/bin/env bash}" run () { - cd "$(dirname $1)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -x -e -u -o pipefail $(basename $1) + cd "$(dirname "$1")" && env "${TESTS_ENVIRONMENT[@]}" "${bash[@]}" -x -e -u -o pipefail "$(basename "$1")" } diff --git a/mk/run-test.sh b/mk/run-test.sh index 1256bfcf7..543c845e1 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -26,12 +26,13 @@ run_test () { run_test -if [ $status -eq 0 ]; then +if [[ "$status" = 0 ]]; then echo "$post_run_msg [${green}PASS$normal]" -elif [ $status -eq 99 ]; then +elif [[ "$status" = 99 ]]; then echo "$post_run_msg [${yellow}SKIP$normal]" else echo "$post_run_msg [${red}FAIL$normal]" + # shellcheck disable=SC2001 echo "$log" | sed 's/^/ /' exit "$status" fi diff --git a/scripts/bigsur-nixbld-user-migration.sh b/scripts/bigsur-nixbld-user-migration.sh index f1619fd56..0eb312e07 100755 --- a/scripts/bigsur-nixbld-user-migration.sh +++ b/scripts/bigsur-nixbld-user-migration.sh @@ -3,7 +3,7 @@ ((NEW_NIX_FIRST_BUILD_UID=301)) id_available(){ - dscl . list /Users UniqueID | grep -E '\b'$1'\b' >/dev/null + dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null } change_nixbld_names_and_ids(){ @@ -26,18 +26,18 @@ change_nixbld_names_and_ids(){ fi done - if [[ $name == _* ]]; then + if [[ "$name" == _* ]]; then echo " It looks like $name has already been renamed--skipping." else # first 3 are cleanup, it's OK if they aren't here - sudo dscl . delete /Users/$name dsAttrTypeNative:_writers_passwd &>/dev/null || true - sudo dscl . change /Users/$name NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true + sudo dscl . delete "/Users/$name" dsAttrTypeNative:_writers_passwd &>/dev/null || true + sudo dscl . change "/Users/$name" NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true # remove existing user from group - sudo dseditgroup -o edit -t user -d $name nixbld || true - sudo dscl . change /Users/$name UniqueID $uid $next_id - sudo dscl . change /Users/$name RecordName $name _$name + sudo dseditgroup -o edit -t user -d "$name" nixbld || true + sudo dscl . change "/Users/$name" UniqueID "$uid" "$next_id" + sudo dscl . change "/Users/$name" RecordName "$name" "_$name" # add renamed user to group - sudo dseditgroup -o edit -t user -a _$name nixbld + sudo dseditgroup -o edit -t user -a "_$name" nixbld echo " $name migrated to _$name (uid: $next_id)" fi done < <(dscl . list /Users UniqueID | grep nixbld | sort -n -k2) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index f11fa7140..222145a7a 100644 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash source common.sh testDir="$PWD" @@ -21,11 +22,14 @@ import $testDir/undefined-variable.nix " testRepl () { - local nixArgs=("$@") + local nixArgs + nixArgs=("$@") rm -rf repl-result-out || true # cleanup from other runs backed by a foreign nix store - local replOutput="$(nix repl "${nixArgs[@]}" <<< "$replCmds")" + local replOutput + replOutput="$(nix repl "${nixArgs[@]}" <<< "$replCmds")" echo "$replOutput" - local outPath=$(echo "$replOutput" |& + local outPath + outPath=$(echo "$replOutput" |& grep -o -E "$NIX_STORE_DIR/\w*-simple") nix path-info "${nixArgs[@]}" "$outPath" [ "$(realpath ./repl-result-out)" == "$outPath" ] || fail "nix repl :bl doesn't make a symlink" @@ -34,11 +38,11 @@ testRepl () { # simple.nix prints a PATH during build echo "$replOutput" | grepQuiet -s 'PATH=' || fail "nix repl :log doesn't output logs" - local replOutput="$(nix repl "${nixArgs[@]}" <<< "$replFailingCmds" 2>&1)" + replOutput="$(nix repl "${nixArgs[@]}" <<< "$replFailingCmds" 2>&1)" echo "$replOutput" echo "$replOutput" | grepQuiet -s 'This should fail' \ || fail "nix repl :log doesn't output logs for a failed derivation" - local replOutput="$(nix repl --show-trace "${nixArgs[@]}" <<< "$replUndefinedVariable" 2>&1)" + replOutput="$(nix repl --show-trace "${nixArgs[@]}" <<< "$replUndefinedVariable" 2>&1)" echo "$replOutput" echo "$replOutput" | grepQuiet -s "while evaluating the file" \ || fail "nix repl --show-trace doesn't show the trace" @@ -48,7 +52,7 @@ testRepl () { nix repl "${nixArgs[@]}" 2>&1 <<< "builtins.currentSystem" \ | grep "$(nix-instantiate --eval -E 'builtins.currentSystem')" - expectStderr 1 nix repl ${testDir}/simple.nix \ + expectStderr 1 nix repl "${testDir}/simple.nix" \ | grepQuiet -s "error: path '$testDir/simple.nix' is not a flake" } @@ -63,10 +67,11 @@ stripColors () { } testReplResponseGeneral () { - local grepMode="$1"; shift - local commands="$1"; shift - local expectedResponse="$1"; shift - local response="$(nix repl "$@" <<< "$commands" | stripColors)" + local grepMode commands expectedResponse response + grepMode="$1"; shift + commands="$1"; shift + expectedResponse="$1"; shift + response="$(nix repl "$@" <<< "$commands" | stripColors)" echo "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \ || fail "repl command set: @@ -91,6 +96,8 @@ testReplResponseNoRegex () { } # :a uses the newest version of a symbol +# +# shellcheck disable=SC2016 testReplResponse ' :a { a = "1"; } :a { a = "2"; } @@ -101,6 +108,8 @@ testReplResponse ' # note the escaped \, # \\ # because the second argument is a regex +# +# shellcheck disable=SC2016 testReplResponseNoRegex ' "$" + "{hi}" ' '"\${hi}"' @@ -108,12 +117,12 @@ testReplResponseNoRegex ' testReplResponse ' drvPath ' '".*-simple.drv"' \ ---file $testDir/simple.nix +--file "$testDir/simple.nix" testReplResponse ' drvPath ' '".*-simple.drv"' \ ---file $testDir/simple.nix --experimental-features 'ca-derivations' +--file "$testDir/simple.nix" --experimental-features 'ca-derivations' mkdir -p flake && cat < flake/flake.nix { From d7b04d61a91fab9f97a271fe973d98596fe7a3dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 13:10:48 +0000 Subject: [PATCH 166/910] Bump zeebe-io/backport-action from 2.5.0 to 3.0.2 Bumps [zeebe-io/backport-action](https://github.com/zeebe-io/backport-action) from 2.5.0 to 3.0.2. - [Release notes](https://github.com/zeebe-io/backport-action/releases) - [Commits](https://github.com/zeebe-io/backport-action/compare/v2.5.0...v3.0.2) --- updated-dependencies: - dependency-name: zeebe-io/backport-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 8f83b913c..dd110de6c 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -21,7 +21,7 @@ jobs: fetch-depth: 0 - name: Create backport PRs # should be kept in sync with `version` - uses: zeebe-io/backport-action@v2.5.0 + uses: zeebe-io/backport-action@v3.0.2 with: # Config README: https://github.com/zeebe-io/backport-action#backport-action github_token: ${{ secrets.GITHUB_TOKEN }} From ebc29017fc2971d2dc36007a8d5114e695997923 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 28 May 2024 09:28:06 -0400 Subject: [PATCH 167/910] dev shell: excludes are per hook As suggested by Robert in https://github.com/NixOS/nix/pull/10787#discussion_r1617145374 --- maintainers/flake-module.nix | 1425 +++++++++++++++++----------------- 1 file changed, 714 insertions(+), 711 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index dd986e686..1f19c673a 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -10,720 +10,723 @@ # https://flake.parts/options/pre-commit-hooks-nix.html#options pre-commit.settings = { hooks = { - clang-format.enable = true; - shellcheck.enable = true; + clang-format = { + enable = true; + excludes = [ + # We don't want to format test data + # ''tests/(?!nixos/).*\.nix'' + ''^tests/unit/[^/]*/data/.*$'' + + # Don't format vendored code + ''^src/toml11/.*'' + ''^doc/manual/redirects\.js$'' + ''^doc/manual/theme/highlight\.js$'' + + # We haven't applied formatting to these files yet + ''^doc/manual/redirects\.js$'' + ''^doc/manual/theme/highlight\.js$'' + ''^precompiled-headers\.h$'' + ''^src/build-remote/build-remote\.cc$'' + ''^src/libcmd/built-path\.cc$'' + ''^src/libcmd/built-path\.hh$'' + ''^src/libcmd/command\.cc$'' + ''^src/libcmd/command\.hh$'' + ''^src/libcmd/common-eval-args\.cc$'' + ''^src/libcmd/common-eval-args\.hh$'' + ''^src/libcmd/editor-for\.cc$'' + ''^src/libcmd/installable-attr-path\.cc$'' + ''^src/libcmd/installable-attr-path\.hh$'' + ''^src/libcmd/installable-derived-path\.cc$'' + ''^src/libcmd/installable-derived-path\.hh$'' + ''^src/libcmd/installable-flake\.cc$'' + ''^src/libcmd/installable-flake\.hh$'' + ''^src/libcmd/installable-value\.cc$'' + ''^src/libcmd/installable-value\.hh$'' + ''^src/libcmd/installables\.cc$'' + ''^src/libcmd/installables\.hh$'' + ''^src/libcmd/legacy\.hh$'' + ''^src/libcmd/markdown\.cc$'' + ''^src/libcmd/misc-store-flags\.cc$'' + ''^src/libcmd/repl-interacter\.cc$'' + ''^src/libcmd/repl-interacter\.hh$'' + ''^src/libcmd/repl\.cc$'' + ''^src/libcmd/repl\.hh$'' + ''^src/libexpr-c/nix_api_expr\.cc$'' + ''^src/libexpr-c/nix_api_external\.cc$'' + ''^src/libexpr/attr-path\.cc$'' + ''^src/libexpr/attr-path\.hh$'' + ''^src/libexpr/attr-set\.cc$'' + ''^src/libexpr/attr-set\.hh$'' + ''^src/libexpr/eval-cache\.cc$'' + ''^src/libexpr/eval-cache\.hh$'' + ''^src/libexpr/eval-error\.cc$'' + ''^src/libexpr/eval-inline\.hh$'' + ''^src/libexpr/eval-settings\.cc$'' + ''^src/libexpr/eval-settings\.hh$'' + ''^src/libexpr/eval\.cc$'' + ''^src/libexpr/eval\.hh$'' + ''^src/libexpr/flake/config\.cc$'' + ''^src/libexpr/flake/flake\.cc$'' + ''^src/libexpr/flake/flake\.hh$'' + ''^src/libexpr/flake/flakeref\.cc$'' + ''^src/libexpr/flake/flakeref\.hh$'' + ''^src/libexpr/flake/lockfile\.cc$'' + ''^src/libexpr/flake/lockfile\.hh$'' + ''^src/libexpr/flake/url-name\.cc$'' + ''^src/libexpr/function-trace\.cc$'' + ''^src/libexpr/gc-small-vector\.hh$'' + ''^src/libexpr/get-drvs\.cc$'' + ''^src/libexpr/get-drvs\.hh$'' + ''^src/libexpr/json-to-value\.cc$'' + ''^src/libexpr/nixexpr\.cc$'' + ''^src/libexpr/nixexpr\.hh$'' + ''^src/libexpr/parser-state\.hh$'' + ''^src/libexpr/pos-table\.hh$'' + ''^src/libexpr/primops\.cc$'' + ''^src/libexpr/primops\.hh$'' + ''^src/libexpr/primops/context\.cc$'' + ''^src/libexpr/primops/fetchClosure\.cc$'' + ''^src/libexpr/primops/fetchMercurial\.cc$'' + ''^src/libexpr/primops/fetchTree\.cc$'' + ''^src/libexpr/primops/fromTOML\.cc$'' + ''^src/libexpr/print-ambiguous\.cc$'' + ''^src/libexpr/print-ambiguous\.hh$'' + ''^src/libexpr/print-options\.hh$'' + ''^src/libexpr/print\.cc$'' + ''^src/libexpr/print\.hh$'' + ''^src/libexpr/search-path\.cc$'' + ''^src/libexpr/symbol-table\.hh$'' + ''^src/libexpr/value-to-json\.cc$'' + ''^src/libexpr/value-to-json\.hh$'' + ''^src/libexpr/value-to-xml\.cc$'' + ''^src/libexpr/value-to-xml\.hh$'' + ''^src/libexpr/value\.hh$'' + ''^src/libexpr/value/context\.cc$'' + ''^src/libexpr/value/context\.hh$'' + ''^src/libfetchers/attrs\.cc$'' + ''^src/libfetchers/cache\.cc$'' + ''^src/libfetchers/cache\.hh$'' + ''^src/libfetchers/fetch-settings\.cc$'' + ''^src/libfetchers/fetch-settings\.hh$'' + ''^src/libfetchers/fetch-to-store\.cc$'' + ''^src/libfetchers/fetchers\.cc$'' + ''^src/libfetchers/fetchers\.hh$'' + ''^src/libfetchers/filtering-source-accessor\.cc$'' + ''^src/libfetchers/filtering-source-accessor\.hh$'' + ''^src/libfetchers/fs-source-accessor\.cc$'' + ''^src/libfetchers/fs-source-accessor\.hh$'' + ''^src/libfetchers/git-utils\.cc$'' + ''^src/libfetchers/git-utils\.hh$'' + ''^src/libfetchers/github\.cc$'' + ''^src/libfetchers/indirect\.cc$'' + ''^src/libfetchers/memory-source-accessor\.cc$'' + ''^src/libfetchers/path\.cc$'' + ''^src/libfetchers/registry\.cc$'' + ''^src/libfetchers/registry\.hh$'' + ''^src/libfetchers/tarball\.cc$'' + ''^src/libfetchers/tarball\.hh$'' + ''^src/libfetchers/unix/git\.cc$'' + ''^src/libfetchers/unix/mercurial\.cc$'' + ''^src/libmain/common-args\.cc$'' + ''^src/libmain/common-args\.hh$'' + ''^src/libmain/loggers\.cc$'' + ''^src/libmain/loggers\.hh$'' + ''^src/libmain/progress-bar\.cc$'' + ''^src/libmain/shared\.cc$'' + ''^src/libmain/shared\.hh$'' + ''^src/libmain/unix/stack\.cc$'' + ''^src/libstore/binary-cache-store\.cc$'' + ''^src/libstore/binary-cache-store\.hh$'' + ''^src/libstore/build-result\.hh$'' + ''^src/libstore/builtins\.hh$'' + ''^src/libstore/builtins/buildenv\.cc$'' + ''^src/libstore/builtins/buildenv\.hh$'' + ''^src/libstore/common-protocol-impl\.hh$'' + ''^src/libstore/common-protocol\.cc$'' + ''^src/libstore/common-protocol\.hh$'' + ''^src/libstore/content-address\.cc$'' + ''^src/libstore/content-address\.hh$'' + ''^src/libstore/daemon\.cc$'' + ''^src/libstore/daemon\.hh$'' + ''^src/libstore/derivations\.cc$'' + ''^src/libstore/derivations\.hh$'' + ''^src/libstore/derived-path-map\.cc$'' + ''^src/libstore/derived-path-map\.hh$'' + ''^src/libstore/derived-path\.cc$'' + ''^src/libstore/derived-path\.hh$'' + ''^src/libstore/downstream-placeholder\.cc$'' + ''^src/libstore/downstream-placeholder\.hh$'' + ''^src/libstore/dummy-store\.cc$'' + ''^src/libstore/export-import\.cc$'' + ''^src/libstore/filetransfer\.cc$'' + ''^src/libstore/filetransfer\.hh$'' + ''^src/libstore/gc-store\.hh$'' + ''^src/libstore/globals\.cc$'' + ''^src/libstore/globals\.hh$'' + ''^src/libstore/http-binary-cache-store\.cc$'' + ''^src/libstore/legacy-ssh-store\.cc$'' + ''^src/libstore/legacy-ssh-store\.hh$'' + ''^src/libstore/length-prefixed-protocol-helper\.hh$'' + ''^src/libstore/linux/personality\.cc$'' + ''^src/libstore/linux/personality\.hh$'' + ''^src/libstore/local-binary-cache-store\.cc$'' + ''^src/libstore/local-fs-store\.cc$'' + ''^src/libstore/local-fs-store\.hh$'' + ''^src/libstore/log-store\.cc$'' + ''^src/libstore/log-store\.hh$'' + ''^src/libstore/machines\.cc$'' + ''^src/libstore/machines\.hh$'' + ''^src/libstore/make-content-addressed\.cc$'' + ''^src/libstore/make-content-addressed\.hh$'' + ''^src/libstore/misc\.cc$'' + ''^src/libstore/names\.cc$'' + ''^src/libstore/names\.hh$'' + ''^src/libstore/nar-accessor\.cc$'' + ''^src/libstore/nar-accessor\.hh$'' + ''^src/libstore/nar-info-disk-cache\.cc$'' + ''^src/libstore/nar-info-disk-cache\.hh$'' + ''^src/libstore/nar-info\.cc$'' + ''^src/libstore/nar-info\.hh$'' + ''^src/libstore/outputs-spec\.cc$'' + ''^src/libstore/outputs-spec\.hh$'' + ''^src/libstore/parsed-derivations\.cc$'' + ''^src/libstore/path-info\.cc$'' + ''^src/libstore/path-info\.hh$'' + ''^src/libstore/path-references\.cc$'' + ''^src/libstore/path-regex\.hh$'' + ''^src/libstore/path-with-outputs\.cc$'' + ''^src/libstore/path\.cc$'' + ''^src/libstore/path\.hh$'' + ''^src/libstore/pathlocks\.cc$'' + ''^src/libstore/pathlocks\.hh$'' + ''^src/libstore/profiles\.cc$'' + ''^src/libstore/profiles\.hh$'' + ''^src/libstore/realisation\.cc$'' + ''^src/libstore/realisation\.hh$'' + ''^src/libstore/remote-fs-accessor\.cc$'' + ''^src/libstore/remote-fs-accessor\.hh$'' + ''^src/libstore/remote-store-connection\.hh$'' + ''^src/libstore/remote-store\.cc$'' + ''^src/libstore/remote-store\.hh$'' + ''^src/libstore/s3-binary-cache-store\.cc$'' + ''^src/libstore/s3\.hh$'' + ''^src/libstore/serve-protocol-impl\.cc$'' + ''^src/libstore/serve-protocol-impl\.hh$'' + ''^src/libstore/serve-protocol\.cc$'' + ''^src/libstore/serve-protocol\.hh$'' + ''^src/libstore/sqlite\.cc$'' + ''^src/libstore/sqlite\.hh$'' + ''^src/libstore/ssh-store-config\.hh$'' + ''^src/libstore/ssh-store\.cc$'' + ''^src/libstore/ssh\.cc$'' + ''^src/libstore/ssh\.hh$'' + ''^src/libstore/store-api\.cc$'' + ''^src/libstore/store-api\.hh$'' + ''^src/libstore/store-dir-config\.hh$'' + ''^src/libstore/unix/build/derivation-goal\.cc$'' + ''^src/libstore/unix/build/derivation-goal\.hh$'' + ''^src/libstore/build/drv-output-substitution-goal\.cc$'' + ''^src/libstore/build/drv-output-substitution-goal\.hh$'' + ''^src/libstore/build/entry-points\.cc$'' + ''^src/libstore/build/goal\.cc$'' + ''^src/libstore/build/goal\.hh$'' + ''^src/libstore/unix/build/hook-instance\.cc$'' + ''^src/libstore/unix/build/local-derivation-goal\.cc$'' + ''^src/libstore/unix/build/local-derivation-goal\.hh$'' + ''^src/libstore/build/substitution-goal\.cc$'' + ''^src/libstore/build/substitution-goal\.hh$'' + ''^src/libstore/build/worker\.cc$'' + ''^src/libstore/build/worker\.hh$'' + ''^src/libstore/unix/builtins/fetchurl\.cc$'' + ''^src/libstore/unix/builtins/unpack-channel\.cc$'' + ''^src/libstore/gc\.cc$'' + ''^src/libstore/unix/local-overlay-store\.cc$'' + ''^src/libstore/unix/local-overlay-store\.hh$'' + ''^src/libstore/local-store\.cc$'' + ''^src/libstore/local-store\.hh$'' + ''^src/libstore/unix/user-lock\.cc$'' + ''^src/libstore/unix/user-lock\.hh$'' + ''^src/libstore/optimise-store\.cc$'' + ''^src/libstore/unix/pathlocks\.cc$'' + ''^src/libstore/posix-fs-canonicalise\.cc$'' + ''^src/libstore/posix-fs-canonicalise\.hh$'' + ''^src/libstore/uds-remote-store\.cc$'' + ''^src/libstore/uds-remote-store\.hh$'' + ''^src/libstore/windows/build\.cc$'' + ''^src/libstore/worker-protocol-impl\.hh$'' + ''^src/libstore/worker-protocol\.cc$'' + ''^src/libstore/worker-protocol\.hh$'' + ''^src/libutil-c/nix_api_util_internal\.h$'' + ''^src/libutil/archive\.cc$'' + ''^src/libutil/archive\.hh$'' + ''^src/libutil/args\.cc$'' + ''^src/libutil/args\.hh$'' + ''^src/libutil/args/root\.hh$'' + ''^src/libutil/callback\.hh$'' + ''^src/libutil/canon-path\.cc$'' + ''^src/libutil/canon-path\.hh$'' + ''^src/libutil/chunked-vector\.hh$'' + ''^src/libutil/closure\.hh$'' + ''^src/libutil/comparator\.hh$'' + ''^src/libutil/compute-levels\.cc$'' + ''^src/libutil/config-impl\.hh$'' + ''^src/libutil/config\.cc$'' + ''^src/libutil/config\.hh$'' + ''^src/libutil/current-process\.cc$'' + ''^src/libutil/current-process\.hh$'' + ''^src/libutil/english\.cc$'' + ''^src/libutil/english\.hh$'' + ''^src/libutil/environment-variables\.cc$'' + ''^src/libutil/error\.cc$'' + ''^src/libutil/error\.hh$'' + ''^src/libutil/exit\.hh$'' + ''^src/libutil/experimental-features\.cc$'' + ''^src/libutil/experimental-features\.hh$'' + ''^src/libutil/file-content-address\.cc$'' + ''^src/libutil/file-content-address\.hh$'' + ''^src/libutil/file-descriptor\.cc$'' + ''^src/libutil/file-descriptor\.hh$'' + ''^src/libutil/file-path-impl\.hh$'' + ''^src/libutil/file-path\.hh$'' + ''^src/libutil/file-system\.cc$'' + ''^src/libutil/file-system\.hh$'' + ''^src/libutil/finally\.hh$'' + ''^src/libutil/fmt\.hh$'' + ''^src/libutil/fs-sink\.cc$'' + ''^src/libutil/fs-sink\.hh$'' + ''^src/libutil/git\.cc$'' + ''^src/libutil/git\.hh$'' + ''^src/libutil/hash\.cc$'' + ''^src/libutil/hash\.hh$'' + ''^src/libutil/hilite\.cc$'' + ''^src/libutil/hilite\.hh$'' + ''^src/libutil/source-accessor\.hh$'' + ''^src/libutil/json-impls\.hh$'' + ''^src/libutil/json-utils\.cc$'' + ''^src/libutil/json-utils\.hh$'' + ''^src/libutil/linux/cgroup\.cc$'' + ''^src/libutil/linux/namespaces\.cc$'' + ''^src/libutil/logging\.cc$'' + ''^src/libutil/logging\.hh$'' + ''^src/libutil/lru-cache\.hh$'' + ''^src/libutil/memory-source-accessor\.cc$'' + ''^src/libutil/memory-source-accessor\.hh$'' + ''^src/libutil/pool\.hh$'' + ''^src/libutil/position\.cc$'' + ''^src/libutil/position\.hh$'' + ''^src/libutil/posix-source-accessor\.cc$'' + ''^src/libutil/posix-source-accessor\.hh$'' + ''^src/libutil/processes\.hh$'' + ''^src/libutil/ref\.hh$'' + ''^src/libutil/references\.cc$'' + ''^src/libutil/references\.hh$'' + ''^src/libutil/regex-combinators\.hh$'' + ''^src/libutil/serialise\.cc$'' + ''^src/libutil/serialise\.hh$'' + ''^src/libutil/signals\.hh$'' + ''^src/libutil/signature/local-keys\.cc$'' + ''^src/libutil/signature/local-keys\.hh$'' + ''^src/libutil/signature/signer\.cc$'' + ''^src/libutil/signature/signer\.hh$'' + ''^src/libutil/source-accessor\.cc$'' + ''^src/libutil/source-accessor\.hh$'' + ''^src/libutil/source-path\.cc$'' + ''^src/libutil/source-path\.hh$'' + ''^src/libutil/split\.hh$'' + ''^src/libutil/suggestions\.cc$'' + ''^src/libutil/suggestions\.hh$'' + ''^src/libutil/sync\.hh$'' + ''^src/libutil/terminal\.cc$'' + ''^src/libutil/terminal\.hh$'' + ''^src/libutil/thread-pool\.cc$'' + ''^src/libutil/thread-pool\.hh$'' + ''^src/libutil/topo-sort\.hh$'' + ''^src/libutil/types\.hh$'' + ''^src/libutil/unix/file-descriptor\.cc$'' + ''^src/libutil/unix/file-path\.cc$'' + ''^src/libutil/unix/monitor-fd\.hh$'' + ''^src/libutil/unix/processes\.cc$'' + ''^src/libutil/unix/signals-impl\.hh$'' + ''^src/libutil/unix/signals\.cc$'' + ''^src/libutil/unix-domain-socket\.cc$'' + ''^src/libutil/unix/users\.cc$'' + ''^src/libutil/url-parts\.hh$'' + ''^src/libutil/url\.cc$'' + ''^src/libutil/url\.hh$'' + ''^src/libutil/users\.cc$'' + ''^src/libutil/users\.hh$'' + ''^src/libutil/util\.cc$'' + ''^src/libutil/util\.hh$'' + ''^src/libutil/variant-wrapper\.hh$'' + ''^src/libutil/windows/environment-variables\.cc$'' + ''^src/libutil/windows/file-descriptor\.cc$'' + ''^src/libutil/windows/file-path\.cc$'' + ''^src/libutil/windows/processes\.cc$'' + ''^src/libutil/windows/users\.cc$'' + ''^src/libutil/windows/windows-error\.cc$'' + ''^src/libutil/windows/windows-error\.hh$'' + ''^src/libutil/xml-writer\.cc$'' + ''^src/libutil/xml-writer\.hh$'' + ''^src/nix-build/nix-build\.cc$'' + ''^src/nix-channel/nix-channel\.cc$'' + ''^src/nix-collect-garbage/nix-collect-garbage\.cc$'' + ''^src/nix-env/buildenv.nix$'' + ''^src/nix-env/nix-env\.cc$'' + ''^src/nix-env/user-env\.cc$'' + ''^src/nix-env/user-env\.hh$'' + ''^src/nix-instantiate/nix-instantiate\.cc$'' + ''^src/nix-store/dotgraph\.cc$'' + ''^src/nix-store/graphml\.cc$'' + ''^src/nix-store/nix-store\.cc$'' + ''^src/nix/add-to-store\.cc$'' + ''^src/nix/app\.cc$'' + ''^src/nix/build\.cc$'' + ''^src/nix/bundle\.cc$'' + ''^src/nix/cat\.cc$'' + ''^src/nix/config-check\.cc$'' + ''^src/nix/config\.cc$'' + ''^src/nix/copy\.cc$'' + ''^src/nix/derivation-add\.cc$'' + ''^src/nix/derivation-show\.cc$'' + ''^src/nix/derivation\.cc$'' + ''^src/nix/develop\.cc$'' + ''^src/nix/diff-closures\.cc$'' + ''^src/nix/dump-path\.cc$'' + ''^src/nix/edit\.cc$'' + ''^src/nix/eval\.cc$'' + ''^src/nix/flake\.cc$'' + ''^src/nix/fmt\.cc$'' + ''^src/nix/hash\.cc$'' + ''^src/nix/log\.cc$'' + ''^src/nix/ls\.cc$'' + ''^src/nix/main\.cc$'' + ''^src/nix/make-content-addressed\.cc$'' + ''^src/nix/nar\.cc$'' + ''^src/nix/optimise-store\.cc$'' + ''^src/nix/path-from-hash-part\.cc$'' + ''^src/nix/path-info\.cc$'' + ''^src/nix/prefetch\.cc$'' + ''^src/nix/profile\.cc$'' + ''^src/nix/realisation\.cc$'' + ''^src/nix/registry\.cc$'' + ''^src/nix/repl\.cc$'' + ''^src/nix/run\.cc$'' + ''^src/nix/run\.hh$'' + ''^src/nix/search\.cc$'' + ''^src/nix/sigs\.cc$'' + ''^src/nix/store-copy-log\.cc$'' + ''^src/nix/store-delete\.cc$'' + ''^src/nix/store-gc\.cc$'' + ''^src/nix/store-info\.cc$'' + ''^src/nix/store-repair\.cc$'' + ''^src/nix/store\.cc$'' + ''^src/nix/unix/daemon\.cc$'' + ''^src/nix/upgrade-nix\.cc$'' + ''^src/nix/verify\.cc$'' + ''^src/nix/why-depends\.cc$'' + + ''^tests/functional/plugins/plugintest\.cc'' + ''^tests/functional/test-libstoreconsumer/main\.cc'' + ''^tests/nixos/ca-fd-leak/sender\.c'' + ''^tests/nixos/ca-fd-leak/smuggler\.c'' + ''^tests/unit/libexpr-support/tests/libexpr\.hh'' + ''^tests/unit/libexpr-support/tests/value/context\.cc'' + ''^tests/unit/libexpr-support/tests/value/context\.hh'' + ''^tests/unit/libexpr/derived-path\.cc'' + ''^tests/unit/libexpr/error_traces\.cc'' + ''^tests/unit/libexpr/eval\.cc'' + ''^tests/unit/libexpr/flake/flakeref\.cc'' + ''^tests/unit/libexpr/flake/url-name\.cc'' + ''^tests/unit/libexpr/json\.cc'' + ''^tests/unit/libexpr/main\.cc'' + ''^tests/unit/libexpr/primops\.cc'' + ''^tests/unit/libexpr/search-path\.cc'' + ''^tests/unit/libexpr/trivial\.cc'' + ''^tests/unit/libexpr/value/context\.cc'' + ''^tests/unit/libexpr/value/print\.cc'' + ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.hh'' + ''^tests/unit/libstore-support/tests/libstore\.hh'' + ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' + ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' + ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' + ''^tests/unit/libstore-support/tests/path\.cc'' + ''^tests/unit/libstore-support/tests/path\.hh'' + ''^tests/unit/libstore-support/tests/protocol\.hh'' + ''^tests/unit/libstore/common-protocol\.cc'' + ''^tests/unit/libstore/content-address\.cc'' + ''^tests/unit/libstore/derivation\.cc'' + ''^tests/unit/libstore/derived-path\.cc'' + ''^tests/unit/libstore/downstream-placeholder\.cc'' + ''^tests/unit/libstore/machines\.cc'' + ''^tests/unit/libstore/nar-info-disk-cache\.cc'' + ''^tests/unit/libstore/nar-info\.cc'' + ''^tests/unit/libstore/outputs-spec\.cc'' + ''^tests/unit/libstore/path-info\.cc'' + ''^tests/unit/libstore/path\.cc'' + ''^tests/unit/libstore/serve-protocol\.cc'' + ''^tests/unit/libstore/worker-protocol\.cc'' + ''^tests/unit/libutil-support/tests/characterization\.hh'' + ''^tests/unit/libutil-support/tests/hash\.cc'' + ''^tests/unit/libutil-support/tests/hash\.hh'' + ''^tests/unit/libutil/args\.cc'' + ''^tests/unit/libutil/canon-path\.cc'' + ''^tests/unit/libutil/chunked-vector\.cc'' + ''^tests/unit/libutil/closure\.cc'' + ''^tests/unit/libutil/compression\.cc'' + ''^tests/unit/libutil/config\.cc'' + ''^tests/unit/libutil/file-content-address\.cc'' + ''^tests/unit/libutil/git\.cc'' + ''^tests/unit/libutil/hash\.cc'' + ''^tests/unit/libutil/hilite\.cc'' + ''^tests/unit/libutil/json-utils\.cc'' + ''^tests/unit/libutil/logging\.cc'' + ''^tests/unit/libutil/lru-cache\.cc'' + ''^tests/unit/libutil/pool\.cc'' + ''^tests/unit/libutil/references\.cc'' + ''^tests/unit/libutil/suggestions\.cc'' + ''^tests/unit/libutil/tests\.cc'' + ''^tests/unit/libutil/url\.cc'' + ''^tests/unit/libutil/xml-writer\.cc'' + ]; + }; + shellcheck = { + enable = true; + excludes = [ + # We haven't linted these files yet + ''^config/install-sh$'' + ''^misc/systemv/nix-daemon$'' + ''^misc/bash/completion\.sh$'' + ''^misc/fish/completion\.fish$'' + ''^misc/zsh/completion\.zsh$'' + ''^scripts/check-hydra-status\.sh$'' + ''^scripts/create-darwin-volume\.sh$'' + ''^scripts/install-darwin-multi-user\.sh$'' + ''^scripts/install-multi-user\.sh$'' + ''^scripts/install-nix-from-closure\.sh$'' + ''^scripts/install-systemd-multi-user\.sh$'' + ''^src/nix/get-env\.sh$'' + ''^tests/functional/add\.sh$'' + ''^tests/functional/bash-profile\.sh$'' + ''^tests/functional/binary-cache-build-remote\.sh$'' + ''^tests/functional/binary-cache\.sh$'' + ''^tests/functional/brotli\.sh$'' + ''^tests/functional/build-delete\.sh$'' + ''^tests/functional/build-dry\.sh$'' + ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' + ''^tests/functional/build-remote-content-addressed-floating\.sh$'' + ''^tests/functional/build-remote-input-addressed\.sh$'' + ''^tests/functional/build-remote-trustless-after\.sh$'' + ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' + ''^tests/functional/build-remote-trustless\.sh$'' + ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' + ''^tests/functional/build-remote\.sh$'' + ''^tests/functional/build\.sh$'' + ''^tests/functional/ca/build-cache\.sh$'' + ''^tests/functional/ca/build-dry\.sh$'' + ''^tests/functional/ca/build-with-garbage-path\.sh$'' + ''^tests/functional/ca/build\.sh$'' + ''^tests/functional/ca/common\.sh$'' + ''^tests/functional/ca/concurrent-builds\.sh$'' + ''^tests/functional/ca/derivation-json\.sh$'' + ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' + ''^tests/functional/ca/eval-store\.sh$'' + ''^tests/functional/ca/gc\.sh$'' + ''^tests/functional/ca/import-derivation\.sh$'' + ''^tests/functional/ca/new-build-cmd\.sh$'' + ''^tests/functional/ca/nix-copy\.sh$'' + ''^tests/functional/ca/nix-run\.sh$'' + ''^tests/functional/ca/nix-shell\.sh$'' + ''^tests/functional/ca/post-hook\.sh$'' + ''^tests/functional/ca/recursive\.sh$'' + ''^tests/functional/ca/repl\.sh$'' + ''^tests/functional/ca/selfref-gc\.sh$'' + ''^tests/functional/ca/signatures\.sh$'' + ''^tests/functional/ca/substitute\.sh$'' + ''^tests/functional/ca/why-depends\.sh$'' + ''^tests/functional/case-hack\.sh$'' + ''^tests/functional/check-refs\.sh$'' + ''^tests/functional/check-reqs\.sh$'' + ''^tests/functional/check\.sh$'' + ''^tests/functional/chroot-store\.sh$'' + ''^tests/functional/common\.sh$'' + ''^tests/functional/common/init\.sh$'' + ''^tests/functional/completions\.sh$'' + ''^tests/functional/compression-levels\.sh$'' + ''^tests/functional/compute-levels\.sh$'' + ''^tests/functional/config\.sh$'' + ''^tests/functional/db-migration\.sh$'' + ''^tests/functional/debugger\.sh$'' + ''^tests/functional/dependencies\.builder0\.sh$'' + ''^tests/functional/dependencies\.sh$'' + ''^tests/functional/derivation-json\.sh$'' + ''^tests/functional/dump-db\.sh$'' + ''^tests/functional/dyn-drv/build-built-drv\.sh$'' + ''^tests/functional/dyn-drv/common\.sh$'' + ''^tests/functional/dyn-drv/dep-built-drv\.sh$'' + ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' + ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' + ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' + ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' + ''^tests/functional/eval-store\.sh$'' + ''^tests/functional/eval\.sh$'' + ''^tests/functional/experimental-features\.sh$'' + ''^tests/functional/export-graph\.sh$'' + ''^tests/functional/export\.sh$'' + ''^tests/functional/extra-sandbox-profile\.sh$'' + ''^tests/functional/fetchClosure\.sh$'' + ''^tests/functional/fetchGit\.sh$'' + ''^tests/functional/fetchGitRefs\.sh$'' + ''^tests/functional/fetchGitSubmodules\.sh$'' + ''^tests/functional/fetchGitVerification\.sh$'' + ''^tests/functional/fetchMercurial\.sh$'' + ''^tests/functional/fetchPath\.sh$'' + ''^tests/functional/fetchTree-file\.sh$'' + ''^tests/functional/fetchurl\.sh$'' + ''^tests/functional/filter-source\.sh$'' + ''^tests/functional/fixed\.builder1\.sh$'' + ''^tests/functional/fixed\.builder2\.sh$'' + ''^tests/functional/fixed\.sh$'' + ''^tests/functional/flakes/absolute-attr-paths\.sh$'' + ''^tests/functional/flakes/absolute-paths\.sh$'' + ''^tests/functional/flakes/build-paths\.sh$'' + ''^tests/functional/flakes/bundle\.sh$'' + ''^tests/functional/flakes/check\.sh$'' + ''^tests/functional/flakes/circular\.sh$'' + ''^tests/functional/flakes/common\.sh$'' + ''^tests/functional/flakes/config\.sh$'' + ''^tests/functional/flakes/develop\.sh$'' + ''^tests/functional/flakes/flake-in-submodule\.sh$'' + ''^tests/functional/flakes/flakes\.sh$'' + ''^tests/functional/flakes/follow-paths\.sh$'' + ''^tests/functional/flakes/init\.sh$'' + ''^tests/functional/flakes/inputs\.sh$'' + ''^tests/functional/flakes/mercurial\.sh$'' + ''^tests/functional/flakes/prefetch\.sh$'' + ''^tests/functional/flakes/run\.sh$'' + ''^tests/functional/flakes/search-root\.sh$'' + ''^tests/functional/flakes/show\.sh$'' + ''^tests/functional/flakes/unlocked-override\.sh$'' + ''^tests/functional/fmt\.sh$'' + ''^tests/functional/fmt\.simple\.sh$'' + ''^tests/functional/function-trace\.sh$'' + ''^tests/functional/gc-auto\.sh$'' + ''^tests/functional/gc-concurrent\.builder\.sh$'' + ''^tests/functional/gc-concurrent\.sh$'' + ''^tests/functional/gc-concurrent2\.builder\.sh$'' + ''^tests/functional/gc-non-blocking\.sh$'' + ''^tests/functional/gc-runtime\.sh$'' + ''^tests/functional/gc\.sh$'' + ''^tests/functional/git-hashing/common\.sh$'' + ''^tests/functional/git-hashing/simple\.sh$'' + ''^tests/functional/hash-convert\.sh$'' + ''^tests/functional/hash-path\.sh$'' + ''^tests/functional/help\.sh$'' + ''^tests/functional/import-derivation\.sh$'' + ''^tests/functional/impure-derivations\.sh$'' + ''^tests/functional/impure-env\.sh$'' + ''^tests/functional/impure-eval\.sh$'' + ''^tests/functional/install-darwin\.sh$'' + ''^tests/functional/lang-test-infra\.sh$'' + ''^tests/functional/lang\.sh$'' + ''^tests/functional/lang/framework\.sh$'' + ''^tests/functional/legacy-ssh-store\.sh$'' + ''^tests/functional/linux-sandbox\.sh$'' + ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' + ''^tests/functional/local-overlay-store/add-lower\.sh$'' + ''^tests/functional/local-overlay-store/bad-uris\.sh$'' + ''^tests/functional/local-overlay-store/build-inner\.sh$'' + ''^tests/functional/local-overlay-store/build\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init-inner\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init\.sh$'' + ''^tests/functional/local-overlay-store/common\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs\.sh$'' + ''^tests/functional/local-overlay-store/gc-inner\.sh$'' + ''^tests/functional/local-overlay-store/gc\.sh$'' + ''^tests/functional/local-overlay-store/optimise-inner\.sh$'' + ''^tests/functional/local-overlay-store/optimise\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add-inner\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add\.sh$'' + ''^tests/functional/local-overlay-store/remount\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle-inner\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle\.sh$'' + ''^tests/functional/local-overlay-store/verify-inner\.sh$'' + ''^tests/functional/local-overlay-store/verify\.sh$'' + ''^tests/functional/logging\.sh$'' + ''^tests/functional/misc\.sh$'' + ''^tests/functional/multiple-outputs\.sh$'' + ''^tests/functional/nar-access\.sh$'' + ''^tests/functional/nested-sandboxing\.sh$'' + ''^tests/functional/nested-sandboxing/command\.sh$'' + ''^tests/functional/nix-build\.sh$'' + ''^tests/functional/nix-channel\.sh$'' + ''^tests/functional/nix-collect-garbage-d\.sh$'' + ''^tests/functional/nix-copy-ssh-common\.sh$'' + ''^tests/functional/nix-copy-ssh-ng\.sh$'' + ''^tests/functional/nix-copy-ssh\.sh$'' + ''^tests/functional/nix-daemon-untrusting\.sh$'' + ''^tests/functional/nix-profile\.sh$'' + ''^tests/functional/nix-shell\.sh$'' + ''^tests/functional/nix_path\.sh$'' + ''^tests/functional/optimise-store\.sh$'' + ''^tests/functional/output-normalization\.sh$'' + ''^tests/functional/parallel\.builder\.sh$'' + ''^tests/functional/parallel\.sh$'' + ''^tests/functional/pass-as-file\.sh$'' + ''^tests/functional/path-from-hash-part\.sh$'' + ''^tests/functional/path-info\.sh$'' + ''^tests/functional/placeholders\.sh$'' + ''^tests/functional/plugins\.sh$'' + ''^tests/functional/post-hook\.sh$'' + ''^tests/functional/pure-eval\.sh$'' + ''^tests/functional/push-to-store-old\.sh$'' + ''^tests/functional/push-to-store\.sh$'' + ''^tests/functional/read-only-store\.sh$'' + ''^tests/functional/readfile-context\.sh$'' + ''^tests/functional/recursive\.sh$'' + ''^tests/functional/referrers\.sh$'' + ''^tests/functional/remote-store\.sh$'' + ''^tests/functional/repair\.sh$'' + ''^tests/functional/restricted\.sh$'' + ''^tests/functional/search\.sh$'' + ''^tests/functional/secure-drv-outputs\.sh$'' + ''^tests/functional/selfref-gc\.sh$'' + ''^tests/functional/shell\.sh$'' + ''^tests/functional/shell\.shebang\.sh$'' + ''^tests/functional/signing\.sh$'' + ''^tests/functional/simple\.builder\.sh$'' + ''^tests/functional/simple\.sh$'' + ''^tests/functional/ssh-relay\.sh$'' + ''^tests/functional/store-info\.sh$'' + ''^tests/functional/structured-attrs\.sh$'' + ''^tests/functional/substitute-with-invalid-ca\.sh$'' + ''^tests/functional/suggestions\.sh$'' + ''^tests/functional/supplementary-groups\.sh$'' + ''^tests/functional/tarball\.sh$'' + ''^tests/functional/test-infra\.sh$'' + ''^tests/functional/test-libstoreconsumer\.sh$'' + ''^tests/functional/timeout\.sh$'' + ''^tests/functional/toString-path\.sh$'' + ''^tests/functional/user-envs-migration\.sh$'' + ''^tests/functional/user-envs-test-case\.sh$'' + ''^tests/functional/user-envs\.builder\.sh$'' + ''^tests/functional/user-envs\.sh$'' + ''^tests/functional/why-depends\.sh$'' + ''^tests/functional/zstd\.sh$'' + ''^tests/unit/libutil/data/git/check-data\.sh$'' + ]; + }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 }; - - excludes = [ - # We don't want to format test data - # ''tests/(?!nixos/).*\.nix'' - ''^tests/unit/[^/]*/data/.*$'' - - # Don't format vendored code - ''^src/toml11/.*'' - ''^doc/manual/redirects\.js$'' - ''^doc/manual/theme/highlight\.js$'' - - # We haven't applied formatting to these files yet (C++) - ''^doc/manual/redirects\.js$'' - ''^doc/manual/theme/highlight\.js$'' - ''^precompiled-headers\.h$'' - ''^src/build-remote/build-remote\.cc$'' - ''^src/libcmd/built-path\.cc$'' - ''^src/libcmd/built-path\.hh$'' - ''^src/libcmd/command\.cc$'' - ''^src/libcmd/command\.hh$'' - ''^src/libcmd/common-eval-args\.cc$'' - ''^src/libcmd/common-eval-args\.hh$'' - ''^src/libcmd/editor-for\.cc$'' - ''^src/libcmd/installable-attr-path\.cc$'' - ''^src/libcmd/installable-attr-path\.hh$'' - ''^src/libcmd/installable-derived-path\.cc$'' - ''^src/libcmd/installable-derived-path\.hh$'' - ''^src/libcmd/installable-flake\.cc$'' - ''^src/libcmd/installable-flake\.hh$'' - ''^src/libcmd/installable-value\.cc$'' - ''^src/libcmd/installable-value\.hh$'' - ''^src/libcmd/installables\.cc$'' - ''^src/libcmd/installables\.hh$'' - ''^src/libcmd/legacy\.hh$'' - ''^src/libcmd/markdown\.cc$'' - ''^src/libcmd/misc-store-flags\.cc$'' - ''^src/libcmd/repl-interacter\.cc$'' - ''^src/libcmd/repl-interacter\.hh$'' - ''^src/libcmd/repl\.cc$'' - ''^src/libcmd/repl\.hh$'' - ''^src/libexpr-c/nix_api_expr\.cc$'' - ''^src/libexpr-c/nix_api_external\.cc$'' - ''^src/libexpr/attr-path\.cc$'' - ''^src/libexpr/attr-path\.hh$'' - ''^src/libexpr/attr-set\.cc$'' - ''^src/libexpr/attr-set\.hh$'' - ''^src/libexpr/eval-cache\.cc$'' - ''^src/libexpr/eval-cache\.hh$'' - ''^src/libexpr/eval-error\.cc$'' - ''^src/libexpr/eval-inline\.hh$'' - ''^src/libexpr/eval-settings\.cc$'' - ''^src/libexpr/eval-settings\.hh$'' - ''^src/libexpr/eval\.cc$'' - ''^src/libexpr/eval\.hh$'' - ''^src/libexpr/flake/config\.cc$'' - ''^src/libexpr/flake/flake\.cc$'' - ''^src/libexpr/flake/flake\.hh$'' - ''^src/libexpr/flake/flakeref\.cc$'' - ''^src/libexpr/flake/flakeref\.hh$'' - ''^src/libexpr/flake/lockfile\.cc$'' - ''^src/libexpr/flake/lockfile\.hh$'' - ''^src/libexpr/flake/url-name\.cc$'' - ''^src/libexpr/function-trace\.cc$'' - ''^src/libexpr/gc-small-vector\.hh$'' - ''^src/libexpr/get-drvs\.cc$'' - ''^src/libexpr/get-drvs\.hh$'' - ''^src/libexpr/json-to-value\.cc$'' - ''^src/libexpr/nixexpr\.cc$'' - ''^src/libexpr/nixexpr\.hh$'' - ''^src/libexpr/parser-state\.hh$'' - ''^src/libexpr/pos-table\.hh$'' - ''^src/libexpr/primops\.cc$'' - ''^src/libexpr/primops\.hh$'' - ''^src/libexpr/primops/context\.cc$'' - ''^src/libexpr/primops/fetchClosure\.cc$'' - ''^src/libexpr/primops/fetchMercurial\.cc$'' - ''^src/libexpr/primops/fetchTree\.cc$'' - ''^src/libexpr/primops/fromTOML\.cc$'' - ''^src/libexpr/print-ambiguous\.cc$'' - ''^src/libexpr/print-ambiguous\.hh$'' - ''^src/libexpr/print-options\.hh$'' - ''^src/libexpr/print\.cc$'' - ''^src/libexpr/print\.hh$'' - ''^src/libexpr/search-path\.cc$'' - ''^src/libexpr/symbol-table\.hh$'' - ''^src/libexpr/value-to-json\.cc$'' - ''^src/libexpr/value-to-json\.hh$'' - ''^src/libexpr/value-to-xml\.cc$'' - ''^src/libexpr/value-to-xml\.hh$'' - ''^src/libexpr/value\.hh$'' - ''^src/libexpr/value/context\.cc$'' - ''^src/libexpr/value/context\.hh$'' - ''^src/libfetchers/attrs\.cc$'' - ''^src/libfetchers/cache\.cc$'' - ''^src/libfetchers/cache\.hh$'' - ''^src/libfetchers/fetch-settings\.cc$'' - ''^src/libfetchers/fetch-settings\.hh$'' - ''^src/libfetchers/fetch-to-store\.cc$'' - ''^src/libfetchers/fetchers\.cc$'' - ''^src/libfetchers/fetchers\.hh$'' - ''^src/libfetchers/filtering-source-accessor\.cc$'' - ''^src/libfetchers/filtering-source-accessor\.hh$'' - ''^src/libfetchers/fs-source-accessor\.cc$'' - ''^src/libfetchers/fs-source-accessor\.hh$'' - ''^src/libfetchers/git-utils\.cc$'' - ''^src/libfetchers/git-utils\.hh$'' - ''^src/libfetchers/github\.cc$'' - ''^src/libfetchers/indirect\.cc$'' - ''^src/libfetchers/memory-source-accessor\.cc$'' - ''^src/libfetchers/path\.cc$'' - ''^src/libfetchers/registry\.cc$'' - ''^src/libfetchers/registry\.hh$'' - ''^src/libfetchers/tarball\.cc$'' - ''^src/libfetchers/tarball\.hh$'' - ''^src/libfetchers/unix/git\.cc$'' - ''^src/libfetchers/unix/mercurial\.cc$'' - ''^src/libmain/common-args\.cc$'' - ''^src/libmain/common-args\.hh$'' - ''^src/libmain/loggers\.cc$'' - ''^src/libmain/loggers\.hh$'' - ''^src/libmain/progress-bar\.cc$'' - ''^src/libmain/shared\.cc$'' - ''^src/libmain/shared\.hh$'' - ''^src/libmain/unix/stack\.cc$'' - ''^src/libstore/binary-cache-store\.cc$'' - ''^src/libstore/binary-cache-store\.hh$'' - ''^src/libstore/build-result\.hh$'' - ''^src/libstore/builtins\.hh$'' - ''^src/libstore/builtins/buildenv\.cc$'' - ''^src/libstore/builtins/buildenv\.hh$'' - ''^src/libstore/common-protocol-impl\.hh$'' - ''^src/libstore/common-protocol\.cc$'' - ''^src/libstore/common-protocol\.hh$'' - ''^src/libstore/content-address\.cc$'' - ''^src/libstore/content-address\.hh$'' - ''^src/libstore/daemon\.cc$'' - ''^src/libstore/daemon\.hh$'' - ''^src/libstore/derivations\.cc$'' - ''^src/libstore/derivations\.hh$'' - ''^src/libstore/derived-path-map\.cc$'' - ''^src/libstore/derived-path-map\.hh$'' - ''^src/libstore/derived-path\.cc$'' - ''^src/libstore/derived-path\.hh$'' - ''^src/libstore/downstream-placeholder\.cc$'' - ''^src/libstore/downstream-placeholder\.hh$'' - ''^src/libstore/dummy-store\.cc$'' - ''^src/libstore/export-import\.cc$'' - ''^src/libstore/filetransfer\.cc$'' - ''^src/libstore/filetransfer\.hh$'' - ''^src/libstore/gc-store\.hh$'' - ''^src/libstore/globals\.cc$'' - ''^src/libstore/globals\.hh$'' - ''^src/libstore/http-binary-cache-store\.cc$'' - ''^src/libstore/legacy-ssh-store\.cc$'' - ''^src/libstore/legacy-ssh-store\.hh$'' - ''^src/libstore/length-prefixed-protocol-helper\.hh$'' - ''^src/libstore/linux/personality\.cc$'' - ''^src/libstore/linux/personality\.hh$'' - ''^src/libstore/local-binary-cache-store\.cc$'' - ''^src/libstore/local-fs-store\.cc$'' - ''^src/libstore/local-fs-store\.hh$'' - ''^src/libstore/log-store\.cc$'' - ''^src/libstore/log-store\.hh$'' - ''^src/libstore/machines\.cc$'' - ''^src/libstore/machines\.hh$'' - ''^src/libstore/make-content-addressed\.cc$'' - ''^src/libstore/make-content-addressed\.hh$'' - ''^src/libstore/misc\.cc$'' - ''^src/libstore/names\.cc$'' - ''^src/libstore/names\.hh$'' - ''^src/libstore/nar-accessor\.cc$'' - ''^src/libstore/nar-accessor\.hh$'' - ''^src/libstore/nar-info-disk-cache\.cc$'' - ''^src/libstore/nar-info-disk-cache\.hh$'' - ''^src/libstore/nar-info\.cc$'' - ''^src/libstore/nar-info\.hh$'' - ''^src/libstore/outputs-spec\.cc$'' - ''^src/libstore/outputs-spec\.hh$'' - ''^src/libstore/parsed-derivations\.cc$'' - ''^src/libstore/path-info\.cc$'' - ''^src/libstore/path-info\.hh$'' - ''^src/libstore/path-references\.cc$'' - ''^src/libstore/path-regex\.hh$'' - ''^src/libstore/path-with-outputs\.cc$'' - ''^src/libstore/path\.cc$'' - ''^src/libstore/path\.hh$'' - ''^src/libstore/pathlocks\.cc$'' - ''^src/libstore/pathlocks\.hh$'' - ''^src/libstore/profiles\.cc$'' - ''^src/libstore/profiles\.hh$'' - ''^src/libstore/realisation\.cc$'' - ''^src/libstore/realisation\.hh$'' - ''^src/libstore/remote-fs-accessor\.cc$'' - ''^src/libstore/remote-fs-accessor\.hh$'' - ''^src/libstore/remote-store-connection\.hh$'' - ''^src/libstore/remote-store\.cc$'' - ''^src/libstore/remote-store\.hh$'' - ''^src/libstore/s3-binary-cache-store\.cc$'' - ''^src/libstore/s3\.hh$'' - ''^src/libstore/serve-protocol-impl\.cc$'' - ''^src/libstore/serve-protocol-impl\.hh$'' - ''^src/libstore/serve-protocol\.cc$'' - ''^src/libstore/serve-protocol\.hh$'' - ''^src/libstore/sqlite\.cc$'' - ''^src/libstore/sqlite\.hh$'' - ''^src/libstore/ssh-store-config\.hh$'' - ''^src/libstore/ssh-store\.cc$'' - ''^src/libstore/ssh\.cc$'' - ''^src/libstore/ssh\.hh$'' - ''^src/libstore/store-api\.cc$'' - ''^src/libstore/store-api\.hh$'' - ''^src/libstore/store-dir-config\.hh$'' - ''^src/libstore/unix/build/derivation-goal\.cc$'' - ''^src/libstore/unix/build/derivation-goal\.hh$'' - ''^src/libstore/build/drv-output-substitution-goal\.cc$'' - ''^src/libstore/build/drv-output-substitution-goal\.hh$'' - ''^src/libstore/build/entry-points\.cc$'' - ''^src/libstore/build/goal\.cc$'' - ''^src/libstore/build/goal\.hh$'' - ''^src/libstore/unix/build/hook-instance\.cc$'' - ''^src/libstore/unix/build/local-derivation-goal\.cc$'' - ''^src/libstore/unix/build/local-derivation-goal\.hh$'' - ''^src/libstore/build/substitution-goal\.cc$'' - ''^src/libstore/build/substitution-goal\.hh$'' - ''^src/libstore/build/worker\.cc$'' - ''^src/libstore/build/worker\.hh$'' - ''^src/libstore/unix/builtins/fetchurl\.cc$'' - ''^src/libstore/unix/builtins/unpack-channel\.cc$'' - ''^src/libstore/gc\.cc$'' - ''^src/libstore/unix/local-overlay-store\.cc$'' - ''^src/libstore/unix/local-overlay-store\.hh$'' - ''^src/libstore/local-store\.cc$'' - ''^src/libstore/local-store\.hh$'' - ''^src/libstore/unix/user-lock\.cc$'' - ''^src/libstore/unix/user-lock\.hh$'' - ''^src/libstore/optimise-store\.cc$'' - ''^src/libstore/unix/pathlocks\.cc$'' - ''^src/libstore/posix-fs-canonicalise\.cc$'' - ''^src/libstore/posix-fs-canonicalise\.hh$'' - ''^src/libstore/uds-remote-store\.cc$'' - ''^src/libstore/uds-remote-store\.hh$'' - ''^src/libstore/windows/build\.cc$'' - ''^src/libstore/worker-protocol-impl\.hh$'' - ''^src/libstore/worker-protocol\.cc$'' - ''^src/libstore/worker-protocol\.hh$'' - ''^src/libutil-c/nix_api_util_internal\.h$'' - ''^src/libutil/archive\.cc$'' - ''^src/libutil/archive\.hh$'' - ''^src/libutil/args\.cc$'' - ''^src/libutil/args\.hh$'' - ''^src/libutil/args/root\.hh$'' - ''^src/libutil/callback\.hh$'' - ''^src/libutil/canon-path\.cc$'' - ''^src/libutil/canon-path\.hh$'' - ''^src/libutil/chunked-vector\.hh$'' - ''^src/libutil/closure\.hh$'' - ''^src/libutil/comparator\.hh$'' - ''^src/libutil/compute-levels\.cc$'' - ''^src/libutil/config-impl\.hh$'' - ''^src/libutil/config\.cc$'' - ''^src/libutil/config\.hh$'' - ''^src/libutil/current-process\.cc$'' - ''^src/libutil/current-process\.hh$'' - ''^src/libutil/english\.cc$'' - ''^src/libutil/english\.hh$'' - ''^src/libutil/environment-variables\.cc$'' - ''^src/libutil/error\.cc$'' - ''^src/libutil/error\.hh$'' - ''^src/libutil/exit\.hh$'' - ''^src/libutil/experimental-features\.cc$'' - ''^src/libutil/experimental-features\.hh$'' - ''^src/libutil/file-content-address\.cc$'' - ''^src/libutil/file-content-address\.hh$'' - ''^src/libutil/file-descriptor\.cc$'' - ''^src/libutil/file-descriptor\.hh$'' - ''^src/libutil/file-path-impl\.hh$'' - ''^src/libutil/file-path\.hh$'' - ''^src/libutil/file-system\.cc$'' - ''^src/libutil/file-system\.hh$'' - ''^src/libutil/finally\.hh$'' - ''^src/libutil/fmt\.hh$'' - ''^src/libutil/fs-sink\.cc$'' - ''^src/libutil/fs-sink\.hh$'' - ''^src/libutil/git\.cc$'' - ''^src/libutil/git\.hh$'' - ''^src/libutil/hash\.cc$'' - ''^src/libutil/hash\.hh$'' - ''^src/libutil/hilite\.cc$'' - ''^src/libutil/hilite\.hh$'' - ''^src/libutil/source-accessor\.hh$'' - ''^src/libutil/json-impls\.hh$'' - ''^src/libutil/json-utils\.cc$'' - ''^src/libutil/json-utils\.hh$'' - ''^src/libutil/linux/cgroup\.cc$'' - ''^src/libutil/linux/namespaces\.cc$'' - ''^src/libutil/logging\.cc$'' - ''^src/libutil/logging\.hh$'' - ''^src/libutil/lru-cache\.hh$'' - ''^src/libutil/memory-source-accessor\.cc$'' - ''^src/libutil/memory-source-accessor\.hh$'' - ''^src/libutil/pool\.hh$'' - ''^src/libutil/position\.cc$'' - ''^src/libutil/position\.hh$'' - ''^src/libutil/posix-source-accessor\.cc$'' - ''^src/libutil/posix-source-accessor\.hh$'' - ''^src/libutil/processes\.hh$'' - ''^src/libutil/ref\.hh$'' - ''^src/libutil/references\.cc$'' - ''^src/libutil/references\.hh$'' - ''^src/libutil/regex-combinators\.hh$'' - ''^src/libutil/serialise\.cc$'' - ''^src/libutil/serialise\.hh$'' - ''^src/libutil/signals\.hh$'' - ''^src/libutil/signature/local-keys\.cc$'' - ''^src/libutil/signature/local-keys\.hh$'' - ''^src/libutil/signature/signer\.cc$'' - ''^src/libutil/signature/signer\.hh$'' - ''^src/libutil/source-accessor\.cc$'' - ''^src/libutil/source-accessor\.hh$'' - ''^src/libutil/source-path\.cc$'' - ''^src/libutil/source-path\.hh$'' - ''^src/libutil/split\.hh$'' - ''^src/libutil/suggestions\.cc$'' - ''^src/libutil/suggestions\.hh$'' - ''^src/libutil/sync\.hh$'' - ''^src/libutil/terminal\.cc$'' - ''^src/libutil/terminal\.hh$'' - ''^src/libutil/thread-pool\.cc$'' - ''^src/libutil/thread-pool\.hh$'' - ''^src/libutil/topo-sort\.hh$'' - ''^src/libutil/types\.hh$'' - ''^src/libutil/unix/file-descriptor\.cc$'' - ''^src/libutil/unix/file-path\.cc$'' - ''^src/libutil/unix/monitor-fd\.hh$'' - ''^src/libutil/unix/processes\.cc$'' - ''^src/libutil/unix/signals-impl\.hh$'' - ''^src/libutil/unix/signals\.cc$'' - ''^src/libutil/unix-domain-socket\.cc$'' - ''^src/libutil/unix/users\.cc$'' - ''^src/libutil/url-parts\.hh$'' - ''^src/libutil/url\.cc$'' - ''^src/libutil/url\.hh$'' - ''^src/libutil/users\.cc$'' - ''^src/libutil/users\.hh$'' - ''^src/libutil/util\.cc$'' - ''^src/libutil/util\.hh$'' - ''^src/libutil/variant-wrapper\.hh$'' - ''^src/libutil/windows/environment-variables\.cc$'' - ''^src/libutil/windows/file-descriptor\.cc$'' - ''^src/libutil/windows/file-path\.cc$'' - ''^src/libutil/windows/processes\.cc$'' - ''^src/libutil/windows/users\.cc$'' - ''^src/libutil/windows/windows-error\.cc$'' - ''^src/libutil/windows/windows-error\.hh$'' - ''^src/libutil/xml-writer\.cc$'' - ''^src/libutil/xml-writer\.hh$'' - ''^src/nix-build/nix-build\.cc$'' - ''^src/nix-channel/nix-channel\.cc$'' - ''^src/nix-collect-garbage/nix-collect-garbage\.cc$'' - ''^src/nix-env/buildenv.nix$'' - ''^src/nix-env/nix-env\.cc$'' - ''^src/nix-env/user-env\.cc$'' - ''^src/nix-env/user-env\.hh$'' - ''^src/nix-instantiate/nix-instantiate\.cc$'' - ''^src/nix-store/dotgraph\.cc$'' - ''^src/nix-store/graphml\.cc$'' - ''^src/nix-store/nix-store\.cc$'' - ''^src/nix/add-to-store\.cc$'' - ''^src/nix/app\.cc$'' - ''^src/nix/build\.cc$'' - ''^src/nix/bundle\.cc$'' - ''^src/nix/cat\.cc$'' - ''^src/nix/config-check\.cc$'' - ''^src/nix/config\.cc$'' - ''^src/nix/copy\.cc$'' - ''^src/nix/derivation-add\.cc$'' - ''^src/nix/derivation-show\.cc$'' - ''^src/nix/derivation\.cc$'' - ''^src/nix/develop\.cc$'' - ''^src/nix/diff-closures\.cc$'' - ''^src/nix/dump-path\.cc$'' - ''^src/nix/edit\.cc$'' - ''^src/nix/eval\.cc$'' - ''^src/nix/flake\.cc$'' - ''^src/nix/fmt\.cc$'' - ''^src/nix/hash\.cc$'' - ''^src/nix/log\.cc$'' - ''^src/nix/ls\.cc$'' - ''^src/nix/main\.cc$'' - ''^src/nix/make-content-addressed\.cc$'' - ''^src/nix/nar\.cc$'' - ''^src/nix/optimise-store\.cc$'' - ''^src/nix/path-from-hash-part\.cc$'' - ''^src/nix/path-info\.cc$'' - ''^src/nix/prefetch\.cc$'' - ''^src/nix/profile\.cc$'' - ''^src/nix/realisation\.cc$'' - ''^src/nix/registry\.cc$'' - ''^src/nix/repl\.cc$'' - ''^src/nix/run\.cc$'' - ''^src/nix/run\.hh$'' - ''^src/nix/search\.cc$'' - ''^src/nix/sigs\.cc$'' - ''^src/nix/store-copy-log\.cc$'' - ''^src/nix/store-delete\.cc$'' - ''^src/nix/store-gc\.cc$'' - ''^src/nix/store-info\.cc$'' - ''^src/nix/store-repair\.cc$'' - ''^src/nix/store\.cc$'' - ''^src/nix/unix/daemon\.cc$'' - ''^src/nix/upgrade-nix\.cc$'' - ''^src/nix/verify\.cc$'' - ''^src/nix/why-depends\.cc$'' - - ''^tests/functional/plugins/plugintest\.cc'' - ''^tests/functional/test-libstoreconsumer/main\.cc'' - ''^tests/nixos/ca-fd-leak/sender\.c'' - ''^tests/nixos/ca-fd-leak/smuggler\.c'' - ''^tests/unit/libexpr-support/tests/libexpr\.hh'' - ''^tests/unit/libexpr-support/tests/value/context\.cc'' - ''^tests/unit/libexpr-support/tests/value/context\.hh'' - ''^tests/unit/libexpr/derived-path\.cc'' - ''^tests/unit/libexpr/error_traces\.cc'' - ''^tests/unit/libexpr/eval\.cc'' - ''^tests/unit/libexpr/flake/flakeref\.cc'' - ''^tests/unit/libexpr/flake/url-name\.cc'' - ''^tests/unit/libexpr/json\.cc'' - ''^tests/unit/libexpr/main\.cc'' - ''^tests/unit/libexpr/primops\.cc'' - ''^tests/unit/libexpr/search-path\.cc'' - ''^tests/unit/libexpr/trivial\.cc'' - ''^tests/unit/libexpr/value/context\.cc'' - ''^tests/unit/libexpr/value/print\.cc'' - ''^tests/unit/libfetchers/public-key\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.hh'' - ''^tests/unit/libstore-support/tests/libstore\.hh'' - ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' - ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' - ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' - ''^tests/unit/libstore-support/tests/path\.cc'' - ''^tests/unit/libstore-support/tests/path\.hh'' - ''^tests/unit/libstore-support/tests/protocol\.hh'' - ''^tests/unit/libstore/common-protocol\.cc'' - ''^tests/unit/libstore/content-address\.cc'' - ''^tests/unit/libstore/derivation\.cc'' - ''^tests/unit/libstore/derived-path\.cc'' - ''^tests/unit/libstore/downstream-placeholder\.cc'' - ''^tests/unit/libstore/machines\.cc'' - ''^tests/unit/libstore/nar-info-disk-cache\.cc'' - ''^tests/unit/libstore/nar-info\.cc'' - ''^tests/unit/libstore/outputs-spec\.cc'' - ''^tests/unit/libstore/path-info\.cc'' - ''^tests/unit/libstore/path\.cc'' - ''^tests/unit/libstore/serve-protocol\.cc'' - ''^tests/unit/libstore/worker-protocol\.cc'' - ''^tests/unit/libutil-support/tests/characterization\.hh'' - ''^tests/unit/libutil-support/tests/hash\.cc'' - ''^tests/unit/libutil-support/tests/hash\.hh'' - ''^tests/unit/libutil/args\.cc'' - ''^tests/unit/libutil/canon-path\.cc'' - ''^tests/unit/libutil/chunked-vector\.cc'' - ''^tests/unit/libutil/closure\.cc'' - ''^tests/unit/libutil/compression\.cc'' - ''^tests/unit/libutil/config\.cc'' - ''^tests/unit/libutil/file-content-address\.cc'' - ''^tests/unit/libutil/git\.cc'' - ''^tests/unit/libutil/hash\.cc'' - ''^tests/unit/libutil/hilite\.cc'' - ''^tests/unit/libutil/json-utils\.cc'' - ''^tests/unit/libutil/logging\.cc'' - ''^tests/unit/libutil/lru-cache\.cc'' - ''^tests/unit/libutil/pool\.cc'' - ''^tests/unit/libutil/references\.cc'' - ''^tests/unit/libutil/suggestions\.cc'' - ''^tests/unit/libutil/tests\.cc'' - ''^tests/unit/libutil/url\.cc'' - ''^tests/unit/libutil/xml-writer\.cc'' - - # We haven't lintted these files yet (shell) - ''^config/install-sh$'' - ''^misc/systemv/nix-daemon$'' - ''^misc/bash/completion\.sh$'' - ''^misc/fish/completion\.fish$'' - ''^misc/zsh/completion\.zsh$'' - ''^scripts/check-hydra-status\.sh$'' - ''^scripts/create-darwin-volume\.sh$'' - ''^scripts/install-darwin-multi-user\.sh$'' - ''^scripts/install-multi-user\.sh$'' - ''^scripts/install-nix-from-closure\.sh$'' - ''^scripts/install-systemd-multi-user\.sh$'' - ''^src/nix/get-env\.sh$'' - ''^tests/functional/add\.sh$'' - ''^tests/functional/bash-profile\.sh$'' - ''^tests/functional/binary-cache-build-remote\.sh$'' - ''^tests/functional/binary-cache\.sh$'' - ''^tests/functional/brotli\.sh$'' - ''^tests/functional/build-delete\.sh$'' - ''^tests/functional/build-dry\.sh$'' - ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' - ''^tests/functional/build-remote-content-addressed-floating\.sh$'' - ''^tests/functional/build-remote-input-addressed\.sh$'' - ''^tests/functional/build-remote-trustless-after\.sh$'' - ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' - ''^tests/functional/build-remote-trustless\.sh$'' - ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' - ''^tests/functional/build-remote\.sh$'' - ''^tests/functional/build\.sh$'' - ''^tests/functional/ca/build-cache\.sh$'' - ''^tests/functional/ca/build-dry\.sh$'' - ''^tests/functional/ca/build-with-garbage-path\.sh$'' - ''^tests/functional/ca/build\.sh$'' - ''^tests/functional/ca/common\.sh$'' - ''^tests/functional/ca/concurrent-builds\.sh$'' - ''^tests/functional/ca/derivation-json\.sh$'' - ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' - ''^tests/functional/ca/eval-store\.sh$'' - ''^tests/functional/ca/gc\.sh$'' - ''^tests/functional/ca/import-derivation\.sh$'' - ''^tests/functional/ca/new-build-cmd\.sh$'' - ''^tests/functional/ca/nix-copy\.sh$'' - ''^tests/functional/ca/nix-run\.sh$'' - ''^tests/functional/ca/nix-shell\.sh$'' - ''^tests/functional/ca/post-hook\.sh$'' - ''^tests/functional/ca/recursive\.sh$'' - ''^tests/functional/ca/repl\.sh$'' - ''^tests/functional/ca/selfref-gc\.sh$'' - ''^tests/functional/ca/signatures\.sh$'' - ''^tests/functional/ca/substitute\.sh$'' - ''^tests/functional/ca/why-depends\.sh$'' - ''^tests/functional/case-hack\.sh$'' - ''^tests/functional/check-refs\.sh$'' - ''^tests/functional/check-reqs\.sh$'' - ''^tests/functional/check\.sh$'' - ''^tests/functional/chroot-store\.sh$'' - ''^tests/functional/common\.sh$'' - ''^tests/functional/common/init\.sh$'' - ''^tests/functional/completions\.sh$'' - ''^tests/functional/compression-levels\.sh$'' - ''^tests/functional/compute-levels\.sh$'' - ''^tests/functional/config\.sh$'' - ''^tests/functional/db-migration\.sh$'' - ''^tests/functional/debugger\.sh$'' - ''^tests/functional/dependencies\.builder0\.sh$'' - ''^tests/functional/dependencies\.sh$'' - ''^tests/functional/derivation-json\.sh$'' - ''^tests/functional/dump-db\.sh$'' - ''^tests/functional/dyn-drv/build-built-drv\.sh$'' - ''^tests/functional/dyn-drv/common\.sh$'' - ''^tests/functional/dyn-drv/dep-built-drv\.sh$'' - ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' - ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' - ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' - ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' - ''^tests/functional/eval-store\.sh$'' - ''^tests/functional/eval\.sh$'' - ''^tests/functional/experimental-features\.sh$'' - ''^tests/functional/export-graph\.sh$'' - ''^tests/functional/export\.sh$'' - ''^tests/functional/extra-sandbox-profile\.sh$'' - ''^tests/functional/fetchClosure\.sh$'' - ''^tests/functional/fetchGit\.sh$'' - ''^tests/functional/fetchGitRefs\.sh$'' - ''^tests/functional/fetchGitSubmodules\.sh$'' - ''^tests/functional/fetchGitVerification\.sh$'' - ''^tests/functional/fetchMercurial\.sh$'' - ''^tests/functional/fetchPath\.sh$'' - ''^tests/functional/fetchTree-file\.sh$'' - ''^tests/functional/fetchurl\.sh$'' - ''^tests/functional/filter-source\.sh$'' - ''^tests/functional/fixed\.builder1\.sh$'' - ''^tests/functional/fixed\.builder2\.sh$'' - ''^tests/functional/fixed\.sh$'' - ''^tests/functional/flakes/absolute-attr-paths\.sh$'' - ''^tests/functional/flakes/absolute-paths\.sh$'' - ''^tests/functional/flakes/build-paths\.sh$'' - ''^tests/functional/flakes/bundle\.sh$'' - ''^tests/functional/flakes/check\.sh$'' - ''^tests/functional/flakes/circular\.sh$'' - ''^tests/functional/flakes/common\.sh$'' - ''^tests/functional/flakes/config\.sh$'' - ''^tests/functional/flakes/develop\.sh$'' - ''^tests/functional/flakes/flake-in-submodule\.sh$'' - ''^tests/functional/flakes/flakes\.sh$'' - ''^tests/functional/flakes/follow-paths\.sh$'' - ''^tests/functional/flakes/init\.sh$'' - ''^tests/functional/flakes/inputs\.sh$'' - ''^tests/functional/flakes/mercurial\.sh$'' - ''^tests/functional/flakes/prefetch\.sh$'' - ''^tests/functional/flakes/run\.sh$'' - ''^tests/functional/flakes/search-root\.sh$'' - ''^tests/functional/flakes/show\.sh$'' - ''^tests/functional/flakes/unlocked-override\.sh$'' - ''^tests/functional/fmt\.sh$'' - ''^tests/functional/fmt\.simple\.sh$'' - ''^tests/functional/function-trace\.sh$'' - ''^tests/functional/gc-auto\.sh$'' - ''^tests/functional/gc-concurrent\.builder\.sh$'' - ''^tests/functional/gc-concurrent\.sh$'' - ''^tests/functional/gc-concurrent2\.builder\.sh$'' - ''^tests/functional/gc-non-blocking\.sh$'' - ''^tests/functional/gc-runtime\.sh$'' - ''^tests/functional/gc\.sh$'' - ''^tests/functional/git-hashing/common\.sh$'' - ''^tests/functional/git-hashing/simple\.sh$'' - ''^tests/functional/hash-convert\.sh$'' - ''^tests/functional/hash-path\.sh$'' - ''^tests/functional/help\.sh$'' - ''^tests/functional/import-derivation\.sh$'' - ''^tests/functional/impure-derivations\.sh$'' - ''^tests/functional/impure-env\.sh$'' - ''^tests/functional/impure-eval\.sh$'' - ''^tests/functional/install-darwin\.sh$'' - ''^tests/functional/lang-test-infra\.sh$'' - ''^tests/functional/lang\.sh$'' - ''^tests/functional/lang/framework\.sh$'' - ''^tests/functional/legacy-ssh-store\.sh$'' - ''^tests/functional/linux-sandbox\.sh$'' - ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' - ''^tests/functional/local-overlay-store/add-lower\.sh$'' - ''^tests/functional/local-overlay-store/bad-uris\.sh$'' - ''^tests/functional/local-overlay-store/build-inner\.sh$'' - ''^tests/functional/local-overlay-store/build\.sh$'' - ''^tests/functional/local-overlay-store/check-post-init-inner\.sh$'' - ''^tests/functional/local-overlay-store/check-post-init\.sh$'' - ''^tests/functional/local-overlay-store/common\.sh$'' - ''^tests/functional/local-overlay-store/delete-duplicate-inner\.sh$'' - ''^tests/functional/local-overlay-store/delete-duplicate\.sh$'' - ''^tests/functional/local-overlay-store/delete-refs-inner\.sh$'' - ''^tests/functional/local-overlay-store/delete-refs\.sh$'' - ''^tests/functional/local-overlay-store/gc-inner\.sh$'' - ''^tests/functional/local-overlay-store/gc\.sh$'' - ''^tests/functional/local-overlay-store/optimise-inner\.sh$'' - ''^tests/functional/local-overlay-store/optimise\.sh$'' - ''^tests/functional/local-overlay-store/redundant-add-inner\.sh$'' - ''^tests/functional/local-overlay-store/redundant-add\.sh$'' - ''^tests/functional/local-overlay-store/remount\.sh$'' - ''^tests/functional/local-overlay-store/stale-file-handle-inner\.sh$'' - ''^tests/functional/local-overlay-store/stale-file-handle\.sh$'' - ''^tests/functional/local-overlay-store/verify-inner\.sh$'' - ''^tests/functional/local-overlay-store/verify\.sh$'' - ''^tests/functional/logging\.sh$'' - ''^tests/functional/misc\.sh$'' - ''^tests/functional/multiple-outputs\.sh$'' - ''^tests/functional/nar-access\.sh$'' - ''^tests/functional/nested-sandboxing\.sh$'' - ''^tests/functional/nested-sandboxing/command\.sh$'' - ''^tests/functional/nix-build\.sh$'' - ''^tests/functional/nix-channel\.sh$'' - ''^tests/functional/nix-collect-garbage-d\.sh$'' - ''^tests/functional/nix-copy-ssh-common\.sh$'' - ''^tests/functional/nix-copy-ssh-ng\.sh$'' - ''^tests/functional/nix-copy-ssh\.sh$'' - ''^tests/functional/nix-daemon-untrusting\.sh$'' - ''^tests/functional/nix-profile\.sh$'' - ''^tests/functional/nix-shell\.sh$'' - ''^tests/functional/nix_path\.sh$'' - ''^tests/functional/optimise-store\.sh$'' - ''^tests/functional/output-normalization\.sh$'' - ''^tests/functional/parallel\.builder\.sh$'' - ''^tests/functional/parallel\.sh$'' - ''^tests/functional/pass-as-file\.sh$'' - ''^tests/functional/path-from-hash-part\.sh$'' - ''^tests/functional/path-info\.sh$'' - ''^tests/functional/placeholders\.sh$'' - ''^tests/functional/plugins\.sh$'' - ''^tests/functional/post-hook\.sh$'' - ''^tests/functional/pure-eval\.sh$'' - ''^tests/functional/push-to-store-old\.sh$'' - ''^tests/functional/push-to-store\.sh$'' - ''^tests/functional/read-only-store\.sh$'' - ''^tests/functional/readfile-context\.sh$'' - ''^tests/functional/recursive\.sh$'' - ''^tests/functional/referrers\.sh$'' - ''^tests/functional/remote-store\.sh$'' - ''^tests/functional/repair\.sh$'' - ''^tests/functional/restricted\.sh$'' - ''^tests/functional/search\.sh$'' - ''^tests/functional/secure-drv-outputs\.sh$'' - ''^tests/functional/selfref-gc\.sh$'' - ''^tests/functional/shell\.sh$'' - ''^tests/functional/shell\.shebang\.sh$'' - ''^tests/functional/signing\.sh$'' - ''^tests/functional/simple\.builder\.sh$'' - ''^tests/functional/simple\.sh$'' - ''^tests/functional/ssh-relay\.sh$'' - ''^tests/functional/store-info\.sh$'' - ''^tests/functional/structured-attrs\.sh$'' - ''^tests/functional/substitute-with-invalid-ca\.sh$'' - ''^tests/functional/suggestions\.sh$'' - ''^tests/functional/supplementary-groups\.sh$'' - ''^tests/functional/tarball\.sh$'' - ''^tests/functional/test-infra\.sh$'' - ''^tests/functional/test-libstoreconsumer\.sh$'' - ''^tests/functional/timeout\.sh$'' - ''^tests/functional/toString-path\.sh$'' - ''^tests/functional/user-envs-migration\.sh$'' - ''^tests/functional/user-envs-test-case\.sh$'' - ''^tests/functional/user-envs\.builder\.sh$'' - ''^tests/functional/user-envs\.sh$'' - ''^tests/functional/why-depends\.sh$'' - ''^tests/functional/zstd\.sh$'' - ''^tests/unit/libutil/data/git/check-data\.sh$'' - ]; }; - }; # We'll be pulling from this in the main flake From bcdee80a0d80071b05a9c1bc5e649e762dcd1ecc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 17:54:02 -0400 Subject: [PATCH 168/910] More work on the scheduler for windows - Get a rump derivation goal: hook instance will come later, local derivation goal will come after that. - Start cleaning up the channel / waiting code with an abstraction. --- maintainers/flake-module.nix | 4 +- .../{unix => }/build/derivation-goal.cc | 77 ++++++--- .../{unix => }/build/derivation-goal.hh | 10 +- .../build/drv-output-substitution-goal.hh | 11 +- src/libstore/build/substitution-goal.hh | 11 +- src/libstore/build/worker.cc | 163 ++++-------------- src/libstore/build/worker.hh | 23 +-- src/libutil/muxable-pipe.hh | 82 +++++++++ src/libutil/processes.hh | 4 - src/libutil/unix/muxable-pipe.cc | 47 +++++ src/libutil/windows/muxable-pipe.cc | 70 ++++++++ src/libutil/windows/processes.cc | 26 +-- src/libutil/windows/windows-async-pipe.hh | 7 + 13 files changed, 331 insertions(+), 204 deletions(-) rename src/libstore/{unix => }/build/derivation-goal.cc (97%) rename src/libstore/{unix => }/build/derivation-goal.hh (97%) create mode 100644 src/libutil/muxable-pipe.hh create mode 100644 src/libutil/unix/muxable-pipe.cc create mode 100644 src/libutil/windows/muxable-pipe.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 1f19c673a..e0d66cbd3 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -223,8 +223,8 @@ ''^src/libstore/store-api\.cc$'' ''^src/libstore/store-api\.hh$'' ''^src/libstore/store-dir-config\.hh$'' - ''^src/libstore/unix/build/derivation-goal\.cc$'' - ''^src/libstore/unix/build/derivation-goal\.hh$'' + ''^src/libstore/build/derivation-goal\.cc$'' + ''^src/libstore/build/derivation-goal\.hh$'' ''^src/libstore/build/drv-output-substitution-goal\.cc$'' ''^src/libstore/build/drv-output-substitution-goal\.hh$'' ''^src/libstore/build/entry-points\.cc$'' diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc similarity index 97% rename from src/libstore/unix/build/derivation-goal.cc rename to src/libstore/build/derivation-goal.cc index 339f7273b..4226fb61a 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1,5 +1,8 @@ #include "derivation-goal.hh" -#include "hook-instance.hh" +#ifndef _WIN32 // TODO enable build hook on Windows +# include "hook-instance.hh" +#endif +#include "processes.hh" #include "worker.hh" #include "builtins.hh" #include "builtins/buildenv.hh" @@ -19,19 +22,8 @@ #include #include -#include -#include -#include -#include #include -#include #include -#include -#include -#include - -#include -#include #include @@ -101,7 +93,9 @@ std::string DerivationGoal::key() void DerivationGoal::killChild() { +#ifndef _WIN32 // TODO enable build hook on Windows hook.reset(); +#endif } @@ -641,9 +635,17 @@ void DerivationGoal::started() buildMode == bmCheck ? "checking outputs of '%s'" : "building '%s'", worker.store.printStorePath(drvPath)); fmt("building '%s'", worker.store.printStorePath(drvPath)); +#ifndef _WIN32 // TODO enable build hook on Windows if (hook) msg += fmt(" on '%s'", machineName); +#endif act = std::make_unique(*logger, lvlInfo, actBuild, msg, - Logger::Fields{worker.store.printStorePath(drvPath), hook ? machineName : "", 1, 1}); + Logger::Fields{worker.store.printStorePath(drvPath), +#ifndef _WIN32 // TODO enable build hook on Windows + hook ? machineName : +#endif + "", + 1, + 1}); mcRunningBuilds = std::make_unique>(worker.runningBuilds); worker.updateProgress(); } @@ -778,7 +780,13 @@ static void movePath(const Path & src, const Path & dst) { auto st = lstat(src); - bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); + bool changePerm = ( +#ifndef _WIN32 + geteuid() +#else + !isRootUser() +#endif + && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); if (changePerm) chmod_(src, st.st_mode | S_IWUSR); @@ -796,7 +804,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 = fmt("%1%.old-%2%-%3%", storePath, getpid(), random()); + Path oldPath = fmt("%1%.old-%2%-%3%", storePath, getpid(), rand()); if (pathExists(storePath)) movePath(storePath, oldPath); @@ -818,14 +826,20 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath) int DerivationGoal::getChildStatus() { +#ifndef _WIN32 // TODO enable build hook on Windows return hook->pid.kill(); +#else + return 0; +#endif } void DerivationGoal::closeReadPipes() { - hook->builderOut.readSide = -1; - hook->fromHook.readSide = -1; +#ifndef _WIN32 // TODO enable build hook on Windows + hook->builderOut.readSide.close(); + hook->fromHook.readSide.close(); +#endif } @@ -1019,13 +1033,16 @@ void DerivationGoal::buildDone() BuildResult::Status st = BuildResult::MiscFailure; +#ifndef _WIN32 if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) st = BuildResult::TimedOut; else if (hook && (!WIFEXITED(status) || WEXITSTATUS(status) != 100)) { } - else { + else +#endif + { assert(derivationType); st = dynamic_cast(&e) ? BuildResult::NotDeterministic : @@ -1112,6 +1129,9 @@ void DerivationGoal::resolvedFinished() HookReply DerivationGoal::tryBuildHook() { +#ifdef _WIN32 // TODO enable build hook on Windows + return rpDecline; +#else if (settings.buildHook.get().empty() || !worker.tryBuildHook || !useDerivation) return rpDecline; if (!worker.hook) @@ -1205,17 +1225,18 @@ HookReply DerivationGoal::tryBuildHook() } hook->sink = FdSink(); - hook->toHook.writeSide = -1; + hook->toHook.writeSide.close(); /* Create the log file and pipe. */ Path logFile = openLogFile(); - std::set fds; + std::set fds; fds.insert(hook->fromHook.readSide.get()); fds.insert(hook->builderOut.readSide.get()); worker.childStarted(shared_from_this(), fds, false, false); return rpAccept; +#endif } @@ -1251,7 +1272,11 @@ Path DerivationGoal::openLogFile() Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2), settings.compressLog ? ".bz2" : ""); - fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); + fdLogFile = toDescriptor(open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC +#ifndef _WIN32 + | O_CLOEXEC +#endif + , 0666)); if (!fdLogFile) throw SysError("creating log file '%1%'", logFileName); logFileSink = std::make_shared(fdLogFile.get()); @@ -1271,13 +1296,17 @@ void DerivationGoal::closeLogFile() if (logSink2) logSink2->finish(); if (logFileSink) logFileSink->flush(); logSink = logFileSink = 0; - fdLogFile = -1; + fdLogFile.close(); } -bool DerivationGoal::isReadDesc(int fd) +bool DerivationGoal::isReadDesc(Descriptor fd) { +#ifdef _WIN32 // TODO enable build hook on Windows + return false; +#else return fd == hook->builderOut.readSide.get(); +#endif } void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) @@ -1310,6 +1339,7 @@ void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) if (logSink) (*logSink)(data); } +#ifndef _WIN32 // TODO enable build hook on Windows if (hook && fd == hook->fromHook.readSide.get()) { for (auto c : data) if (c == '\n') { @@ -1344,6 +1374,7 @@ void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) } else currentHookLine += c; } +#endif } diff --git a/src/libstore/unix/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh similarity index 97% rename from src/libstore/unix/build/derivation-goal.hh rename to src/libstore/build/derivation-goal.hh index be6eb50c4..04f13aedd 100644 --- a/src/libstore/unix/build/derivation-goal.hh +++ b/src/libstore/build/derivation-goal.hh @@ -2,7 +2,9 @@ ///@file #include "parsed-derivations.hh" -#include "user-lock.hh" +#ifndef _WIN32 +# include "user-lock.hh" +#endif #include "outputs-spec.hh" #include "store-api.hh" #include "pathlocks.hh" @@ -12,7 +14,9 @@ namespace nix { using std::map; +#ifndef _WIN32 // TODO enable build hook on Windows struct HookInstance; +#endif typedef enum {rpAccept, rpDecline, rpPostpone} HookReply; @@ -178,10 +182,12 @@ struct DerivationGoal : public Goal std::string currentHookLine; +#ifndef _WIN32 // TODO enable build hook on Windows /** * The build hook. */ std::unique_ptr hook; +#endif /** * The sort of derivation we are building. @@ -287,7 +293,7 @@ struct DerivationGoal : public Goal virtual void cleanupPostOutputsRegisteredModeCheck(); virtual void cleanupPostOutputsRegisteredModeNonCheck(); - virtual bool isReadDesc(int fd); + virtual bool isReadDesc(Descriptor fd); /** * Callback used by the worker to write to the log. diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh index 4f06a9e9e..6967ca84f 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -7,10 +7,7 @@ #include "store-api.hh" #include "goal.hh" #include "realisation.hh" - -#ifdef _WIN32 -# include "windows-async-pipe.hh" -#endif +#include "muxable-pipe.hh" namespace nix { @@ -48,11 +45,7 @@ class DrvOutputSubstitutionGoal : public Goal { struct DownloadState { -#ifndef _WIN32 - Pipe outPipe; -#else - windows::AsyncPipe outPipe; -#endif + MuxablePipe outPipe; std::promise> promise; }; diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index 58d309424..1a051fc1f 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -3,10 +3,7 @@ #include "store-api.hh" #include "goal.hh" - -#ifdef _WIN32 -# include "windows-async-pipe.hh" -#endif +#include "muxable-pipe.hh" namespace nix { @@ -48,11 +45,7 @@ struct PathSubstitutionGoal : public Goal /** * Pipe for the substituter's standard output. */ -#ifndef _WIN32 - Pipe outPipe; -#else - windows::AsyncPipe outPipe; -#endif + MuxablePipe outPipe; /** * The substituter thread. diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index ac6f693a2..e6ed80346 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -3,19 +3,13 @@ #include "worker.hh" #include "substitution-goal.hh" #include "drv-output-substitution-goal.hh" +#include "derivation-goal.hh" #ifndef _WIN32 // TODO Enable building on Windows # include "local-derivation-goal.hh" # include "hook-instance.hh" #endif #include "signals.hh" -#ifndef _WIN32 -# include -#else -# include -# include "windows-error.hh" -#endif - namespace nix { Worker::Worker(Store & store, Store & evalStore) @@ -49,7 +43,6 @@ Worker::~Worker() assert(expectedNarSize == 0); } -#ifndef _WIN32 // TODO Enable building on Windows std::shared_ptr Worker::makeDerivationGoalCommon( const StorePath & drvPath, @@ -73,9 +66,13 @@ std::shared_ptr Worker::makeDerivationGoal(const StorePath & drv const OutputsSpec & wantedOutputs, BuildMode buildMode) { return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr { - return !dynamic_cast(&store) - ? std::make_shared(drvPath, wantedOutputs, *this, buildMode) - : std::make_shared(drvPath, wantedOutputs, *this, buildMode); + return +#ifndef _WIN32 // TODO Enable building on Windows + dynamic_cast(&store) + ? std::make_shared(drvPath, wantedOutputs, *this, buildMode) + : +#endif + std::make_shared(drvPath, wantedOutputs, *this, buildMode); }); } @@ -83,14 +80,16 @@ std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode) { return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr { - return !dynamic_cast(&store) - ? std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode) - : std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode); + return +#ifndef _WIN32 // TODO Enable building on Windows + dynamic_cast(&store) + ? std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode) + : +#endif + std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode); }); } -#endif - std::shared_ptr Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional ca) { @@ -122,14 +121,10 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode) { return std::visit(overloaded { [&](const DerivedPath::Built & bfd) -> GoalPtr { -#ifndef _WIN32 // TODO Enable building on Windows if (auto bop = std::get_if(&*bfd.drvPath)) return makeDerivationGoal(bop->path, bfd.outputs, buildMode); else throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented."); -#else - throw UnimplementedError("Building derivations not yet implemented on Windows"); -#endif }, [&](const DerivedPath::Opaque & bo) -> GoalPtr { return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair); @@ -155,11 +150,9 @@ static void removeGoal(std::shared_ptr goal, std::map> & void Worker::removeGoal(GoalPtr goal) { -#ifndef _WIN32 // TODO Enable building on Windows if (auto drvGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(drvGoal, derivationGoals); else -#endif if (auto subGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, substitutionGoals); else if (auto subGoal = std::dynamic_pointer_cast(goal)) @@ -204,7 +197,7 @@ unsigned Worker::getNrSubstitutions() } -void Worker::childStarted(GoalPtr goal, const std::set & channels, +void Worker::childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts) { Child child; @@ -298,14 +291,12 @@ void Worker::run(const Goals & _topGoals) for (auto & i : _topGoals) { topGoals.insert(i); -#ifndef _WIN32 // TODO Enable building on Windows if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Built { .drvPath = makeConstantStorePathRef(goal->drvPath), .outputs = goal->wantedOutputs, }); } else -#endif if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Opaque{goal->storePath}); } @@ -428,47 +419,26 @@ void Worker::waitForInput() if (useTimeout) vomit("sleeping %d seconds", timeout); + MuxablePipePollState state; + #ifndef _WIN32 /* Use select() to wait for the input side of any logger pipe to become `available'. Note that `available' (i.e., non-blocking) includes EOF. */ - std::vector pollStatus; - std::map fdToPollStatus; for (auto & i : children) { for (auto & j : i.channels) { - pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN }); - fdToPollStatus[j] = pollStatus.size() - 1; + state.pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN }); + state.fdToPollStatus[j] = state.pollStatus.size() - 1; } } - - if (poll(pollStatus.data(), pollStatus.size(), - useTimeout ? timeout * 1000 : -1) == -1) { - if (errno == EINTR) return; - throw SysError("waiting for input"); - } -#else - OVERLAPPED_ENTRY oentries[0x20] = {0}; - ULONG removed; - bool gotEOF = false; - - // we are on at least Windows Vista / Server 2008 and can get many (countof(oentries)) statuses in one API call - if (!GetQueuedCompletionStatusEx( - ioport.get(), - oentries, - sizeof(oentries) / sizeof(*oentries), - &removed, - useTimeout ? timeout * 1000 : INFINITE, - false)) - { - windows::WinError winError("GetQueuedCompletionStatusEx"); - if (winError.lastError != WAIT_TIMEOUT) - throw winError; - assert(removed == 0); - } else { - assert(0 < removed && removed <= sizeof(oentries)/sizeof(*oentries)); - } #endif + state.poll( +#ifdef _WIN32 + ioport.get(), +#endif + useTimeout ? (std::optional { timeout * 1000 }) : std::nullopt); + auto after = steady_time_point::clock::now(); /* Process all available file descriptors. FIXME: this is @@ -482,75 +452,18 @@ void Worker::waitForInput() GoalPtr goal = j->goal.lock(); assert(goal); -#ifndef _WIN32 - std::set fds2(j->channels); - std::vector buffer(4096); - for (auto & k : fds2) { - const auto fdPollStatusId = get(fdToPollStatus, k); - assert(fdPollStatusId); - assert(*fdPollStatusId < pollStatus.size()); - if (pollStatus.at(*fdPollStatusId).revents) { - ssize_t rd = ::read(fromDescriptorReadOnly(k), buffer.data(), buffer.size()); - // FIXME: is there a cleaner way to handle pt close - // than EIO? Is this even standard? - if (rd == 0 || (rd == -1 && errno == EIO)) { - debug("%1%: got EOF", goal->getName()); - goal->handleEOF(k); - j->channels.erase(k); - } else if (rd == -1) { - if (errno != EINTR) - throw SysError("%s: read failed", goal->getName()); - } else { - printMsg(lvlVomit, "%1%: read %2% bytes", - goal->getName(), rd); - std::string_view data((char *) buffer.data(), rd); - j->lastOutput = after; - goal->handleChildOutput(k, data); - } - } - } -#else - decltype(j->channels)::iterator p = j->channels.begin(); - while (p != j->channels.end()) { - decltype(p) nextp = p; - ++nextp; - for (ULONG i = 0; i < removed; i++) { - if (oentries[i].lpCompletionKey == ((ULONG_PTR)((*p)->readSide.get()) ^ 0x5555)) { - printMsg(lvlVomit, "%s: read %s bytes", goal->getName(), oentries[i].dwNumberOfBytesTransferred); - if (oentries[i].dwNumberOfBytesTransferred > 0) { - std::string data { - (char *) (*p)->buffer.data(), - oentries[i].dwNumberOfBytesTransferred, - }; - //std::cerr << "read [" << data << "]" << std::endl; - j->lastOutput = after; - goal->handleChildOutput((*p)->readSide.get(), data); - } - - if (gotEOF) { - debug("%s: got EOF", goal->getName()); - goal->handleEOF((*p)->readSide.get()); - nextp = j->channels.erase(p); // no need to maintain `j->channels` ? - } else { - BOOL rc = ReadFile((*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped); - if (rc) { - // here is possible (but not obligatory) to call `goal->handleChildOutput` and repeat ReadFile immediately - } else { - windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get()); - if (winError.lastError == ERROR_BROKEN_PIPE) { - debug("%s: got EOF", goal->getName()); - goal->handleEOF((*p)->readSide.get()); - nextp = j->channels.erase(p); // no need to maintain `j->channels` ? - } else if (winError.lastError != ERROR_IO_PENDING) - throw winError; - } - } - break; - } - } - p = nextp; - } -#endif + state.iterate( + j->channels, + [&](Descriptor k, std::string_view data) { + printMsg(lvlVomit, "%1%: read %2% bytes", + goal->getName(), data.size()); + j->lastOutput = after; + goal->handleChildOutput(k, data); + }, + [&](Descriptor k) { + debug("%1%: got EOF", goal->getName()); + goal->handleEOF(k); + }); if (goal->exitCode == Goal::ecBusy && 0 != settings.maxSilentTime && diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index b57734b45..7d67030d7 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -5,10 +5,7 @@ #include "store-api.hh" #include "goal.hh" #include "realisation.hh" - -#ifdef _WIN32 -# include "windows-async-pipe.hh" -#endif +#include "muxable-pipe.hh" #include #include @@ -16,9 +13,7 @@ namespace nix { /* Forward definition. */ -#ifndef _WIN32 // TODO Enable building on Windows struct DerivationGoal; -#endif struct PathSubstitutionGoal; class DrvOutputSubstitutionGoal; @@ -46,17 +41,9 @@ typedef std::chrono::time_point steady_time_point; */ struct Child { - using CommChannel = -#ifndef _WIN32 - Descriptor -#else - windows::AsyncPipe * -#endif - ; - WeakGoalPtr goal; Goal * goal2; // ugly hackery - std::set channels; + std::set channels; bool respectTimeouts; bool inBuildSlot; /** @@ -116,9 +103,7 @@ private: * Maps used to prevent multiple instantiations of a goal for the * same derivation / path. */ -#ifndef _WIN32 // TODO Enable building on Windows std::map> derivationGoals; -#endif std::map> substitutionGoals; std::map> drvOutputSubstitutionGoals; @@ -207,7 +192,6 @@ public: * Make a goal (with caching). */ -#ifndef _WIN32 // TODO Enable building on Windows /** * @ref DerivationGoal "derivation goal" */ @@ -222,7 +206,6 @@ public: std::shared_ptr makeBasicDerivationGoal( const StorePath & drvPath, const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal); -#endif /** * @ref SubstitutionGoal "substitution goal" @@ -263,7 +246,7 @@ public: * Registers a running child process. `inBuildSlot` means that * the process counts towards the jobs limit. */ - void childStarted(GoalPtr goal, const std::set & channels, + void childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts); /** diff --git a/src/libutil/muxable-pipe.hh b/src/libutil/muxable-pipe.hh new file mode 100644 index 000000000..53ac39170 --- /dev/null +++ b/src/libutil/muxable-pipe.hh @@ -0,0 +1,82 @@ +#pragma once +///@file + +#include "file-descriptor.hh" +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif + +#ifndef _WIN32 +# include +#else +# include +# include "windows-error.hh" +#endif + +namespace nix { + +/** + * An "muxable pipe" is a type of pipe supporting endpoints that wait + * for events on multiple pipes at once. + * + * On Unix, this is just a regular anonymous pipe. On Windows, this has + * to be a named pipe because we need I/O Completion Ports to wait on + * multiple pipes. + */ +using MuxablePipe = +#ifndef _WIN32 + Pipe +#else + windows::AsyncPipe +#endif + ; + +/** + * Use pool() (Unix) / I/O Completion Ports (Windows) to wait for the + * input side of any logger pipe to become `available'. Note that + * `available' (i.e., non-blocking) includes EOF. + */ +struct MuxablePipePollState +{ +#ifndef _WIN32 + std::vector pollStatus; + std::map fdToPollStatus; +#else + OVERLAPPED_ENTRY oentries[0x20] = {0}; + ULONG removed; + bool gotEOF = false; + +#endif + + /** + * Check for ready (Unix) / completed (Windows) operations + */ + void poll( +#ifdef _WIN32 + HANDLE ioport, +#endif + std::optional timeout); + + using CommChannel = +#ifndef _WIN32 + Descriptor +#else + windows::AsyncPipe * +#endif + ; + + /** + * Process for ready (Unix) / completed (Windows) operations, + * calling the callbacks as needed. + * + * @param handleRead callback to be passed read data. + * + * @param handleEOF callback for when the `MuxablePipe` has closed. + */ + void iterate( + std::set & channels, + std::function handleRead, + std::function handleEOF); +}; + +} diff --git a/src/libutil/processes.hh b/src/libutil/processes.hh index e319f79e0..9d5367b02 100644 --- a/src/libutil/processes.hh +++ b/src/libutil/processes.hh @@ -118,8 +118,6 @@ public: { } }; -#ifndef _WIN32 - /** * Convert the exit status of a child as returned by wait() into an * error string. @@ -128,6 +126,4 @@ std::string statusToString(int status); bool statusOk(int status); -#endif - } diff --git a/src/libutil/unix/muxable-pipe.cc b/src/libutil/unix/muxable-pipe.cc new file mode 100644 index 000000000..0104663c3 --- /dev/null +++ b/src/libutil/unix/muxable-pipe.cc @@ -0,0 +1,47 @@ +#include + +#include "logging.hh" +#include "util.hh" +#include "muxable-pipe.hh" + +namespace nix { + +void MuxablePipePollState::poll(std::optional timeout) +{ + if (::poll(pollStatus.data(), pollStatus.size(), timeout ? *timeout : -1) == -1) { + if (errno == EINTR) + return; + throw SysError("waiting for input"); + } +} + +void MuxablePipePollState::iterate( + std::set & channels, + std::function handleRead, + std::function handleEOF) +{ + std::set fds2(channels); + std::vector buffer(4096); + for (auto & k : fds2) { + const auto fdPollStatusId = get(fdToPollStatus, k); + assert(fdPollStatusId); + assert(*fdPollStatusId < pollStatus.size()); + if (pollStatus.at(*fdPollStatusId).revents) { + ssize_t rd = ::read(fromDescriptorReadOnly(k), buffer.data(), buffer.size()); + // FIXME: is there a cleaner way to handle pt close + // than EIO? Is this even standard? + if (rd == 0 || (rd == -1 && errno == EIO)) { + handleEOF(k); + channels.erase(k); + } else if (rd == -1) { + if (errno != EINTR) + throw SysError("read failed"); + } else { + std::string_view data((char *) buffer.data(), rd); + handleRead(k, data); + } + } + } +} + +} diff --git a/src/libutil/windows/muxable-pipe.cc b/src/libutil/windows/muxable-pipe.cc new file mode 100644 index 000000000..91a321f7c --- /dev/null +++ b/src/libutil/windows/muxable-pipe.cc @@ -0,0 +1,70 @@ +#include +#include "windows-error.hh" + +#include "logging.hh" +#include "util.hh" +#include "muxable-pipe.hh" + +namespace nix { + +void MuxablePipePollState::poll(HANDLE ioport, std::optional timeout) +{ + /* We are on at least Windows Vista / Server 2008 and can get many + (countof(oentries)) statuses in one API call. */ + if (!GetQueuedCompletionStatusEx( + ioport, oentries, sizeof(oentries) / sizeof(*oentries), &removed, timeout ? *timeout : INFINITE, false)) { + windows::WinError winError("GetQueuedCompletionStatusEx"); + if (winError.lastError != WAIT_TIMEOUT) + throw winError; + assert(removed == 0); + } else { + assert(0 < removed && removed <= sizeof(oentries) / sizeof(*oentries)); + } +} + +void MuxablePipePollState::iterate( + std::set & channels, + std::function handleRead, + std::function handleEOF) +{ + auto p = channels.begin(); + while (p != channels.end()) { + decltype(p) nextp = p; + ++nextp; + for (ULONG i = 0; i < removed; i++) { + if (oentries[i].lpCompletionKey == ((ULONG_PTR) ((*p)->readSide.get()) ^ 0x5555)) { + printMsg(lvlVomit, "read %s bytes", oentries[i].dwNumberOfBytesTransferred); + if (oentries[i].dwNumberOfBytesTransferred > 0) { + std::string data{ + (char *) (*p)->buffer.data(), + oentries[i].dwNumberOfBytesTransferred, + }; + handleRead((*p)->readSide.get(), data); + } + + if (gotEOF) { + handleEOF((*p)->readSide.get()); + nextp = channels.erase(p); // no need to maintain `channels`? + } else { + BOOL rc = ReadFile( + (*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped); + if (rc) { + // here is possible (but not obligatory) to call + // `handleRead` and repeat ReadFile immediately + } else { + windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get()); + if (winError.lastError == ERROR_BROKEN_PIPE) { + handleEOF((*p)->readSide.get()); + nextp = channels.erase(p); // no need to maintain `channels` ? + } else if (winError.lastError != ERROR_IO_PENDING) + throw winError; + } + } + break; + } + } + p = nextp; + } +} + +} diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 5ef4ed1e4..44a32f6a1 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -16,16 +16,6 @@ #include #include -#ifdef __APPLE__ -# include -#endif - -#ifdef __linux__ -# include -# include -#endif - - namespace nix { std::string runProgram(Path program, bool lookupPath, const Strings & args, @@ -34,15 +24,31 @@ std::string runProgram(Path program, bool lookupPath, const Strings & args, throw UnimplementedError("Cannot shell out to git on Windows yet"); } + // Output = error code + "standard out" output stream std::pair runProgram(RunOptions && options) { throw UnimplementedError("Cannot shell out to git on Windows yet"); } + void runProgram2(const RunOptions & options) { throw UnimplementedError("Cannot shell out to git on Windows yet"); } +std::string statusToString(int status) +{ + if (status != 0) + return fmt("with exit code %d", status); + else + return "succeeded"; +} + + +bool statusOk(int status) +{ + return status == 0; +} + } diff --git a/src/libutil/windows/windows-async-pipe.hh b/src/libutil/windows/windows-async-pipe.hh index c980201a8..8f554e403 100644 --- a/src/libutil/windows/windows-async-pipe.hh +++ b/src/libutil/windows/windows-async-pipe.hh @@ -5,6 +5,13 @@ namespace nix::windows { +/*** + * An "async pipe" is a pipe that supports I/O Completion Ports so + * multiple pipes can be listened too. + * + * Unfortunately, only named pipes support that on windows, so we use + * those with randomized temp file names. + */ class AsyncPipe { public: From 2e12b5812647a97a9ed8b7e6aa48fbe7725491aa Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 28 May 2024 10:10:02 -0400 Subject: [PATCH 169/910] Shellcheck some test scripts Progress on #10795 --- .gitignore | 2 +- maintainers/flake-module.nix | 15 +------ mk/common-test.sh | 3 +- .../build-remote-content-addressed-fixed.sh | 2 + ...build-remote-content-addressed-floating.sh | 2 + .../build-remote-input-addressed.sh | 12 ++--- .../build-remote-trustless-after.sh | 9 +++- .../build-remote-trustless-should-fail-0.sh | 6 +++ .../build-remote-trustless-should-pass-0.sh | 2 + .../build-remote-trustless-should-pass-1.sh | 2 + .../build-remote-trustless-should-pass-2.sh | 2 + .../build-remote-trustless-should-pass-3.sh | 2 + tests/functional/build-remote-trustless.sh | 14 ++++-- .../build-remote-with-mounted-ssh-ng.sh | 14 +++--- tests/functional/build-remote.sh | 44 ++++++++++--------- tests/functional/common.sh | 8 ++-- tests/functional/common/init.sh | 2 + tests/functional/common/subst-vars.sh.in | 15 +++++++ ...-functions.sh.in => vars-and-functions.sh} | 19 +++----- tests/functional/local.mk | 2 +- 20 files changed, 106 insertions(+), 71 deletions(-) create mode 100644 tests/functional/common/subst-vars.sh.in rename tests/functional/common/{vars-and-functions.sh.in => vars-and-functions.sh} (96%) diff --git a/.gitignore b/.gitignore index 52aaec23f..28f853715 100644 --- a/.gitignore +++ b/.gitignore @@ -92,7 +92,7 @@ perl/Makefile.config # /tests/functional/ /tests/functional/test-tmp -/tests/functional/common/vars-and-functions.sh +/tests/functional/common/subst-vars.sh /tests/functional/result* /tests/functional/restricted-innocent /tests/functional/shell diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 1f19c673a..bc5dde9aa 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -514,18 +514,6 @@ ''^tests/functional/brotli\.sh$'' ''^tests/functional/build-delete\.sh$'' ''^tests/functional/build-dry\.sh$'' - ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' - ''^tests/functional/build-remote-content-addressed-floating\.sh$'' - ''^tests/functional/build-remote-input-addressed\.sh$'' - ''^tests/functional/build-remote-trustless-after\.sh$'' - ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' - ''^tests/functional/build-remote-trustless\.sh$'' - ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' - ''^tests/functional/build-remote\.sh$'' ''^tests/functional/build\.sh$'' ''^tests/functional/ca/build-cache\.sh$'' ''^tests/functional/ca/build-dry\.sh$'' @@ -554,8 +542,7 @@ ''^tests/functional/check-reqs\.sh$'' ''^tests/functional/check\.sh$'' ''^tests/functional/chroot-store\.sh$'' - ''^tests/functional/common\.sh$'' - ''^tests/functional/common/init\.sh$'' + ''^tests/functional/common/vars-and-functions\.sh$'' ''^tests/functional/completions\.sh$'' ''^tests/functional/compression-levels\.sh$'' ''^tests/functional/compute-levels\.sh$'' diff --git a/mk/common-test.sh b/mk/common-test.sh index de24b6fcc..c80abd381 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -3,8 +3,7 @@ # Remove overall test dir (at most one of the two should match) and # remove file extension. -# shellcheck disable=SC2154 -test_name=$(echo -n "$test" | sed \ +test_name=$(echo -n "${test?must be defined by caller (test runner)}" | sed \ -e "s|^tests/unit/[^/]*/data/||" \ -e "s|^tests/functional/||" \ -e "s|\.sh$||" \ diff --git a/tests/functional/build-remote-content-addressed-fixed.sh b/tests/functional/build-remote-content-addressed-fixed.sh index ae7441591..61a1f4a46 100644 --- a/tests/functional/build-remote-content-addressed-fixed.sh +++ b/tests/functional/build-remote-content-addressed-fixed.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh file=build-hook-ca-fixed.nix diff --git a/tests/functional/build-remote-content-addressed-floating.sh b/tests/functional/build-remote-content-addressed-floating.sh index e83b42b41..33d667f92 100644 --- a/tests/functional/build-remote-content-addressed-floating.sh +++ b/tests/functional/build-remote-content-addressed-floating.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh file=build-hook-ca-floating.nix diff --git a/tests/functional/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh index 49d15c389..986692dbc 100644 --- a/tests/functional/build-remote-input-addressed.sh +++ b/tests/functional/build-remote-input-addressed.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh file=build-hook.nix @@ -11,17 +13,17 @@ registerBuildHook () { # Dummy post-build-hook just to ensure that it's executed correctly. # (we can't reuse the one from `$PWD/push-to-store.sh` because of # https://github.com/NixOS/nix/issues/4341) - cat < $TEST_ROOT/post-build-hook.sh + cat < "$TEST_ROOT/post-build-hook.sh" #!/bin/sh echo "Post hook ran successfully" # Add an empty line to a counter file, just to check that this hook ran properly echo "" >> $TEST_ROOT/post-hook-counter EOF - chmod +x $TEST_ROOT/post-build-hook.sh - rm -f $TEST_ROOT/post-hook-counter + chmod +x "$TEST_ROOT/post-build-hook.sh" + rm -f "$TEST_ROOT/post-hook-counter" - echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> $NIX_CONF_DIR/nix.conf + echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> "$NIX_CONF_DIR/nix.conf" } registerBuildHook @@ -30,4 +32,4 @@ source build-remote.sh # `build-hook.nix` has four derivations to build, and the hook runs twice for # each derivation (once on the builder and once on the host), so the counter # should contain eight lines now -[[ $(cat $TEST_ROOT/post-hook-counter | wc -l) -eq 8 ]] +[[ $(wc -l < "$TEST_ROOT/post-hook-counter") -eq 8 ]] diff --git a/tests/functional/build-remote-trustless-after.sh b/tests/functional/build-remote-trustless-after.sh index 19f59e6ae..2fcdbf10a 100644 --- a/tests/functional/build-remote-trustless-after.sh +++ b/tests/functional/build-remote-trustless-after.sh @@ -1,2 +1,7 @@ -outPath=$(readlink -f $TEST_ROOT/result) -grep 'FOO BAR BAZ' ${remoteDir}/${outPath} +# shellcheck shell=bash + +# Variables must be defined by caller, so +# shellcheck disable=SC2154 + +outPath=$(readlink -f "$TEST_ROOT/result") +grep 'FOO BAR BAZ' "${remoteDir}/${outPath}" diff --git a/tests/functional/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh index 3d4a4b097..269f7f112 100644 --- a/tests/functional/build-remote-trustless-should-fail-0.sh +++ b/tests/functional/build-remote-trustless-should-fail-0.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "daemon-trust-override" @@ -22,8 +24,12 @@ nix-build build-hook.nix -A passthru.input2 \ # copy our already-build `input2` to the remote store. That store object # is input-addressed, so this will fail. +# For script below +# shellcheck disable=SC2034 file=build-hook.nix +# shellcheck disable=SC2034 prog=$(readlink -e ./nix-daemon-untrusting.sh) +# shellcheck disable=SC2034 proto=ssh-ng expectStderr 1 source build-remote-trustless.sh \ diff --git a/tests/functional/build-remote-trustless-should-pass-0.sh b/tests/functional/build-remote-trustless-should-pass-0.sh index 2a7ebd8c6..b81060907 100644 --- a/tests/functional/build-remote-trustless-should-pass-0.sh +++ b/tests/functional/build-remote-trustless-should-pass-0.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Remote trusts us diff --git a/tests/functional/build-remote-trustless-should-pass-1.sh b/tests/functional/build-remote-trustless-should-pass-1.sh index 516bdf092..b8dc038bf 100644 --- a/tests/functional/build-remote-trustless-should-pass-1.sh +++ b/tests/functional/build-remote-trustless-should-pass-1.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Remote trusts us diff --git a/tests/functional/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh index b769a88f0..ba5d1ff7a 100644 --- a/tests/functional/build-remote-trustless-should-pass-2.sh +++ b/tests/functional/build-remote-trustless-should-pass-2.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "daemon-trust-override" diff --git a/tests/functional/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh index 40f81da5a..187b89948 100644 --- a/tests/functional/build-remote-trustless-should-pass-3.sh +++ b/tests/functional/build-remote-trustless-should-pass-3.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "daemon-trust-override" diff --git a/tests/functional/build-remote-trustless.sh b/tests/functional/build-remote-trustless.sh index 81e5253bf..c498d46c3 100644 --- a/tests/functional/build-remote-trustless.sh +++ b/tests/functional/build-remote-trustless.sh @@ -1,5 +1,11 @@ +# shellcheck shell=bash + +# All variables should be defined externally by the scripts that source +# this, `set -u` will catch any that are forgotten. +# shellcheck disable=SC2154 + requireSandboxSupport -[[ $busybox =~ busybox ]] || skipTest "no busybox" +[[ "$busybox" =~ busybox ]] || skipTest "no busybox" unset NIX_STORE_DIR unset NIX_STATE_DIR @@ -8,7 +14,7 @@ remoteDir=$TEST_ROOT/remote # Note: ssh{-ng}://localhost bypasses ssh. See tests/functional/build-remote.sh for # more details. -nix-build $file -o $TEST_ROOT/result --max-jobs 0 \ - --arg busybox $busybox \ - --store $TEST_ROOT/local \ +nix-build "$file" -o "$TEST_ROOT/result" --max-jobs 0 \ + --arg busybox "$busybox" \ + --store "$TEST_ROOT/local" \ --builders "$proto://localhost?remote-program=$prog&remote-store=${remoteDir}%3Fsystem-features=foo%20bar%20baz - - 1 1 foo,bar,baz" diff --git a/tests/functional/build-remote-with-mounted-ssh-ng.sh b/tests/functional/build-remote-with-mounted-ssh-ng.sh index 443acb6ca..e2627af39 100644 --- a/tests/functional/build-remote-with-mounted-ssh-ng.sh +++ b/tests/functional/build-remote-with-mounted-ssh-ng.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireSandboxSupport @@ -6,17 +8,17 @@ requireSandboxSupport enableFeatures mounted-ssh-store nix build -Lvf simple.nix \ - --arg busybox $busybox \ - --out-link $TEST_ROOT/result-from-remote \ + --arg busybox "$busybox" \ + --out-link "$TEST_ROOT/result-from-remote" \ --store mounted-ssh-ng://localhost nix build -Lvf simple.nix \ - --arg busybox $busybox \ - --out-link $TEST_ROOT/result-from-remote-new-cli \ + --arg busybox "$busybox" \ + --out-link "$TEST_ROOT/result-from-remote-new-cli" \ --store 'mounted-ssh-ng://localhost?remote-program=nix daemon' # This verifies that the out link was actually created and valid. The ability # to create out links (permanent gc roots) is the distinguishing feature of # the mounted-ssh-ng store. -cat $TEST_ROOT/result-from-remote/hello | grepQuiet 'Hello World!' -cat $TEST_ROOT/result-from-remote-new-cli/hello | grepQuiet 'Hello World!' +grepQuiet 'Hello World!' < "$TEST_ROOT/result-from-remote/hello" +grepQuiet 'Hello World!' < "$TEST_ROOT/result-from-remote-new-cli/hello" diff --git a/tests/functional/build-remote.sh b/tests/functional/build-remote.sh index d2a2132c1..1a5334577 100644 --- a/tests/functional/build-remote.sh +++ b/tests/functional/build-remote.sh @@ -1,5 +1,9 @@ +# shellcheck shell=bash + +: "${file?must be defined by caller (remote building test case using this)}" + requireSandboxSupport -[[ $busybox =~ busybox ]] || skipTest "no busybox" +[[ "${busybox-}" =~ busybox ]] || skipTest "no busybox" # Avoid store dir being inside sandbox build-dir unset NIX_STORE_DIR @@ -15,50 +19,50 @@ fi builders=( # system-features will automatically be added to the outer URL, but not inner # remote-store URL. - "ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=$(join_by "%20" foo ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," foo ${EXTRA_SYSTEM_FEATURES[@]})" - "$TEST_ROOT/machine2 - - 1 1 $(join_by "," bar ${EXTRA_SYSTEM_FEATURES[@]})" - "ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=$(join_by "%20" baz ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," baz ${EXTRA_SYSTEM_FEATURES[@]})" + "ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=$(join_by "%20" foo "${EXTRA_SYSTEM_FEATURES[@]}") - - 1 1 $(join_by "," foo "${EXTRA_SYSTEM_FEATURES[@]}")" + "$TEST_ROOT/machine2 - - 1 1 $(join_by "," bar "${EXTRA_SYSTEM_FEATURES[@]}")" + "ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=$(join_by "%20" baz "${EXTRA_SYSTEM_FEATURES[@]}") - - 1 1 $(join_by "," baz "${EXTRA_SYSTEM_FEATURES[@]}")" ) -chmod -R +w $TEST_ROOT/machine* || true -rm -rf $TEST_ROOT/machine* || true +chmod -R +w "$TEST_ROOT/machine"* || true +rm -rf "$TEST_ROOT/machine"* || true # Note: ssh://localhost bypasses ssh, directly invoking nix-store as a # child process. This allows us to test LegacySSHStore::buildDerivation(). # ssh-ng://... likewise allows us to test RemoteStore::buildDerivation(). -nix build -L -v -f $file -o $TEST_ROOT/result --max-jobs 0 \ - --arg busybox $busybox \ - --store $TEST_ROOT/machine0 \ +nix build -L -v -f "$file" -o "$TEST_ROOT/result" --max-jobs 0 \ + --arg busybox "$busybox" \ + --store "$TEST_ROOT/machine0" \ --builders "$(join_by '; ' "${builders[@]}")" -outPath=$(readlink -f $TEST_ROOT/result) +outPath=$(readlink -f "$TEST_ROOT/result") -grep 'FOO BAR BAZ' $TEST_ROOT/machine0/$outPath +grep 'FOO BAR BAZ' "$TEST_ROOT/machine0/$outPath" -testPrintOutPath=$(nix build -L -v -f $file --no-link --print-out-paths --max-jobs 0 \ - --arg busybox $busybox \ - --store $TEST_ROOT/machine0 \ +testPrintOutPath=$(nix build -L -v -f "$file" --no-link --print-out-paths --max-jobs 0 \ + --arg busybox "$busybox" \ + --store "$TEST_ROOT/machine0" \ --builders "$(join_by '; ' "${builders[@]}")" ) [[ $testPrintOutPath =~ store.*build-remote ]] # Ensure that input1 was built on store1 due to the required feature. -output=$(nix path-info --store $TEST_ROOT/machine1 --all) +output=$(nix path-info --store "$TEST_ROOT/machine1" --all) echo "$output" | grepQuiet builder-build-remote-input-1.sh echo "$output" | grepQuietInverse builder-build-remote-input-2.sh echo "$output" | grepQuietInverse builder-build-remote-input-3.sh unset output # Ensure that input2 was built on store2 due to the required feature. -output=$(nix path-info --store $TEST_ROOT/machine2 --all) +output=$(nix path-info --store "$TEST_ROOT/machine2" --all) echo "$output" | grepQuietInverse builder-build-remote-input-1.sh echo "$output" | grepQuiet builder-build-remote-input-2.sh echo "$output" | grepQuietInverse builder-build-remote-input-3.sh unset output # Ensure that input3 was built on store3 due to the required feature. -output=$(nix path-info --store $TEST_ROOT/machine3 --all) +output=$(nix path-info --store "$TEST_ROOT/machine3" --all) echo "$output" | grepQuietInverse builder-build-remote-input-1.sh echo "$output" | grepQuietInverse builder-build-remote-input-2.sh echo "$output" | grepQuiet builder-build-remote-input-3.sh @@ -66,7 +70,7 @@ unset output for i in input1 input3; do -nix log --store $TEST_ROOT/machine0 --file "$file" --arg busybox $busybox passthru."$i" | grep hi-$i +nix log --store "$TEST_ROOT/machine0" --file "$file" --arg busybox "$busybox" "passthru.$i" | grep hi-$i done # Behavior of keep-failed @@ -74,9 +78,9 @@ out="$(nix-build 2>&1 failing.nix \ --no-out-link \ --builders "$(join_by '; ' "${builders[@]}")" \ --keep-failed \ - --store $TEST_ROOT/machine0 \ + --store "$TEST_ROOT/machine0" \ -j0 \ - --arg busybox $busybox)" || true + --arg busybox "$busybox")" || true [[ "$out" =~ .*"note: keeping build directory".* ]] diff --git a/tests/functional/common.sh b/tests/functional/common.sh index 4ec17b706..d038aaf59 100644 --- a/tests/functional/common.sh +++ b/tests/functional/common.sh @@ -1,13 +1,15 @@ +# shellcheck shell=bash + set -eu -o pipefail if [[ -z "${COMMON_SH_SOURCED-}" ]]; then COMMON_SH_SOURCED=1 -dir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" +functionalTestsDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" -source "$dir"/common/vars-and-functions.sh -source "$dir"/common/init.sh +source "$functionalTestsDir/common/vars-and-functions.sh" +source "$functionalTestsDir/common/init.sh" if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then startDaemon diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 74da12651..dda1ecd41 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -1,3 +1,5 @@ +# shellcheck shell=bash + test -n "$TEST_ROOT" # We would delete any daemon socket, so let's stop the daemon first. killDaemon diff --git a/tests/functional/common/subst-vars.sh.in b/tests/functional/common/subst-vars.sh.in new file mode 100644 index 000000000..4105e9f35 --- /dev/null +++ b/tests/functional/common/subst-vars.sh.in @@ -0,0 +1,15 @@ +# NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk + +export PATH=@bindir@:$PATH +export coreutils=@coreutils@ +#lsof=@lsof@ + +export dot=@dot@ +export SHELL="@bash@" +export PAGER=cat +export busybox="@sandbox_shell@" + +export version=@PACKAGE_VERSION@ +export system=@system@ + +export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@ diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh similarity index 96% rename from tests/functional/common/vars-and-functions.sh.in rename to tests/functional/common/vars-and-functions.sh index cb1f0d566..ad5b29a94 100644 --- a/tests/functional/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh @@ -8,6 +8,12 @@ COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1 set +x +commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" + +source "$commonDir/subst-vars.sh" +# Make sure shellcheck knows all these will be defined by the above generated snippet +: "${PATH?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" + export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} export NIX_STORE_DIR if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then @@ -37,7 +43,6 @@ unset XDG_CONFIG_HOME unset XDG_CONFIG_DIRS unset XDG_CACHE_HOME -export PATH=@bindir@:$PATH if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH fi @@ -45,18 +50,6 @@ DAEMON_PATH="$PATH" if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" fi -coreutils=@coreutils@ -lsof=@lsof@ - -export dot=@dot@ -export SHELL="@bash@" -export PAGER=cat -export busybox="@sandbox_shell@" - -export version=@PACKAGE_VERSION@ -export system=@system@ - -export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@ export IMPURE_VAR1=foo export IMPURE_VAR2=bar diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 18b920f7d..a94c5712c 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -153,7 +153,7 @@ $(d)/plugins.sh.test $(d)/plugins.sh.test-debug: \ install-tests += $(foreach x, $(nix_tests), $(d)/$(x)) test-clean-files := \ - $(d)/common/vars-and-functions.sh \ + $(d)/common/subst-vars.sh \ $(d)/config.nix clean-files += $(test-clean-files) From 10f864c5ae953647565d10a233558404116047ec Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 28 May 2024 12:43:04 -0400 Subject: [PATCH 170/910] Ensure all functional scripts are (a) executable (b) have shebang This is good for shebang, and also good for future build system simplifications --- tests/functional/add.sh | 2 ++ tests/functional/bash-profile.sh | 2 ++ tests/functional/binary-cache-build-remote.sh | 2 ++ tests/functional/binary-cache.sh | 2 ++ tests/functional/brotli.sh | 2 ++ tests/functional/build-delete.sh | 2 ++ tests/functional/build-dry.sh | 2 ++ tests/functional/build-remote-content-addressed-fixed.sh | 0 tests/functional/build-remote-content-addressed-floating.sh | 0 tests/functional/build-remote-input-addressed.sh | 0 tests/functional/build-remote-trustless-should-fail-0.sh | 0 tests/functional/build-remote-trustless-should-pass-0.sh | 0 tests/functional/build-remote-trustless-should-pass-1.sh | 0 tests/functional/build-remote-trustless-should-pass-2.sh | 0 tests/functional/build-remote-trustless-should-pass-3.sh | 0 tests/functional/build-remote-with-mounted-ssh-ng.sh | 0 tests/functional/build.sh | 2 ++ tests/functional/case-hack.sh | 2 ++ tests/functional/check-refs.sh | 2 ++ tests/functional/check-reqs.sh | 2 ++ tests/functional/check.sh | 2 ++ tests/functional/chroot-store.sh | 2 ++ tests/functional/completions.sh | 2 ++ tests/functional/compression-levels.sh | 2 ++ tests/functional/compute-levels.sh | 2 ++ tests/functional/config.sh | 2 ++ tests/functional/db-migration.sh | 2 ++ tests/functional/debugger.sh | 2 ++ tests/functional/dependencies.sh | 2 ++ tests/functional/derivation-json.sh | 2 ++ tests/functional/dump-db.sh | 2 ++ tests/functional/eval-store.sh | 2 ++ tests/functional/eval.sh | 2 ++ tests/functional/experimental-features.sh | 2 ++ tests/functional/export-graph.sh | 2 ++ tests/functional/export.sh | 2 ++ tests/functional/extra-sandbox-profile.sh | 2 ++ tests/functional/fetchClosure.sh | 2 ++ tests/functional/fetchGit.sh | 2 ++ tests/functional/fetchGitRefs.sh | 2 ++ tests/functional/fetchGitSubmodules.sh | 2 ++ tests/functional/fetchGitVerification.sh | 2 ++ tests/functional/fetchMercurial.sh | 2 ++ tests/functional/fetchPath.sh | 2 ++ tests/functional/fetchTree-file.sh | 2 ++ tests/functional/fetchurl.sh | 2 ++ tests/functional/filter-source.sh | 2 ++ tests/functional/fixed.sh | 2 ++ tests/functional/flakes/absolute-attr-paths.sh | 2 ++ tests/functional/flakes/absolute-paths.sh | 2 ++ tests/functional/flakes/build-paths.sh | 2 ++ tests/functional/flakes/bundle.sh | 2 ++ tests/functional/flakes/check.sh | 2 ++ tests/functional/flakes/circular.sh | 2 ++ tests/functional/flakes/config.sh | 2 ++ tests/functional/flakes/develop.sh | 2 ++ tests/functional/flakes/flake-in-submodule.sh | 2 ++ tests/functional/flakes/flakes.sh | 2 ++ tests/functional/flakes/follow-paths.sh | 2 ++ tests/functional/flakes/init.sh | 2 ++ tests/functional/flakes/inputs.sh | 2 ++ tests/functional/flakes/mercurial.sh | 2 ++ tests/functional/flakes/prefetch.sh | 2 ++ tests/functional/flakes/run.sh | 2 ++ tests/functional/flakes/search-root.sh | 2 ++ tests/functional/flakes/show.sh | 2 ++ tests/functional/flakes/unlocked-override.sh | 2 ++ tests/functional/fmt.sh | 2 ++ tests/functional/function-trace.sh | 2 ++ tests/functional/gc-auto.sh | 2 ++ tests/functional/gc-concurrent.sh | 2 ++ tests/functional/gc-non-blocking.sh | 2 ++ tests/functional/gc-runtime.sh | 2 ++ tests/functional/gc.sh | 2 ++ tests/functional/hash-convert.sh | 2 ++ tests/functional/hash-path.sh | 2 ++ tests/functional/help.sh | 2 ++ tests/functional/import-derivation.sh | 2 ++ tests/functional/impure-derivations.sh | 2 ++ tests/functional/impure-env.sh | 2 ++ tests/functional/impure-eval.sh | 2 ++ tests/functional/lang-test-infra.sh | 2 ++ tests/functional/lang.sh | 2 ++ tests/functional/legacy-ssh-store.sh | 2 ++ tests/functional/linux-sandbox.sh | 2 ++ tests/functional/logging.sh | 2 ++ tests/functional/misc.sh | 2 ++ tests/functional/multiple-outputs.sh | 2 ++ tests/functional/nar-access.sh | 2 ++ tests/functional/nested-sandboxing.sh | 2 ++ tests/functional/nix-build.sh | 2 ++ tests/functional/nix-channel.sh | 2 ++ tests/functional/nix-collect-garbage-d.sh | 2 ++ tests/functional/nix-copy-ssh-ng.sh | 2 ++ tests/functional/nix-copy-ssh.sh | 2 ++ tests/functional/nix-profile.sh | 2 ++ tests/functional/nix-shell.sh | 2 ++ tests/functional/nix_path.sh | 2 ++ tests/functional/optimise-store.sh | 2 ++ tests/functional/output-normalization.sh | 2 ++ tests/functional/pass-as-file.sh | 2 ++ tests/functional/path-from-hash-part.sh | 2 ++ tests/functional/path-info.sh | 2 ++ tests/functional/placeholders.sh | 2 ++ tests/functional/plugins.sh | 2 ++ tests/functional/post-hook.sh | 2 ++ tests/functional/pure-eval.sh | 2 ++ tests/functional/read-only-store.sh | 2 ++ tests/functional/readfile-context.sh | 2 ++ tests/functional/recursive.sh | 2 ++ tests/functional/referrers.sh | 2 ++ tests/functional/remote-store.sh | 2 ++ tests/functional/repair.sh | 2 ++ tests/functional/repl.sh | 1 + tests/functional/restricted.sh | 2 ++ tests/functional/search.sh | 2 ++ tests/functional/secure-drv-outputs.sh | 2 ++ tests/functional/selfref-gc.sh | 2 ++ tests/functional/shell.sh | 2 ++ tests/functional/signing.sh | 2 ++ tests/functional/simple.sh | 2 ++ tests/functional/ssh-relay.sh | 2 ++ tests/functional/store-info.sh | 2 ++ tests/functional/structured-attrs.sh | 2 ++ tests/functional/substitute-with-invalid-ca.sh | 2 ++ tests/functional/suggestions.sh | 2 ++ tests/functional/supplementary-groups.sh | 2 ++ tests/functional/tarball.sh | 2 ++ tests/functional/test-infra.sh | 2 ++ tests/functional/test-libstoreconsumer.sh | 2 ++ tests/functional/timeout.sh | 2 ++ tests/functional/toString-path.sh | 2 ++ tests/functional/user-envs-migration.sh | 2 ++ tests/functional/user-envs.sh | 2 ++ tests/functional/why-depends.sh | 2 ++ tests/functional/zstd.sh | 2 ++ 136 files changed, 253 insertions(+) mode change 100644 => 100755 tests/functional/add.sh mode change 100644 => 100755 tests/functional/bash-profile.sh mode change 100644 => 100755 tests/functional/binary-cache-build-remote.sh mode change 100644 => 100755 tests/functional/binary-cache.sh mode change 100644 => 100755 tests/functional/brotli.sh mode change 100644 => 100755 tests/functional/build-delete.sh mode change 100644 => 100755 tests/functional/build-dry.sh mode change 100644 => 100755 tests/functional/build-remote-content-addressed-fixed.sh mode change 100644 => 100755 tests/functional/build-remote-content-addressed-floating.sh mode change 100644 => 100755 tests/functional/build-remote-input-addressed.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-fail-0.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-0.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-1.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-2.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-3.sh mode change 100644 => 100755 tests/functional/build-remote-with-mounted-ssh-ng.sh mode change 100644 => 100755 tests/functional/build.sh mode change 100644 => 100755 tests/functional/case-hack.sh mode change 100644 => 100755 tests/functional/check-refs.sh mode change 100644 => 100755 tests/functional/check-reqs.sh mode change 100644 => 100755 tests/functional/check.sh mode change 100644 => 100755 tests/functional/chroot-store.sh mode change 100644 => 100755 tests/functional/completions.sh mode change 100644 => 100755 tests/functional/compression-levels.sh mode change 100644 => 100755 tests/functional/compute-levels.sh mode change 100644 => 100755 tests/functional/config.sh mode change 100644 => 100755 tests/functional/db-migration.sh mode change 100644 => 100755 tests/functional/debugger.sh mode change 100644 => 100755 tests/functional/dependencies.sh mode change 100644 => 100755 tests/functional/derivation-json.sh mode change 100644 => 100755 tests/functional/dump-db.sh mode change 100644 => 100755 tests/functional/eval-store.sh mode change 100644 => 100755 tests/functional/eval.sh mode change 100644 => 100755 tests/functional/experimental-features.sh mode change 100644 => 100755 tests/functional/export-graph.sh mode change 100644 => 100755 tests/functional/export.sh mode change 100644 => 100755 tests/functional/extra-sandbox-profile.sh mode change 100644 => 100755 tests/functional/fetchClosure.sh mode change 100644 => 100755 tests/functional/fetchGit.sh mode change 100644 => 100755 tests/functional/fetchGitRefs.sh mode change 100644 => 100755 tests/functional/fetchGitSubmodules.sh mode change 100644 => 100755 tests/functional/fetchGitVerification.sh mode change 100644 => 100755 tests/functional/fetchMercurial.sh mode change 100644 => 100755 tests/functional/fetchPath.sh mode change 100644 => 100755 tests/functional/fetchTree-file.sh mode change 100644 => 100755 tests/functional/fetchurl.sh mode change 100644 => 100755 tests/functional/filter-source.sh mode change 100644 => 100755 tests/functional/fixed.sh mode change 100644 => 100755 tests/functional/flakes/absolute-attr-paths.sh mode change 100644 => 100755 tests/functional/flakes/absolute-paths.sh mode change 100644 => 100755 tests/functional/flakes/build-paths.sh mode change 100644 => 100755 tests/functional/flakes/bundle.sh mode change 100644 => 100755 tests/functional/flakes/check.sh mode change 100644 => 100755 tests/functional/flakes/circular.sh mode change 100644 => 100755 tests/functional/flakes/config.sh mode change 100644 => 100755 tests/functional/flakes/develop.sh mode change 100644 => 100755 tests/functional/flakes/flake-in-submodule.sh mode change 100644 => 100755 tests/functional/flakes/flakes.sh mode change 100644 => 100755 tests/functional/flakes/follow-paths.sh mode change 100644 => 100755 tests/functional/flakes/init.sh mode change 100644 => 100755 tests/functional/flakes/inputs.sh mode change 100644 => 100755 tests/functional/flakes/mercurial.sh mode change 100644 => 100755 tests/functional/flakes/prefetch.sh mode change 100644 => 100755 tests/functional/flakes/run.sh mode change 100644 => 100755 tests/functional/flakes/search-root.sh mode change 100644 => 100755 tests/functional/flakes/show.sh mode change 100644 => 100755 tests/functional/flakes/unlocked-override.sh mode change 100644 => 100755 tests/functional/fmt.sh mode change 100644 => 100755 tests/functional/gc-auto.sh mode change 100644 => 100755 tests/functional/gc-concurrent.sh mode change 100644 => 100755 tests/functional/gc-non-blocking.sh mode change 100644 => 100755 tests/functional/gc-runtime.sh mode change 100644 => 100755 tests/functional/gc.sh mode change 100644 => 100755 tests/functional/hash-convert.sh mode change 100644 => 100755 tests/functional/hash-path.sh mode change 100644 => 100755 tests/functional/help.sh mode change 100644 => 100755 tests/functional/import-derivation.sh mode change 100644 => 100755 tests/functional/impure-derivations.sh mode change 100644 => 100755 tests/functional/impure-env.sh mode change 100644 => 100755 tests/functional/impure-eval.sh mode change 100644 => 100755 tests/functional/lang-test-infra.sh mode change 100644 => 100755 tests/functional/legacy-ssh-store.sh mode change 100644 => 100755 tests/functional/linux-sandbox.sh mode change 100644 => 100755 tests/functional/logging.sh mode change 100644 => 100755 tests/functional/misc.sh mode change 100644 => 100755 tests/functional/multiple-outputs.sh mode change 100644 => 100755 tests/functional/nar-access.sh mode change 100644 => 100755 tests/functional/nested-sandboxing.sh mode change 100644 => 100755 tests/functional/nix-build.sh mode change 100644 => 100755 tests/functional/nix-channel.sh mode change 100644 => 100755 tests/functional/nix-collect-garbage-d.sh mode change 100644 => 100755 tests/functional/nix-copy-ssh-ng.sh mode change 100644 => 100755 tests/functional/nix-copy-ssh.sh mode change 100644 => 100755 tests/functional/nix-profile.sh mode change 100644 => 100755 tests/functional/nix-shell.sh mode change 100644 => 100755 tests/functional/nix_path.sh mode change 100644 => 100755 tests/functional/optimise-store.sh mode change 100644 => 100755 tests/functional/output-normalization.sh mode change 100644 => 100755 tests/functional/pass-as-file.sh mode change 100644 => 100755 tests/functional/path-from-hash-part.sh mode change 100644 => 100755 tests/functional/path-info.sh mode change 100644 => 100755 tests/functional/placeholders.sh mode change 100644 => 100755 tests/functional/plugins.sh mode change 100644 => 100755 tests/functional/post-hook.sh mode change 100644 => 100755 tests/functional/pure-eval.sh mode change 100644 => 100755 tests/functional/read-only-store.sh mode change 100644 => 100755 tests/functional/readfile-context.sh mode change 100644 => 100755 tests/functional/recursive.sh mode change 100644 => 100755 tests/functional/referrers.sh mode change 100644 => 100755 tests/functional/remote-store.sh mode change 100644 => 100755 tests/functional/repair.sh mode change 100644 => 100755 tests/functional/repl.sh mode change 100644 => 100755 tests/functional/restricted.sh mode change 100644 => 100755 tests/functional/search.sh mode change 100644 => 100755 tests/functional/secure-drv-outputs.sh mode change 100644 => 100755 tests/functional/selfref-gc.sh mode change 100644 => 100755 tests/functional/shell.sh mode change 100644 => 100755 tests/functional/signing.sh mode change 100644 => 100755 tests/functional/simple.sh mode change 100644 => 100755 tests/functional/ssh-relay.sh mode change 100644 => 100755 tests/functional/store-info.sh mode change 100644 => 100755 tests/functional/structured-attrs.sh mode change 100644 => 100755 tests/functional/substitute-with-invalid-ca.sh mode change 100644 => 100755 tests/functional/suggestions.sh mode change 100644 => 100755 tests/functional/supplementary-groups.sh mode change 100644 => 100755 tests/functional/tarball.sh mode change 100644 => 100755 tests/functional/test-infra.sh mode change 100644 => 100755 tests/functional/test-libstoreconsumer.sh mode change 100644 => 100755 tests/functional/timeout.sh mode change 100644 => 100755 tests/functional/toString-path.sh mode change 100644 => 100755 tests/functional/user-envs-migration.sh mode change 100644 => 100755 tests/functional/user-envs.sh mode change 100644 => 100755 tests/functional/why-depends.sh mode change 100644 => 100755 tests/functional/zstd.sh diff --git a/tests/functional/add.sh b/tests/functional/add.sh old mode 100644 new mode 100755 index a4bb0e225..9b448e33b --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh path1=$(nix-store --add ./dummy) diff --git a/tests/functional/bash-profile.sh b/tests/functional/bash-profile.sh old mode 100644 new mode 100755 index 3faeaaba1..6cfa5bd9c --- a/tests/functional/bash-profile.sh +++ b/tests/functional/bash-profile.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh old mode 100644 new mode 100755 index 81cd21a4a..0303e9410 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh old mode 100644 new mode 100755 index 2a8d5ccdb..54a3687ca --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "'--no-require-sigs' can’t be used with the daemon" diff --git a/tests/functional/brotli.sh b/tests/functional/brotli.sh old mode 100644 new mode 100755 index dc9bbdb66..02a2a0875 --- a/tests/functional/brotli.sh +++ b/tests/functional/brotli.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh old mode 100644 new mode 100755 index 9c56b00e8..2ef3008f6 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/build-dry.sh b/tests/functional/build-dry.sh old mode 100644 new mode 100755 index 6d1754af5..9336cf745 --- a/tests/functional/build-dry.sh +++ b/tests/functional/build-dry.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh ################################################### diff --git a/tests/functional/build-remote-content-addressed-fixed.sh b/tests/functional/build-remote-content-addressed-fixed.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-content-addressed-floating.sh b/tests/functional/build-remote-content-addressed-floating.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-0.sh b/tests/functional/build-remote-trustless-should-pass-0.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-1.sh b/tests/functional/build-remote-trustless-should-pass-1.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-with-mounted-ssh-ng.sh b/tests/functional/build-remote-with-mounted-ssh-ng.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build.sh b/tests/functional/build.sh old mode 100644 new mode 100755 index 95a20dc6a..9fb4357be --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/case-hack.sh b/tests/functional/case-hack.sh old mode 100644 new mode 100755 index 61bf9b94b..fbc8242ff --- a/tests/functional/case-hack.sh +++ b/tests/functional/case-hack.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh old mode 100644 new mode 100755 index 3b587d1e5..2cebdd84d --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh old mode 100644 new mode 100755 index 856c94cec..2bcd558fd --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/check.sh b/tests/functional/check.sh old mode 100644 new mode 100755 index 38883c5d7..efb93eeb0 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # XXX: This shouldn’t be, but #4813 cause this test to fail diff --git a/tests/functional/chroot-store.sh b/tests/functional/chroot-store.sh old mode 100644 new mode 100755 index 9e589d04b..60b9c50a7 --- a/tests/functional/chroot-store.sh +++ b/tests/functional/chroot-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo example > $TEST_ROOT/example.txt diff --git a/tests/functional/completions.sh b/tests/functional/completions.sh old mode 100644 new mode 100755 index d3d5bbd48..9164c5013 --- a/tests/functional/completions.sh +++ b/tests/functional/completions.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh cd "$TEST_ROOT" diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh old mode 100644 new mode 100755 index 85f12974a..34f66c531 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/compute-levels.sh b/tests/functional/compute-levels.sh old mode 100644 new mode 100755 index de3da2ebd..a8bd27610 --- a/tests/functional/compute-levels.sh +++ b/tests/functional/compute-levels.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh if [[ $(uname -ms) = "Linux x86_64" ]]; then diff --git a/tests/functional/config.sh b/tests/functional/config.sh old mode 100644 new mode 100755 index efdafa8ca..1811b755c --- a/tests/functional/config.sh +++ b/tests/functional/config.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Isolate the home for this test. diff --git a/tests/functional/db-migration.sh b/tests/functional/db-migration.sh old mode 100644 new mode 100755 index 44cd16bc0..a6a5c7744 --- a/tests/functional/db-migration.sh +++ b/tests/functional/db-migration.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test that we can successfully migrate from an older db schema source common.sh diff --git a/tests/functional/debugger.sh b/tests/functional/debugger.sh old mode 100644 new mode 100755 index 63d88cbf3..47e644bb7 --- a/tests/functional/debugger.sh +++ b/tests/functional/debugger.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh old mode 100644 new mode 100755 index b93dacac0..5922a1f98 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/derivation-json.sh b/tests/functional/derivation-json.sh old mode 100644 new mode 100755 index b6be5d977..59c77e6c5 --- a/tests/functional/derivation-json.sh +++ b/tests/functional/derivation-json.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh drvPath=$(nix-instantiate simple.nix) diff --git a/tests/functional/dump-db.sh b/tests/functional/dump-db.sh old mode 100644 new mode 100755 index 48647f403..2d0460275 --- a/tests/functional/dump-db.sh +++ b/tests/functional/dump-db.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "--dump-db requires a local store" diff --git a/tests/functional/eval-store.sh b/tests/functional/eval-store.sh old mode 100644 new mode 100755 index 9937ecbce..0ab608acc --- a/tests/functional/eval-store.sh +++ b/tests/functional/eval-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Using `--eval-store` with the daemon will eventually copy everything diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh old mode 100644 new mode 100755 index 502170d14..acd6e2915 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/experimental-features.sh b/tests/functional/experimental-features.sh old mode 100644 new mode 100755 index 12112b293..38f198eee --- a/tests/functional/experimental-features.sh +++ b/tests/functional/experimental-features.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Skipping these two for now, because we actually *do* want flags and diff --git a/tests/functional/export-graph.sh b/tests/functional/export-graph.sh old mode 100644 new mode 100755 index 1f6232a40..281b5b05b --- a/tests/functional/export-graph.sh +++ b/tests/functional/export-graph.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/export.sh b/tests/functional/export.sh old mode 100644 new mode 100755 index 2238539bc..fce2e333a --- a/tests/functional/export.sh +++ b/tests/functional/export.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/extra-sandbox-profile.sh b/tests/functional/extra-sandbox-profile.sh old mode 100644 new mode 100755 index ac3ca036f..672e5779d --- a/tests/functional/extra-sandbox-profile.sh +++ b/tests/functional/extra-sandbox-profile.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh if [[ $(uname) != Darwin ]]; then skipTest "Need Darwin"; fi diff --git a/tests/functional/fetchClosure.sh b/tests/functional/fetchClosure.sh old mode 100644 new mode 100755 index a02d1ce7a..4026b0790 --- a/tests/functional/fetchClosure.sh +++ b/tests/functional/fetchClosure.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "fetch-closure" diff --git a/tests/functional/fetchGit.sh b/tests/functional/fetchGit.sh old mode 100644 new mode 100755 index 74d6de4e3..d89d59a81 --- a/tests/functional/fetchGit.sh +++ b/tests/functional/fetchGit.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireGit diff --git a/tests/functional/fetchGitRefs.sh b/tests/functional/fetchGitRefs.sh old mode 100644 new mode 100755 index d643fea04..b17cc2090 --- a/tests/functional/fetchGitRefs.sh +++ b/tests/functional/fetchGitRefs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireGit diff --git a/tests/functional/fetchGitSubmodules.sh b/tests/functional/fetchGitSubmodules.sh old mode 100644 new mode 100755 index bd82a0a17..7c5f7b0d6 --- a/tests/functional/fetchGitSubmodules.sh +++ b/tests/functional/fetchGitSubmodules.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh set -u diff --git a/tests/functional/fetchGitVerification.sh b/tests/functional/fetchGitVerification.sh old mode 100644 new mode 100755 index b80e061b5..27f9a8cf5 --- a/tests/functional/fetchGitVerification.sh +++ b/tests/functional/fetchGitVerification.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireGit diff --git a/tests/functional/fetchMercurial.sh b/tests/functional/fetchMercurial.sh old mode 100644 new mode 100755 index 9f7cef7b2..f3774fc74 --- a/tests/functional/fetchMercurial.sh +++ b/tests/functional/fetchMercurial.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh [[ $(type -p hg) ]] || skipTest "Mercurial not installed" diff --git a/tests/functional/fetchPath.sh b/tests/functional/fetchPath.sh old mode 100644 new mode 100755 index 29be38ce2..e466e4494 --- a/tests/functional/fetchPath.sh +++ b/tests/functional/fetchPath.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh touch $TEST_ROOT/foo -t 202211111111 diff --git a/tests/functional/fetchTree-file.sh b/tests/functional/fetchTree-file.sh old mode 100644 new mode 100755 index be698ea35..9c9532876 --- a/tests/functional/fetchTree-file.sh +++ b/tests/functional/fetchTree-file.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/fetchurl.sh b/tests/functional/fetchurl.sh old mode 100644 new mode 100755 index a3620f52b..2255dbbdc --- a/tests/functional/fetchurl.sh +++ b/tests/functional/fetchurl.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/filter-source.sh b/tests/functional/filter-source.sh old mode 100644 new mode 100755 index ba34d2eac..c5e10be93 --- a/tests/functional/filter-source.sh +++ b/tests/functional/filter-source.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh rm -rf $TEST_ROOT/filterin diff --git a/tests/functional/fixed.sh b/tests/functional/fixed.sh old mode 100644 new mode 100755 index 7bbecda91..4d07d00cd --- a/tests/functional/fixed.sh +++ b/tests/functional/fixed.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/flakes/absolute-attr-paths.sh b/tests/functional/flakes/absolute-attr-paths.sh old mode 100644 new mode 100755 index 491adceb7..8ed1755c4 --- a/tests/functional/flakes/absolute-attr-paths.sh +++ b/tests/functional/flakes/absolute-attr-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh flake1Dir=$TEST_ROOT/flake1 diff --git a/tests/functional/flakes/absolute-paths.sh b/tests/functional/flakes/absolute-paths.sh old mode 100644 new mode 100755 index e7bfba12d..a355a7a1c --- a/tests/functional/flakes/absolute-paths.sh +++ b/tests/functional/flakes/absolute-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh old mode 100644 new mode 100755 index 4e5c68095..a336471f0 --- a/tests/functional/flakes/build-paths.sh +++ b/tests/functional/flakes/build-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh flake1Dir=$TEST_ROOT/flake1 diff --git a/tests/functional/flakes/bundle.sh b/tests/functional/flakes/bundle.sh old mode 100644 new mode 100755 index 67bbb05ac..711691e0b --- a/tests/functional/flakes/bundle.sh +++ b/tests/functional/flakes/bundle.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME diff --git a/tests/functional/flakes/check.sh b/tests/functional/flakes/check.sh old mode 100644 new mode 100755 index 0433e5335..3b83dcafe --- a/tests/functional/flakes/check.sh +++ b/tests/functional/flakes/check.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh flakeDir=$TEST_ROOT/flake3 diff --git a/tests/functional/flakes/circular.sh b/tests/functional/flakes/circular.sh old mode 100644 new mode 100755 index d3bb8e8a3..6cab3a72b --- a/tests/functional/flakes/circular.sh +++ b/tests/functional/flakes/circular.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test circular flake dependencies. source ./common.sh diff --git a/tests/functional/flakes/config.sh b/tests/functional/flakes/config.sh old mode 100644 new mode 100755 index d1941a6be..66b917457 --- a/tests/functional/flakes/config.sh +++ b/tests/functional/flakes/config.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME diff --git a/tests/functional/flakes/develop.sh b/tests/functional/flakes/develop.sh old mode 100644 new mode 100755 index e1e53d364..83be2e308 --- a/tests/functional/flakes/develop.sh +++ b/tests/functional/flakes/develop.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ../common.sh clearStore diff --git a/tests/functional/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh old mode 100644 new mode 100755 index 85a4d3389..2988352a9 --- a/tests/functional/flakes/flake-in-submodule.sh +++ b/tests/functional/flakes/flake-in-submodule.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Tests that: diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh old mode 100644 new mode 100755 index 35b0c5d84..3057c0293 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh old mode 100644 new mode 100755 index 1afd91bd2..ea56b9503 --- a/tests/functional/flakes/follow-paths.sh +++ b/tests/functional/flakes/follow-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh old mode 100644 new mode 100755 index 2d4c77ba1..f8d51e819 --- a/tests/functional/flakes/init.sh +++ b/tests/functional/flakes/init.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/inputs.sh b/tests/functional/flakes/inputs.sh old mode 100644 new mode 100755 index 80620488a..0327a3e9e --- a/tests/functional/flakes/inputs.sh +++ b/tests/functional/flakes/inputs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh old mode 100644 new mode 100755 index 7074af6f7..0e9f2d626 --- a/tests/functional/flakes/mercurial.sh +++ b/tests/functional/flakes/mercurial.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh [[ $(type -p hg) ]] || skipTest "Mercurial not installed" diff --git a/tests/functional/flakes/prefetch.sh b/tests/functional/flakes/prefetch.sh old mode 100644 new mode 100755 index bfd0533f9..a451b7120 --- a/tests/functional/flakes/prefetch.sh +++ b/tests/functional/flakes/prefetch.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Test symlinks in zip files (#10649). diff --git a/tests/functional/flakes/run.sh b/tests/functional/flakes/run.sh old mode 100644 new mode 100755 index 9fa51d1c7..4d8b512b9 --- a/tests/functional/flakes/run.sh +++ b/tests/functional/flakes/run.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ../common.sh clearStore diff --git a/tests/functional/flakes/search-root.sh b/tests/functional/flakes/search-root.sh old mode 100644 new mode 100755 index 6b137aa86..c2337edc0 --- a/tests/functional/flakes/search-root.sh +++ b/tests/functional/flakes/search-root.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh old mode 100644 new mode 100755 index a3d300552..22e1f4193 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh flakeDir=$TEST_ROOT/flake diff --git a/tests/functional/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh old mode 100644 new mode 100755 index 8abc8b7d3..680a1505c --- a/tests/functional/flakes/unlocked-override.sh +++ b/tests/functional/flakes/unlocked-override.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/fmt.sh b/tests/functional/fmt.sh old mode 100644 new mode 100755 index 3c1bd9989..8fc9a3979 --- a/tests/functional/fmt.sh +++ b/tests/functional/fmt.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/function-trace.sh b/tests/functional/function-trace.sh index bd804bf18..71f18b67f 100755 --- a/tests/functional/function-trace.sh +++ b/tests/functional/function-trace.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh set +x diff --git a/tests/functional/gc-auto.sh b/tests/functional/gc-auto.sh old mode 100644 new mode 100755 index 281eef20d..98bb7e60e --- a/tests/functional/gc-auto.sh +++ b/tests/functional/gc-auto.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "“min-free” and “max-free” are daemon options" diff --git a/tests/functional/gc-concurrent.sh b/tests/functional/gc-concurrent.sh old mode 100644 new mode 100755 index 2c6622c62..67ea3dc74 --- a/tests/functional/gc-concurrent.sh +++ b/tests/functional/gc-concurrent.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/gc-non-blocking.sh b/tests/functional/gc-non-blocking.sh old mode 100644 new mode 100755 index ec280badb..ecfa421fb --- a/tests/functional/gc-non-blocking.sh +++ b/tests/functional/gc-non-blocking.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test whether the collector is non-blocking, i.e. a build can run in # parallel with it. source common.sh diff --git a/tests/functional/gc-runtime.sh b/tests/functional/gc-runtime.sh old mode 100644 new mode 100755 index dc1826a55..2ee72b61e --- a/tests/functional/gc-runtime.sh +++ b/tests/functional/gc-runtime.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh case $system in diff --git a/tests/functional/gc.sh b/tests/functional/gc.sh old mode 100644 new mode 100755 index ad09a8b39..1f216ebc7 --- a/tests/functional/gc.sh +++ b/tests/functional/gc.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/hash-convert.sh b/tests/functional/hash-convert.sh old mode 100644 new mode 100755 index 9b3afc10b..3a099950f --- a/tests/functional/hash-convert.sh +++ b/tests/functional/hash-convert.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Conversion with `nix hash` `nix-hash` and `nix hash convert` diff --git a/tests/functional/hash-path.sh b/tests/functional/hash-path.sh old mode 100644 new mode 100755 index 4ad9f8ff2..12605ef71 --- a/tests/functional/hash-path.sh +++ b/tests/functional/hash-path.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh try () { diff --git a/tests/functional/help.sh b/tests/functional/help.sh old mode 100644 new mode 100755 index 868f5d2e9..6436fb500 --- a/tests/functional/help.sh +++ b/tests/functional/help.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/import-derivation.sh b/tests/functional/import-derivation.sh old mode 100644 new mode 100755 index 98d61ef49..53efa1f5d --- a/tests/functional/import-derivation.sh +++ b/tests/functional/import-derivation.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/impure-derivations.sh b/tests/functional/impure-derivations.sh old mode 100644 new mode 100755 index 54ed6f5dd..b59f73c77 --- a/tests/functional/impure-derivations.sh +++ b/tests/functional/impure-derivations.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireDaemonNewerThan "2.8pre20220311" diff --git a/tests/functional/impure-env.sh b/tests/functional/impure-env.sh old mode 100644 new mode 100755 index cfea4cae9..3c7df169e --- a/tests/functional/impure-env.sh +++ b/tests/functional/impure-env.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Needs the config option 'impure-env' to work diff --git a/tests/functional/impure-eval.sh b/tests/functional/impure-eval.sh old mode 100644 new mode 100755 index 6c72f01d7..33a5ea409 --- a/tests/functional/impure-eval.sh +++ b/tests/functional/impure-eval.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh export REMOTE_STORE="dummy://" diff --git a/tests/functional/lang-test-infra.sh b/tests/functional/lang-test-infra.sh old mode 100644 new mode 100755 index 30da8977b..f32ccef05 --- a/tests/functional/lang-test-infra.sh +++ b/tests/functional/lang-test-infra.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test the function for lang.sh source common.sh diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 60603cebe..a853cfd81 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh set -o pipefail diff --git a/tests/functional/legacy-ssh-store.sh b/tests/functional/legacy-ssh-store.sh old mode 100644 new mode 100755 index 56b4c2d20..3a1a7b022 --- a/tests/functional/legacy-ssh-store.sh +++ b/tests/functional/legacy-ssh-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh store_uri="ssh://localhost?remote-store=$TEST_ROOT/other-store" diff --git a/tests/functional/linux-sandbox.sh b/tests/functional/linux-sandbox.sh old mode 100644 new mode 100755 index e553791d9..e71224f5e --- a/tests/functional/linux-sandbox.sh +++ b/tests/functional/linux-sandbox.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "the sandbox only runs on the builder side, so it makes no sense to test it with the daemon" diff --git a/tests/functional/logging.sh b/tests/functional/logging.sh old mode 100644 new mode 100755 index 1ccc21d0b..63752f6db --- a/tests/functional/logging.sh +++ b/tests/functional/logging.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/misc.sh b/tests/functional/misc.sh old mode 100644 new mode 100755 index d4379b7ce..9eb80ad22 --- a/tests/functional/misc.sh +++ b/tests/functional/misc.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Tests miscellaneous commands. diff --git a/tests/functional/multiple-outputs.sh b/tests/functional/multiple-outputs.sh old mode 100644 new mode 100755 index 330600d08..af9f8af72 --- a/tests/functional/multiple-outputs.sh +++ b/tests/functional/multiple-outputs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nar-access.sh b/tests/functional/nar-access.sh old mode 100644 new mode 100755 index 87981e7d9..8839fd043 --- a/tests/functional/nar-access.sh +++ b/tests/functional/nar-access.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo "building test path" diff --git a/tests/functional/nested-sandboxing.sh b/tests/functional/nested-sandboxing.sh old mode 100644 new mode 100755 index 61fe043c6..44c3bb2bc --- a/tests/functional/nested-sandboxing.sh +++ b/tests/functional/nested-sandboxing.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # This test is run by `tests/functional/nested-sandboxing/runner.nix` in an extra layer of sandboxing. [[ -d /nix/store ]] || skipTest "running this test without Nix's deps being drawn from /nix/store is not yet supported" diff --git a/tests/functional/nix-build.sh b/tests/functional/nix-build.sh old mode 100644 new mode 100755 index 44a5a14cd..45ff314c7 --- a/tests/functional/nix-build.sh +++ b/tests/functional/nix-build.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix-channel.sh b/tests/functional/nix-channel.sh old mode 100644 new mode 100755 index ca5df3bdd..a4870e7a8 --- a/tests/functional/nix-channel.sh +++ b/tests/functional/nix-channel.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearProfiles diff --git a/tests/functional/nix-collect-garbage-d.sh b/tests/functional/nix-collect-garbage-d.sh old mode 100644 new mode 100755 index bf30f8938..07aaf61e9 --- a/tests/functional/nix-collect-garbage-d.sh +++ b/tests/functional/nix-collect-garbage-d.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix-copy-ssh-ng.sh b/tests/functional/nix-copy-ssh-ng.sh old mode 100644 new mode 100755 index 62e99cd24..1fd735b9d --- a/tests/functional/nix-copy-ssh-ng.sh +++ b/tests/functional/nix-copy-ssh-ng.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh source nix-copy-ssh-common.sh "ssh-ng" diff --git a/tests/functional/nix-copy-ssh.sh b/tests/functional/nix-copy-ssh.sh old mode 100644 new mode 100755 index 12e8346bc..1dc256e49 --- a/tests/functional/nix-copy-ssh.sh +++ b/tests/functional/nix-copy-ssh.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh source nix-copy-ssh-common.sh "ssh" diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh old mode 100644 new mode 100755 index 7c4da6283..3e5846cf2 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh old mode 100644 new mode 100755 index 04c83138e..c38107e64 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix_path.sh b/tests/functional/nix_path.sh old mode 100644 new mode 100755 index 2b222b4a1..e6a2193f3 --- a/tests/functional/nix_path.sh +++ b/tests/functional/nix_path.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Regression for https://github.com/NixOS/nix/issues/5998 and https://github.com/NixOS/nix/issues/5980 source common.sh diff --git a/tests/functional/optimise-store.sh b/tests/functional/optimise-store.sh old mode 100644 new mode 100755 index 8c2d05cd5..70ce954f9 --- a/tests/functional/optimise-store.sh +++ b/tests/functional/optimise-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/output-normalization.sh b/tests/functional/output-normalization.sh old mode 100644 new mode 100755 index 0f6df5e31..2b319201a --- a/tests/functional/output-normalization.sh +++ b/tests/functional/output-normalization.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh testNormalization () { diff --git a/tests/functional/pass-as-file.sh b/tests/functional/pass-as-file.sh old mode 100644 new mode 100755 index 2c0bc5031..21d9ffc6d --- a/tests/functional/pass-as-file.sh +++ b/tests/functional/pass-as-file.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/path-from-hash-part.sh b/tests/functional/path-from-hash-part.sh old mode 100644 new mode 100755 index bdd104434..41d1b7410 --- a/tests/functional/path-from-hash-part.sh +++ b/tests/functional/path-from-hash-part.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh path=$(nix build --no-link --print-out-paths -f simple.nix) diff --git a/tests/functional/path-info.sh b/tests/functional/path-info.sh old mode 100644 new mode 100755 index 763935eb7..8597de683 --- a/tests/functional/path-info.sh +++ b/tests/functional/path-info.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo foo > $TEST_ROOT/foo diff --git a/tests/functional/placeholders.sh b/tests/functional/placeholders.sh old mode 100644 new mode 100755 index cd1bb7bc2..f2b8bf6bf --- a/tests/functional/placeholders.sh +++ b/tests/functional/placeholders.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/plugins.sh b/tests/functional/plugins.sh old mode 100644 new mode 100755 index baf71a362..ab4876df9 --- a/tests/functional/plugins.sh +++ b/tests/functional/plugins.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh if [[ $BUILD_SHARED_LIBS != 1 ]]; then diff --git a/tests/functional/post-hook.sh b/tests/functional/post-hook.sh old mode 100644 new mode 100755 index 752f8220c..c0b1ab3aa --- a/tests/functional/post-hook.sh +++ b/tests/functional/post-hook.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/pure-eval.sh b/tests/functional/pure-eval.sh old mode 100644 new mode 100755 index 5334bf28e..6d8aa35ec --- a/tests/functional/pure-eval.sh +++ b/tests/functional/pure-eval.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/read-only-store.sh b/tests/functional/read-only-store.sh old mode 100644 new mode 100755 index 834ac1b51..ecc57642d --- a/tests/functional/read-only-store.sh +++ b/tests/functional/read-only-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "read-only-local-store" diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh old mode 100644 new mode 100755 index 31e70ddb1..76fad9349 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh old mode 100644 new mode 100755 index 0bf00f8fa..a9966aabd --- a/tests/functional/recursive.sh +++ b/tests/functional/recursive.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures 'recursive-nix' diff --git a/tests/functional/referrers.sh b/tests/functional/referrers.sh old mode 100644 new mode 100755 index 81323c280..898032e42 --- a/tests/functional/referrers.sh +++ b/tests/functional/referrers.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "uses some low-level store manipulations that aren’t available through the daemon" diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh old mode 100644 new mode 100755 index e2c16f18a..171a5d391 --- a/tests/functional/remote-store.sh +++ b/tests/functional/remote-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/repair.sh b/tests/functional/repair.sh old mode 100644 new mode 100755 index c8f07b1c6..552e04280 --- a/tests/functional/repair.sh +++ b/tests/functional/repair.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "--repair needs a local store" diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh old mode 100644 new mode 100755 index 222145a7a..fca982807 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash + source common.sh testDir="$PWD" diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh old mode 100644 new mode 100755 index 3de26eb36..ab4cad5cf --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/search.sh b/tests/functional/search.sh old mode 100644 new mode 100755 index d9c7a75da..ce17411d2 --- a/tests/functional/search.sh +++ b/tests/functional/search.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/secure-drv-outputs.sh b/tests/functional/secure-drv-outputs.sh old mode 100644 new mode 100755 index 50a9c4428..7d81db58b --- a/tests/functional/secure-drv-outputs.sh +++ b/tests/functional/secure-drv-outputs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test that users cannot register specially-crafted derivations that # produce output paths belonging to other derivations. This could be # used to inject malware into the store. diff --git a/tests/functional/selfref-gc.sh b/tests/functional/selfref-gc.sh old mode 100644 new mode 100755 index 3f1f50eea..37ce33089 --- a/tests/functional/selfref-gc.sh +++ b/tests/functional/selfref-gc.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireDaemonNewerThan "2.6.0pre20211215" diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh old mode 100644 new mode 100755 index 8a3fef3e7..e6bb4b161 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh old mode 100644 new mode 100755 index 942b51630..bcbd3b675 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh old mode 100644 new mode 100755 index 50d44f93f..846738cbd --- a/tests/functional/simple.sh +++ b/tests/functional/simple.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh drvPath=$(nix-instantiate simple.nix) diff --git a/tests/functional/ssh-relay.sh b/tests/functional/ssh-relay.sh old mode 100644 new mode 100755 index 053b2f00d..059c66434 --- a/tests/functional/ssh-relay.sh +++ b/tests/functional/ssh-relay.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo foo > $TEST_ROOT/hello.sh diff --git a/tests/functional/store-info.sh b/tests/functional/store-info.sh old mode 100644 new mode 100755 index 18a8131a9..2398f5beb --- a/tests/functional/store-info.sh +++ b/tests/functional/store-info.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh STORE_INFO=$(nix store info 2>&1) diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh old mode 100644 new mode 100755 index 6711efbb4..ba7f5967e --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # 27ce722638 required some incompatible changes to the nix file, so skip this diff --git a/tests/functional/substitute-with-invalid-ca.sh b/tests/functional/substitute-with-invalid-ca.sh old mode 100644 new mode 100755 index 4d0b01e0f..d8af67237 --- a/tests/functional/substitute-with-invalid-ca.sh +++ b/tests/functional/substitute-with-invalid-ca.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh BINARY_CACHE=file://$cacheDir diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh old mode 100644 new mode 100755 index f18fefef9..6ec1cd322 --- a/tests/functional/suggestions.sh +++ b/tests/functional/suggestions.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/supplementary-groups.sh b/tests/functional/supplementary-groups.sh old mode 100644 new mode 100755 index d18fb2414..9d474219f --- a/tests/functional/supplementary-groups.sh +++ b/tests/functional/supplementary-groups.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireSandboxSupport diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh old mode 100644 new mode 100755 index 062f27ad6..ce162ddce --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh old mode 100644 new mode 100755 index 54ae120e7..37322b356 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test the functions for testing themselves! # Also test some assumptions on how bash works that they rely on. source common.sh diff --git a/tests/functional/test-libstoreconsumer.sh b/tests/functional/test-libstoreconsumer.sh old mode 100644 new mode 100755 index 8a77cf5a1..d1a1accb6 --- a/tests/functional/test-libstoreconsumer.sh +++ b/tests/functional/test-libstoreconsumer.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh drv="$(nix-instantiate simple.nix)" diff --git a/tests/functional/timeout.sh b/tests/functional/timeout.sh old mode 100644 new mode 100755 index b179b79a2..441c83b0e --- a/tests/functional/timeout.sh +++ b/tests/functional/timeout.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test the `--timeout' option. source common.sh diff --git a/tests/functional/toString-path.sh b/tests/functional/toString-path.sh old mode 100644 new mode 100755 index 07eb87465..d790109f4 --- a/tests/functional/toString-path.sh +++ b/tests/functional/toString-path.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh mkdir -p $TEST_ROOT/foo diff --git a/tests/functional/user-envs-migration.sh b/tests/functional/user-envs-migration.sh old mode 100644 new mode 100755 index 187372b16..992586b95 --- a/tests/functional/user-envs-migration.sh +++ b/tests/functional/user-envs-migration.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test that the migration of user environments # (https://github.com/NixOS/nix/pull/5226) does preserve everything diff --git a/tests/functional/user-envs.sh b/tests/functional/user-envs.sh old mode 100644 new mode 100755 index a849d5439..ec9d036f8 --- a/tests/functional/user-envs.sh +++ b/tests/functional/user-envs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh source ./user-envs-test-case.sh diff --git a/tests/functional/why-depends.sh b/tests/functional/why-depends.sh old mode 100644 new mode 100755 index 9680bf80e..69b365069 --- a/tests/functional/why-depends.sh +++ b/tests/functional/why-depends.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/zstd.sh b/tests/functional/zstd.sh old mode 100644 new mode 100755 index ba7c20501..3bf9d5601 --- a/tests/functional/zstd.sh +++ b/tests/functional/zstd.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore From 2bd66922eef1c41e74efe1cca722b0ee7d640af1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 29 May 2024 01:05:40 +0200 Subject: [PATCH 171/910] add empty line to documentation comments after `@brief` field (#10800) * add empty line to documentation comments after `@brief` field Co-authored-by: Cole Helbling --- src/libexpr-c/nix_api_value.h | 1 + src/libstore-c/nix_api_store.h | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index b2b3439ef..f568b5c27 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -79,6 +79,7 @@ typedef struct nix_realised_string nix_realised_string; * @{ */ /** @brief Function pointer for primops + * * When you want to return an error, call nix_set_err_msg(context, NIX_ERR_UNKNOWN, "your error message here"). * * @param[in] user_data Arbitrary data that was initially supplied to nix_alloc_primop diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index e9441be1a..d3cb8fab8 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -54,8 +54,10 @@ nix_err nix_libstore_init_no_load_config(nix_c_context * context); nix_err nix_init_plugins(nix_c_context * context); /** - * @brief Open a nix store + * @brief Open a nix store. + * * Store instances may share state and resources behind the scenes. + * * @param[out] context Optional, stores error information * @param[in] uri URI of the Nix store, copied. See [*Store URL format* in the Nix Reference * Manual](https://nixos.org/manual/nix/stable/store/types/#store-url-format). @@ -157,7 +159,9 @@ nix_err nix_store_realise( /** * @brief get the version of a nix store. + * * If the store doesn't have a version (like the dummy store), returns an empty string. + * * @param[out] context Optional, stores error information * @param[in] store nix store reference * @param[in] callback Called with the version. From 5786e1ae7c300b3c7434e7df99b41f180dc42e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 29 May 2024 09:50:51 +0200 Subject: [PATCH 172/910] docs: mention importNative/exec in allow-unsafe-native-code-during-evaluation (#10803) * docs: mention importNative/exec in allow-unsafe-native-code-during-evaluation Both of these still needs their own actual documentation, but they are at least now mentioned that they exist and what they're enabled by. Co-authored-by: Qyriad Co-authored-by: Valentin Gagarin --- src/libexpr/eval-settings.hh | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 60d3a6f25..dbfc3b2c7 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -15,8 +15,24 @@ struct EvalSettings : Config static std::string resolvePseudoUrl(std::string_view url); - Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", - "Whether builtin functions that allow executing native code should be enabled."}; + Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"( + Enable built-in functions that allow executing native code. + + In particular, this adds: + - `builtins.importNative` *path* + + Load a dynamic shared object (DSO) at *path* which exposes a function pointer to a procedure that initialises a Nix language value, and return that value. + The procedure must have the following signature: + ```cpp + extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); + ``` + + The [Nix C++ API documentation](@docroot@/contributing/documentation.md#api-documentation) has more details on evaluator internals. + + - `builtins.exec` *arguments* + + Execute a program, where *arguments* are specified as a list of strings, and parse its output as a Nix expression. + )"}; Setting nixPath{ this, getDefaultNixPath(), "nix-path", From e2182d07d9320020993fe84baff262f81243501e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sat, 25 May 2024 23:42:24 +0200 Subject: [PATCH 173/910] fixup extension of changelog entry --- .../{fix-silent-unknown-options => fix-silent-unknown-options.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/manual/rl-next/{fix-silent-unknown-options => fix-silent-unknown-options.md} (100%) diff --git a/doc/manual/rl-next/fix-silent-unknown-options b/doc/manual/rl-next/fix-silent-unknown-options.md similarity index 100% rename from doc/manual/rl-next/fix-silent-unknown-options rename to doc/manual/rl-next/fix-silent-unknown-options.md From 18ac6545fc9081d943dbe007ea8f58c69b26279e Mon Sep 17 00:00:00 2001 From: Qyriad Date: Tue, 21 May 2024 05:29:52 -0600 Subject: [PATCH 174/910] print type and value in "flake attr is not a derivation" errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This turns errors like: error: flake output attribute 'hydraJobs' is not a derivation or path into errors like: error: expected flake output attribute 'hydraJobs' to be a derivation or path but found a set: { binaryTarball = «thunk»; build = «thunk»; etc> } This change affects all InstallableFlake commands. Source: https://git.lix.systems/lix-project/lix/commit/20981461d4a2a62c68f4bc7c4258473f7cd7d8e1 Signed-off-by: Jörg Thalheim --- .../print-value-in-installable-flake-error.md | 18 ++++++++++++++++++ src/libcmd/installable-flake.cc | 9 +++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 doc/manual/rl-next/print-value-in-installable-flake-error.md diff --git a/doc/manual/rl-next/print-value-in-installable-flake-error.md b/doc/manual/rl-next/print-value-in-installable-flake-error.md new file mode 100644 index 000000000..bb35e252e --- /dev/null +++ b/doc/manual/rl-next/print-value-in-installable-flake-error.md @@ -0,0 +1,18 @@ +--- +synopsis: New-cli flake commands that expect derivations now print the failing value and its type +prs: 10778 +--- + +In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. + +Before: + +``` + error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path +```` + +After: + +``` + error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } +``` diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 6ff837ddc..d42fa7aac 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -106,9 +106,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths() fmt("while evaluating the flake output attribute '%s'", attrPath))) { return { *derivedPathWithInfo }; + } else { + throw Error( + "expected flake output attribute '%s' to be a derivation or path but found %s: %s", + attrPath, + showType(v), + ValuePrinter(*this->state, v, errorPrintOptions) + ); } - else - throw Error("flake output attribute '%s' is not a derivation or path", attrPath); } auto drvPath = attr->forceDerivation(); From 1c70eb8eee8a439443736f226bda60b69a69f9a4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 29 May 2024 21:42:53 +0200 Subject: [PATCH 175/910] libcmd: Fix #10774 --- src/libcmd/common-eval-args.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 155b43b70..cd0f19257 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -20,7 +20,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the value *expr* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "expr"}, - .handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr(expr)}); }} + .handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr{expr}}); }} }); addFlag({ @@ -28,7 +28,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the string *string* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "string"}, - .handler = {[&](std::string name, std::string s) { autoArgs.insert_or_assign(name, AutoArg{AutoArgString(s)}); }}, + .handler = {[&](std::string name, std::string s) { autoArgs.insert_or_assign(name, AutoArg{AutoArgString{s}}); }}, }); addFlag({ @@ -36,7 +36,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the contents of file *path* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "path"}, - .handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile(path)}); }}, + .handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile{path}}); }}, .completer = completePath }); From 0ed356f3c0d6d7e716d113e563c89dcdbd92b11e Mon Sep 17 00:00:00 2001 From: "J. Dekker" Date: Thu, 30 May 2024 15:03:20 +0200 Subject: [PATCH 176/910] scripts/install.in: add riscv64 support to installer The artifacts are already built and hosted, the install script just needs to be taught about riscv64. Signed-off-by: J. Dekker --- scripts/install.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/install.in b/scripts/install.in index 7d2e52b26..b4e808d8e 100755 --- a/scripts/install.in +++ b/scripts/install.in @@ -50,6 +50,11 @@ case "$(uname -s).$(uname -m)" in path=@tarballPath_armv7l-linux@ system=armv7l-linux ;; + Linux.riscv64) + hash=@tarballHash_riscv64-linux@ + path=@tarballPath_riscv64-linux@ + system=riscv64-linux + ;; Darwin.x86_64) hash=@tarballHash_x86_64-darwin@ path=@tarballPath_x86_64-darwin@ From 98b85b2166736f08ae7e4a5e0014e92c8c79ebf7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 30 May 2024 18:19:22 +0200 Subject: [PATCH 177/910] nix/main: Add AliasStatus::{Deprecated,AcceptedShorthand} --- src/nix/main.cc | 68 ++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index 541d1a1b3..3602402ae 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -42,6 +42,19 @@ void chrootHelper(int argc, char * * argv); namespace nix { +enum struct AliasStatus { + /** Aliases that don't go away */ + AcceptedShorthand, + /** Aliases that will go away */ + Deprecated, +}; + +/** An alias, except for the original syntax, which is in the map key. */ +struct AliasInfo { + AliasStatus status; + std::vector replacement; +}; + /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { @@ -134,29 +147,29 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs }); } - std::map> aliases = { - {"add-to-store", {"store", "add-path"}}, - {"cat-nar", {"nar", "cat"}}, - {"cat-store", {"store", "cat"}}, - {"copy-sigs", {"store", "copy-sigs"}}, - {"dev-shell", {"develop"}}, - {"diff-closures", {"store", "diff-closures"}}, - {"dump-path", {"store", "dump-path"}}, - {"hash-file", {"hash", "file"}}, - {"hash-path", {"hash", "path"}}, - {"ls-nar", {"nar", "ls"}}, - {"ls-store", {"store", "ls"}}, - {"make-content-addressable", {"store", "make-content-addressed"}}, - {"optimise-store", {"store", "optimise"}}, - {"ping-store", {"store", "ping"}}, - {"sign-paths", {"store", "sign"}}, - {"show-derivation", {"derivation", "show"}}, - {"show-config", {"config", "show"}}, - {"to-base16", {"hash", "to-base16"}}, - {"to-base32", {"hash", "to-base32"}}, - {"to-base64", {"hash", "to-base64"}}, - {"verify", {"store", "verify"}}, - {"doctor", {"config", "check"}}, + std::map aliases = { + {"add-to-store", { AliasStatus::Deprecated, {"store", "add-path"}}}, + {"cat-nar", { AliasStatus::Deprecated, {"nar", "cat"}}}, + {"cat-store", { AliasStatus::Deprecated, {"store", "cat"}}}, + {"copy-sigs", { AliasStatus::Deprecated, {"store", "copy-sigs"}}}, + {"dev-shell", { AliasStatus::Deprecated, {"develop"}}}, + {"diff-closures", { AliasStatus::Deprecated, {"store", "diff-closures"}}}, + {"dump-path", { AliasStatus::Deprecated, {"store", "dump-path"}}}, + {"hash-file", { AliasStatus::Deprecated, {"hash", "file"}}}, + {"hash-path", { AliasStatus::Deprecated, {"hash", "path"}}}, + {"ls-nar", { AliasStatus::Deprecated, {"nar", "ls"}}}, + {"ls-store", { AliasStatus::Deprecated, {"store", "ls"}}}, + {"make-content-addressable", { AliasStatus::Deprecated, {"store", "make-content-addressed"}}}, + {"optimise-store", { AliasStatus::Deprecated, {"store", "optimise"}}}, + {"ping-store", { AliasStatus::Deprecated, {"store", "ping"}}}, + {"sign-paths", { AliasStatus::Deprecated, {"store", "sign"}}}, + {"show-derivation", { AliasStatus::Deprecated, {"derivation", "show"}}}, + {"show-config", { AliasStatus::Deprecated, {"config", "show"}}}, + {"to-base16", { AliasStatus::Deprecated, {"hash", "to-base16"}}}, + {"to-base32", { AliasStatus::Deprecated, {"hash", "to-base32"}}}, + {"to-base64", { AliasStatus::Deprecated, {"hash", "to-base64"}}}, + {"verify", { AliasStatus::Deprecated, {"store", "verify"}}}, + {"doctor", { AliasStatus::Deprecated, {"config", "check"}}}, }; bool aliasUsed = false; @@ -167,10 +180,13 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs auto arg = *pos; auto i = aliases.find(arg); if (i == aliases.end()) return pos; - warn("'%s' is a deprecated alias for '%s'", - arg, concatStringsSep(" ", i->second)); + auto & info = i->second; + if (info.status == AliasStatus::Deprecated) { + warn("'%s' is a deprecated alias for '%s'", + arg, concatStringsSep(" ", info.replacement)); + } pos = args.erase(pos); - for (auto j = i->second.rbegin(); j != i->second.rend(); ++j) + for (auto j = info.replacement.rbegin(); j != info.replacement.rend(); ++j) pos = args.insert(pos, *j); aliasUsed = true; return pos; From c692f6af132dc62f72edbb2c7a73bfa838398904 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 30 May 2024 18:22:11 +0200 Subject: [PATCH 178/910] nix env shell: Move from nix shell, add shorthand alias --- doc/manual/rl-next/nix-env-shell.md | 12 ++++ src/nix/env.cc | 98 +++++++++++++++++++++++++++++ src/nix/main.cc | 1 + src/nix/run.cc | 77 ----------------------- tests/functional/shell.sh | 3 + 5 files changed, 114 insertions(+), 77 deletions(-) create mode 100644 doc/manual/rl-next/nix-env-shell.md create mode 100644 src/nix/env.cc diff --git a/doc/manual/rl-next/nix-env-shell.md b/doc/manual/rl-next/nix-env-shell.md new file mode 100644 index 000000000..b2344417a --- /dev/null +++ b/doc/manual/rl-next/nix-env-shell.md @@ -0,0 +1,12 @@ +--- +synopsis: "`nix env shell` is the new `nix shell`, and `nix shell` remains an accepted alias" +issues: 10504 +prs: 10807 +--- + +This is part of an effort to bring more structure to the CLI subcommands. + +`nix env` will be about the process environment. +Future commands may include `nix env run` and `nix env print-env`. + +It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. diff --git a/src/nix/env.cc b/src/nix/env.cc new file mode 100644 index 000000000..47efdf308 --- /dev/null +++ b/src/nix/env.cc @@ -0,0 +1,98 @@ +#include "command.hh" +#include "run.hh" +#include + +using namespace nix; + +struct CmdEnv : NixMultiCommand +{ + CmdEnv() : NixMultiCommand("env", RegisterCommand::getCommandsFor({"env"})) + { } + + std::string description() override + { + return "manipulate the process environment"; + } + + Category category() override { return catUtility; } +}; + +static auto rCmdEnv = registerCommand("env"); + +struct CmdShell : InstallablesCommand, MixEnvironment +{ + + using InstallablesCommand::run; + + std::vector command = { getEnv("SHELL").value_or("bash") }; + + CmdShell() + { + addFlag({ + .longName = "command", + .shortName = 'c', + .description = "Command and arguments to be executed, defaulting to `$SHELL`", + .labels = {"command", "args"}, + .handler = {[&](std::vector ss) { + if (ss.empty()) throw UsageError("--command requires at least one argument"); + command = ss; + }} + }); + } + + std::string description() override + { + return "run a shell in which the specified packages are available"; + } + + std::string doc() override + { + return + #include "shell.md" + ; + } + + void run(ref store, Installables && installables) override + { + auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); + + auto accessor = store->getFSAccessor(); + + std::unordered_set done; + std::queue todo; + for (auto & path : outPaths) todo.push(path); + + setEnviron(); + + std::vector pathAdditions; + + while (!todo.empty()) { + auto path = todo.front(); + todo.pop(); + if (!done.insert(path).second) continue; + + if (true) + pathAdditions.push_back(store->printStorePath(path) + "/bin"); + + auto propPath = accessor->resolveSymlinks( + CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"); + if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { + for (auto & p : tokenizeString(accessor->readFile(propPath))) + todo.push(store->parseStorePath(p)); + } + } + + auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); + unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); + auto unixPathString = concatStringsSep(":", unixPath); + setEnv("PATH", unixPathString.c_str()); + + Strings args; + for (auto & arg : command) args.push_back(arg); + + runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); + } +}; + +static auto rCmdShell = registerCommand2({"env", "shell"}); + diff --git a/src/nix/main.cc b/src/nix/main.cc index 3602402ae..94fd05fba 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -163,6 +163,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs {"optimise-store", { AliasStatus::Deprecated, {"store", "optimise"}}}, {"ping-store", { AliasStatus::Deprecated, {"store", "ping"}}}, {"sign-paths", { AliasStatus::Deprecated, {"store", "sign"}}}, + {"shell", { AliasStatus::AcceptedShorthand, {"env", "shell"}}}, {"show-derivation", { AliasStatus::Deprecated, {"derivation", "show"}}}, {"show-config", { AliasStatus::Deprecated, {"config", "show"}}}, {"to-base16", { AliasStatus::Deprecated, {"hash", "to-base16"}}}, diff --git a/src/nix/run.cc b/src/nix/run.cc index cc999ddf4..7d3122470 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -71,83 +71,6 @@ void runProgramInStore(ref store, } -struct CmdShell : InstallablesCommand, MixEnvironment -{ - - using InstallablesCommand::run; - - std::vector command = { getEnv("SHELL").value_or("bash") }; - - CmdShell() - { - addFlag({ - .longName = "command", - .shortName = 'c', - .description = "Command and arguments to be executed, defaulting to `$SHELL`", - .labels = {"command", "args"}, - .handler = {[&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }} - }); - } - - std::string description() override - { - return "run a shell in which the specified packages are available"; - } - - std::string doc() override - { - return - #include "shell.md" - ; - } - - void run(ref store, Installables && installables) override - { - auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); - - auto accessor = store->getFSAccessor(); - - std::unordered_set done; - std::queue todo; - for (auto & path : outPaths) todo.push(path); - - setEnviron(); - - std::vector pathAdditions; - - while (!todo.empty()) { - auto path = todo.front(); - todo.pop(); - if (!done.insert(path).second) continue; - - if (true) - pathAdditions.push_back(store->printStorePath(path) + "/bin"); - - auto propPath = accessor->resolveSymlinks( - CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"); - if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { - for (auto & p : tokenizeString(accessor->readFile(propPath))) - todo.push(store->parseStorePath(p)); - } - } - - auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); - unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = concatStringsSep(":", unixPath); - setEnv("PATH", unixPathString.c_str()); - - Strings args; - for (auto & arg : command) args.push_back(arg); - - runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); - } -}; - -static auto rCmdShell = registerCommand("shell"); - struct CmdRun : InstallableValueCommand { using InstallableCommand::run; diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index e6bb4b161..1760eefff 100755 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -5,6 +5,9 @@ source common.sh clearStore clearCache +# nix shell is an alias for nix env shell. We'll use the shorter form in the rest of the test. +nix env shell -f shell-hello.nix hello -c hello | grep 'Hello World' + nix shell -f shell-hello.nix hello -c hello | grep 'Hello World' nix shell -f shell-hello.nix hello -c hello NixOS | grep 'Hello NixOS' From d93cc11491f2ebba81ebb56ef8faa1b29bb7699a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 30 May 2024 18:40:53 +0200 Subject: [PATCH 179/910] Format --- src/nix/env.cc | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/nix/env.cc b/src/nix/env.cc index 47efdf308..021c47cbb 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -6,15 +6,20 @@ using namespace nix; struct CmdEnv : NixMultiCommand { - CmdEnv() : NixMultiCommand("env", RegisterCommand::getCommandsFor({"env"})) - { } + CmdEnv() + : NixMultiCommand("env", RegisterCommand::getCommandsFor({"env"})) + { + } std::string description() override { return "manipulate the process environment"; } - Category category() override { return catUtility; } + Category category() override + { + return catUtility; + } }; static auto rCmdEnv = registerCommand("env"); @@ -24,20 +29,20 @@ struct CmdShell : InstallablesCommand, MixEnvironment using InstallablesCommand::run; - std::vector command = { getEnv("SHELL").value_or("bash") }; + std::vector command = {getEnv("SHELL").value_or("bash")}; CmdShell() { - addFlag({ - .longName = "command", - .shortName = 'c', - .description = "Command and arguments to be executed, defaulting to `$SHELL`", - .labels = {"command", "args"}, - .handler = {[&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }} - }); + addFlag( + {.longName = "command", + .shortName = 'c', + .description = "Command and arguments to be executed, defaulting to `$SHELL`", + .labels = {"command", "args"}, + .handler = {[&](std::vector ss) { + if (ss.empty()) + throw UsageError("--command requires at least one argument"); + command = ss; + }}}); } std::string description() override @@ -48,19 +53,21 @@ struct CmdShell : InstallablesCommand, MixEnvironment std::string doc() override { return - #include "shell.md" - ; +#include "shell.md" + ; } void run(ref store, Installables && installables) override { - auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); + auto outPaths = + Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); auto accessor = store->getFSAccessor(); std::unordered_set done; std::queue todo; - for (auto & path : outPaths) todo.push(path); + for (auto & path : outPaths) + todo.push(path); setEnviron(); @@ -69,7 +76,8 @@ struct CmdShell : InstallablesCommand, MixEnvironment while (!todo.empty()) { auto path = todo.front(); todo.pop(); - if (!done.insert(path).second) continue; + if (!done.insert(path).second) + continue; if (true) pathAdditions.push_back(store->printStorePath(path) + "/bin"); @@ -88,11 +96,11 @@ struct CmdShell : InstallablesCommand, MixEnvironment setEnv("PATH", unixPathString.c_str()); Strings args; - for (auto & arg : command) args.push_back(arg); + for (auto & arg : command) + args.push_back(arg); runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); } }; static auto rCmdShell = registerCommand2({"env", "shell"}); - From 73f9afd71626120c2ffcd06b7ae66a7ec6787e36 Mon Sep 17 00:00:00 2001 From: "J. Dekker" Date: Thu, 30 May 2024 20:11:28 +0200 Subject: [PATCH 180/910] upload-release.pl: add riscv64 to nix-fallback-paths.nix This uses the x86_64-linux's cross-compiled output as we don't have a native riscv64 builder. Signed-off-by: J. Dekker --- maintainers/upload-release.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 9e73524a6..4c4e2bd6f 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -243,6 +243,7 @@ write_file("$tmpDir/fallback-paths.nix", " x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" . " i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" . " aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" . + " riscv64-linux = \"" . getStorePath("buildCross.riscv64-unknown-linux-gnu.x86_64-linux") . "\";\n" . " x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" . " aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" . "}\n"); From a9031978da28a3cf759d64b36cbac131c8323945 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Thu, 30 May 2024 23:20:42 +0200 Subject: [PATCH 181/910] libfetchers: handle nonexistent refs in GitLab repos more gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: $ nix flake lock --override-input nixpkgs gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent fetching git input 'git+file:///home/linus/projects/lix' fetching gitlab input 'gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent' error: [json.exception.type_error.302] type must be string, but is null After: /tmp/inst/bin/nix flake lock --override-input nixpkgs gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent warning: unknown experimental feature 'repl-flake' error: … while updating the lock file of flake 'git+file:///home/joerg/git/nix?ref=refs/heads/master&rev=62693c2c37c8edd92f95114eb1387b461fc671df' … while updating the flake input 'nixpkgs' … while fetching the input 'gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent' error: No commits returned by GitLab API -- does the git ref really exist? Adapted from: https://git.lix.systems/lix-project/lix/commit/3df013597d7a2b5e400839e6625c05bd47de4dca --- src/libfetchers/github.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index d62a7482e..267e8607f 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -433,9 +433,15 @@ struct GitLabInputScheme : GitArchiveInputScheme store->toRealPath( downloadFile(store, url, "source", headers).storePath))); - return RefInfo { - .rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1) - }; + if (json.is_array() && json.size() == 1 && json[0]["id"] != nullptr) { + return RefInfo { + .rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1) + }; + } if (json.is_array() && json.size() == 0) { + throw Error("No commits returned by GitLab API -- does the git ref really exist?"); + } else { + throw Error("Unexpected response received from GitLab: %s", json); + } } DownloadUrl getDownloadUrl(const Input & input) const override From e1a817fb1bd8e622172800258000820d7d0c8092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 31 May 2024 10:38:55 +0200 Subject: [PATCH 182/910] fix nix edit in pure mode FilteringSourceAccessor was not delegating getPhysicalPath to its inner accessor. --- src/libfetchers/filtering-source-accessor.cc | 6 ++++++ src/libfetchers/filtering-source-accessor.hh | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/libfetchers/filtering-source-accessor.cc b/src/libfetchers/filtering-source-accessor.cc index dfd9e536d..d4557b6d4 100644 --- a/src/libfetchers/filtering-source-accessor.cc +++ b/src/libfetchers/filtering-source-accessor.cc @@ -2,6 +2,12 @@ namespace nix { +std::optional FilteringSourceAccessor::getPhysicalPath(const CanonPath & path) +{ + checkAccess(path); + return next->getPhysicalPath(prefix / path); +} + std::string FilteringSourceAccessor::readFile(const CanonPath & path) { checkAccess(path); diff --git a/src/libfetchers/filtering-source-accessor.hh b/src/libfetchers/filtering-source-accessor.hh index 9ec7bc21f..1f8d84e53 100644 --- a/src/libfetchers/filtering-source-accessor.hh +++ b/src/libfetchers/filtering-source-accessor.hh @@ -30,6 +30,8 @@ struct FilteringSourceAccessor : SourceAccessor displayPrefix.clear(); } + std::optional getPhysicalPath(const CanonPath & path) override; + std::string readFile(const CanonPath & path) override; bool pathExists(const CanonPath & path) override; From 69c159811ea280836d01f2968ae1c7bf3bda1a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 31 May 2024 11:04:50 +0200 Subject: [PATCH 183/910] add regression test for nix edit --- tests/functional/flakes/edit.sh | 13 +++++++++++++ tests/functional/local.mk | 1 + tests/functional/simple.nix | 1 + 3 files changed, 15 insertions(+) create mode 100755 tests/functional/flakes/edit.sh diff --git a/tests/functional/flakes/edit.sh b/tests/functional/flakes/edit.sh new file mode 100755 index 000000000..0fdf8b95a --- /dev/null +++ b/tests/functional/flakes/edit.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +source ./common.sh + +requireGit + +flake1Dir=$TEST_ROOT/flake1 + +createGitRepo "$flake1Dir" +createSimpleGitFlake "$flake1Dir" + +export EDITOR=cat +nix edit "$flake1Dir#" | grepQuiet simple.builder.sh diff --git a/tests/functional/local.mk b/tests/functional/local.mk index a94c5712c..5be2cddb2 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -2,6 +2,7 @@ nix_tests = \ test-infra.sh \ flakes/flakes.sh \ flakes/develop.sh \ + flakes/edit.sh \ flakes/run.sh \ flakes/mercurial.sh \ flakes/circular.sh \ diff --git a/tests/functional/simple.nix b/tests/functional/simple.nix index 4223c0f23..2035ca294 100644 --- a/tests/functional/simple.nix +++ b/tests/functional/simple.nix @@ -5,4 +5,5 @@ mkDerivation { builder = ./simple.builder.sh; PATH = ""; goodPath = path; + meta.position = "${__curPos.file}:${toString __curPos.line}"; } From 473d2d56fc2ef401cccd17ae736330b50c168c4a Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Wed, 29 May 2024 21:12:34 -0700 Subject: [PATCH 184/910] Remove 100s of CPU time (10%) from build times (1465s -> 1302s) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Result's from Mic92's framework 13th Gen Intel Core i7-1360P: Before: 3595.92s user 183.01s system 1360% cpu 4:37.74 total After: 3486.07s user 168.93s system 1354% cpu 4:29.79 total I saw that boost/lexical_cast was costing about 100s in CPU time on our compiles. We can fix this trivially by doing explicit template instantiation in exactly one place and eliminating all other includes of it, which is a code improvement anyway by hiding the boost. Before: ``` lix/lix2 » ClangBuildAnalyzer --analyze buildtimeold.bin Analyzing build trace from 'buildtimeold.bin'... **** Time summary: Compilation (551 times): Parsing (frontend): 1465.3 s Codegen & opts (backend): 1110.9 s **** Expensive headers: 178153 ms: ../src/libcmd/installable-value.hh (included 52 times, avg 3426 ms), included via: 40x: command.hh 5x: command-installable-value.hh 3x: installable-flake.hh 2x: 2x: installable-attr-path.hh 176217 ms: ../src/libutil/error.hh (included 246 times, avg 716 ms), included via: 36x: command.hh installable-value.hh installables.hh derived-path.hh config.hh experimental-features.hh 12x: globals.hh config.hh experimental-features.hh 11x: file-system.hh file-descriptor.hh 6x: serialise.hh strings.hh 6x: 6x: archive.hh serialise.hh strings.hh ... 173243 ms: ../src/libstore/store-api.hh (included 152 times, avg 1139 ms), included via: 55x: 39x: command.hh installable-value.hh installables.hh 7x: libexpr.hh 4x: local-store.hh 4x: command-installable-value.hh installable-value.hh installables.hh 3x: binary-cache-store.hh ... 170482 ms: ../src/libutil/serialise.hh (included 201 times, avg 848 ms), included via: 37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh 14x: store-api.hh nar-info.hh hash.hh 11x: 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh 7x: libexpr.hh value.hh source-path.hh archive.hh 6x: fetchers.hh hash.hh ... 169397 ms: ../src/libcmd/installables.hh (included 53 times, avg 3196 ms), included via: 40x: command.hh installable-value.hh 5x: command-installable-value.hh installable-value.hh 3x: installable-flake.hh installable-value.hh 2x: 1x: installable-derived-path.hh 1x: installable-value.hh ... 159740 ms: ../src/libutil/strings.hh (included 221 times, avg 722 ms), included via: 37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh serialise.hh 19x: 14x: store-api.hh nar-info.hh hash.hh serialise.hh 11x: serialise.hh 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh 7x: libexpr.hh value.hh source-path.hh archive.hh serialise.hh ... 156796 ms: ../src/libcmd/command.hh (included 51 times, avg 3074 ms), included via: 42x: 7x: command-installable-value.hh 2x: installable-attr-path.hh 150392 ms: ../src/libutil/types.hh (included 251 times, avg 599 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh 11x: file-system.hh 10x: globals.hh 6x: fetchers.hh 6x: serialise.hh strings.hh error.hh 5x: archive.hh ... 133101 ms: /nix/store/644b90j1vms44nr18yw3520pzkrg4dd1-boost-1.81.0-dev/include/boost/lexical_cast.hpp (included 226 times, avg 588 ms), included via : 37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh serialise.hh strings.hh 19x: file-system.hh 11x: store-api.hh nar-info.hh hash.hh serialise.hh strings.hh 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh 7x: libexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh 6x: eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh ... 132887 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/memory (included 262 times, avg 507 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh 16x: gtest.h 11x: file-system.hh types.hh ref.hh 10x: globals.hh types.hh ref.hh 10x: json.hpp 6x: serialise.hh ... done in 0.6s. ``` After: ``` lix/lix2 » maintainers/buildtime_report.sh build Processing all files and saving to '/home/jade/lix/lix2/maintainers/../buildtime.bin'... done in 0.6s. Run 'ClangBuildAnalyzer --analyze /home/jade/lix/lix2/maintainers/../buildtime.bin' to analyze it. Analyzing build trace from '/home/jade/lix/lix2/maintainers/../buildtime.bin'... **** Time summary: Compilation (551 times): Parsing (frontend): 1302.1 s Codegen & opts (backend): 956.3 s **** Expensive headers: 178145 ms: ../src/libutil/error.hh (included 246 times, avg 724 ms), included via: 36x: command.hh installable-value.hh installables.hh derived-path.hh config.hh experimental-features.hh 12x: globals.hh config.hh experimental-features.hh 11x: file-system.hh file-descriptor.hh 6x: 6x: serialise.hh strings.hh 6x: fetchers.hh hash.hh serialise.hh strings.hh ... 154043 ms: ../src/libcmd/installable-value.hh (included 52 times, avg 2962 ms), included via: 40x: command.hh 5x: command-installable-value.hh 3x: installable-flake.hh 2x: 2x: installable-attr-path.hh 153593 ms: ../src/libstore/store-api.hh (included 152 times, avg 1010 ms), included via: 55x: 39x: command.hh installable-value.hh installables.hh 7x: libexpr.hh 4x: local-store.hh 4x: command-installable-value.hh installable-value.hh installables.hh 3x: binary-cache-store.hh ... 149948 ms: ../src/libutil/types.hh (included 251 times, avg 597 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh 11x: file-system.hh 10x: globals.hh 6x: fetchers.hh 6x: serialise.hh strings.hh error.hh 5x: archive.hh ... 144560 ms: ../src/libcmd/installables.hh (included 53 times, avg 2727 ms), included via: 40x: command.hh installable-value.hh 5x: command-installable-value.hh installable-value.hh 3x: installable-flake.hh installable-value.hh 2x: 1x: installable-value.hh 1x: installable-derived-path.hh ... 136585 ms: ../src/libcmd/command.hh (included 51 times, avg 2678 ms), included via: 42x: 7x: command-installable-value.hh 2x: installable-attr-path.hh 133394 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/memory (included 262 times, avg 509 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh 16x: gtest.h 11x: file-system.hh types.hh ref.hh 10x: globals.hh types.hh ref.hh 10x: json.hpp 6x: serialise.hh ... 89315 ms: ../src/libstore/derived-path.hh (included 178 times, avg 501 ms), included via: 37x: command.hh installable-value.hh installables.hh 25x: store-api.hh realisation.hh 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh context.hh 6x: eval.hh attr-set.hh nixexpr.hh value.hh context.hh 6x: libexpr.hh value.hh context.hh 6x: shared.hh ... 87347 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/ostream (included 273 times, avg 319 ms), included via: 35x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh memory unique_ptr.h 12x: regex sstream istream 10x: file-system.hh types.hh ref.hh memory unique_ptr.h 10x: gtest.h memory unique_ptr.h 10x: globals.hh types.hh ref.hh memory unique_ptr.h 6x: fetchers.hh types.hh ref.hh memory unique_ptr.h ... 85249 ms: ../src/libutil/config.hh (included 213 times, avg 400 ms), included via: 37x: command.hh installable-value.hh installables.hh derived-path.hh 20x: globals.hh 20x: logging.hh 16x: store-api.hh logging.hh 6x: 6x: eval.hh attr-set.hh nixexpr.hh value.hh context.hh derived-path.hh ... done in 0.5s. ``` Adapated from https://git.lix.systems/lix-project/lix/commit/18aa3e1d570b4ecbb9962376e5fba5757dad8da9 --- src/libexpr/eval.cc | 2 +- src/libexpr/lexer.l | 9 +++---- src/libexpr/nixexpr.cc | 1 + src/libexpr/primops.cc | 1 + src/libexpr/print.cc | 1 + src/libmain/progress-bar.cc | 1 + src/libstore/binary-cache-store.cc | 1 + src/libstore/daemon.cc | 2 ++ src/libstore/machines.cc | 2 +- src/libutil/current-process.cc | 1 + src/libutil/file-system.hh | 2 -- src/libutil/logging.cc | 1 + src/libutil/processes.hh | 2 -- src/libutil/util.cc | 39 ++++++++++++++++++++++++++++++ src/libutil/util.hh | 21 ++-------------- src/nix-env/user-env.cc | 3 ++- src/nix/develop.cc | 1 + 17 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d7e3a2cdb..c1dadeee0 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -28,13 +28,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index ee2b6b807..8c0f9d1f2 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -20,8 +20,6 @@ #pragma clang diagnostic ignored "-Wunneeded-internal-declaration" #endif -#include - #include "nixexpr.hh" #include "parser-tab.hh" @@ -129,9 +127,10 @@ or { return OR_KW; } {ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; } {INT} { errno = 0; - try { - yylval->n = boost::lexical_cast(yytext); - } catch (const boost::bad_lexical_cast &) { + std::optional numMay = string2Int(yytext); + if (numMay.has_value()) { + yylval->n = *numMay; + } else { throw ParseError(ErrorInfo{ .msg = HintFmt("invalid integer '%1%'", yytext), .pos = state->positions[CUR_POS], diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index c1e2b0448..44198a252 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -6,6 +6,7 @@ #include "print.hh" #include +#include namespace nix { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 1b1a9be3d..7371bd488 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -26,6 +26,7 @@ #include #include +#include #include #ifndef _WIN32 diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index d53fadac7..920490cfa 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -1,5 +1,6 @@ #include #include +#include #include "print.hh" #include "ansicolor.hh" diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index ce45eae2b..bb4c52ef7 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 5153ca64f..56e47697c 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index eb6a4e690..4e231ca99 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -19,6 +19,8 @@ # include "monitor-fd.hh" #endif +#include + namespace nix::daemon { Sink & operator << (Sink & sink, const Logger::Fields & fields) diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index 20f24e883..256cf9188 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -148,7 +148,7 @@ static Machine parseBuilderLine(const std::set & defaultSystems, co }; auto parseFloatField = [&](size_t fieldIndex) { - const auto result = string2Int(tokens[fieldIndex]); + const auto result = string2Float(tokens[fieldIndex]); if (!result) { throw FormatError("bad machine specification: failed to convert column #%lu in a row: '%s' to 'float'", fieldIndex, line); } diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index 9efb68d47..6ca48220d 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -7,6 +7,7 @@ #include "file-system.hh" #include "processes.hh" #include "signals.hh" +#include #ifdef __APPLE__ # include diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 933e88441..c6b6ecedb 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -20,8 +20,6 @@ #endif #include -#include - #include #include #include diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 2511c8849..5fa01f0d9 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -8,6 +8,7 @@ #include "position.hh" #include +#include #include #include diff --git a/src/libutil/processes.hh b/src/libutil/processes.hh index 9d5367b02..168fcaa55 100644 --- a/src/libutil/processes.hh +++ b/src/libutil/processes.hh @@ -12,8 +12,6 @@ #include #include -#include - #include #include #include diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 103ce4232..99fbcaf9d 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -7,6 +7,8 @@ #include #include +#include +#include #ifdef NDEBUG #error "Nix may not be built with assertions disabled (i.e. with -DNDEBUG)." @@ -111,6 +113,43 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites) return s; } +template +std::optional string2Int(const std::string_view s) +{ + if (s.substr(0, 1) == "-" && !std::numeric_limits::is_signed) + return std::nullopt; + try { + return boost::lexical_cast(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } +} + +// Explicitly instantiated in one place for faster compilation +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); + +template +std::optional string2Float(const std::string_view s) +{ + try { + return boost::lexical_cast(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } +} + +template std::optional string2Float(const std::string_view s); +template std::optional string2Float(const std::string_view s); + bool hasPrefix(std::string_view s, std::string_view prefix) { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 8b049875a..0919f43c3 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -5,7 +5,6 @@ #include "error.hh" #include "logging.hh" -#include #include #include @@ -102,16 +101,7 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites); * Parse a string into an integer. */ template -std::optional string2Int(const std::string_view s) -{ - if (s.substr(0, 1) == "-" && !std::numeric_limits::is_signed) - return std::nullopt; - try { - return boost::lexical_cast(s.data(), s.size()); - } catch (const boost::bad_lexical_cast &) { - return std::nullopt; - } -} +std::optional string2Int(const std::string_view s); /** * Like string2Int(), but support an optional suffix 'K', 'M', 'G' or @@ -141,14 +131,7 @@ N string2IntWithUnitPrefix(std::string_view s) * Parse a string into a float. */ template -std::optional string2Float(const std::string_view s) -{ - try { - return boost::lexical_cast(s.data(), s.size()); - } catch (const boost::bad_lexical_cast &) { - return std::nullopt; - } -} +std::optional string2Float(const std::string_view s); /** diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index f7b091f8f..5246b03e4 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -9,8 +9,9 @@ #include "eval-inline.hh" #include "profiles.hh" #include "print-ambiguous.hh" -#include +#include +#include namespace nix { diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 0363ca829..27287a1a8 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -14,6 +14,7 @@ #include #include +#include #include #include From 5fde77b16610782506bc26d6706c5663b814db02 Mon Sep 17 00:00:00 2001 From: fricklerhandwerk Date: Fri, 31 May 2024 19:12:35 +0200 Subject: [PATCH 185/910] move Hydra jobs into a separate file Co-Authored-By: Tom Bereknyei --- build/hydra.nix | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 165 ++++------------------------------------------- 2 files changed, 182 insertions(+), 151 deletions(-) create mode 100644 build/hydra.nix diff --git a/build/hydra.nix b/build/hydra.nix new file mode 100644 index 000000000..98054873d --- /dev/null +++ b/build/hydra.nix @@ -0,0 +1,168 @@ +{ inputs +, binaryTarball +, forAllCrossSystems +, forAllSystems +, installScriptFor +, lib +, linux64BitSystems +, nixpkgsFor +, self +, testNixVersions +}: +let + inherit (inputs) nixpkgs nixpkgs-regression; + inherit (lib) fileset; +in +{ + hydraJobs = { + # Binary package for various platforms. + build = forAllSystems (system: self.packages.${system}.nix); + + shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); + + buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); + + buildCross = forAllCrossSystems (crossSystem: + lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); + + buildNoGc = forAllSystems (system: + self.packages.${system}.nix.override { enableGC = false; } + ); + + buildNoTests = forAllSystems (system: + self.packages.${system}.nix.override { + doCheck = false; + doInstallCheck = false; + installUnitTests = false; + } + ); + + # Toggles some settings for better coverage. Windows needs these + # library combinations, and Debian build Nix with GNU readline too. + buildReadlineNoMarkdown = forAllSystems (system: + self.packages.${system}.nix.override { + enableMarkdown = false; + readlineFlavor = "readline"; + } + ); + + # Perl bindings for various platforms. + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); + + # Binary tarball for various platforms, containing a Nix store + # with the closure of 'nix' package, and the second half of + # the installation script. + binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); + + binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: + forAllCrossSystems (crossSystem: + binaryTarball + self.packages.${system}."nix-${crossSystem}" + nixpkgsFor.${system}.cross.${crossSystem})); + + # The first half of the installation script. This is uploaded + # to https://nixos.org/nix/install. It downloads the binary + # tarball for the user's system and calls the second half of the + # installation script. + installerScript = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."i686-linux" + self.hydraJobs.binaryTarball."aarch64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + self.hydraJobs.binaryTarball."aarch64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; + installerScriptForGHA = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; + + # docker image with Nix inside + dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); + + # Line coverage analysis. + coverage = nixpkgsFor.x86_64-linux.native.nix.override { + pname = "nix-coverage"; + withCoverageChecks = true; + }; + + # API docs for Nix's unstable internal C++ interfaces. + internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + inherit fileset; + doBuild = false; + enableInternalAPIDocs = true; + }; + + # API docs for Nix's C bindings. + external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + inherit fileset; + doBuild = false; + enableExternalAPIDocs = true; + }; + + # System tests. + tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { + + # Make sure that nix-env still produces the exact same result + # on a particular version of Nixpkgs. + evalNixpkgs = + let + inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; + in + runCommand "eval-nixos" { buildInputs = [ nix ]; } + '' + type -p nix-env + # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. + ( + set -x + time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages + [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] + ) + mkdir $out + ''; + + nixpkgsLibTests = + forAllSystems (system: + import (nixpkgs + "/lib/tests/release.nix") + { + pkgs = nixpkgsFor.${system}.native; + nixVersions = [ self.packages.${system}.nix ]; + } + ); + }; + + metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { + pkgs = nixpkgsFor.x86_64-linux.native; + nixpkgs = nixpkgs-regression; + }; + + installTests = forAllSystems (system: + let pkgs = nixpkgsFor.${system}.native; in + pkgs.runCommand "install-tests" + { + againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; + againstCurrentUnstable = + # FIXME: temporarily disable this on macOS because of #3605. + if system == "x86_64-linux" + then testNixVersions pkgs pkgs.nix pkgs.nixUnstable + else null; + # Disabled because the latest stable version doesn't handle + # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work + # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; + } "touch $out"); + + installerTests = import ../tests/installer { + binaryTarballs = self.hydraJobs.binaryTarball; + inherit nixpkgsFor; + }; + }; +} diff --git a/flake.nix b/flake.nix index 6356eedc0..866dab00e 100644 --- a/flake.nix +++ b/flake.nix @@ -232,157 +232,20 @@ # 'nix.perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - hydraJobs = { - - # Binary package for various platforms. - build = forAllSystems (system: self.packages.${system}.nix); - - shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); - - buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); - - buildCross = forAllCrossSystems (crossSystem: - lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}")); - - buildNoGc = forAllSystems (system: - self.packages.${system}.nix.override { enableGC = false; } - ); - - buildNoTests = forAllSystems (system: - self.packages.${system}.nix.override { - doCheck = false; - doInstallCheck = false; - installUnitTests = false; - } - ); - - # Toggles some settings for better coverage. Windows needs these - # library combinations, and Debian build Nix with GNU readline too. - buildReadlineNoMarkdown = forAllSystems (system: - self.packages.${system}.nix.override { - enableMarkdown = false; - readlineFlavor = "readline"; - } - ); - - # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); - - # Binary tarball for various platforms, containing a Nix store - # with the closure of 'nix' package, and the second half of - # the installation script. - binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); - - binaryTarballCross = lib.genAttrs ["x86_64-linux"] (system: - forAllCrossSystems (crossSystem: - binaryTarball - self.packages.${system}."nix-${crossSystem}" - nixpkgsFor.${system}.cross.${crossSystem})); - - # The first half of the installation script. This is uploaded - # to https://nixos.org/nix/install. It downloads the binary - # tarball for the user's system and calls the second half of the - # installation script. - installerScript = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."i686-linux" - self.hydraJobs.binaryTarball."aarch64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - self.hydraJobs.binaryTarball."aarch64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; - installerScriptForGHA = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; - - # docker image with Nix inside - dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); - - # Line coverage analysis. - coverage = nixpkgsFor.x86_64-linux.native.nix.override { - pname = "nix-coverage"; - withCoverageChecks = true; - }; - - # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { - inherit fileset; - doBuild = false; - enableInternalAPIDocs = true; - }; - - # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { - inherit fileset; - doBuild = false; - enableExternalAPIDocs = true; - }; - - # System tests. - tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { - - # Make sure that nix-env still produces the exact same result - # on a particular version of Nixpkgs. - evalNixpkgs = - let - inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; - in - runCommand "eval-nixos" { buildInputs = [ nix ]; } - '' - type -p nix-env - # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. - ( - set -x - time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages - [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] - ) - mkdir $out - ''; - - nixpkgsLibTests = - forAllSystems (system: - import (nixpkgs + "/lib/tests/release.nix") - { pkgs = nixpkgsFor.${system}.native; - nixVersions = [ self.packages.${system}.nix ]; - } - ); - }; - - metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { - pkgs = nixpkgsFor.x86_64-linux.native; - nixpkgs = nixpkgs-regression; - }; - - installTests = forAllSystems (system: - let pkgs = nixpkgsFor.${system}.native; in - pkgs.runCommand "install-tests" { - againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; - againstCurrentUnstable = - # FIXME: temporarily disable this on macOS because of #3605. - if system == "x86_64-linux" - then testNixVersions pkgs pkgs.nix pkgs.nixUnstable - else null; - # Disabled because the latest stable version doesn't handle - # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work - # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; - } "touch $out"); - - installerTests = import ./tests/installer { - binaryTarballs = self.hydraJobs.binaryTarball; - inherit nixpkgsFor; - }; - - }; + inherit (import ./build/hydra.nix { + inherit + inputs + binaryTarball + forAllCrossSystems + forAllSystems + installScriptFor + lib + linux64BitSystems + nixpkgsFor + self + testNixVersions + ; + }) hydraJobs; checks = forAllSystems (system: { binaryTarball = self.hydraJobs.binaryTarball.${system}; From 0067f49e8765710f8d9ccd753b516c0c8f13e87f Mon Sep 17 00:00:00 2001 From: fricklerhandwerk Date: Fri, 31 May 2024 20:37:58 +0200 Subject: [PATCH 186/910] move more declarations --- build/hydra.nix | 28 ++++++++++++++++++++++++---- flake.nix | 24 ------------------------ 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 98054873d..bc2bb3b52 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -2,16 +2,36 @@ , binaryTarball , forAllCrossSystems , forAllSystems -, installScriptFor , lib , linux64BitSystems , nixpkgsFor , self -, testNixVersions }: let inherit (inputs) nixpkgs nixpkgs-regression; inherit (lib) fileset; + + installScriptFor = tarballs: + nixpkgsFor.x86_64-linux.native.callPackage ./scripts/installer.nix { + inherit tarballs; + }; + + testNixVersions = pkgs: client: daemon: + pkgs.callPackage ../package.nix { + pname = + "nix-tests" + + lib.optionalString + (lib.versionAtLeast daemon.version "2.4pre20211005" && + lib.versionAtLeast client.version "2.4pre20211005") + "-${client.version}-against-${daemon.version}"; + + inherit fileset; + + test-client = client; + test-daemon = daemon; + + doBuild = false; + }; in { hydraJobs = { @@ -96,14 +116,14 @@ in }; # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { inherit fileset; doBuild = false; enableInternalAPIDocs = true; }; # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { inherit fileset; doBuild = false; enableExternalAPIDocs = true; diff --git a/flake.nix b/flake.nix index 866dab00e..a57031cfc 100644 --- a/flake.nix +++ b/flake.nix @@ -104,28 +104,6 @@ cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv"); }); - installScriptFor = tarballs: - nixpkgsFor.x86_64-linux.native.callPackage ./scripts/installer.nix { - inherit tarballs; - }; - - testNixVersions = pkgs: client: daemon: - pkgs.callPackage ./package.nix { - pname = - "nix-tests" - + lib.optionalString - (lib.versionAtLeast daemon.version "2.4pre20211005" && - lib.versionAtLeast client.version "2.4pre20211005") - "-${client.version}-against-${daemon.version}"; - - inherit fileset; - - test-client = client; - test-daemon = daemon; - - doBuild = false; - }; - binaryTarball = nix: pkgs: pkgs.callPackage ./scripts/binary-tarball.nix { inherit nix; }; @@ -238,12 +216,10 @@ binaryTarball forAllCrossSystems forAllSystems - installScriptFor lib linux64BitSystems nixpkgsFor self - testNixVersions ; }) hydraJobs; From 1c46b9b2c5a14fa1c13750e7a504139e52676f8e Mon Sep 17 00:00:00 2001 From: fricklerhandwerk Date: Fri, 31 May 2024 23:27:14 +0200 Subject: [PATCH 187/910] fix path --- build/hydra.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/hydra.nix b/build/hydra.nix index bc2bb3b52..7a7cecd2c 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -12,7 +12,7 @@ let inherit (lib) fileset; installScriptFor = tarballs: - nixpkgsFor.x86_64-linux.native.callPackage ./scripts/installer.nix { + nixpkgsFor.x86_64-linux.native.callPackage ../scripts/installer.nix { inherit tarballs; }; From 300b129fc778ecf020ae624474aecd51c72e8fc3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 18:27:20 -0400 Subject: [PATCH 188/910] `hydra.nix` Can just return the obj for that name --- build/hydra.nix | 258 ++++++++++++++++++++++++------------------------ flake.nix | 4 +- 2 files changed, 130 insertions(+), 132 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 7a7cecd2c..595aad324 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -34,155 +34,153 @@ let }; in { - hydraJobs = { - # Binary package for various platforms. - build = forAllSystems (system: self.packages.${system}.nix); + # Binary package for various platforms. + build = forAllSystems (system: self.packages.${system}.nix); - shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); + shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); - buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); + buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); - buildCross = forAllCrossSystems (crossSystem: - lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); + buildCross = forAllCrossSystems (crossSystem: + lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); - buildNoGc = forAllSystems (system: - self.packages.${system}.nix.override { enableGC = false; } - ); + buildNoGc = forAllSystems (system: + self.packages.${system}.nix.override { enableGC = false; } + ); - buildNoTests = forAllSystems (system: - self.packages.${system}.nix.override { - doCheck = false; - doInstallCheck = false; - installUnitTests = false; - } - ); + buildNoTests = forAllSystems (system: + self.packages.${system}.nix.override { + doCheck = false; + doInstallCheck = false; + installUnitTests = false; + } + ); - # Toggles some settings for better coverage. Windows needs these - # library combinations, and Debian build Nix with GNU readline too. - buildReadlineNoMarkdown = forAllSystems (system: - self.packages.${system}.nix.override { - enableMarkdown = false; - readlineFlavor = "readline"; - } - ); + # Toggles some settings for better coverage. Windows needs these + # library combinations, and Debian build Nix with GNU readline too. + buildReadlineNoMarkdown = forAllSystems (system: + self.packages.${system}.nix.override { + enableMarkdown = false; + readlineFlavor = "readline"; + } + ); - # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); + # Perl bindings for various platforms. + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); - # Binary tarball for various platforms, containing a Nix store - # with the closure of 'nix' package, and the second half of - # the installation script. - binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); + # Binary tarball for various platforms, containing a Nix store + # with the closure of 'nix' package, and the second half of + # the installation script. + binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); - binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: - forAllCrossSystems (crossSystem: - binaryTarball - self.packages.${system}."nix-${crossSystem}" - nixpkgsFor.${system}.cross.${crossSystem})); + binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: + forAllCrossSystems (crossSystem: + binaryTarball + self.packages.${system}."nix-${crossSystem}" + nixpkgsFor.${system}.cross.${crossSystem})); - # The first half of the installation script. This is uploaded - # to https://nixos.org/nix/install. It downloads the binary - # tarball for the user's system and calls the second half of the - # installation script. - installerScript = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."i686-linux" - self.hydraJobs.binaryTarball."aarch64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - self.hydraJobs.binaryTarball."aarch64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; - installerScriptForGHA = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; + # The first half of the installation script. This is uploaded + # to https://nixos.org/nix/install. It downloads the binary + # tarball for the user's system and calls the second half of the + # installation script. + installerScript = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."i686-linux" + self.hydraJobs.binaryTarball."aarch64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + self.hydraJobs.binaryTarball."aarch64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; + installerScriptForGHA = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; - # docker image with Nix inside - dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); + # docker image with Nix inside + dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); - # Line coverage analysis. - coverage = nixpkgsFor.x86_64-linux.native.nix.override { - pname = "nix-coverage"; - withCoverageChecks = true; - }; + # Line coverage analysis. + coverage = nixpkgsFor.x86_64-linux.native.nix.override { + pname = "nix-coverage"; + withCoverageChecks = true; + }; - # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableInternalAPIDocs = true; - }; + # API docs for Nix's unstable internal C++ interfaces. + internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { + inherit fileset; + doBuild = false; + enableInternalAPIDocs = true; + }; - # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableExternalAPIDocs = true; - }; + # API docs for Nix's C bindings. + external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { + inherit fileset; + doBuild = false; + enableExternalAPIDocs = true; + }; - # System tests. - tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { + # System tests. + tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { - # Make sure that nix-env still produces the exact same result - # on a particular version of Nixpkgs. - evalNixpkgs = - let - inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; - in - runCommand "eval-nixos" { buildInputs = [ nix ]; } - '' - type -p nix-env - # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. - ( - set -x - time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages - [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] - ) - mkdir $out - ''; + # Make sure that nix-env still produces the exact same result + # on a particular version of Nixpkgs. + evalNixpkgs = + let + inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; + in + runCommand "eval-nixos" { buildInputs = [ nix ]; } + '' + type -p nix-env + # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. + ( + set -x + time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages + [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] + ) + mkdir $out + ''; - nixpkgsLibTests = - forAllSystems (system: - import (nixpkgs + "/lib/tests/release.nix") - { - pkgs = nixpkgsFor.${system}.native; - nixVersions = [ self.packages.${system}.nix ]; - } - ); - }; + nixpkgsLibTests = + forAllSystems (system: + import (nixpkgs + "/lib/tests/release.nix") + { + pkgs = nixpkgsFor.${system}.native; + nixVersions = [ self.packages.${system}.nix ]; + } + ); + }; - metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { - pkgs = nixpkgsFor.x86_64-linux.native; - nixpkgs = nixpkgs-regression; - }; + metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { + pkgs = nixpkgsFor.x86_64-linux.native; + nixpkgs = nixpkgs-regression; + }; - installTests = forAllSystems (system: - let pkgs = nixpkgsFor.${system}.native; in - pkgs.runCommand "install-tests" - { - againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; - againstCurrentUnstable = - # FIXME: temporarily disable this on macOS because of #3605. - if system == "x86_64-linux" - then testNixVersions pkgs pkgs.nix pkgs.nixUnstable - else null; - # Disabled because the latest stable version doesn't handle - # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work - # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; - } "touch $out"); + installTests = forAllSystems (system: + let pkgs = nixpkgsFor.${system}.native; in + pkgs.runCommand "install-tests" + { + againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; + againstCurrentUnstable = + # FIXME: temporarily disable this on macOS because of #3605. + if system == "x86_64-linux" + then testNixVersions pkgs pkgs.nix pkgs.nixUnstable + else null; + # Disabled because the latest stable version doesn't handle + # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work + # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; + } "touch $out"); - installerTests = import ../tests/installer { - binaryTarballs = self.hydraJobs.binaryTarball; - inherit nixpkgsFor; - }; + installerTests = import ../tests/installer { + binaryTarballs = self.hydraJobs.binaryTarball; + inherit nixpkgsFor; }; } diff --git a/flake.nix b/flake.nix index a57031cfc..6fb159d6e 100644 --- a/flake.nix +++ b/flake.nix @@ -210,7 +210,7 @@ # 'nix.perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - inherit (import ./build/hydra.nix { + hydraJobs = import ./build/hydra.nix { inherit inputs binaryTarball @@ -221,7 +221,7 @@ nixpkgsFor self ; - }) hydraJobs; + }; checks = forAllSystems (system: { binaryTarball = self.hydraJobs.binaryTarball.${system}; From e0b159549b7d88f3b134633336190e68e90eb5dd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 1 Jun 2024 16:39:59 -0400 Subject: [PATCH 189/910] Misc Windows fixes 1. Fix build by making the legacy SSH Storey's secret `logFD` setting not a setting on Windows. (It doesn't make sense to specify `void *` handles by integer cross-proccess, I don't think.) 2. Move some files that don't need to be Unix-only anymore back to their original locations. --- maintainers/flake-module.nix | 12 ++++----- src/libfetchers/{unix => }/git.cc | 27 ++++++++++++++++--- src/libfetchers/local.mk | 6 ----- src/libfetchers/{unix => }/mercurial.cc | 0 src/libstore/{unix => }/builtins/fetchurl.cc | 0 .../{unix => }/builtins/unpack-channel.cc | 0 src/libstore/legacy-ssh-store.hh | 6 ++++- .../{unix => }/local-overlay-store.cc | 0 .../{unix => }/local-overlay-store.hh | 0 .../{unix => }/local-overlay-store.md | 0 src/libstore/local.mk | 2 +- 11 files changed, 36 insertions(+), 17 deletions(-) rename src/libfetchers/{unix => }/git.cc (98%) rename src/libfetchers/{unix => }/mercurial.cc (100%) rename src/libstore/{unix => }/builtins/fetchurl.cc (100%) rename src/libstore/{unix => }/builtins/unpack-channel.cc (100%) rename src/libstore/{unix => }/local-overlay-store.cc (100%) rename src/libstore/{unix => }/local-overlay-store.hh (100%) rename src/libstore/{unix => }/local-overlay-store.md (100%) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 419994bdb..a42828208 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -125,8 +125,8 @@ ''^src/libfetchers/registry\.hh$'' ''^src/libfetchers/tarball\.cc$'' ''^src/libfetchers/tarball\.hh$'' - ''^src/libfetchers/unix/git\.cc$'' - ''^src/libfetchers/unix/mercurial\.cc$'' + ''^src/libfetchers/git\.cc$'' + ''^src/libfetchers/mercurial\.cc$'' ''^src/libmain/common-args\.cc$'' ''^src/libmain/common-args\.hh$'' ''^src/libmain/loggers\.cc$'' @@ -237,11 +237,11 @@ ''^src/libstore/build/substitution-goal\.hh$'' ''^src/libstore/build/worker\.cc$'' ''^src/libstore/build/worker\.hh$'' - ''^src/libstore/unix/builtins/fetchurl\.cc$'' - ''^src/libstore/unix/builtins/unpack-channel\.cc$'' + ''^src/libstore/builtins/fetchurl\.cc$'' + ''^src/libstore/builtins/unpack-channel\.cc$'' ''^src/libstore/gc\.cc$'' - ''^src/libstore/unix/local-overlay-store\.cc$'' - ''^src/libstore/unix/local-overlay-store\.hh$'' + ''^src/libstore/local-overlay-store\.cc$'' + ''^src/libstore/local-overlay-store\.hh$'' ''^src/libstore/local-store\.cc$'' ''^src/libstore/local-store\.hh$'' ''^src/libstore/unix/user-lock\.cc$'' diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/git.cc similarity index 98% rename from src/libfetchers/unix/git.cc rename to src/libfetchers/git.cc index fa7ef3621..ce80932f6 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/git.cc @@ -19,7 +19,10 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +#endif using namespace std::string_literals; @@ -40,6 +43,7 @@ bool isCacheFileWithinTtl(time_t now, const struct stat & st) bool touchCacheFile(const Path & path, time_t touch_time) { +#ifndef _WIN32 // TODO implement struct timeval times[2]; times[0].tv_sec = touch_time; times[0].tv_usec = 0; @@ -47,6 +51,9 @@ bool touchCacheFile(const Path & path, time_t touch_time) times[1].tv_usec = 0; return lutimes(path.c_str(), times) == 0; +#else + return false; +#endif } Path getCachePath(std::string_view key, bool shallow) @@ -98,7 +105,15 @@ bool storeCachedHead(const std::string & actualUrl, const std::string & headRef) try { runProgram("git", true, { "-C", cacheDir, "--git-dir", ".", "symbolic-ref", "--", "HEAD", headRef }); } catch (ExecError &e) { - if (!WIFEXITED(e.status)) throw; + if ( +#ifndef WIN32 // TODO abstract over exit status handling on Windows + !WIFEXITED(e.status) +#else + e.status != 0 +#endif + ) + throw; + return false; } /* No need to touch refs/HEAD, because `git symbolic-ref` updates the mtime. */ @@ -329,7 +344,13 @@ struct GitInputScheme : InputScheme .program = "git", .args = {"-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "check-ignore", "--quiet", std::string(path.rel())}, }); - auto exitCode = WEXITSTATUS(result.first); + auto exitCode = +#ifndef WIN32 // TODO abstract over exit status handling on Windows + WEXITSTATUS(result.first) +#else + result.first +#endif + ; if (exitCode != 0) { // The path is not `.gitignore`d, we can add the file. diff --git a/src/libfetchers/local.mk b/src/libfetchers/local.mk index 0fef1466b..e229a0993 100644 --- a/src/libfetchers/local.mk +++ b/src/libfetchers/local.mk @@ -5,16 +5,10 @@ libfetchers_NAME = libnixfetchers libfetchers_DIR := $(d) libfetchers_SOURCES := $(wildcard $(d)/*.cc) -ifdef HOST_UNIX - libfetchers_SOURCES += $(wildcard $(d)/unix/*.cc) -endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libfetchers := -I $(d) -ifdef HOST_UNIX - INCLUDE_libfetchers += -I $(d)/unix -endif libfetchers_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/mercurial.cc similarity index 100% rename from src/libfetchers/unix/mercurial.cc rename to src/libfetchers/mercurial.cc diff --git a/src/libstore/unix/builtins/fetchurl.cc b/src/libstore/builtins/fetchurl.cc similarity index 100% rename from src/libstore/unix/builtins/fetchurl.cc rename to src/libstore/builtins/fetchurl.cc diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/builtins/unpack-channel.cc similarity index 100% rename from src/libstore/unix/builtins/unpack-channel.cc rename to src/libstore/builtins/unpack-channel.cc diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index 6c3c95080..b683ed580 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -26,10 +26,14 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Store { +#ifndef _WIN32 // Hack for getting remote build log output. // Intentionally not in `LegacySSHStoreConfig` so that it doesn't appear in // the documentation - const Setting logFD{this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"}; + const Setting logFD{this, INVALID_DESCRIPTOR, "log-fd", "file descriptor to which SSH's stderr is connected"}; +#else + Descriptor logFD = INVALID_DESCRIPTOR; +#endif struct Connection; diff --git a/src/libstore/unix/local-overlay-store.cc b/src/libstore/local-overlay-store.cc similarity index 100% rename from src/libstore/unix/local-overlay-store.cc rename to src/libstore/local-overlay-store.cc diff --git a/src/libstore/unix/local-overlay-store.hh b/src/libstore/local-overlay-store.hh similarity index 100% rename from src/libstore/unix/local-overlay-store.hh rename to src/libstore/local-overlay-store.hh diff --git a/src/libstore/unix/local-overlay-store.md b/src/libstore/local-overlay-store.md similarity index 100% rename from src/libstore/unix/local-overlay-store.md rename to src/libstore/local-overlay-store.md diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 60174d009..5dc8f3370 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -6,7 +6,7 @@ libstore_DIR := $(d) libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) ifdef HOST_UNIX - libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc) + libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/build/*.cc) endif ifdef HOST_LINUX libstore_SOURCES += $(wildcard $(d)/linux/*.cc) From 68090d7ff19054e76bf90cb309c4fe57281443dc Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Sun, 2 Jun 2024 14:26:18 +0300 Subject: [PATCH 190/910] Fix empty outputsToInstall for InstallableAttrPath Fixes assertion failure if outputsToInstall is empty by defaulting to the "out" output. That is, behavior between the following commands should be consistent: $ nix build --no-link --json .#nothing-to-install-no-out error: derivation '/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-nothing-to-install-no-out.drv' does not have wanted outputs 'out' $ nix build --no-link --file default.nix --json nothing-to-install-no-out error: derivation '/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-nothing-to-install-no-out.drv' does not have wanted outputs 'out' Real-world example of this issue: $ nix build --json .#.legacyPackages.aarch64-linux.texlive.pkgs.iwona error: derivation '/nix/store/dj0h6b0pnlnan5nidnhqa0bmzq4rv6sx-iwona-0.995b.drv' does not have wanted outputs 'out' $ git rev-parse HEAD eee33247cf6941daea8398c976bd2dda7962b125 $ nix build --json --file . texlive.pkgs.iwona nix: src/libstore/outputs-spec.hh:46: nix::OutputsSpec::Names::Names(std::set >&&): Assertion `!empty()' failed. Aborted (core dumped) --- src/libcmd/installable-attr-path.cc | 2 ++ tests/functional/build.sh | 8 ++++++++ tests/functional/multiple-outputs.nix | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/src/libcmd/installable-attr-path.cc b/src/libcmd/installable-attr-path.cc index 3ec1c1614..8917e7a01 100644 --- a/src/libcmd/installable-attr-path.cc +++ b/src/libcmd/installable-attr-path.cc @@ -75,6 +75,8 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths() std::set outputsToInstall; for (auto & output : packageInfo.queryOutputs(false, true)) outputsToInstall.insert(output.first); + if (outputsToInstall.empty()) + outputsToInstall.insert("out"); return OutputsSpec::Names { std::move(outputsToInstall) }; }, [&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 9fb4357be..a14e6d672 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -45,6 +45,14 @@ nix build -f multiple-outputs.nix --json e --no-link | jq --exit-status ' (.outputs | keys == ["a_a", "b"])) ' +# Tests that we can handle empty 'outputsToInstall' (assuming that default +# output "out" exists). +nix build -f multiple-outputs.nix --json nothing-to-install --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*nothing-to-install.drv")) and + (.outputs | keys == ["out"])) +' + # But not when it's overriden. nix build -f multiple-outputs.nix --json e^a_a --no-link nix build -f multiple-outputs.nix --json e^a_a --no-link | jq --exit-status ' diff --git a/tests/functional/multiple-outputs.nix b/tests/functional/multiple-outputs.nix index 413d392e4..6ba7c523d 100644 --- a/tests/functional/multiple-outputs.nix +++ b/tests/functional/multiple-outputs.nix @@ -96,6 +96,12 @@ rec { buildCommand = "mkdir $a_a $b $c"; }; + nothing-to-install = mkDerivation { + name = "nothing-to-install"; + meta.outputsToInstall = [ ]; + buildCommand = "mkdir $out"; + }; + independent = mkDerivation { name = "multiple-outputs-independent"; outputs = [ "first" "second" ]; From 6a507f5d3bc47244d99544f14a81b78fc84eb2be Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:41:51 -0700 Subject: [PATCH 191/910] housekeeping: shellcheck test/functional/add.sh --- maintainers/flake-module.nix | 1 - tests/functional/add.sh | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index a42828208..3006d5e30 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -507,7 +507,6 @@ ''^scripts/install-nix-from-closure\.sh$'' ''^scripts/install-systemd-multi-user\.sh$'' ''^src/nix/get-env\.sh$'' - ''^tests/functional/add\.sh$'' ''^tests/functional/bash-profile\.sh$'' ''^tests/functional/binary-cache-build-remote\.sh$'' ''^tests/functional/binary-cache\.sh$'' diff --git a/tests/functional/add.sh b/tests/functional/add.sh index 9b448e33b..a6cf88e1a 100755 --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -3,10 +3,10 @@ source common.sh path1=$(nix-store --add ./dummy) -echo $path1 +echo "$path1" path2=$(nix-store --add-fixed sha256 --recursive ./dummy) -echo $path2 +echo "$path2" if test "$path1" != "$path2"; then echo "nix-store --add and --add-fixed mismatch" @@ -14,18 +14,18 @@ if test "$path1" != "$path2"; then fi path3=$(nix-store --add-fixed sha256 ./dummy) -echo $path3 +echo "$path3" test "$path1" != "$path3" || exit 1 path4=$(nix-store --add-fixed sha1 --recursive ./dummy) -echo $path4 +echo "$path4" test "$path1" != "$path4" || exit 1 -hash1=$(nix-store -q --hash $path1) -echo $hash1 +hash1=$(nix-store -q --hash "$path1") +echo "$hash1" hash2=$(nix-hash --type sha256 --base32 ./dummy) -echo $hash2 +echo "$hash2" test "$hash1" = "sha256:$hash2" From 25e2b1f7f7260057e05ce9f6853e90ada5e63ed1 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Mon, 3 Jun 2024 02:11:06 +0200 Subject: [PATCH 192/910] improve note in `nix_value_force` documentation --- src/libexpr-c/nix_api_expr.h | 6 ++---- src/libexpr-c/nix_api_value.h | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index b026ea70b..0d324b148 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -129,10 +129,8 @@ nix_err nix_value_call_multi( * * This function converts these Values into their final type. * - * @note You don't need this function for basic API usage, since all functions - * that return a value call it for you. The only place you will see a - * NIX_TYPE_THUNK is in the arguments that are passed to a PrimOp function - * you supplied to nix_alloc_primop. + * @note You don't need this function for basic API usage very often, since all functions that return a `Value` call it + * for you. This function is mainly needed before calling @ref getters. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index f568b5c27..244860707 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -148,7 +148,8 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state); * @brief Functions to inspect and change Nix language values, represented by Value. * @{ */ -/** @name Getters +/** @anchor getters + * @name Getters */ /**@{*/ /** @brief Get value type From 57aa901071aaf5fda08c18cd599ef78e08418315 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 11:08:37 -0400 Subject: [PATCH 193/910] manual: Put the JSON guideline on its own page --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/contributing/cli-guideline.md | 82 ------------------- doc/manual/src/contributing/json-guideline.md | 81 ++++++++++++++++++ 3 files changed, 82 insertions(+), 82 deletions(-) create mode 100644 doc/manual/src/contributing/json-guideline.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 2516cc3db..18e7e8380 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -121,6 +121,7 @@ - [Documentation](contributing/documentation.md) - [Experimental Features](contributing/experimental-features.md) - [CLI guideline](contributing/cli-guideline.md) + - [JSON guideline](contributing/json-guideline.md) - [C++ style guide](contributing/cxx.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} diff --git a/doc/manual/src/contributing/cli-guideline.md b/doc/manual/src/contributing/cli-guideline.md index e90d6de8d..23df844ec 100644 --- a/doc/manual/src/contributing/cli-guideline.md +++ b/doc/manual/src/contributing/cli-guideline.md @@ -389,88 +389,6 @@ colors, no emojis and using ASCII instead of Unicode symbols). The same should happen when TTY is not detected on STDERR. We should not display progress / status section, but only print warnings and errors. -## Returning future proof JSON - -The schema of JSON output should allow for backwards compatible extension. This section explains how to achieve this. - -Two definitions are helpful here, because while JSON only defines one "key-value" -object type, we use it to cover two use cases: - - - **dictionary**: a map from names to value that all have the same type. In - C++ this would be a `std::map` with string keys. - - **record**: a fixed set of attributes each with their own type. In C++, this - would be represented by a `struct`. - -It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type. - -This leads to the following guidelines: - - - The top-level (root) value must be a record. - - Otherwise, one can not change the structure of a command's output. - - - The value of a dictionary item must be a record. - - Otherwise, the item type can not be extended. - - - List items should be records. - - Otherwise, one can not change the structure of the list items. - - If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. If the order of the items needs to be preserved, return a list of records. - - - Streaming JSON should return records. - - An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. These JSON values can be considered top-level values or list items, and they must be records. - -### Examples - - -This is bad, because all keys must be assumed to be store types: - -```json -{ - "local": { ... }, - "remote": { ... }, - "http": { ... } -} -``` - -This is good, because the it is extensible at the root, and is somewhat self-documenting: - -```json -{ - "storeTypes": { "local": { ... }, ... }, - "pluginSupport": true -} -``` - -While the dictionary of store types seems like a very complete response at first, a use case may arise that warrants returning additional information. -For example, the presence of plugin support may be crucial information for a client to proceed when their desired store type is missing. - - - -The following representation is bad because it is not extensible: - -```json -{ "outputs": [ "out" "bin" ] } -``` - -However, simply converting everything to records is not enough, because the order of outputs must be preserved: - -```json -{ "outputs": { "bin": {}, "out": {} } } -``` - -The first item is the default output. Deriving this information from the outputs ordering is not great, but this is how Nix currently happens to work. -While it is possible for a JSON parser to preserve the order of fields, we can not rely on this capability to be present in all JSON libraries. - -This representation is extensible and preserves the ordering: - -```json -{ "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] } -``` - ## Dialog with the user CLIs don't always make it clear when an action has taken place. For every diff --git a/doc/manual/src/contributing/json-guideline.md b/doc/manual/src/contributing/json-guideline.md new file mode 100644 index 000000000..a671cd66b --- /dev/null +++ b/doc/manual/src/contributing/json-guideline.md @@ -0,0 +1,81 @@ +## Returning future proof JSON + +The schema of JSON output should allow for backwards compatible extension. This section explains how to achieve this. + +Two definitions are helpful here, because while JSON only defines one "key-value" +object type, we use it to cover two use cases: + + - **dictionary**: a map from names to value that all have the same type. In + C++ this would be a `std::map` with string keys. + - **record**: a fixed set of attributes each with their own type. In C++, this + would be represented by a `struct`. + +It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type. + +This leads to the following guidelines: + + - The top-level (root) value must be a record. + + Otherwise, one can not change the structure of a command's output. + + - The value of a dictionary item must be a record. + + Otherwise, the item type can not be extended. + + - List items should be records. + + Otherwise, one can not change the structure of the list items. + + If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. If the order of the items needs to be preserved, return a list of records. + + - Streaming JSON should return records. + + An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. These JSON values can be considered top-level values or list items, and they must be records. + +### Examples + + +This is bad, because all keys must be assumed to be store types: + +```json +{ + "local": { ... }, + "remote": { ... }, + "http": { ... } +} +``` + +This is good, because the it is extensible at the root, and is somewhat self-documenting: + +```json +{ + "storeTypes": { "local": { ... }, ... }, + "pluginSupport": true +} +``` + +While the dictionary of store types seems like a very complete response at first, a use case may arise that warrants returning additional information. +For example, the presence of plugin support may be crucial information for a client to proceed when their desired store type is missing. + + + +The following representation is bad because it is not extensible: + +```json +{ "outputs": [ "out" "bin" ] } +``` + +However, simply converting everything to records is not enough, because the order of outputs must be preserved: + +```json +{ "outputs": { "bin": {}, "out": {} } } +``` + +The first item is the default output. Deriving this information from the outputs ordering is not great, but this is how Nix currently happens to work. +While it is possible for a JSON parser to preserve the order of fields, we can not rely on this capability to be present in all JSON libraries. + +This representation is extensible and preserves the ordering: + +```json +{ "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] } +``` From c50e14276e79f9effae2f8af9d6f6c41ce8cb503 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 17:28:07 -0400 Subject: [PATCH 194/910] manual: Extend JSON guidlines with optional field info Co-authored-by: Robert Hensing Co-authored-by: Valentin Gagarin --- doc/manual/src/contributing/json-guideline.md | 71 +++++++++++++++---- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/doc/manual/src/contributing/json-guideline.md b/doc/manual/src/contributing/json-guideline.md index a671cd66b..b4bc92af9 100644 --- a/doc/manual/src/contributing/json-guideline.md +++ b/doc/manual/src/contributing/json-guideline.md @@ -1,16 +1,23 @@ -## Returning future proof JSON +# JSON guideline -The schema of JSON output should allow for backwards compatible extension. This section explains how to achieve this. +Nix consumes and produces JSON in a variety of contexts. +These guidelines ensure consistent practices for all our JSON interfaces, for ease of use, and so that experience in one part carries over to another. -Two definitions are helpful here, because while JSON only defines one "key-value" -object type, we use it to cover two use cases: +## Extensibility - - **dictionary**: a map from names to value that all have the same type. In - C++ this would be a `std::map` with string keys. - - **record**: a fixed set of attributes each with their own type. In C++, this - would be represented by a `struct`. +The schema of JSON input and output should allow for backwards compatible extension. +This section explains how to achieve this. -It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type. +Two definitions are helpful here, because while JSON only defines one "key-value" object type, we use it to cover two use cases: + + - **dictionary**: a map from names to value that all have the same type. + In C++ this would be a `std::map` with string keys. + + - **record**: a fixed set of attributes each with their own type. + In C++, this would be represented by a `struct`. + +It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. +For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type, and dictionary items with a colliding name can not be represented anymore. This leads to the following guidelines: @@ -26,15 +33,16 @@ This leads to the following guidelines: Otherwise, one can not change the structure of the list items. - If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. If the order of the items needs to be preserved, return a list of records. + If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. + If the order of the items needs to be preserved, return a list of records. - Streaming JSON should return records. - An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. These JSON values can be considered top-level values or list items, and they must be records. + An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. + These JSON values can be considered top-level values or list items, and they must be records. ### Examples - This is bad, because all keys must be assumed to be store types: ```json @@ -79,3 +87,42 @@ This representation is extensible and preserves the ordering: ```json { "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] } ``` + +## Self-describing values + +As described in the previous section, it's crucial that schemas can be extended with with new fields without breaking compatibility. +However, that should *not* mean we use the presence/absence of fields to indicate optional information *within* a version of the schema. +Instead, always include the field, and use `null` to indicate the "nothing" case. + +### Examples + +Here are two JSON objects: + +```json +{ + "foo": {} +} +``` +```json +{ + "foo": {}, + "bar": {} +} +``` + +Since they differ in which fields they contain, they should *not* both be valid values of the same schema. +At most, they can match two different schemas where the second (with `foo` and `bar`) is considered a newer version of the first (with just `foo`). +Within each version, all fields are mandatory (always `foo`, and always `foo` and `bar`). +Only *between* each version, `bar` gets added as a new mandatory field. + +Here are another two JSON objects: + +```json +{ "foo": null } +``` +```json +{ "foo": { "bar": 1 } } +``` + +Since they both contain a `foo` field, they could be valid values of the same schema. +The schema would have `foo` has an optional field, which is either `null` or an object where `bar` is an integer. From 213a7a87b484d989d72fb233a47ecbb326b4ae32 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 11:01:22 -0400 Subject: [PATCH 195/910] Decouple within-build (structured attrs) and unstable CLI path info JSON See code comment for details. Co-authored-by: Robert Hensing --- src/libstore/parsed-derivations.cc | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc index a29281953..d8459d4d7 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -135,18 +135,37 @@ static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*"); /** * Write a JSON representation of store object metadata, such as the * hash and the references. + * + * @note Do *not* use `ValidPathInfo::toJSON` because this function is + * subject to stronger stability requirements since it is used to + * prepare build environments. Perhaps someday we'll have a versionining + * mechanism to allow this to evolve again and get back in sync, but for + * now we must not change - not even extend - the behavior. */ static nlohmann::json pathInfoToJSON( Store & store, const StorePathSet & storePaths) { - nlohmann::json::array_t jsonList = nlohmann::json::array(); + using nlohmann::json; + + nlohmann::json::array_t jsonList = json::array(); for (auto & storePath : storePaths) { auto info = store.queryPathInfo(storePath); - auto & jsonPath = jsonList.emplace_back( - info->toJSON(store, false, HashFormat::Nix32)); + auto & jsonPath = jsonList.emplace_back(json::object()); + + jsonPath["narHash"] = info->narHash.to_string(HashFormat::Nix32, true); + jsonPath["narSize"] = info->narSize; + + { + auto & jsonRefs = jsonPath["references"] = json::array(); + for (auto & ref : info->references) + jsonRefs.emplace_back(store.printStorePath(ref)); + } + + if (info->ca) + jsonPath["ca"] = renderContentAddress(info->ca); // Add the path to the object whose metadata we are including. jsonPath["path"] = store.printStorePath(storePath); From 84c65135a5059515d423a1b7d71921f4725e4c4c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 12 Feb 2024 10:51:20 -0500 Subject: [PATCH 196/910] `ValidPathInfo` JSON format should use `null` not omit field Co-authored-by: Robert Hensing Co-authored-by: Robert Hensing --- doc/manual/rl-next/store-object-info.md | 11 ++++++ doc/manual/src/glossary.md | 7 ++-- .../src/protocols/json/store-object-info.md | 22 +++++++----- src/libstore/path-info.cc | 34 +++++++++---------- src/libutil/json-utils.cc | 7 ++-- src/libutil/json-utils.hh | 2 +- tests/functional/signing.sh | 22 ++++++------ .../libstore/data/path-info/empty_impure.json | 10 ++++++ .../libstore/data/path-info/empty_pure.json | 6 ++++ tests/unit/libstore/path-info.cc | 24 +++++++++---- tests/unit/libutil/json-utils.cc | 7 ++-- 11 files changed, 98 insertions(+), 54 deletions(-) create mode 100644 doc/manual/rl-next/store-object-info.md create mode 100644 tests/unit/libstore/data/path-info/empty_impure.json create mode 100644 tests/unit/libstore/data/path-info/empty_pure.json diff --git a/doc/manual/rl-next/store-object-info.md b/doc/manual/rl-next/store-object-info.md new file mode 100644 index 000000000..ab8f5fec0 --- /dev/null +++ b/doc/manual/rl-next/store-object-info.md @@ -0,0 +1,11 @@ +--- +synopsis: Store object info JSON format now uses `null` rather than omitting fields. +prs: 9995 +--- + +The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. +For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. +This makes records of this sort more self-describing, and easier to consume programmatically. + +We will follow this design principle going forward; +the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 080b25d30..55ad9e1c2 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -12,7 +12,7 @@ For how Nix uses content addresses, see: - [Content-Addressing File System Objects](@docroot@/store/file-system-object/content-address.md) - - [content-addressed store object](#gloss-content-addressed-store-object) + - [Content-Addressing Store Objects](@docroot@/store/store-object/content-address.md) - [content-addressed derivation](#gloss-content-addressed-derivation) Software Heritage's writing on [*Intrinsic and Extrinsic identifiers*](https://www.softwareheritage.org/2020/07/09/intrinsic-vs-extrinsic-identifiers) is also a good introduction to the value of content-addressing over other referencing schemes. @@ -137,9 +137,12 @@ - [content-addressed store object]{#gloss-content-addressed-store-object} - A [store object] whose [store path] is determined by its contents. + A [store object] which is [content-addressed](#gloss-content-address), + i.e. whose [store path] is determined by its contents. This includes derivations, the outputs of [content-addressed derivations](#gloss-content-addressed-derivation), and the outputs of [fixed-output derivations](#gloss-fixed-output-derivation). + See [Content-Addressing Store Objects](@docroot@/store/store-object/content-address.md) for details. + - [substitute]{#gloss-substitute} A substitute is a command invocation stored in the [Nix database] that diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index 22a14715f..9f647a96c 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -24,9 +24,11 @@ Info about a [store object]. An array of [store paths][store path], possibly including this one. -* `ca` (optional): +* `ca`: - Content address of this store object's file system object, used to compute its store path. + If the store object is [content-addressed], + this is the content address of this store object's file system object, used to compute its store path. + Otherwise (i.e. if it is [input-addressed]), this is `null`. [store path]: @docroot@/store/store-path.md [file system object]: @docroot@/store/file-system-object.md @@ -37,28 +39,30 @@ Info about a [store object]. These are not intrinsic properties of the store object. In other words, the same store object residing in different store could have different values for these properties. -* `deriver` (optional): +* `deriver`: - The path to the [derivation] from which this store object is produced. + If known, the path to the [derivation] from which this store object was produced. + Otherwise `null`. [derivation]: @docroot@/glossary.md#gloss-store-derivation * `registrationTime` (optional): - When this derivation was added to the store. + If known, when this derivation was added to the store. + Otherwise `null`. -* `ultimate` (optional): +* `ultimate`: Whether this store object is trusted because we built it ourselves, rather than substituted a build product from elsewhere. -* `signatures` (optional): +* `signatures`: Signatures claiming that this store object is what it claims to be. Not relevant for [content-addressed] store objects, but useful for [input-addressed] store objects. - [content-addressed]: @docroot@/glossary.md#gloss-content-addressed-store-object - [input-addressed]: @docroot@/glossary.md#gloss-input-addressed-store-object +[content-addressed]: @docroot@/store/store-object/content-address.md +[input-addressed]: @docroot@/glossary.md#gloss-input-addressed-store-object ### `.narinfo` extra fields diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index 6523cb425..ddd7f50d9 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -161,28 +161,23 @@ nlohmann::json UnkeyedValidPathInfo::toJSON( jsonObject["narSize"] = narSize; { - auto& jsonRefs = (jsonObject["references"] = json::array()); + auto & jsonRefs = jsonObject["references"] = json::array(); for (auto & ref : references) jsonRefs.emplace_back(store.printStorePath(ref)); } - if (ca) - jsonObject["ca"] = renderContentAddress(ca); + jsonObject["ca"] = ca ? (std::optional { renderContentAddress(*ca) }) : std::nullopt; if (includeImpureInfo) { - if (deriver) - jsonObject["deriver"] = store.printStorePath(*deriver); + jsonObject["deriver"] = deriver ? (std::optional { store.printStorePath(*deriver) }) : std::nullopt; - if (registrationTime) - jsonObject["registrationTime"] = registrationTime; + jsonObject["registrationTime"] = registrationTime ? (std::optional { registrationTime }) : std::nullopt; - if (ultimate) - jsonObject["ultimate"] = ultimate; + jsonObject["ultimate"] = ultimate; - if (!sigs.empty()) { - for (auto & sig : sigs) - jsonObject["signatures"].push_back(sig); - } + auto & sigsObj = jsonObject["signatures"] = json::array(); + for (auto & sig : sigs) + sigsObj.push_back(sig); } return jsonObject; @@ -210,20 +205,25 @@ UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON( throw; } + // New format as this as nullable but mandatory field; handling + // missing is for back-compat. if (json.contains("ca")) - res.ca = ContentAddress::parse(getString(valueAt(json, "ca"))); + if (auto * rawCa = getNullable(valueAt(json, "ca"))) + res.ca = ContentAddress::parse(getString(*rawCa)); if (json.contains("deriver")) - res.deriver = store.parseStorePath(getString(valueAt(json, "deriver"))); + if (auto * rawDeriver = getNullable(valueAt(json, "deriver"))) + res.deriver = store.parseStorePath(getString(*rawDeriver)); if (json.contains("registrationTime")) - res.registrationTime = getInteger(valueAt(json, "registrationTime")); + if (auto * rawRegistrationTime = getNullable(valueAt(json, "registrationTime"))) + res.registrationTime = getInteger(*rawRegistrationTime); if (json.contains("ultimate")) res.ultimate = getBoolean(valueAt(json, "ultimate")); if (json.contains("signatures")) - res.sigs = valueAt(json, "signatures"); + res.sigs = getStringSet(valueAt(json, "signatures")); return res; } diff --git a/src/libutil/json-utils.cc b/src/libutil/json-utils.cc index 1b911bf75..dff068e07 100644 --- a/src/libutil/json-utils.cc +++ b/src/libutil/json-utils.cc @@ -39,12 +39,9 @@ std::optional optionalValueAt(const nlohmann::json::object_t & m } -std::optional getNullable(const nlohmann::json & value) +const nlohmann::json * getNullable(const nlohmann::json & value) { - if (value.is_null()) - return std::nullopt; - - return value.get(); + return value.is_null() ? nullptr : &value; } /** diff --git a/src/libutil/json-utils.hh b/src/libutil/json-utils.hh index 08c98cc8c..fe7a406cf 100644 --- a/src/libutil/json-utils.hh +++ b/src/libutil/json-utils.hh @@ -29,7 +29,7 @@ std::optional optionalValueAt(const nlohmann::json::object_t & v * Downcast the json object, failing with a nice error if the conversion fails. * See https://json.nlohmann.me/features/types/ */ -std::optional getNullable(const nlohmann::json & value); +const nlohmann::json * getNullable(const nlohmann::json & value); const nlohmann::json::object_t & getObject(const nlohmann::json & value); const nlohmann::json::array_t & getArray(const nlohmann::json & value); const nlohmann::json::string_t & getString(const nlohmann::json & value); diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh index bcbd3b675..cf84ab377 100755 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -15,9 +15,9 @@ outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROO # Verify that the path got signed. info=$(nix path-info --json $outPath) -[[ $info =~ '"ultimate":true' ]] -[[ $info =~ 'cache1.example.org' ]] -[[ $info =~ 'cache2.example.org' ]] +echo $info | jq -e '.[] | .ultimate == true' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Test "nix store verify". nix store verify -r $outPath @@ -39,8 +39,8 @@ nix store verify -r $outPath # Verify that the path did not get signed but does have the ultimate bit. info=$(nix path-info --json $outPath2) -[[ $info =~ '"ultimate":true' ]] -(! [[ $info =~ 'signatures' ]]) +echo $info | jq -e '.[] | .ultimate == true' +echo $info | jq -e '.[] | .signatures == []' # Test "nix store verify". nix store verify -r $outPath2 @@ -57,7 +57,7 @@ nix store verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1 # Build something content-addressed. outPathCA=$(IMPURE_VAR1=foo IMPURE_VAR2=bar nix-build ./fixed.nix -A good.0 --no-out-link) -[[ $(nix path-info --json $outPathCA) =~ '"ca":"fixed:md5:' ]] +nix path-info --json $outPathCA | jq -e '.[] | .ca | startswith("fixed:md5:")' # Content-addressed paths don't need signatures, so they verify # regardless of --sigs-needed. @@ -73,15 +73,15 @@ nix copy --to file://$cacheDir $outPath2 # Verify that signatures got copied. info=$(nix path-info --store file://$cacheDir --json $outPath2) -(! [[ $info =~ '"ultimate":true' ]]) -[[ $info =~ 'cache1.example.org' ]] -(! [[ $info =~ 'cache2.example.org' ]]) +echo $info | jq -e '.[] | .ultimate == false' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo $info | expect 4 jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Verify that adding a signature to a path in a binary cache works. nix store sign --store file://$cacheDir --key-file $TEST_ROOT/sk2 $outPath2 info=$(nix path-info --store file://$cacheDir --json $outPath2) -[[ $info =~ 'cache1.example.org' ]] -[[ $info =~ 'cache2.example.org' ]] +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Copying to a diverted store should fail due to a lack of signatures by trusted keys. chmod -R u+w $TEST_ROOT/store0 || true diff --git a/tests/unit/libstore/data/path-info/empty_impure.json b/tests/unit/libstore/data/path-info/empty_impure.json new file mode 100644 index 000000000..be982dcef --- /dev/null +++ b/tests/unit/libstore/data/path-info/empty_impure.json @@ -0,0 +1,10 @@ +{ + "ca": null, + "deriver": null, + "narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=", + "narSize": 0, + "references": [], + "registrationTime": null, + "signatures": [], + "ultimate": false +} diff --git a/tests/unit/libstore/data/path-info/empty_pure.json b/tests/unit/libstore/data/path-info/empty_pure.json new file mode 100644 index 000000000..10d9f508a --- /dev/null +++ b/tests/unit/libstore/data/path-info/empty_pure.json @@ -0,0 +1,6 @@ +{ + "ca": null, + "narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=", + "narSize": 0, + "references": [] +} diff --git a/tests/unit/libstore/path-info.cc b/tests/unit/libstore/path-info.cc index 80d6fcfed..06c662b74 100644 --- a/tests/unit/libstore/path-info.cc +++ b/tests/unit/libstore/path-info.cc @@ -19,7 +19,15 @@ class PathInfoTest : public CharacterizationTest, public LibStoreTest } }; -static UnkeyedValidPathInfo makePathInfo(const Store & store, bool includeImpureInfo) { +static UnkeyedValidPathInfo makeEmpty() +{ + return { + Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + }; +} + +static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo) +{ UnkeyedValidPathInfo info = ValidPathInfo { store, "foo", @@ -50,22 +58,21 @@ static UnkeyedValidPathInfo makePathInfo(const Store & store, bool includeImpure return info; } -#define JSON_TEST(STEM, PURE) \ +#define JSON_TEST(STEM, OBJ, PURE) \ TEST_F(PathInfoTest, PathInfo_ ## STEM ## _from_json) { \ readTest(#STEM, [&](const auto & encoded_) { \ auto encoded = json::parse(encoded_); \ UnkeyedValidPathInfo got = UnkeyedValidPathInfo::fromJSON( \ *store, \ encoded); \ - auto expected = makePathInfo(*store, PURE); \ + auto expected = OBJ; \ ASSERT_EQ(got, expected); \ }); \ } \ \ TEST_F(PathInfoTest, PathInfo_ ## STEM ## _to_json) { \ writeTest(#STEM, [&]() -> json { \ - return makePathInfo(*store, PURE) \ - .toJSON(*store, PURE, HashFormat::SRI); \ + return OBJ.toJSON(*store, PURE, HashFormat::SRI); \ }, [](const auto & file) { \ return json::parse(readFile(file)); \ }, [](const auto & file, const auto & got) { \ @@ -73,7 +80,10 @@ static UnkeyedValidPathInfo makePathInfo(const Store & store, bool includeImpure }); \ } -JSON_TEST(pure, false) -JSON_TEST(impure, true) +JSON_TEST(empty_pure, makeEmpty(), false) +JSON_TEST(empty_impure, makeEmpty(), true) + +JSON_TEST(pure, makeFull(*store, false), false) +JSON_TEST(impure, makeFull(*store, true), true) } diff --git a/tests/unit/libutil/json-utils.cc b/tests/unit/libutil/json-utils.cc index c9370a74b..704a4acb0 100644 --- a/tests/unit/libutil/json-utils.cc +++ b/tests/unit/libutil/json-utils.cc @@ -175,13 +175,16 @@ TEST(optionalValueAt, empty) { TEST(getNullable, null) { auto json = R"(null)"_json; - ASSERT_EQ(getNullable(json), std::nullopt); + ASSERT_EQ(getNullable(json), nullptr); } TEST(getNullable, empty) { auto json = R"({})"_json; - ASSERT_EQ(getNullable(json), std::optional { R"({})"_json }); + auto * p = getNullable(json); + + ASSERT_NE(p, nullptr); + ASSERT_EQ(*p, R"({})"_json); } } /* namespace nix */ From cfc18a77395b5ef49763f77c3cc67a95f762a0eb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 18:46:38 -0400 Subject: [PATCH 197/910] Modernize `Hash` ordering with C++20 `<=>` Progress on #10832 This doesn't switch to auto-deriving the fields, but by defining `<=>` we allow deriving `<=>` in downstream types where `Hash` is used. --- src/libutil/hash.cc | 17 +++++------------ src/libutil/hash.hh | 11 +++-------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index d4c9d6533..2f2ed8138 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -50,21 +50,14 @@ bool Hash::operator == (const Hash & h2) const } -bool Hash::operator != (const Hash & h2) const +std::strong_ordering Hash::operator <=> (const Hash & h) const { - return !(*this == h2); -} - - -bool Hash::operator < (const Hash & h) const -{ - if (hashSize < h.hashSize) return true; - if (hashSize > h.hashSize) return false; + if (auto cmp = algo <=> h.algo; cmp != 0) return cmp; + if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp; for (unsigned int i = 0; i < hashSize; i++) { - if (hash[i] < h.hash[i]) return true; - if (hash[i] > h.hash[i]) return false; + if (auto cmp = hash[i] <=> h.hash[i]; cmp != 0) return cmp; } - return false; + return std::strong_ordering::equivalent; } diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index e14aae43c..ef96d08c9 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -86,19 +86,14 @@ private: public: /** - * Check whether two hash are equal. + * Check whether two hashes are equal. */ bool operator == (const Hash & h2) const; /** - * Check whether two hash are not equal. + * Compare how two hashes are ordered. */ - bool operator != (const Hash & h2) const; - - /** - * For sorting. - */ - bool operator < (const Hash & h) const; + std::strong_ordering operator <=> (const Hash & h2) const; /** * Returns the length of a base-16 representation of this hash. From 1e99f324d907bc51b99822d3fa3ba7eee21a1d46 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 3 Jun 2024 09:36:05 -0400 Subject: [PATCH 198/910] Fix shellcheck issue 8b86f415c1a8565085e70475933e459a29d67283 was merged from a CI run that predated the new linting. --- tests/functional/flakes/eval-cache.sh | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 tests/functional/flakes/eval-cache.sh diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh old mode 100644 new mode 100755 index 90c7abd3c..0f8df1b91 --- a/tests/functional/flakes/eval-cache.sh +++ b/tests/functional/flakes/eval-cache.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit From deac00c6d0a019874016021a74e3d42306afd2e6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 15:49:15 +0200 Subject: [PATCH 199/910] Rename large-path-warning-threshold -> warn-large-path-threshold --- src/libstore/globals.hh | 4 ++-- src/libstore/store-api.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 1f3548497..843e77bcf 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1263,10 +1263,10 @@ public: )" }; - Setting largePathWarningThreshold{ + Setting warnLargePathThreshold{ this, std::numeric_limits::max(), - "large-path-warning-threshold", + "warn-large-path-threshold", R"( Warn when copying a path larger than this number of bytes to the Nix store (as determined by its NAR serialisation). diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 9b519dd84..c67ccd7d4 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -168,7 +168,7 @@ std::pair StoreDirConfig::computeStorePath( PathFilter & filter) const { auto [h, size] = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); - if (size && *size >= settings.largePathWarningThreshold) + if (size && *size >= settings.warnLargePathThreshold) warn("hashed large path '%s' (%s)", path, renderSize(*size)); return { makeFixedOutputPathFromCA( @@ -212,7 +212,7 @@ StorePath Store::addToStore( }); LengthSource lengthSource(*source); auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); - if (lengthSource.total >= settings.largePathWarningThreshold) + if (lengthSource.total >= settings.warnLargePathThreshold) warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total)); return storePath; } From d2bfc7e55a8e83964461698f9fbc90b927ef427e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 15:55:19 +0200 Subject: [PATCH 200/910] Add release note --- doc/manual/rl-next/warn-large-path.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/manual/rl-next/warn-large-path.md diff --git a/doc/manual/rl-next/warn-large-path.md b/doc/manual/rl-next/warn-large-path.md new file mode 100644 index 000000000..5ee3e307f --- /dev/null +++ b/doc/manual/rl-next/warn-large-path.md @@ -0,0 +1,11 @@ +--- +synopsis: Large path warnings +prs: 10661 +--- + +Nix can now warn when evaluation of a Nix expression causes a large +path to be copied to the Nix store. The threshold for this warning can +be configured using [the `warn-large-path-threshold` +setting](@docroot@/command-ref/conf-file.md#warn-large-path-threshold), +e.g. `--warn-large-path-threshold 100M` will warn about paths larger +than 100 MiB. From 2d4c9d8f4a41520e53441985b1fce5be87b731c8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Apr 2024 01:21:50 +0200 Subject: [PATCH 201/910] Add builtins.warn --- src/libexpr/eval-settings.cc | 4 ++++ src/libexpr/eval-settings.hh | 34 +++++++++++++++++++++++++++++---- src/libexpr/primops.cc | 37 ++++++++++++++++++++++++++++++++++++ tests/functional/lang.sh | 7 +++++++ 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 2ccbe327f..85b1677ea 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -48,6 +48,10 @@ EvalSettings::EvalSettings() { auto var = getEnv("NIX_PATH"); if (var) nixPath = parseNixPath(*var); + + var = getEnv("NIX_ABORT_ON_WARN"); + if (var && (var == "1" || var == "yes" || var == "true")) + builtinsAbortOnWarn = true; } Strings EvalSettings::getDefaultNixPath() diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index dbfc3b2c7..f1fb539bd 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -158,13 +158,39 @@ struct EvalSettings : Config Setting builtinsTraceDebugger{this, false, "debugger-on-trace", R"( - If set to true and the `--debugger` flag is given, - [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) will - enter the debugger like - [`builtins.break`](@docroot@/language/builtins.md#builtins-break). + If set to true and the `--debugger` flag is given, the following functions + will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break). + + * [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) + * [`builtins.traceVerbose`](@docroot@/language/builtins.md#builtins-traceVerbose) + if [`trace-verbose`](#conf-trace-verbose) is set to true. + * [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) This is useful for debugging warnings in third-party Nix code. )"}; + + Setting builtinsDebuggerOnWarn{this, false, "debugger-on-warn", + R"( + If set to true and the `--debugger` flag is given, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) + will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break). + + This is useful for debugging warnings in third-party Nix code. + + Use [`debugger-on-trace`](#conf-debugger-on-trace) to also enter the debugger on legacy warnings that are logged with [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace). + )"}; + + Setting builtinsAbortOnWarn{this, false, "abort-on-warn", + R"( + If set to true, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) will throw an error when logging a warning. + + This will give you a stack trace that leads to the location of the warning. + + This is useful for finding information about warnings in third-party Nix code when you can not start the interactive debugger, such as when Nix is called from a non-interactive script. See [`debugger-on-warn`](#conf-debugger-on-warn). + + Currently, a stack trace can only be produced when the debugger is enabled, or when evaluation is aborted. + + This option can be enabled by setting `NIX_ABORT_ON_WARN=1` in the environment. + )"}; }; extern EvalSettings evalSettings; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7371bd488..9319709c1 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1043,6 +1043,43 @@ static RegisterPrimOp primop_trace({ .fun = prim_trace, }); +static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + state.forceValue(*args[0], pos); + if (args[0]->type() == nString) + printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", args[0]->string_view()); + else + printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", ValuePrinter(state, *args[0])); + + if (evalSettings.builtinsAbortOnWarn) { + state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); + } + if ((evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) && state.debugRepl && !state.debugTraces.empty()) { + const DebugTrace & last = state.debugTraces.front(); + state.runDebugRepl(nullptr, last.env, last.expr); + } + state.forceValue(*args[1], pos); + v = *args[1]; +} + +static RegisterPrimOp primop_warn({ + .name = "__warn", + .args = {"e1", "e2"}, + .doc = R"( + Evaluate *e1*, which must be a string and print iton standard error as a warning. + Then return *e2*. + This function is useful for non-critical situations where attention is advisable. + + If the + [`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace) + or [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) + option is set to `true` and the `--debugger` flag is given, the + interactive debugger will be started when `warn` is called (like + [`break`](@docroot@/language/builtins.md#builtins-break)). + )", + .fun = prim_warn, +}); + /* Takes two arguments and evaluates to the second one. Used as the * builtins.traceVerbose implementation when --trace-verbose is not enabled diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index a853cfd81..843ff7cfa 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -36,6 +36,13 @@ nix-instantiate --eval -E 'let x = builtins.trace { x = x; } true; in x' \ nix-instantiate --eval -E 'let x = { repeating = x; tracing = builtins.trace x true; }; in x.tracing'\ 2>&1 | grepQuiet -F 'trace: { repeating = «repeated»; tracing = «potential infinite recursion»; }' +nix-instantiate --eval -E 'builtins.warn "Hello" 123' 2>&1 | grepQuiet 'warning: Hello' +nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' 2>/dev/null | grepQuiet 123 +nix-instantiate --eval -E 'let x = builtins.warn { x = x; } true; in x' \ + 2>&1 | grepQuiet -E 'warning: { x = «potential infinite recursion»; }' +expectStderr 1 nix-instantiate --eval --abort-on-warn -E 'builtins.warn "Hello" 123' | grepQuiet Hello +NIX_ABORT_ON_WARN=1 expectStderr 1 nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' | grepQuiet "while doing something interesting" + set +x badDiff=0 From 923cbea2af8eb1ccbd29b6b3542c974081bdddc1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Apr 2024 18:07:45 +0200 Subject: [PATCH 202/910] builtins.warn: Use logWarning Constructing ErrorInfo is a little awkward for now, but this does produce a richer log entry. --- src/libexpr/primops.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9319709c1..c20e0c359 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1046,10 +1046,20 @@ static RegisterPrimOp primop_trace({ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - if (args[0]->type() == nString) - printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", args[0]->string_view()); - else - printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", ValuePrinter(state, *args[0])); + + { + BaseError msg(args[0]->type() == nString + ? std::string(args[0]->string_view()) + : ({ + std::stringstream s; + s << ValuePrinter(state, *args[0]); + s.str(); + })); + msg.atPos(state.positions[pos]); + auto info = msg.info(); + info.level = lvlWarn; + logWarning(info); + } if (evalSettings.builtinsAbortOnWarn) { state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); From da82d67022fd77f7f38556f2dabac416110ec55e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Apr 2024 10:56:32 +0200 Subject: [PATCH 203/910] builtins.warn: Require string argument ... so that we may perhaps later extend the interface. Note that Nixpkgs' lib.warn already requires a string coercible argument, so this is reasonable. Also note that string coercible values aren't all strings, but in practice, for warn, they are. --- src/libexpr/primops.cc | 12 ++++-------- tests/functional/lang.sh | 6 ++++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c20e0c359..5ff7d5314 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1045,16 +1045,12 @@ static RegisterPrimOp primop_trace({ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - state.forceValue(*args[0], pos); + // We only accept a string argument for now. The use case for pretty printing a value is covered by `trace`. + // By rejecting non-strings we allow future versions to add more features without breaking existing code. + auto msgStr = state.forceString(*args[0], pos, "while evaluating the first argument; the message passed to builtins.warn"); { - BaseError msg(args[0]->type() == nString - ? std::string(args[0]->string_view()) - : ({ - std::stringstream s; - s << ValuePrinter(state, *args[0]); - s.str(); - })); + BaseError msg(std::string{msgStr}); msg.atPos(state.positions[pos]); auto info = msg.info(); info.level = lvlWarn; diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 843ff7cfa..569e7082e 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -38,8 +38,10 @@ nix-instantiate --eval -E 'let x = { repeating = x; tracing = builtins.trace x t nix-instantiate --eval -E 'builtins.warn "Hello" 123' 2>&1 | grepQuiet 'warning: Hello' nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' 2>/dev/null | grepQuiet 123 -nix-instantiate --eval -E 'let x = builtins.warn { x = x; } true; in x' \ - 2>&1 | grepQuiet -E 'warning: { x = «potential infinite recursion»; }' + +# warn does not accept non-strings for now +expectStderr 1 nix-instantiate --eval -E 'let x = builtins.warn { x = x; } true; in x' \ + | grepQuiet "expected a string but found a set" expectStderr 1 nix-instantiate --eval --abort-on-warn -E 'builtins.warn "Hello" 123' | grepQuiet Hello NIX_ABORT_ON_WARN=1 expectStderr 1 nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' | grepQuiet "while doing something interesting" From c07500e14dce5036ea4b8cf956cf34df4b282873 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 1 May 2024 23:02:43 +0200 Subject: [PATCH 204/910] refactor: Extract EvalState::{runDebugRepl,canDebug} --- src/libexpr/eval-error.cc | 7 +------ src/libexpr/eval.cc | 18 ++++++++++++++++++ src/libexpr/eval.hh | 12 ++++++++++++ src/libexpr/primops.cc | 15 ++++++--------- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 282f5554a..5a0c67514 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -73,12 +73,7 @@ EvalErrorBuilder::addTrace(PosIdx pos, std::string_view formatString, const A template void EvalErrorBuilder::debugThrow() { - if (error.state.debugRepl && !error.state.debugTraces.empty()) { - const DebugTrace & last = error.state.debugTraces.front(); - const Env * env = &last.env; - const Expr * expr = &last.expr; - error.state.runDebugRepl(&error, *env, *expr); - } + error.state.runDebugRepl(&error); // `EvalState` is the only class that can construct an `EvalErrorBuilder`, // and it does so in dynamic storage. This is the final method called on diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c1dadeee0..6a38bbe45 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -785,6 +785,24 @@ public: } }; +bool EvalState::canDebug() +{ + return debugRepl && !debugTraces.empty(); +} + +void EvalState::runDebugRepl(const Error * error) +{ + if (!canDebug()) + return; + + assert(!debugTraces.empty()); + const DebugTrace & last = debugTraces.front(); + const Env & env = last.env; + const Expr & expr = last.expr; + + runDebugRepl(error, env, expr); +} + void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & expr) { // Make sure we have a debugger to run and we're not already in a debugger. diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7ca2d6227..06a687620 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -276,6 +276,18 @@ public: return std::shared_ptr();; } + /** Whether a debug repl can be started. If `false`, `runDebugRepl(error)` will return without starting a repl. */ + bool canDebug(); + + /** Use front of `debugTraces`; see `runDebugRepl(error,env,expr)` */ + void runDebugRepl(const Error * error); + + /** + * Run a debug repl with the given error, environment and expression. + * @param error The error to debug, may be nullptr. + * @param env The environment to debug, matching the expression. + * @param expr The expression to debug, matching the environment. + */ void runDebugRepl(const Error * error, const Env & env, const Expr & expr); template diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5ff7d5314..459d19e05 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -780,15 +780,14 @@ static RegisterPrimOp primop_break({ )", .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) { - if (state.debugRepl && !state.debugTraces.empty()) { + if (state.canDebug()) { auto error = Error(ErrorInfo { .level = lvlInfo, .msg = HintFmt("breakpoint reached"), .pos = state.positions[pos], }); - auto & dt = state.debugTraces.front(); - state.runDebugRepl(&error, dt.env, dt.expr); + state.runDebugRepl(&error); } // Return the value we were passed. @@ -1018,9 +1017,8 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu printError("trace: %1%", args[0]->string_view()); else printError("trace: %1%", ValuePrinter(state, *args[0])); - if (evalSettings.builtinsTraceDebugger && state.debugRepl && !state.debugTraces.empty()) { - const DebugTrace & last = state.debugTraces.front(); - state.runDebugRepl(nullptr, last.env, last.expr); + if (evalSettings.builtinsTraceDebugger) { + state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); v = *args[1]; @@ -1060,9 +1058,8 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value if (evalSettings.builtinsAbortOnWarn) { state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); } - if ((evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) && state.debugRepl && !state.debugTraces.empty()) { - const DebugTrace & last = state.debugTraces.front(); - state.runDebugRepl(nullptr, last.env, last.expr); + if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { + state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); v = *args[1]; From 831d96d8d79424595b955791dd007c3317b76b3d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 7 May 2024 09:13:58 +0200 Subject: [PATCH 205/910] builtins.warn: Do not throw EvalError --- src/libexpr/primops.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 459d19e05..9741e3177 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1056,7 +1056,8 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value } if (evalSettings.builtinsAbortOnWarn) { - state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); + // Not an EvalError or subclass, which would cause the error to be stored in the eval cache. + state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); } if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { state.runDebugRepl(nullptr); From 70b10362247ef563ef4cad02709ec2bdb5564206 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 May 2024 12:51:46 +0200 Subject: [PATCH 206/910] builtins.warn: Use new EvalBaseError + "evaluation warning" --- doc/manual/rl-next/builtins-warn.md | 10 ++++++++++ src/libexpr/eval-error.cc | 8 ++++++++ src/libexpr/eval-error.hh | 20 +++++++++++++++++--- src/libexpr/primops.cc | 12 +++++++++--- src/libutil/error.cc | 5 ++++- src/libutil/error.hh | 5 +++++ 6 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 doc/manual/rl-next/builtins-warn.md diff --git a/doc/manual/rl-next/builtins-warn.md b/doc/manual/rl-next/builtins-warn.md new file mode 100644 index 000000000..0f7a6ebba --- /dev/null +++ b/doc/manual/rl-next/builtins-warn.md @@ -0,0 +1,10 @@ +--- +synopsis: "New builtin: `builtins.warn`" +issues: 306026 +prs: 10592 +--- + +`builtins.warn` behaves like `builtins.trace "warning: ${msg}`, has an accurate log level, and is controlled by the options +[`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), +[`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and +[`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 5a0c67514..bd84e0428 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -70,6 +70,13 @@ EvalErrorBuilder::addTrace(PosIdx pos, std::string_view formatString, const A return *this; } +template +EvalErrorBuilder & EvalErrorBuilder::setIsFromExpr() +{ + error.err.isFromExpr = true; + return *this; +} + template void EvalErrorBuilder::debugThrow() { @@ -85,6 +92,7 @@ void EvalErrorBuilder::debugThrow() throw error; } +template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index 27407eb6e..fe48e054b 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -15,27 +15,39 @@ class EvalState; template class EvalErrorBuilder; -class EvalError : public Error +/** + * Base class for all errors that occur during evaluation. + * + * Most subclasses should inherit from `EvalError` instead of this class. + */ +class EvalBaseError : public Error { template friend class EvalErrorBuilder; public: EvalState & state; - EvalError(EvalState & state, ErrorInfo && errorInfo) + EvalBaseError(EvalState & state, ErrorInfo && errorInfo) : Error(errorInfo) , state(state) { } template - explicit EvalError(EvalState & state, const std::string & formatString, const Args &... formatArgs) + explicit EvalBaseError(EvalState & state, const std::string & formatString, const Args &... formatArgs) : Error(formatString, formatArgs...) , state(state) { } }; +/** + * `EvalError` is the base class for almost all errors that occur during evaluation. + * + * All instances of `EvalError` should show a degree of purity that allows them to be + * cached in pure mode. This means that they should not depend on the configuration or the overall environment. + */ +MakeError(EvalError, EvalBaseError); MakeError(ParseError, Error); MakeError(AssertionError, EvalError); MakeError(ThrownError, AssertionError); @@ -90,6 +102,8 @@ public: [[nodiscard, gnu::noinline]] EvalErrorBuilder & addTrace(PosIdx pos, HintFmt hint); + [[nodiscard, gnu::noinline]] EvalErrorBuilder & setIsFromExpr(); + template [[nodiscard, gnu::noinline]] EvalErrorBuilder & addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9741e3177..22d7f188f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -806,7 +806,7 @@ static RegisterPrimOp primop_abort({ NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtins.abort").toOwned(); - state.error("evaluation aborted with the following error message: '%1%'", s).debugThrow(); + state.error("evaluation aborted with the following error message: '%1%'", s).setIsFromExpr().debugThrow(); } }); @@ -825,7 +825,7 @@ static RegisterPrimOp primop_throw({ NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtin.throw").toOwned(); - state.error(s).debugThrow(); + state.error(s).setIsFromExpr().debugThrow(); } }); @@ -1052,12 +1052,13 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value msg.atPos(state.positions[pos]); auto info = msg.info(); info.level = lvlWarn; + info.isFromExpr = true; logWarning(info); } if (evalSettings.builtinsAbortOnWarn) { // Not an EvalError or subclass, which would cause the error to be stored in the eval cache. - state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); + state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").setIsFromExpr().debugThrow(); } if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { state.runDebugRepl(nullptr); @@ -1080,6 +1081,11 @@ static RegisterPrimOp primop_warn({ option is set to `true` and the `--debugger` flag is given, the interactive debugger will be started when `warn` is called (like [`break`](@docroot@/language/builtins.md#builtins-break)). + + If the + [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn) + option is set, the evaluation will be aborted after the warning is printed. + This is useful to reveal the stack trace of the warning, when the context is non-interactive and a debugger can not be launched. )", .fun = prim_warn, }); diff --git a/src/libutil/error.cc b/src/libutil/error.cc index fd4f4efd1..e01f06448 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -240,7 +240,10 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s break; } case Verbosity::lvlWarn: { - prefix = ANSI_WARNING "warning"; + if (einfo.isFromExpr) + prefix = ANSI_WARNING "evaluation warning"; + else + prefix = ANSI_WARNING "warning"; break; } case Verbosity::lvlInfo: { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 87d181c94..269000016 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -89,6 +89,11 @@ struct ErrorInfo { HintFmt msg; std::shared_ptr pos; std::list traces; + /** + * Some messages are generated directly by expressions; notably `builtins.warn`, `abort`, `throw`. + * These may be rendered differently, so that users can distinguish them. + */ + bool isFromExpr = false; /** * Exit status. From 3a0b0af2ace69bb98ba1f86e1da4ec8f13c31840 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:30:35 +0200 Subject: [PATCH 207/910] Fix typo in doc/manual/rl-next/builtins-warn.md Co-authored-by: Eelco Dolstra --- doc/manual/rl-next/builtins-warn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/builtins-warn.md b/doc/manual/rl-next/builtins-warn.md index 0f7a6ebba..f805e1610 100644 --- a/doc/manual/rl-next/builtins-warn.md +++ b/doc/manual/rl-next/builtins-warn.md @@ -4,7 +4,7 @@ issues: 306026 prs: 10592 --- -`builtins.warn` behaves like `builtins.trace "warning: ${msg}`, has an accurate log level, and is controlled by the options +`builtins.warn` behaves like `builtins.trace "warning: ${msg}"`, has an accurate log level, and is controlled by the options [`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). From 8df206be541045f5ca340f909db1c43ec088f4a7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:38:50 +0200 Subject: [PATCH 208/910] Update nixpkgs ref to 24.05 --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 6fb159d6e..a7bedd819 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ # TODO switch to nixos-23.11-small # https://nixpk.gs/pr-tracker.html?pr=291954 - inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.11"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; From 2edcdf85087edd2f2b4c7f6d73fa6302a655506c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:39:09 +0200 Subject: [PATCH 209/910] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/b550fe4b4776908ac2a861124307045f8e717c8e' (2024-02-28) → 'github:NixOS/nixpkgs/88dca77be222aedd1f47d2cf0942dffefee76216' (2024-06-03) --- flake.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 409463ad8..7770484a6 100644 --- a/flake.lock +++ b/flake.lock @@ -69,16 +69,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1709083642, - "narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=", + "lastModified": 1717413331, + "narHash": "sha256-oxsqQB/UwJNCTWUpndQgoz14731mgaWg/zYHrVwGoco=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b550fe4b4776908ac2a861124307045f8e717c8e", + "rev": "88dca77be222aedd1f47d2cf0942dffefee76216", "type": "github" }, "original": { "owner": "NixOS", - "ref": "release-23.11", + "ref": "release-24.05", "repo": "nixpkgs", "type": "github" } From 2477e4e3b84b23b091befa1869a1fc6186fe74dc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 27 Feb 2024 19:42:41 +0100 Subject: [PATCH 210/910] libexpr: Use GC_set_sp_corrector instead of patch Manually tested by printing to stderr in both branches (sp in os stack, or not), and triggering a GC in a filterSource function, e.g.: let generateTree = n: if n == 0 then "ha" else { left = generateTree (n - 1); right = generateTree (n - 1); }; in builtins.deepSeq (generateTree 18) ... Note that the darwin still uses the strategy of disabling GC, despite having an implementation that compiles. The proper solution will be enabled and tested later. --- configure.ac | 2 +- .../boehmgc-coroutine-sp-fallback.diff | 99 ------------------- flake.nix | 2 - src/libexpr/eval.cc | 66 +++++++++++-- src/libexpr/local.mk | 4 +- 5 files changed, 62 insertions(+), 111 deletions(-) delete mode 100644 dep-patches/boehmgc-coroutine-sp-fallback.diff diff --git a/configure.ac b/configure.ac index 90a6d45d5..0a015fe48 100644 --- a/configure.ac +++ b/configure.ac @@ -366,7 +366,7 @@ fi AC_ARG_ENABLE(gc, AS_HELP_STRING([--enable-gc],[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]), gc=$enableval, gc=yes) if test "$gc" = yes; then - PKG_CHECK_MODULES([BDW_GC], [bdw-gc]) + PKG_CHECK_MODULES([BDW_GC], [bdw-gc >= 8.2.4]) CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS" AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.]) fi diff --git a/dep-patches/boehmgc-coroutine-sp-fallback.diff b/dep-patches/boehmgc-coroutine-sp-fallback.diff deleted file mode 100644 index 2afbe9671..000000000 --- a/dep-patches/boehmgc-coroutine-sp-fallback.diff +++ /dev/null @@ -1,99 +0,0 @@ -diff --git a/darwin_stop_world.c b/darwin_stop_world.c -index 0468aaec..b348d869 100644 ---- a/darwin_stop_world.c -+++ b/darwin_stop_world.c -@@ -356,6 +356,7 @@ GC_INNER void GC_push_all_stacks(void) - int nthreads = 0; - word total_size = 0; - mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ; -+ size_t stack_limit; - if (!EXPECT(GC_thr_initialized, TRUE)) - GC_thr_init(); - -@@ -411,6 +412,19 @@ GC_INNER void GC_push_all_stacks(void) - GC_push_all_stack_sections(lo, hi, p->traced_stack_sect); - } - if (altstack_lo) { -+ // When a thread goes into a coroutine, we lose its original sp until -+ // control flow returns to the thread. -+ // While in the coroutine, the sp points outside the thread stack, -+ // so we can detect this and push the entire thread stack instead, -+ // as an approximation. -+ // We assume that the coroutine has similarly added its entire stack. -+ // This could be made accurate by cooperating with the application -+ // via new functions and/or callbacks. -+ stack_limit = pthread_get_stacksize_np(p->id); -+ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack -+ altstack_lo = altstack_hi - stack_limit; -+ } -+ - total_size += altstack_hi - altstack_lo; - GC_push_all_stack(altstack_lo, altstack_hi); - } -diff --git a/include/gc.h b/include/gc.h -index edab6c22..f2c61282 100644 ---- a/include/gc.h -+++ b/include/gc.h -@@ -2172,6 +2172,11 @@ GC_API void GC_CALL GC_win32_free_heap(void); - (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page) - #endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */ - -+#if !__APPLE__ -+/* Patch doesn't work on apple */ -+#define NIX_BOEHM_PATCH_VERSION 1 -+#endif -+ - #ifdef __cplusplus - } /* extern "C" */ - #endif -diff --git a/pthread_stop_world.c b/pthread_stop_world.c -index b5d71e62..aed7b0bf 100644 ---- a/pthread_stop_world.c -+++ b/pthread_stop_world.c -@@ -768,6 +768,8 @@ STATIC void GC_restart_handler(int sig) - /* world is stopped. Should not fail if it isn't. */ - GC_INNER void GC_push_all_stacks(void) - { -+ size_t stack_limit; -+ pthread_attr_t pattr; - GC_bool found_me = FALSE; - size_t nthreads = 0; - int i; -@@ -851,6 +853,37 @@ GC_INNER void GC_push_all_stacks(void) - hi = p->altstack + p->altstack_size; - /* FIXME: Need to scan the normal stack too, but how ? */ - /* FIXME: Assume stack grows down */ -+ } else { -+#ifdef HAVE_PTHREAD_ATTR_GET_NP -+ if (!pthread_attr_init(&pattr) -+ || !pthread_attr_get_np(p->id, &pattr)) -+#else /* HAVE_PTHREAD_GETATTR_NP */ -+ if (pthread_getattr_np(p->id, &pattr)) -+#endif -+ { -+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!"); -+ } -+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) { -+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!"); -+ } -+ if (pthread_attr_destroy(&pattr)) { -+ ABORT("GC_push_all_stacks: pthread_attr_destroy failed!"); -+ } -+ // When a thread goes into a coroutine, we lose its original sp until -+ // control flow returns to the thread. -+ // While in the coroutine, the sp points outside the thread stack, -+ // so we can detect this and push the entire thread stack instead, -+ // as an approximation. -+ // We assume that the coroutine has similarly added its entire stack. -+ // This could be made accurate by cooperating with the application -+ // via new functions and/or callbacks. -+ #ifndef STACK_GROWS_UP -+ if (lo >= hi || lo < hi - stack_limit) { // sp outside stack -+ lo = hi - stack_limit; -+ } -+ #else -+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix." -+ #endif - } - GC_push_all_stack_sections(lo, hi, traced_stack_sect); - # ifdef STACK_GROWS_UP diff --git a/flake.nix b/flake.nix index a7bedd819..7a07a70ba 100644 --- a/flake.nix +++ b/flake.nix @@ -150,8 +150,6 @@ enableLargeConfig = true; }).overrideAttrs(o: { patches = (o.patches or []) ++ [ - ./dep-patches/boehmgc-coroutine-sp-fallback.diff - # https://github.com/ivmai/bdwgc/pull/586 ./dep-patches/boehmgc-traceable_allocator-public.diff ]; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c1dadeee0..30521b072 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -250,6 +251,50 @@ class BoehmGCStackAllocator : public StackAllocator { static BoehmGCStackAllocator boehmGCStackAllocator; +/** + * When a thread goes into a coroutine, we lose its original sp until + * control flow returns to the thread. + * While in the coroutine, the sp points outside the thread stack, + * so we can detect this and push the entire thread stack instead, + * as an approximation. + * The coroutine's stack is covered by `BoehmGCStackAllocator`. + * This is not an optimal solution, because the garbage is scanned when a + * coroutine is active, for both the coroutine and the original thread stack. + * However, the implementation is quite lean, and usually we don't have active + * coroutines during evaluation, so this is acceptable. + */ +void fixupBoehmStackPointer(void ** sp_ptr, void * pthread_id) { + void *& sp = *sp_ptr; + pthread_attr_t pattr; + size_t osStackSize; + void * osStackLow; + void * osStackBase; + + #ifdef __APPLE__ + osStackSize = pthread_get_stacksize_np((pthread_t)pthread_id); + osStackLow = pthread_get_stackaddr_np((pthread_t)pthread_id); + #else + if (pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } + if (pthread_getattr_np((pthread_t)pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); + } + if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { + throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); + } + if (pthread_attr_destroy(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); + } + #endif + osStackBase = (char *)osStackLow + osStackSize; + // NOTE: We assume the stack grows down, as it does on all architectures we support. + // Architectures that grow the stack up are rare. + if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack + sp = osStackBase; + } +} + #endif @@ -303,16 +348,21 @@ void initGC() GC_set_oom_fn(oomHandler); + // TODO: Comment suggests an implementation that works on darwin and windows + // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 + #ifndef __APPLE__ + GC_set_sp_corrector(&fixupBoehmStackPointer); + #endif + 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 + if (!GC_get_sp_corrector()) { + printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); + /* Used to disable GC when entering coroutines on macOS */ + create_coro_gc_hook = []() -> std::shared_ptr { + return 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 diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index ecadc5e5d..d128064a5 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -15,7 +15,9 @@ libexpr_SOURCES := \ INCLUDE_libexpr := -I $(d) -libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) +libexpr_CXXFLAGS += \ + $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) \ + -DGC_THREADS libexpr_LIBS = libutil libstore libfetchers From b311f51f845fc51f51487ebcfff55848d38f3be7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:52:13 +0200 Subject: [PATCH 211/910] boehmgc-nix: Remove released traceable_allocator patch --- flake.nix | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 7a07a70ba..896a2481c 100644 --- a/flake.nix +++ b/flake.nix @@ -146,14 +146,9 @@ ++ [ "-DUSE_SSH=exec" ]; }); - boehmgc-nix = (final.boehmgc.override { + boehmgc-nix = final.boehmgc.override { enableLargeConfig = true; - }).overrideAttrs(o: { - patches = (o.patches or []) ++ [ - # https://github.com/ivmai/bdwgc/pull/586 - ./dep-patches/boehmgc-traceable_allocator-public.diff - ]; - }); + }; libseccomp-nix = final.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; From cc6f31525253b73e4776b5f733e0950e1706d546 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 5 Mar 2024 16:39:06 +0100 Subject: [PATCH 212/910] nix: Disable GC during coroutine when bdwgc < 8.4 This re-enables support for older bwdgc versions without complicating the code too much. Coroutines generally only interfere with GC during source filtering, so it's not too bad of a regression on older bdwgc. This seems preferable over conditional compilation to enable the patch etc; we've already spent a lot of complexity budget on this GC-coroutine interaction... --- configure.ac | 2 +- src/libexpr/eval.cc | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 0a015fe48..90a6d45d5 100644 --- a/configure.ac +++ b/configure.ac @@ -366,7 +366,7 @@ fi AC_ARG_ENABLE(gc, AS_HELP_STRING([--enable-gc],[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]), gc=$enableval, gc=yes) if test "$gc" = yes; then - PKG_CHECK_MODULES([BDW_GC], [bdw-gc >= 8.2.4]) + PKG_CHECK_MODULES([BDW_GC], [bdw-gc]) CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS" AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.]) fi diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 30521b072..1eb60a1b8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -348,14 +348,14 @@ void initGC() GC_set_oom_fn(oomHandler); - // TODO: Comment suggests an implementation that works on darwin and windows - // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 - #ifndef __APPLE__ - GC_set_sp_corrector(&fixupBoehmStackPointer); - #endif - StackAllocator::defaultAllocator = &boehmGCStackAllocator; + // TODO: Remove __APPLE__ condition. + // Comment suggests an implementation that works on darwin and windows + // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 + #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 4 && !defined(__APPLE__) + GC_set_sp_corrector(&fixupBoehmStackPointer); + if (!GC_get_sp_corrector()) { printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); /* Used to disable GC when entering coroutines on macOS */ @@ -363,6 +363,10 @@ void initGC() return std::make_shared(); }; } + #else + #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bwd-gc to 8.4 or later." + #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 From f01f65b615795d3833dbb00768ce93fd701facde Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 16:15:49 +0200 Subject: [PATCH 213/910] Fix nixpkgsLibTests --- build/hydra.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 595aad324..fe9ca936c 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -151,10 +151,11 @@ in nixpkgsLibTests = forAllSystems (system: - import (nixpkgs + "/lib/tests/release.nix") + import (nixpkgs + "/lib/tests/test-with-nix.nix") { + lib = nixpkgsFor.${system}.native.lib; + nix = self.packages.${system}.nix; pkgs = nixpkgsFor.${system}.native; - nixVersions = [ self.packages.${system}.nix ]; } ); }; From 60675251620ed73d245fe633db94cde6742fa4d9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 16:35:45 +0200 Subject: [PATCH 214/910] hydraJobs.installTests..againstCurrent{Unstable -> Latest} Nixpkgs has reshuffled its Nix versions. --- build/hydra.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index fe9ca936c..84878fb5e 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -170,10 +170,10 @@ in pkgs.runCommand "install-tests" { againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; - againstCurrentUnstable = + againstCurrentLatest = # FIXME: temporarily disable this on macOS because of #3605. if system == "x86_64-linux" - then testNixVersions pkgs pkgs.nix pkgs.nixUnstable + then testNixVersions pkgs pkgs.nix pkgs.nixVersions.latest else null; # Disabled because the latest stable version doesn't handle # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work From 449e4b9232a601b7cbc04a03866c278ae5a0f51d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 16:42:11 +0200 Subject: [PATCH 215/910] Change checkOverrideNixVersion for NixOS 24.05 --- tests/nixos/default.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 4edf40c16..4267db945 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -33,7 +33,9 @@ let checkOverrideNixVersion = { pkgs, lib, ... }: { # pkgs.nix: The new Nix in this repo # We disallow it, to make sure we don't accidentally use it. - system.forbiddenDependenciesRegex = lib.strings.escapeRegex "nix-${pkgs.nix.version}"; + system.forbiddenDependenciesRegexes = [ + (lib.strings.escapeRegex "nix-${pkgs.nix.version}") + ]; }; in From 6558025e776d89f687c3c25adc5ae01870622db3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 17:00:48 +0200 Subject: [PATCH 216/910] Fix eval remoteBuilds_*_2_13 --- build/hydra.nix | 2 +- flake.lock | 17 +++++++++++++++++ flake.nix | 1 + tests/nixos/default.nix | 14 +++++++++++--- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 84878fb5e..857b7f1f0 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -129,7 +129,7 @@ in }; # System tests. - tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { + tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor self; } // { # Make sure that nix-env still produces the exact same result # on a particular version of Nixpkgs. diff --git a/flake.lock b/flake.lock index 7770484a6..ee976a3d9 100644 --- a/flake.lock +++ b/flake.lock @@ -83,6 +83,22 @@ "type": "github" } }, + "nixpkgs-23-11": { + "locked": { + "lastModified": 1717159533, + "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -131,6 +147,7 @@ "flake-parts": "flake-parts", "libgit2": "libgit2", "nixpkgs": "nixpkgs", + "nixpkgs-23-11": "nixpkgs-23-11", "nixpkgs-regression": "nixpkgs-regression", "pre-commit-hooks": "pre-commit-hooks" } diff --git a/flake.nix b/flake.nix index 896a2481c..b07e05684 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,7 @@ # https://nixpk.gs/pr-tracker.html?pr=291954 inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; + inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 4267db945..303fbc562 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -1,4 +1,4 @@ -{ lib, nixpkgs, nixpkgsFor }: +{ lib, nixpkgs, nixpkgsFor, self }: let @@ -60,7 +60,11 @@ in imports = [ ./remote-builds.nix ]; builders.config = { lib, pkgs, ... }: { imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; + nix.package = lib.mkForce ( + self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { + meta = o.meta // { knownVulnerabilities = []; }; + }) + ); }; }); @@ -82,7 +86,11 @@ in imports = [ ./remote-builds.nix ]; nodes.client = { lib, pkgs, ... }: { imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_13; + nix.package = lib.mkForce ( + self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { + meta = o.meta // { knownVulnerabilities = []; }; + }) + ); }; }); From efc2508e8bccc0ef990d0b4d48e6d452e1cc9d37 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 17:26:16 +0200 Subject: [PATCH 217/910] Refactor hydraJobs.tests.remoteBuilds_*_2_18 --- tests/nixos/default.nix | 141 ++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 93 deletions(-) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 303fbc562..78f91ae1b 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -37,6 +37,21 @@ let (lib.strings.escapeRegex "nix-${pkgs.nix.version}") ]; }; + + otherNixes.nix_2_3.setNixPackage = { lib, pkgs, ... }: { + imports = [ checkOverrideNixVersion ]; + nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; + }; + + otherNixes.nix_2_13.setNixPackage = { lib, pkgs, ... }: { + imports = [ checkOverrideNixVersion ]; + nix.package = lib.mkForce ( + self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { + meta = o.meta // { knownVulnerabilities = []; }; + }) + ); + }; + in { @@ -44,108 +59,48 @@ in remoteBuilds = runNixOSTestFor "x86_64-linux" ./remote-builds.nix; - # Test our Nix as a client against remotes that are older - - remoteBuilds_remote_2_3 = runNixOSTestFor "x86_64-linux" { - name = "remoteBuilds_remote_2_3"; - imports = [ ./remote-builds.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; - }; - }; - - remoteBuilds_remote_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuilds_remote_2_13"; - imports = [ ./remote-builds.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce ( - self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { - meta = o.meta // { knownVulnerabilities = []; }; - }) - ); - }; - }); - - # TODO: (nixpkgs update) remoteBuilds_remote_2_18 = ... - - # Test our Nix as a builder for clients that are older - - remoteBuilds_local_2_3 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuilds_local_2_3"; - imports = [ ./remote-builds.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; - }; - }); - - remoteBuilds_local_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuilds_local_2_13"; - imports = [ ./remote-builds.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce ( - self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { - meta = o.meta // { knownVulnerabilities = []; }; - }) - ); - }; - }); - - # TODO: (nixpkgs update) remoteBuilds_local_2_18 = ... - - # End remoteBuilds tests - remoteBuildsSshNg = runNixOSTestFor "x86_64-linux" ./remote-builds-ssh-ng.nix; - # Test our Nix as a client against remotes that are older - - remoteBuildsSshNg_remote_2_3 = runNixOSTestFor "x86_64-linux" { - name = "remoteBuildsSshNg_remote_2_3"; - imports = [ ./remote-builds-ssh-ng.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; +} +// lib.concatMapAttrs ( + nixVersion: { setNixPackage, ... }: + { + "remoteBuilds_remote_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + name = "remoteBuilds_remote_${nixVersion}"; + imports = [ ./remote-builds.nix ]; + builders.config = { lib, pkgs, ... }: { + imports = [ setNixPackage ]; + }; }; - }; - remoteBuildsSshNg_remote_2_13 = runNixOSTestFor "x86_64-linux" { - name = "remoteBuildsSshNg_remote_2_13"; - imports = [ ./remote-builds-ssh-ng.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_13; + "remoteBuilds_local_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + name = "remoteBuilds_local_${nixVersion}"; + imports = [ ./remote-builds.nix ]; + nodes.client = { lib, pkgs, ... }: { + imports = [ setNixPackage ]; + }; }; - }; - # TODO: (nixpkgs update) remoteBuildsSshNg_remote_2_18 = ... - - # Test our Nix as a builder for clients that are older - - # FIXME: these tests don't work yet - /* - remoteBuildsSshNg_local_2_3 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuildsSshNg_local_2_3"; - imports = [ ./remote-builds-ssh-ng.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; + "remoteBuildsSshNg_remote_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + name = "remoteBuildsSshNg_remote_${nixVersion}"; + imports = [ ./remote-builds-ssh-ng.nix ]; + builders.config = { lib, pkgs, ... }: { + imports = [ setNixPackage ]; + }; }; - }); - remoteBuildsSshNg_local_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuildsSshNg_local_2_13"; - imports = [ ./remote-builds-ssh-ng.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_13; - }; - }); + # FIXME: these tests don't work yet - # TODO: (nixpkgs update) remoteBuildsSshNg_local_2_18 = ... - */ + # "remoteBuildsSshNg_local_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + # name = "remoteBuildsSshNg_local_${nixVersion}"; + # imports = [ ./remote-builds-ssh-ng.nix ]; + # nodes.client = { lib, pkgs, ... }: { + # imports = [ overridingModule ]; + # }; + # }; + } +) otherNixes +// { nix-copy-closure = runNixOSTestFor "x86_64-linux" ./nix-copy-closure.nix; From 8a510f4ede604f91a2f0fea108cf50bf564a5ed1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 17:33:38 +0200 Subject: [PATCH 218/910] Add tests.remoteBuilds_*_2_18 --- tests/nixos/default.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 78f91ae1b..d8531b2c8 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -52,6 +52,11 @@ let ); }; + otherNixes.nix_2_18.setNixPackage = { lib, pkgs, ... }: { + imports = [ checkOverrideNixVersion ]; + nix.package = lib.mkForce pkgs.nixVersions.nix_2_18; + }; + in { From 754ea9058daebaaa85e7c6e683a54b948b96bdec Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 18:06:42 +0200 Subject: [PATCH 219/910] release notes: 2.23.0 --- doc/manual/rl-next/builtins-warn.md | 10 -- doc/manual/rl-next/consistent-nix-build.md | 6 -- doc/manual/rl-next/derivation-json-change.md | 12 --- .../rl-next/fix-silent-unknown-options.md | 28 ----- doc/manual/rl-next/nix-env-shell.md | 12 --- .../print-value-in-installable-flake-error.md | 18 ---- .../shallow-git-fetching-by-default.md | 12 --- doc/manual/rl-next/store-object-info.md | 11 -- doc/manual/rl-next/warn-large-path.md | 11 -- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/release-notes/rl-2.23.md | 102 ++++++++++++++++++ 11 files changed, 103 insertions(+), 120 deletions(-) delete mode 100644 doc/manual/rl-next/builtins-warn.md delete mode 100644 doc/manual/rl-next/consistent-nix-build.md delete mode 100644 doc/manual/rl-next/derivation-json-change.md delete mode 100644 doc/manual/rl-next/fix-silent-unknown-options.md delete mode 100644 doc/manual/rl-next/nix-env-shell.md delete mode 100644 doc/manual/rl-next/print-value-in-installable-flake-error.md delete mode 100644 doc/manual/rl-next/shallow-git-fetching-by-default.md delete mode 100644 doc/manual/rl-next/store-object-info.md delete mode 100644 doc/manual/rl-next/warn-large-path.md create mode 100644 doc/manual/src/release-notes/rl-2.23.md diff --git a/doc/manual/rl-next/builtins-warn.md b/doc/manual/rl-next/builtins-warn.md deleted file mode 100644 index f805e1610..000000000 --- a/doc/manual/rl-next/builtins-warn.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: "New builtin: `builtins.warn`" -issues: 306026 -prs: 10592 ---- - -`builtins.warn` behaves like `builtins.trace "warning: ${msg}"`, has an accurate log level, and is controlled by the options -[`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), -[`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and -[`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). diff --git a/doc/manual/rl-next/consistent-nix-build.md b/doc/manual/rl-next/consistent-nix-build.md deleted file mode 100644 index d5929dc8e..000000000 --- a/doc/manual/rl-next/consistent-nix-build.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -synopsis: Show all FOD errors with `nix build --keep-going` ---- - -`nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means -that if e.g. multiple FODs fail to build, all hash mismatches are displayed. diff --git a/doc/manual/rl-next/derivation-json-change.md b/doc/manual/rl-next/derivation-json-change.md deleted file mode 100644 index 2a1d40e83..000000000 --- a/doc/manual/rl-next/derivation-json-change.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: Modify `nix derivation {add,show}` JSON format -issues: 9866 -prs: 10722 ---- - -The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). -In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, -rather than one field with an arcane `:`-separated format. - -This JSON format is only used by the experimental `nix derivation` family of commands, at this time. -Future revisions are expected as the JSON format is still not entirely in compliance even after these changes. diff --git a/doc/manual/rl-next/fix-silent-unknown-options.md b/doc/manual/rl-next/fix-silent-unknown-options.md deleted file mode 100644 index 0977260ac..000000000 --- a/doc/manual/rl-next/fix-silent-unknown-options.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -synopsis: Warn on unknown settings anywhere in the command line -prs: 10701 ---- - -All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line. - -Before: - -```console -$ nix-instantiate --option foobar baz --expr '{}' -warning: unknown setting 'foobar' -$ nix-instantiate '{}' --option foobar baz --expr -$ nix eval --expr '{}' --option foobar baz -{ } -``` - -After: - -```console -$ nix-instantiate --option foobar baz --expr '{}' -warning: unknown setting 'foobar' -$ nix-instantiate '{}' --option foobar baz --expr -warning: unknown setting 'foobar' -$ nix eval --expr '{}' --option foobar baz -warning: unknown setting 'foobar' -{ } -``` diff --git a/doc/manual/rl-next/nix-env-shell.md b/doc/manual/rl-next/nix-env-shell.md deleted file mode 100644 index b2344417a..000000000 --- a/doc/manual/rl-next/nix-env-shell.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: "`nix env shell` is the new `nix shell`, and `nix shell` remains an accepted alias" -issues: 10504 -prs: 10807 ---- - -This is part of an effort to bring more structure to the CLI subcommands. - -`nix env` will be about the process environment. -Future commands may include `nix env run` and `nix env print-env`. - -It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. diff --git a/doc/manual/rl-next/print-value-in-installable-flake-error.md b/doc/manual/rl-next/print-value-in-installable-flake-error.md deleted file mode 100644 index bb35e252e..000000000 --- a/doc/manual/rl-next/print-value-in-installable-flake-error.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -synopsis: New-cli flake commands that expect derivations now print the failing value and its type -prs: 10778 ---- - -In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. - -Before: - -``` - error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path -```` - -After: - -``` - error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } -``` diff --git a/doc/manual/rl-next/shallow-git-fetching-by-default.md b/doc/manual/rl-next/shallow-git-fetching-by-default.md deleted file mode 100644 index 4d044f881..000000000 --- a/doc/manual/rl-next/shallow-git-fetching-by-default.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: "`fetchTree` now fetches git repositories shallowly by default" -prs: 10028 ---- - -`builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. - -Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. - -From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. - -The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. diff --git a/doc/manual/rl-next/store-object-info.md b/doc/manual/rl-next/store-object-info.md deleted file mode 100644 index ab8f5fec0..000000000 --- a/doc/manual/rl-next/store-object-info.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -synopsis: Store object info JSON format now uses `null` rather than omitting fields. -prs: 9995 ---- - -The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. -For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. -This makes records of this sort more self-describing, and easier to consume programmatically. - -We will follow this design principle going forward; -the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. diff --git a/doc/manual/rl-next/warn-large-path.md b/doc/manual/rl-next/warn-large-path.md deleted file mode 100644 index 5ee3e307f..000000000 --- a/doc/manual/rl-next/warn-large-path.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -synopsis: Large path warnings -prs: 10661 ---- - -Nix can now warn when evaluation of a Nix expression causes a large -path to be copied to the Nix store. The threshold for this warning can -be configured using [the `warn-large-path-threshold` -setting](@docroot@/command-ref/conf-file.md#warn-large-path-threshold), -e.g. `--warn-large-path-threshold 100M` will warn about paths larger -than 100 MiB. diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 18e7e8380..cb54a3822 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -125,6 +125,7 @@ - [C++ style guide](contributing/cxx.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} + - [Release 2.23 (2024-06-03)](release-notes/rl-2.23.md) - [Release 2.22 (2024-04-23)](release-notes/rl-2.22.md) - [Release 2.21 (2024-03-11)](release-notes/rl-2.21.md) - [Release 2.20 (2024-01-29)](release-notes/rl-2.20.md) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md new file mode 100644 index 000000000..c08782122 --- /dev/null +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -0,0 +1,102 @@ +# Release 2.23.0 (2024-06-03) + +- New builtin: `builtins.warn` [#306026](https://github.com/NixOS/nix/issues/306026) [#10592](https://github.com/NixOS/nix/pull/10592) + + `builtins.warn` behaves like `builtins.trace "warning: ${msg}"`, has an accurate log level, and is controlled by the options + [`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), + [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and + [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). + +- Show all FOD errors with `nix build --keep-going` + + `nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means + that if e.g. multiple FODs fail to build, all hash mismatches are displayed. + +- Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) + + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). + In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, + rather than one field with an arcane `:`-separated format. + + This JSON format is only used by the experimental `nix derivation` family of commands, at this time. + Future revisions are expected as the JSON format is still not entirely in compliance even after these changes. + +- Warn on unknown settings anywhere in the command line [#10701](https://github.com/NixOS/nix/pull/10701) + + All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line. + + Before: + + ```console + $ nix-instantiate --option foobar baz --expr '{}' + warning: unknown setting 'foobar' + $ nix-instantiate '{}' --option foobar baz --expr + $ nix eval --expr '{}' --option foobar baz + { } + ``` + + After: + + ```console + $ nix-instantiate --option foobar baz --expr '{}' + warning: unknown setting 'foobar' + $ nix-instantiate '{}' --option foobar baz --expr + warning: unknown setting 'foobar' + $ nix eval --expr '{}' --option foobar baz + warning: unknown setting 'foobar' + { } + ``` + +- `nix env shell` is the new `nix shell`, and `nix shell` remains an accepted alias [#10504](https://github.com/NixOS/nix/issues/10504) [#10807](https://github.com/NixOS/nix/pull/10807) + + This is part of an effort to bring more structure to the CLI subcommands. + + `nix env` will be about the process environment. + Future commands may include `nix env run` and `nix env print-env`. + + It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. + +- New-cli flake commands that expect derivations now print the failing value and its type [#10778](https://github.com/NixOS/nix/pull/10778) + + In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. + + Before: + + ``` + error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path + ```` + + After: + + ``` + error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } + ``` + +- `fetchTree` now fetches git repositories shallowly by default [#10028](https://github.com/NixOS/nix/pull/10028) + + `builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. + + Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. + + From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. + + The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. + +- Store object info JSON format now uses `null` rather than omitting fields. [#9995](https://github.com/NixOS/nix/pull/9995) + + The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. + For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. + This makes records of this sort more self-describing, and easier to consume programmatically. + + We will follow this design principle going forward; + the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. + +- Large path warnings [#10661](https://github.com/NixOS/nix/pull/10661) + + Nix can now warn when evaluation of a Nix expression causes a large + path to be copied to the Nix store. The threshold for this warning can + be configured using [the `warn-large-path-threshold` + setting](@docroot@/command-ref/conf-file.md#warn-large-path-threshold), + e.g. `--warn-large-path-threshold 100M` will warn about paths larger + than 100 MiB. + From 879089e80d91c68b4487d5540d170d565958c720 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 18:13:37 +0200 Subject: [PATCH 220/910] Edit release notes --- doc/manual/src/release-notes/rl-2.23.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index c08782122..0f799c1df 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -7,15 +7,15 @@ [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). -- Show all FOD errors with `nix build --keep-going` +- Make `nix build --keep-going` consistent with `nix-build --keep-going` - `nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means - that if e.g. multiple FODs fail to build, all hash mismatches are displayed. + This means that if e.g. multiple fixed-output derivations fail to + build, all hash mismatches are displayed. - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). - In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, + In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. This JSON format is only used by the experimental `nix derivation` family of commands, at this time. @@ -56,33 +56,33 @@ It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. -- New-cli flake commands that expect derivations now print the failing value and its type [#10778](https://github.com/NixOS/nix/pull/10778) +- Flake operations that expect derivations now print the failing value and its type [#10778](https://github.com/NixOS/nix/pull/10778) In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. Before: ``` - error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path + error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path ```` After: ``` - error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } + error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } ``` -- `fetchTree` now fetches git repositories shallowly by default [#10028](https://github.com/NixOS/nix/pull/10028) +- `fetchTree` now fetches Git repositories shallowly by default [#10028](https://github.com/NixOS/nix/pull/10028) - `builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. + `builtins.fetchTree` now clones Git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. - Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. + Previously, the default behavior was to clone the full history of a specific tag or branch (e.g. `ref`) and only afterwards extract the files of one specific revision. From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. -- Store object info JSON format now uses `null` rather than omitting fields. [#9995](https://github.com/NixOS/nix/pull/9995) +- Store object info JSON format now uses `null` rather than omitting fields [#9995](https://github.com/NixOS/nix/pull/9995) The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. From e6ba450ce2b2073ecb72195bb100d0143d1083ac Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:17:39 +0200 Subject: [PATCH 221/910] .clang-format: Remove duplicated key --- .clang-format | 1 - 1 file changed, 1 deletion(-) diff --git a/.clang-format b/.clang-format index f5d7fb711..3067583e1 100644 --- a/.clang-format +++ b/.clang-format @@ -31,4 +31,3 @@ AlwaysBreakBeforeMultilineStrings: true IndentPPDirectives: AfterHash PPIndentWidth: 2 BinPackArguments: false -BinPackParameters: false From 27f880c098c684d19df1cea34fe72388d00c9699 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:19:37 +0200 Subject: [PATCH 222/910] Format after clang-format update --- src/libexpr-c/nix_api_expr.h | 12 +++--- tests/unit/libstore/store-reference.cc | 42 +++++++++---------- .../libutil-support/tests/string_callback.hh | 2 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 0d324b148..ede7d099f 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -114,12 +114,12 @@ nix_err nix_value_call_multi( * * @see nix_value_call_multi */ -#define NIX_VALUE_CALL(context, state, value, fn, ...) \ - do { \ - Value * args_array[] = {__VA_ARGS__}; \ - size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ - nix_value_call_multi(context, state, fn, nargs, args_array, value); \ - } while (0) +#define NIX_VALUE_CALL(context, state, value, fn, ...) \ + do { \ + Value * args_array[] = {__VA_ARGS__}; \ + size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ + nix_value_call_multi(context, state, fn, nargs, args_array, value); \ + } while (0) /** * @brief Forces the evaluation of a Nix value. diff --git a/tests/unit/libstore/store-reference.cc b/tests/unit/libstore/store-reference.cc index 16e033ec4..052cd7bed 100644 --- a/tests/unit/libstore/store-reference.cc +++ b/tests/unit/libstore/store-reference.cc @@ -21,29 +21,29 @@ class StoreReferenceTest : public CharacterizationTest, public LibStoreTest } }; -#define URI_TEST_READ(STEM, OBJ) \ - TEST_F(StoreReferenceTest, PathInfo_##STEM##_from_uri) \ - { \ - readTest(#STEM, ([&](const auto & encoded) { \ - StoreReference expected = OBJ; \ - auto got = StoreReference::parse(encoded); \ - ASSERT_EQ(got, expected); \ - })); \ - } +#define URI_TEST_READ(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_from_uri) \ + { \ + readTest(#STEM, ([&](const auto & encoded) { \ + StoreReference expected = OBJ; \ + auto got = StoreReference::parse(encoded); \ + ASSERT_EQ(got, expected); \ + })); \ + } -#define URI_TEST_WRITE(STEM, OBJ) \ - TEST_F(StoreReferenceTest, PathInfo_##STEM##_to_uri) \ - { \ - writeTest( \ - #STEM, \ - [&]() -> StoreReference { return OBJ; }, \ - [](const auto & file) { return StoreReference::parse(readFile(file)); }, \ - [](const auto & file, const auto & got) { return writeFile(file, got.render()); }); \ - } +#define URI_TEST_WRITE(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_to_uri) \ + { \ + writeTest( \ + #STEM, \ + [&]() -> StoreReference { return OBJ; }, \ + [](const auto & file) { return StoreReference::parse(readFile(file)); }, \ + [](const auto & file, const auto & got) { return writeFile(file, got.render()); }); \ + } -#define URI_TEST(STEM, OBJ) \ - URI_TEST_READ(STEM, OBJ) \ - URI_TEST_WRITE(STEM, OBJ) +#define URI_TEST(STEM, OBJ) \ + URI_TEST_READ(STEM, OBJ) \ + URI_TEST_WRITE(STEM, OBJ) URI_TEST( auto, diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh index 3a3e545e9..a02ea3a1b 100644 --- a/tests/unit/libutil-support/tests/string_callback.hh +++ b/tests/unit/libutil-support/tests/string_callback.hh @@ -11,6 +11,6 @@ inline void * observe_string_cb_data(std::string & out) }; #define OBSERVE_STRING(str) \ - (nix_get_string_callback) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) + (nix_get_string_callback) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) } From 5ddc11d0eb99bb00a1c1d35f6256ff08f2043897 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:47:07 +0200 Subject: [PATCH 223/910] Update nixpkgs --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index ee976a3d9..f64e3ea37 100644 --- a/flake.lock +++ b/flake.lock @@ -69,11 +69,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1717413331, - "narHash": "sha256-oxsqQB/UwJNCTWUpndQgoz14731mgaWg/zYHrVwGoco=", + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88dca77be222aedd1f47d2cf0942dffefee76216", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", "type": "github" }, "original": { From d494ac15e2e356ea331638e1fa138ee3df0088f8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:38:38 +0200 Subject: [PATCH 224/910] Use Nixpkgs changelog-d --- flake.nix | 6 ++---- maintainers/release-notes | 2 +- misc/changelog-d.cabal.nix | 31 ------------------------------- misc/changelog-d.nix | 31 ------------------------------- 4 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 misc/changelog-d.cabal.nix delete mode 100644 misc/changelog-d.nix diff --git a/flake.nix b/flake.nix index b07e05684..a146079f4 100644 --- a/flake.nix +++ b/flake.nix @@ -159,8 +159,6 @@ }; }); - changelog-d-nix = final.buildPackages.callPackage ./misc/changelog-d.nix { }; - nix = let officialRelease = false; @@ -224,7 +222,7 @@ rl-next = let pkgs = nixpkgsFor.${system}.native; in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } '' - LANG=C.UTF-8 ${pkgs.changelog-d-nix}/bin/changelog-d ${./doc/manual/rl-next} >$out + LANG=C.UTF-8 ${pkgs.changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out ''; repl-completion = nixpkgsFor.${system}.native.callPackage ./tests/repl-completion.nix { }; } // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) { @@ -238,7 +236,7 @@ ); packages = forAllSystems (system: rec { - inherit (nixpkgsFor.${system}.native) nix changelog-d-nix; + inherit (nixpkgsFor.${system}.native) nix changelog-d; default = nix; } // (lib.optionalAttrs (builtins.elem system linux64BitSystems) { nix-static = nixpkgsFor.${system}.static.nix; diff --git a/maintainers/release-notes b/maintainers/release-notes index 2d84485c1..0fca5abf2 100755 --- a/maintainers/release-notes +++ b/maintainers/release-notes @@ -1,5 +1,5 @@ #!/usr/bin/env nix -#!nix shell .#changelog-d-nix --command bash +#!nix shell .#changelog-d --command bash # --- CONFIGURATION --- diff --git a/misc/changelog-d.cabal.nix b/misc/changelog-d.cabal.nix deleted file mode 100644 index 76f9353cd..000000000 --- a/misc/changelog-d.cabal.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ mkDerivation, aeson, base, bytestring, cabal-install-parsers -, Cabal-syntax, containers, directory, filepath, frontmatter -, generic-lens-lite, lib, mtl, optparse-applicative, parsec, pretty -, regex-applicative, text, pkgs -}: -let rev = "f30f6969e9cd8b56242309639d58acea21c99d06"; -in -mkDerivation { - pname = "changelog-d"; - version = "0.1"; - src = pkgs.fetchurl { - name = "changelog-d-${rev}.tar.gz"; - url = "https://codeberg.org/roberth/changelog-d/archive/${rev}.tar.gz"; - hash = "sha256-8a2+i5u7YoszAgd5OIEW0eYUcP8yfhtoOIhLJkylYJ4="; - } // { inherit rev; }; - isLibrary = false; - isExecutable = true; - libraryHaskellDepends = [ - aeson base bytestring cabal-install-parsers Cabal-syntax containers - directory filepath frontmatter generic-lens-lite mtl parsec pretty - regex-applicative text - ]; - executableHaskellDepends = [ - base bytestring Cabal-syntax directory filepath - optparse-applicative - ]; - doHaddock = false; - description = "Concatenate changelog entries into a single one"; - license = lib.licenses.gpl3Plus; - mainProgram = "changelog-d"; -} diff --git a/misc/changelog-d.nix b/misc/changelog-d.nix deleted file mode 100644 index 1b20f4596..000000000 --- a/misc/changelog-d.nix +++ /dev/null @@ -1,31 +0,0 @@ -# Taken temporarily from -{ - callPackage, - lib, - haskell, - haskellPackages, -}: - -let - hsPkg = haskellPackages.callPackage ./changelog-d.cabal.nix { }; - - addCompletions = haskellPackages.generateOptparseApplicativeCompletions ["changelog-d"]; - - haskellModifications = - lib.flip lib.pipe [ - addCompletions - haskell.lib.justStaticExecutables - ]; - - mkDerivationOverrides = finalAttrs: oldAttrs: { - - version = oldAttrs.version + "-git-${lib.strings.substring 0 7 oldAttrs.src.rev}"; - - meta = oldAttrs.meta // { - homepage = "https://codeberg.org/roberth/changelog-d"; - maintainers = [ lib.maintainers.roberth ]; - }; - - }; -in - (haskellModifications hsPkg).overrideAttrs mkDerivationOverrides From 5d460d563e057957e9089302cdf59bd32352f028 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:09:38 +0200 Subject: [PATCH 225/910] TMP: Disable tests.setuid.i686-linux Temporarily(?) blocked on https://github.com/NixOS/nixpkgs/pull/297475#issuecomment-2145589501 --- tests/nixos/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index d8531b2c8..710f8a273 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -124,7 +124,7 @@ in containers = runNixOSTestFor "x86_64-linux" ./containers/containers.nix; setuid = lib.genAttrs - ["i686-linux" "x86_64-linux"] + ["x86_64-linux"] (system: runNixOSTestFor system ./setuid.nix); fetch-git = runNixOSTestFor "x86_64-linux" ./fetch-git; From 9019b7a37a7e1ef8ede59a8a643286189069bdb5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:56:04 +0200 Subject: [PATCH 226/910] doc/rl-2.23.md: Fix broken link --- doc/manual/src/release-notes/rl-2.23.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index 0f799c1df..71dc4a91a 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -14,7 +14,7 @@ - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) - The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline#returning-future-proof-json). In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. From e0885fc216b51dbea5f589d2b5c593be6ae91805 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 20:01:15 +0200 Subject: [PATCH 227/910] Fix link --- doc/manual/src/release-notes/rl-2.23.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index 71dc4a91a..3c59b8583 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -14,7 +14,7 @@ - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) - The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline#returning-future-proof-json). + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline.md#returning-future-proof-json). In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. From 06be6812a674e23abc9e24fa2fbd7a7c20684c74 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 3 Jun 2024 14:08:37 -0400 Subject: [PATCH 228/910] Create and install a `nix-util.pc` Before, `-lnixutil` was just stuck in `nix-store.pc`, but that doesn't seem so nice. This prepares us to distribute `libnixutil` in a separate package if we want, but it should be a good change either way. I suspect it wasn't done before because libutil was an extra unstable interface, but I don't think we need worry about that. *All* the C++ is less stable than the C (or that's the goal at least). For what it's worth, Lix also created this pkg-config file *en passant* during their rename: https://git.lix.systems/lix-project/lix/commit/c97e17144e0d0b666d7b79d8b4b0d581bfdf373b#diff-3c4f60cc44a0e35444c7f45331cfa50f76637118 --- src/libstore/nix-store.pc.in | 3 ++- src/libutil/local.mk | 2 ++ src/libutil/nix-util.pc.in | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/libutil/nix-util.pc.in diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in index dc42d0bca..cd3f2b8da 100644 --- a/src/libstore/nix-store.pc.in +++ b/src/libstore/nix-store.pc.in @@ -5,5 +5,6 @@ includedir=@includedir@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixstore -lnixutil +Requires: nix-util +Libs: -L${libdir} -lnixstore Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libutil/local.mk b/src/libutil/local.mk index 5cd8d4ac8..e9b498e65 100644 --- a/src/libutil/local.mk +++ b/src/libutil/local.mk @@ -40,3 +40,5 @@ $(foreach i, $(wildcard $(d)/signature/*.hh), \ ifeq ($(HAVE_LIBCPUID), 1) libutil_LDFLAGS += -lcpuid endif + +$(eval $(call install-file-in, $(buildprefix)$(d)/nix-util.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libutil/nix-util.pc.in b/src/libutil/nix-util.pc.in new file mode 100644 index 000000000..85bb1e70e --- /dev/null +++ b/src/libutil/nix-util.pc.in @@ -0,0 +1,9 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix +Description: Nix Package Manager +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnixutil +Cflags: -I${includedir}/nix -std=c++2a From bf72b78ef2110f4bda6105b8adff131dc9435bff Mon Sep 17 00:00:00 2001 From: Eli Flanagan <163922304+eflanagan0@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:22:50 -0400 Subject: [PATCH 229/910] docs: fix python nix-shell example (#10841) * docs: fix python nix-shell example This Python code snippet depended on Python 2 which has been marked as insecure in 24.05. I modernized the example so new users will not be surprised upon copying and pasting the snippet for exploration. Co-authored-by: John Ericson --- doc/manual/src/command-ref/nix-shell.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index 1eaf3c36a..a4dd051fc 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -202,14 +202,14 @@ For example, here is a Python script that depends on Python and the ```python #! /usr/bin/env nix-shell -#! nix-shell -i python --packages python pythonPackages.prettytable +#! nix-shell -i python3 --packages python3 python3Packages.prettytable import prettytable # Print a simple table. t = prettytable.PrettyTable(["N", "N^2"]) for n in range(1, 10): t.add_row([n, n * n]) -print t +print(t) ``` Similarly, the following is a Perl script that specifies that it From 214051ba79b990db363cef7f5d892d8b2a6d516f Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 4 Jun 2024 09:41:04 +0200 Subject: [PATCH 230/910] clarify not on `nix_value_force` (#10842) * clarify not on `nix_value_force` Co-authored-by: Valentin Gagarin --- src/libexpr-c/nix_api_expr.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 0d324b148..13787271b 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -129,8 +129,7 @@ nix_err nix_value_call_multi( * * This function converts these Values into their final type. * - * @note You don't need this function for basic API usage very often, since all functions that return a `Value` call it - * for you. This function is mainly needed before calling @ref getters. + * @note This function is mainly needed before calling @ref getters, but not for API calls that return a `Value`. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. From 65884201035a3771040d462538072b2d7a0a2c63 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 10:17:58 +0200 Subject: [PATCH 231/910] Bring back FreeBSD --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index a146079f4..5dbc554fc 100644 --- a/flake.nix +++ b/flake.nix @@ -46,6 +46,7 @@ "armv7l-unknown-linux-gnueabihf" "riscv64-unknown-linux-gnu" "x86_64-unknown-netbsd" + "x86_64-unknown-freebsd" "x86_64-w64-mingw32" ]; From 4e0d058fc314d7fabeef97e9193b493e975ca6db Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 10:18:22 +0200 Subject: [PATCH 232/910] eval.cc: Fix for Windows --- src/libexpr/eval.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 1eb60a1b8..7c676829e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,8 @@ #define GC_INCLUDE_NEW +#include + #include #include #include From 8f1a26667e7c5aeac5522ae706cf511b2d725eaa Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Tue, 4 Jun 2024 19:35:40 +0530 Subject: [PATCH 233/910] add call to `checkInterrupt` in a bunch of places This brings back the old behaviour. We check for interrupts in places that may iterate over wide directories. --- src/libcmd/repl.cc | 1 + src/libstore/builtins/buildenv.cc | 2 ++ src/libstore/gc.cc | 5 ++++- src/libstore/globals.cc | 6 +++++- src/libstore/local-binary-cache-store.cc | 2 ++ src/libstore/local-store.cc | 2 ++ src/libstore/posix-fs-canonicalise.cc | 4 +++- src/libstore/profiles.cc | 2 ++ src/libutil/linux/cgroup.cc | 2 ++ src/libutil/posix-source-accessor.cc | 1 + src/libutil/unix/file-descriptor.cc | 1 + src/nix/flake.cc | 2 ++ src/nix/run.cc | 2 ++ 13 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a069dd52c..603a543bc 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -260,6 +260,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) auto dir = std::string(cur, 0, slash); auto prefix2 = std::string(cur, slash + 1); for (auto & entry : std::filesystem::directory_iterator{dir == "" ? "/" : dir}) { + checkInterrupt(); auto name = entry.path().filename().string(); if (name[0] != '.' && hasPrefix(name, prefix2)) completions.insert(prev + entry.path().string()); diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index ab35c861d..0f7bcd99b 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -1,5 +1,6 @@ #include "buildenv.hh" #include "derivations.hh" +#include "signals.hh" #include #include @@ -30,6 +31,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, } for (const auto & ent : srcFiles) { + checkInterrupt(); auto name = ent.path().filename(); if (name.string()[0] == '.') /* not matched by glob */ diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 8286dff27..c6ecbd783 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -162,6 +162,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) /* Read the `temproots' directory for per-process temporary root files. */ for (auto & i : std::filesystem::directory_iterator{tempRootsDir}) { + checkInterrupt(); auto name = i.path().filename().string(); if (name[0] == '.') { // Ignore hidden files. Some package managers (notably portage) create @@ -228,8 +229,10 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R type = std::filesystem::symlink_status(path).type(); if (type == std::filesystem::file_type::directory) { - for (auto & i : std::filesystem::directory_iterator{path}) + for (auto & i : std::filesystem::directory_iterator{path}) { + checkInterrupt(); findRoots(i.path().string(), i.symlink_status().type(), roots); + } } else if (type == std::filesystem::file_type::symlink) { diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index d9cab2fb8..88f899dbf 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -4,6 +4,7 @@ #include "args.hh" #include "abstract-setting-to-json.hh" #include "compute-levels.hh" +#include "signals.hh" #include #include @@ -346,14 +347,17 @@ void initPlugins() std::vector pluginFiles; try { auto ents = std::filesystem::directory_iterator{pluginFile}; - for (const auto & ent : ents) + for (const auto & ent : ents) { + checkInterrupt(); pluginFiles.emplace_back(ent.path()); + } } catch (std::filesystem::filesystem_error & e) { if (e.code() != std::errc::not_a_directory) throw; pluginFiles.emplace_back(pluginFile); } for (const auto & file : pluginFiles) { + checkInterrupt(); /* handle is purposefully leaked as there may be state in the DSO needed by the action of the plugin. */ #ifndef _WIN32 // TODO implement via DLL loading on Windows diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 87a6026f1..dde25937c 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -1,6 +1,7 @@ #include "binary-cache-store.hh" #include "globals.hh" #include "nar-info-disk-cache.hh" +#include "signals.hh" #include @@ -84,6 +85,7 @@ protected: StorePathSet paths; for (auto & entry : std::filesystem::directory_iterator{binaryCacheDir}) { + checkInterrupt(); auto name = entry.path().filename().string(); if (name.size() != 40 || !hasSuffix(name, ".narinfo")) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index dd06e5b65..cbe3c6fe8 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1407,6 +1407,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) printInfo("checking link hashes..."); for (auto & link : std::filesystem::directory_iterator{linksDir}) { + checkInterrupt(); auto name = link.path().filename(); printMsg(lvlTalkative, "checking contents of '%s'", name); PosixSourceAccessor accessor; @@ -1499,6 +1500,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair invalid states. */ for (auto & i : std::filesystem::directory_iterator{realStoreDir.to_string()}) { + checkInterrupt(); try { storePathsInStoreDir.insert({i.path().filename().string()}); } catch (BadStorePath &) { } diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc index d8bae13f5..8cb13d810 100644 --- a/src/libstore/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -144,13 +144,15 @@ static void canonicalisePathMetaData_( #endif if (S_ISDIR(st.st_mode)) { - for (auto & i : std::filesystem::directory_iterator{path}) + for (auto & i : std::filesystem::directory_iterator{path}) { + checkInterrupt(); canonicalisePathMetaData_( i.path().string(), #ifndef _WIN32 uidRange, #endif inodesSeen); + } } } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index d0da96262..46efedfe3 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -1,4 +1,5 @@ #include "profiles.hh" +#include "signals.hh" #include "store-api.hh" #include "local-fs-store.hh" #include "users.hh" @@ -38,6 +39,7 @@ std::pair> findGenerations(Path pro auto profileName = std::string(baseNameOf(profile)); for (auto & i : std::filesystem::directory_iterator{profileDir}) { + checkInterrupt(); if (auto n = parseName(profileName, i.path().filename().string())) { auto path = i.path().string(); gens.push_back({ diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index ec4077478..140ff4566 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -1,4 +1,5 @@ #include "cgroup.hh" +#include "signals.hh" #include "util.hh" #include "file-system.hh" #include "finally.hh" @@ -65,6 +66,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ for (auto & entry : std::filesystem::directory_iterator{cgroup}) { + checkInterrupt(); if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue; destroyCgroup(cgroup / entry.path().filename(), false); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 225fc852c..bcf453cba 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -133,6 +133,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & assertNoSymlinks(path); DirEntries res; for (auto & entry : std::filesystem::directory_iterator{makeAbsPath(path)}) { + checkInterrupt(); auto type = [&]() -> std::optional { std::filesystem::file_type nativeType; try { diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 84a33af81..a74f16ce1 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -125,6 +125,7 @@ void closeMostFDs(const std::set & exceptions) #if __linux__ try { for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) { + checkInterrupt(); auto fd = std::stoi(s.path().filename()); if (!exceptions.count(fd)) { debug("closing leaked FD %d", fd); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 78a8a55c3..feacdbcb3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -7,6 +7,7 @@ #include "eval-settings.hh" #include "flake/flake.hh" #include "get-drvs.hh" +#include "signals.hh" #include "store-api.hh" #include "derivations.hh" #include "outputs-spec.hh" @@ -867,6 +868,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand createDirs(to); for (auto & entry : std::filesystem::directory_iterator{from}) { + checkInterrupt(); auto from2 = entry.path().string(); auto to2 = to + "/" + entry.path().filename().string(); auto st = lstat(from2); diff --git a/src/nix/run.cc b/src/nix/run.cc index cc999ddf4..1ecc83fba 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -3,6 +3,7 @@ #include "command-installable-value.hh" #include "common-args.hh" #include "shared.hh" +#include "signals.hh" #include "store-api.hh" #include "derivations.hh" #include "local-fs-store.hh" @@ -249,6 +250,7 @@ void chrootHelper(int argc, char * * argv) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); for (auto entry : std::filesystem::directory_iterator{"/"}) { + checkInterrupt(); auto src = entry.path().string(); Path dst = tmpDir + "/" + entry.path().filename().string(); if (pathExists(dst)) continue; From 7d295c594e1d100c9dcbdd89198eb3882856cfcb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jun 2024 16:05:56 +0200 Subject: [PATCH 234/910] Make EvalState::srcToStore thread-safe --- src/libexpr/eval.cc | 8 ++++---- src/libexpr/eval.hh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6a38bbe45..7dbb467e5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2415,10 +2415,10 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat if (nix::isDerivation(path.path.abs())) error("file names are not allowed to end in '%1%'", drvExtension).debugThrow(); - auto i = srcToStore.find(path); + auto dstPathCached = get(*srcToStore.lock(), path); - auto dstPath = i != srcToStore.end() - ? i->second + auto dstPath = dstPathCached + ? *dstPathCached : [&]() { auto dstPath = fetchToStore( *store, @@ -2429,7 +2429,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat nullptr, repair); allowPath(dstPath); - srcToStore.insert_or_assign(path, dstPath); + srcToStore.lock()->try_emplace(path, dstPath); printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath)); return dstPath; }(); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 06a687620..dac763268 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -306,7 +306,7 @@ private: /* Cache for calls to addToStore(); maps source paths to the store paths. */ - std::map srcToStore; + Sync> srcToStore; /** * A cache from path names to parse trees. From fbbca59453fcbc91ff63256b617523b5962b28f8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 May 2024 19:18:38 +0200 Subject: [PATCH 235/910] Make RegexCache thread-safe --- src/libexpr/primops.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 22d7f188f..9177b0a2f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4062,17 +4062,23 @@ static RegisterPrimOp primop_convertHash({ struct RegexCache { - // TODO use C++20 transparent comparison when available - std::unordered_map cache; - std::list keys; + struct State + { + // TODO use C++20 transparent comparison when available + std::unordered_map cache; + std::list keys; + }; + + Sync state_; std::regex get(std::string_view re) { - auto it = cache.find(re); - if (it != cache.end()) + auto state(state_.lock()); + auto it = state->cache.find(re); + if (it != state->cache.end()) return it->second; - keys.emplace_back(re); - return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second; + state->keys.emplace_back(re); + return state->cache.emplace(state->keys.back(), std::regex(state->keys.back(), std::regex::extended)).first->second; } }; From 49c6f349116918d052a01b3c13cecbeaeeae657c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 4 Jun 2024 21:55:05 +0200 Subject: [PATCH 236/910] docs: fixup description of builtins.importNative (#10810) There was an argument missing and the fact that the imported function is called. --- src/libexpr/eval-settings.hh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index f1fb539bd..d01de980f 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -19,18 +19,18 @@ struct EvalSettings : Config Enable built-in functions that allow executing native code. In particular, this adds: - - `builtins.importNative` *path* - - Load a dynamic shared object (DSO) at *path* which exposes a function pointer to a procedure that initialises a Nix language value, and return that value. - The procedure must have the following signature: + - `builtins.importNative` *path* *symbol* + + Opens dynamic shared object (DSO) at *path*, loads the function with the symbol name *symbol* from it and runs it. + The loaded function must have the following signature: ```cpp extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); - ``` - + ``` + The [Nix C++ API documentation](@docroot@/contributing/documentation.md#api-documentation) has more details on evaluator internals. - + - `builtins.exec` *arguments* - + Execute a program, where *arguments* are specified as a list of strings, and parse its output as a Nix expression. )"}; From 80ba7778e730eccc1afff5a8f53887001bd4ee78 Mon Sep 17 00:00:00 2001 From: Enno Richter Date: Wed, 5 Jun 2024 07:31:18 +0200 Subject: [PATCH 237/910] flake check: Recognize well known homeModule/homeModules attributes --- src/nix/flake.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 755ead19f..fb7ea6211 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -771,6 +771,8 @@ struct CmdFlakeCheck : FlakeCommand || name == "flakeModules" || name == "herculesCI" || name == "homeConfigurations" + || name == "homeModule" + || name == "homeModules" || name == "nixopsConfigurations" ) // Known but unchecked community attribute From d2eeabf3e68f65493a8bfeb2ba762c37fcf60e15 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Jun 2024 16:17:24 +0200 Subject: [PATCH 238/910] PackageInfo::queryDrvPath(): Don't dereference an empty optional Fixes a regression introduced in f923ed6b6a7318e8fc77e8d3aeda6796671f67cb. https://hydra.nixos.org/build/262267313 --- src/libexpr/get-drvs.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index ed16a51a1..0d2aecc58 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -82,8 +82,7 @@ std::optional PackageInfo::queryDrvPath() const } else drvPath = {std::nullopt}; } - drvPath.value_or(std::nullopt); - return *drvPath; + return drvPath.value_or(std::nullopt); } From 3e72ed9743a74f210e9dd493c914a2190345751f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Jun 2024 16:19:01 +0200 Subject: [PATCH 239/910] Typo --- src/libexpr/print.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 920490cfa..10fe7923f 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -278,7 +278,7 @@ private: storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"); } - /* This unforutately breaks printing nested values because of + /* This unfortunately breaks printing nested values because of how the pretty printer is used (when pretting printing and warning to same terminal / std stream). */ #if 0 From 162d94d975328bce0b5f04f5555b760b2222cac4 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 30 May 2024 00:22:25 +0200 Subject: [PATCH 240/910] tests/nixos: make the tarball-flakes test better reflect real use cases In most real world cases, the Link header is set on the redirect, not on the final file. This regressed in Lix earlier and while new unit tests were added to cover it, this integration test should probably have also caught it. Source: https://git.lix.systems/lix-project/lix/commit/a3256a93759206dc64e597e2ad790ce582e84cf3 --- tests/nixos/tarball-flakes.nix | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index e30d15739..2e7f98b5e 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -5,7 +5,7 @@ let root = pkgs.runCommand "nixpkgs-flake" {} '' - mkdir -p $out/stable + mkdir -p $out/{stable,tags} set -x dir=nixpkgs-${nixpkgs.shortRev} @@ -14,9 +14,13 @@ let find $dir -print0 | xargs -0 touch -h -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} -- tar cfz $out/stable/${nixpkgs.rev}.tar.gz $dir --hard-dereference - echo 'Redirect "/latest.tar.gz" "/stable/${nixpkgs.rev}.tar.gz"' > $out/.htaccess - - echo 'Header set Link "; rel=\"immutable\""' > $out/stable/.htaccess + # Set the "Link" header on the redirect but not the final response to + # simulate an S3-like serving environment where the final host cannot set + # arbitrary headers. + cat >$out/tags/.htaccess <; rel=\"immutable\"" + EOF ''; in @@ -59,7 +63,7 @@ in machine.wait_for_unit("httpd.service") - out = machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz") + out = machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz") print(out) info = json.loads(out) @@ -71,14 +75,15 @@ in assert info["revCount"] == 1234 # Check that fetching with rev/revCount/narHash succeeds. - machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?rev=" + info["revision"]) - machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?revCount=" + str(info["revCount"])) - machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?narHash=" + info["locked"]["narHash"]) + + machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?rev=" + info["revision"]) + machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?revCount=" + str(info["revCount"])) + machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?narHash=" + info["locked"]["narHash"]) # Check that fetching fails if we provide incorrect attributes. - machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?rev=493300eb13ae6fb387fbd47bf54a85915acc31c0") - machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?revCount=789") - machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?narHash=sha256-tbudgBSg+bHWHiHnlteNzN8TUvI80ygS9IULh4rklEw=") + machine.fail("nix flake metadata --json http://localhost/tags/latest.tar.gz?rev=493300eb13ae6fb387fbd47bf54a85915acc31c0") + machine.fail("nix flake metadata --json http://localhost/tags/latest.tar.gz?revCount=789") + machine.fail("nix flake metadata --json http://localhost/tags/latest.tar.gz?narHash=sha256-tbudgBSg+bHWHiHnlteNzN8TUvI80ygS9IULh4rklEw=") ''; } From e291087747146ab862edf0c6a88b12f355fb497c Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Thu, 4 Apr 2024 17:31:01 +0200 Subject: [PATCH 241/910] libutil: guard Finally against invalid exception throws throwing exceptions is fine, but throwing exceptions during exception handling is hard enough to do correctly that we should just forbid it entirely out of an overabundance of caution. in cases where terminate is the correct answer the users of Finally must call it manually now. Source: https://git.lix.systems/lix-project/lix/commit/6c777476c9e97abfc5232f0707985caf6df2baea --- src/libutil/finally.hh | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh index f9f0195a1..bda4227e6 100644 --- a/src/libutil/finally.hh +++ b/src/libutil/finally.hh @@ -2,6 +2,8 @@ ///@file #include +#include +#include /** * A trivial class to run a function at the end of a scope. @@ -21,5 +23,25 @@ public: Finally(Finally &&other) : fun(std::move(other.fun)) { other.movedFrom = true; } - ~Finally() { if (!movedFrom) fun(); } + ~Finally() noexcept(false) + { + try { + if (!movedFrom) + fun(); + } catch (...) { + // finally may only throw an exception if exception handling is not already + // in progress. if handling *is* in progress we have to return cleanly here + // but are still prohibited from doing so since eating the exception would, + // in almost all cases, mess up error handling even more. the only good way + // to handle this is to abort entirely and leave a message, so we'll assert + // (and rethrow anyway, just as a defense against possible NASSERT builds.) + if (std::uncaught_exceptions()) { + assert(false && + "Finally function threw an exception during exception handling. " + "this is not what you want, please use some other methods (like " + "std::promise or async) instead."); + } + throw; + } + } }; From 2f39caf1806a595eb5e24f89bb9c38681c138360 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Jan 2024 15:05:46 +0100 Subject: [PATCH 242/910] Sync: Add support for shared locks --- src/libutil/sync.hh | 53 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index 47e4512b1..20dd6ee52 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -24,8 +25,8 @@ namespace nix { * Here, "data" is automatically unlocked when "data_" goes out of * scope. */ -template -class Sync +template +class SyncBase { private: M mutex; @@ -33,23 +34,22 @@ private: public: - Sync() { } - Sync(const T & data) : data(data) { } - Sync(T && data) noexcept : data(std::move(data)) { } + SyncBase() { } + SyncBase(const T & data) : data(data) { } + SyncBase(T && data) noexcept : data(std::move(data)) { } + template class Lock { - private: - Sync * s; - std::unique_lock lk; - friend Sync; - Lock(Sync * s) : s(s), lk(s->mutex) { } + protected: + SyncBase * s; + L lk; + friend SyncBase; + Lock(SyncBase * s) : s(s), lk(s->mutex) { } public: Lock(Lock && l) : s(l.s) { abort(); } Lock(const Lock & l) = delete; ~Lock() { } - T * operator -> () { return &s->data; } - T & operator * () { return s->data; } void wait(std::condition_variable & cv) { @@ -83,7 +83,34 @@ public: } }; - Lock lock() { return Lock(this); } + struct WriteLock : Lock + { + T * operator -> () { return &WriteLock::s->data; } + T & operator * () { return WriteLock::s->data; } + }; + + /** + * Acquire write (exclusive) access to the inner value. + */ + WriteLock lock() { return WriteLock(this); } + + struct ReadLock : Lock + { + const T * operator -> () { return &ReadLock::s->data; } + const T & operator * () { return ReadLock::s->data; } + }; + + /** + * Acquire read access to the inner value. When using + * `std::shared_mutex`, this will use a shared lock. + */ + ReadLock read() const { return ReadLock(const_cast(this)); } }; +template +using Sync = SyncBase, std::unique_lock>; + +template +using SharedSync = SyncBase, std::shared_lock>; + } From fd9e49480a40989c407c1de32b86ec35f7b37a87 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Jun 2024 22:24:00 +0200 Subject: [PATCH 243/910] PosixSourceAccessor: Use SharedSync --- src/libutil/posix-source-accessor.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index bcf453cba..35047f89e 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -90,14 +90,14 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path) std::optional PosixSourceAccessor::cachedLstat(const CanonPath & path) { - static Sync>> _cache; + static SharedSync>> _cache; // Note: we convert std::filesystem::path to Path because the // former is not hashable on libc++. Path absPath = makeAbsPath(path).string(); { - auto cache(_cache.lock()); + auto cache(_cache.read()); auto i = cache->find(absPath); if (i != cache->end()) return i->second; } From 7e6a7c925858c3720dec1864ed26e62718a36652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 10:32:38 +0200 Subject: [PATCH 244/910] make it possible to push to different docker registries in forks --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4612b4ef5..93c68b96c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,8 +127,8 @@ jobs: authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L - run: docker load -i ./result/image.tar.gz - - run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION - - run: docker tag nix:$NIX_VERSION nixos/nix:master + - run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION + - run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:master # We'll deploy the newly built image to both Docker Hub and Github Container Registry. # # Push to Docker Hub first @@ -137,8 +137,8 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - run: docker push nixos/nix:$NIX_VERSION - - run: docker push nixos/nix:master + - run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION + - run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/nix:master # Push to GitHub Container Registry as well - name: Login to GitHub Container Registry uses: docker/login-action@v3 From e505434332423a0d04331750e8ec1ffeb105b861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 08:50:05 +0200 Subject: [PATCH 245/910] document how to test github ci fully in your own fork --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93c68b96c..1aa3b776e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,15 @@ jobs: authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - run: nix --experimental-features 'nix-command flakes' flake check -L + # Steps to test CI automation in your own fork. + # Cachix: + # 1. Sign-up for https://www.cachix.org/ + # 2. Create a cache for $githubuser-nix-install-tests + # 3. Create a cachix auth token and save it in https://github.com/$githubuser/nix/settings/secrets/actions in "Repository secrets" as CACHIX_AUTH_TOKEN + # Dockerhub: + # 1. Sign-up for https://hub.docker.com/ + # 2. Store your dockerhub username as DOCKERHUB_USERNAME in "Repository secrets" of your fork repository settings (https://github.com/$githubuser/nix/settings/secrets/actions) + # 3. Create an access token in https://hub.docker.com/settings/security and store it as DOCKERHUB_TOKEN in "Repository secrets" of your fork check_secrets: permissions: contents: none From 25b0242ca6aee3484111739e95b6788ea56d1a64 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 9 Jun 2024 19:49:39 +0530 Subject: [PATCH 246/910] `std::filesystem::create_directories` for createDirs The implementation of `nix::createDirs` allows it to be a simple wrapper around `std::filesystem::create_directories` as its return value is not used anywhere. --- src/libcmd/repl-interacter.cc | 4 ++-- src/libutil/file-system.cc | 25 ++----------------------- src/libutil/file-system.hh | 7 +++---- 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index eb4361e25..254a86d7b 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -107,8 +107,8 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter rl_readline_name = "nix-repl"; try { createDirs(dirOf(historyFile)); - } catch (SystemError & e) { - logWarning(e.info()); + } catch (std::filesystem::filesystem_error & e) { + warn(e.what()); } #ifndef USE_READLINE el_hist_size = 1000; diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 919bf5d50..cd5db31bb 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -413,30 +413,9 @@ void deletePath(const fs::path & path) } -Paths createDirs(const Path & path) +void createDirs(const Path & path) { - Paths created; - if (path == "/") return created; - - struct stat st; - if (STAT(path.c_str(), &st) == -1) { - created = createDirs(dirOf(path)); - if (mkdir(path.c_str() -#ifndef _WIN32 // TODO abstract mkdir perms for Windows - , 0777 -#endif - ) == -1 && errno != EEXIST) - throw SysError("creating directory '%1%'", path); - st = STAT(path); - created.push_back(path); - } - - if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1) - throw SysError("statting symlink '%1%'", path); - - if (!S_ISDIR(st.st_mode)) throw Error("'%1%' is not a directory", path); - - return created; + fs::create_directories(path); } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index c6b6ecedb..9405cda0c 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -148,11 +148,10 @@ void deletePath(const std::filesystem::path & path); void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed); /** - * Create a directory and all its parents, if necessary. Returns the - * list of created directories, in order of creation. + * Create a directory and all its parents, if necessary. */ -Paths createDirs(const Path & path); -inline Paths createDirs(PathView path) +void createDirs(const Path & path); +inline void createDirs(PathView path) { return createDirs(Path(path)); } From 372d5a441e1a6fe61740609ac622f53dc77229a3 Mon Sep 17 00:00:00 2001 From: Kirill Radzikhovskyy Date: Mon, 10 Jun 2024 11:17:41 +1000 Subject: [PATCH 247/910] darwin: allow ipc-sysv* in sandbox --- src/libstore/unix/build/sandbox-defaults.sb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libstore/unix/build/sandbox-defaults.sb b/src/libstore/unix/build/sandbox-defaults.sb index 2ad5fb616..6da01b735 100644 --- a/src/libstore/unix/build/sandbox-defaults.sb +++ b/src/libstore/unix/build/sandbox-defaults.sb @@ -17,6 +17,9 @@ R""( ; Allow POSIX semaphores and shared memory. (allow ipc-posix*) +; Allow SYSV semaphores and shared memory. +(allow ipc-sysv*) + ; Allow socket creation. (allow system-socket) From 7a21432e7774617731556d844d05686a8b3ff46d Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Mon, 10 Jun 2024 11:30:39 +0530 Subject: [PATCH 248/910] fix: catch `filesystem_error` thrown by `createDirs` --- src/libstore/store-api.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index c67ccd7d4..ed5275377 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -19,6 +19,7 @@ #include "signals.hh" #include "users.hh" +#include #include using json = nlohmann::json; @@ -1303,7 +1304,7 @@ ref openStore(StoreReference && storeURI) if (!pathExists(chrootStore)) { try { createDirs(chrootStore); - } catch (Error & e) { + } catch (std::filesystem::filesystem_error & e) { return std::make_shared(params); } warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); From 4755e133c410aa2611c23c3f8e106b4194da0c5e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 12:37:17 +0200 Subject: [PATCH 249/910] Fix warning --- src/libexpr/eval-cache.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index cac985829..b1911e3a4 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -31,7 +31,7 @@ struct CachedEvalError : EvalError class EvalCache : public std::enable_shared_from_this { friend class AttrCursor; - friend class CachedEvalError; + friend struct CachedEvalError; std::shared_ptr db; EvalState & state; @@ -87,7 +87,7 @@ typedef std::variant< class AttrCursor : public std::enable_shared_from_this { friend class EvalCache; - friend class CachedEvalError; + friend struct CachedEvalError; ref root; typedef std::optional, Symbol>> Parent; From 0a09597790348a3e0420020b0bab68517669ecf6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 12:37:24 +0200 Subject: [PATCH 250/910] Typo --- src/libexpr/eval.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3524c8b1e..9e6b89544 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -365,7 +365,7 @@ void initGC() }; } #else - #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bwd-gc to 8.4 or later." + #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.4 or later." #endif From f91f34aa653781ef46c4f6008269785f6198a3d4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 12:37:44 +0200 Subject: [PATCH 251/910] bdwgc 8.2.4 has sp_corrector > Support client-defined stack pointer adjustment before thread stack push -- https://github.com/ivmai/bdwgc/releases/tag/v8.2.4 This fixes an inaccuracy in cc6f31525253b73e4776b5f733e0950e1706d546, in the update to Nixpkgs 24.05 https://github.com/NixOS/nix/pull/10835 After this fixup, the build log won't ask for an upgrade, and we'll be able to collect when a coroutine exists, e.g. during filterSource. --- src/libexpr/eval.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 9e6b89544..f441977a5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -354,7 +354,7 @@ void initGC() // TODO: Remove __APPLE__ condition. // Comment suggests an implementation that works on darwin and windows // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 - #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 4 && !defined(__APPLE__) + #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__) GC_set_sp_corrector(&fixupBoehmStackPointer); if (!GC_get_sp_corrector()) { @@ -365,7 +365,7 @@ void initGC() }; } #else - #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.4 or later." + #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later." #endif From 1363f51bcb24ab9948b7b5093490a009947f7453 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 10 Jun 2024 08:32:30 -0400 Subject: [PATCH 252/910] fix: remove usage of XDG_RUNTIME_DIR for TMP --- src/nix-build/nix-build.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index b601604dc..77df08edd 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -477,9 +477,7 @@ static void main_nix_build(int argc, char * * argv) // Set the environment. auto env = getEnv(); - auto tmp = getEnvNonEmpty("TMPDIR"); - if (!tmp) - tmp = getEnvNonEmpty("XDG_RUNTIME_DIR").value_or("/tmp"); + auto tmp = getEnvNonEmpty("TMPDIR").value_or("/tmp"); if (pure) { decltype(env) newEnv; @@ -491,7 +489,7 @@ static void main_nix_build(int argc, char * * argv) env["__ETC_PROFILE_SOURCED"] = "1"; } - env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = *tmp; + env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp; env["NIX_STORE"] = store->storeDir; env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); From 4809e59b7ee0681d420f83a285d52e9424f2fad8 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 10 Jun 2024 09:31:21 -0400 Subject: [PATCH 253/910] fix: warn and document when advanced attributes will have no impact due to __structuredAttrs --- doc/manual/src/language/advanced-attributes.md | 6 ++++++ src/libexpr/eval.cc | 6 ++++++ src/libexpr/eval.hh | 5 ++++- src/libexpr/primops.cc | 14 ++++++++++++++ .../unix/build/local-derivation-goal.cc | 18 ++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 113062db1..64a28a366 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -301,6 +301,12 @@ Derivations can declare some infrequently used optional attributes. (associative) arrays. For example, the attribute `hardening.format = true` ends up as the Bash associative array element `${hardening[format]}`. + > **Warning** + > + > If set to `true`, other advanced attributes such as [`allowedReferences`](#adv-attr-allowedReferences), [`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites), + [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), maxSize, and maxClosureSize. + will have no effect. + - [`outputChecks`]{#adv-attr-outputChecks}\ When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks` attribute allows defining checks per-output. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f441977a5..5a94c67ec 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -423,6 +423,12 @@ EvalState::EvalState( , sRight(symbols.create("right")) , sWrong(symbols.create("wrong")) , sStructuredAttrs(symbols.create("__structuredAttrs")) + , sAllowedReferences(symbols.create("allowedReferences")) + , sAllowedRequisites(symbols.create("allowedRequisites")) + , sDisallowedReferences(symbols.create("disallowedReferences")) + , sDisallowedRequisites(symbols.create("disallowedRequisites")) + , sMaxSize(symbols.create("maxSize")) + , sMaxClosureSize(symbols.create("maxClosureSize")) , sBuilder(symbols.create("builder")) , sArgs(symbols.create("args")) , sContentAddressed(symbols.create("__contentAddressed")) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index dac763268..f916873f1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -173,7 +173,10 @@ public: const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sFile, sLine, sColumn, sFunctor, sToString, - sRight, sWrong, sStructuredAttrs, sBuilder, sArgs, + sRight, sWrong, sStructuredAttrs, + sAllowedReferences, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites, + sMaxSize, sMaxClosureSize, + sBuilder, sArgs, sContentAddressed, sImpure, sOutputHash, sOutputHashAlgo, sOutputHashMode, sRecurseForDerivations, diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9177b0a2f..98649f081 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1308,6 +1308,20 @@ static void derivationStrictInternal( handleOutputs(ss); } + if (i->name == state.sAllowedReferences) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'allowedReferences'; use 'outputChecks..allowedReferences' instead", drvName); + if (i->name == state.sAllowedRequisites) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'allowedRequisites'; use 'outputChecks..allowedRequisites' instead", drvName); + if (i->name == state.sDisallowedReferences) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedReferences'; use 'outputChecks..disallowedReferences' instead", drvName); + if (i->name == state.sDisallowedRequisites) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedRequisites'; use 'outputChecks..disallowedRequisites' instead", drvName); + if (i->name == state.sMaxSize) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'maxSize'; use 'outputChecks..maxSize' instead", drvName); + if (i->name == state.sMaxClosureSize) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'maxClosureSize'; use 'outputChecks..maxClosureSize' instead", drvName); + + } else { auto s = state.coerceToString(pos, *i->value, context, context_below, true).toOwned(); drv.env.emplace(key, s); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 16095cf5d..54644dc3a 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -2904,6 +2904,24 @@ void LocalDerivationGoal::checkOutputs(const std::mapgetStructuredAttrs()) { + if (get(*structuredAttrs, "allowedReferences")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedReferences'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "allowedRequisites")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedRequisites'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "disallowedRequisites")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedRequisites'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "disallowedReferences")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedReferences'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "maxSize")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'maxSize'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "maxClosureSize")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'maxClosureSize'; use 'outputChecks' instead"); + } if (auto outputChecks = get(*structuredAttrs, "outputChecks")) { if (auto output = get(*outputChecks, outputName)) { Checks checks; From de3fd52a95cedb57bc6d9dbda0c597cd2bfb3db5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 15:55:53 +0200 Subject: [PATCH 254/910] Add tests/f/lang/eval-okay-derivation-legacy --- .../lang/eval-okay-derivation-legacy.err.exp | 6 ++++++ .../functional/lang/eval-okay-derivation-legacy.exp | 1 + .../functional/lang/eval-okay-derivation-legacy.nix | 12 ++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 tests/functional/lang/eval-okay-derivation-legacy.err.exp create mode 100644 tests/functional/lang/eval-okay-derivation-legacy.exp create mode 100644 tests/functional/lang/eval-okay-derivation-legacy.nix diff --git a/tests/functional/lang/eval-okay-derivation-legacy.err.exp b/tests/functional/lang/eval-okay-derivation-legacy.err.exp new file mode 100644 index 000000000..94f0854dd --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.err.exp @@ -0,0 +1,6 @@ +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'allowedReferences'; use 'outputChecks..allowedReferences' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'allowedRequisites'; use 'outputChecks..allowedRequisites' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedReferences'; use 'outputChecks..disallowedReferences' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedRequisites'; use 'outputChecks..disallowedRequisites' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'maxClosureSize'; use 'outputChecks..maxClosureSize' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'maxSize'; use 'outputChecks..maxSize' instead diff --git a/tests/functional/lang/eval-okay-derivation-legacy.exp b/tests/functional/lang/eval-okay-derivation-legacy.exp new file mode 100644 index 000000000..4f374a1aa --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.exp @@ -0,0 +1 @@ +"/nix/store/mzgwvrjjir216ra58mwwizi8wj6y9ddr-eval-okay-derivation-legacy" diff --git a/tests/functional/lang/eval-okay-derivation-legacy.nix b/tests/functional/lang/eval-okay-derivation-legacy.nix new file mode 100644 index 000000000..b529cdf90 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.nix @@ -0,0 +1,12 @@ +(builtins.derivationStrict { + name = "eval-okay-derivation-legacy"; + system = "x86_64-linux"; + builder = "/dontcare"; + __structuredAttrs = true; + allowedReferences = [ ]; + disallowedReferences = [ ]; + allowedRequisites = [ ]; + disallowedRequisites = [ ]; + maxSize = 1234; + maxClosureSize = 12345; +}).out From d1dd7abbf008a0744debe2a13fb2f5a1c689007c Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 10 Jun 2024 20:50:35 +0200 Subject: [PATCH 255/910] mention the actual meaning of FODs in the glossary (#10888) * mention the actual meaning of FODs in the glossary Co-authored-by: Alex Groleau Co-authored-by: Daniel Baker Co-authored-by: Robert Hensing --- doc/manual/src/glossary.md | 5 ++--- doc/manual/src/language/advanced-attributes.md | 15 ++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 55ad9e1c2..4c9b2c52e 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -71,10 +71,9 @@ [`__contentAddressed`](./language/advanced-attributes.md#adv-attr-__contentAddressed) attribute set to `true`. -- [fixed-output derivation]{#gloss-fixed-output-derivation} +- [fixed-output derivation]{#gloss-fixed-output-derivation} (FOD) - A derivation which includes the - [`outputHash`](./language/advanced-attributes.md#adv-attr-outputHash) attribute. + A [derivation] where a cryptographic hash of the [output] is determined in advance using the [`outputHash`](./language/advanced-attributes.md#adv-attr-outputHash) attribute, and where the [`builder`](@docroot@/language/derivations.md#attr-builder) executable has access to the network. - [store]{#gloss-store} diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 113062db1..70ec06f57 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -120,12 +120,11 @@ Derivations can declare some infrequently used optional attributes. configuration setting. - [`outputHash`]{#adv-attr-outputHash}; [`outputHashAlgo`]{#adv-attr-outputHashAlgo}; [`outputHashMode`]{#adv-attr-outputHashMode}\ - These attributes declare that the derivation is a so-called - *fixed-output derivation*, which means that a cryptographic hash of - the output is already known in advance. When the build of a - fixed-output derivation finishes, Nix computes the cryptographic - hash of the output and compares it to the hash declared with these - attributes. If there is a mismatch, the build fails. + These attributes declare that the derivation is a so-called *fixed-output derivation* (FOD), which means that a cryptographic hash of the output is already known in advance. + + As opposed to regular derivations, the [`builder`] executable of a fixed-output derivation has access to the network. + Nix computes a cryptographic hash of its output and compares that to the hash declared with these attributes. + If there is a mismatch, the derivation fails. The rationale for fixed-output derivations is derivations such as those produced by the `fetchurl` function. This function downloads a @@ -279,7 +278,9 @@ Derivations can declare some infrequently used optional attributes. > **Note** > - > If set to `false`, the [`builder`](./derivations.md#attr-builder) should be able to run on the system type specified in the [`system` attribute](./derivations.md#attr-system), since the derivation cannot be substituted. + > If set to `false`, the [`builder`] should be able to run on the system type specified in the [`system` attribute](./derivations.md#attr-system), since the derivation cannot be substituted. + + [`builder`]: ./derivations.md#attr-builder - [`__structuredAttrs`]{#adv-attr-structuredAttrs}\ If the special attribute `__structuredAttrs` is set to `true`, the other derivation From 35bdb9cee7dbb7f8733cab1b1fe327f525496773 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 11 Jun 2024 16:05:57 +0200 Subject: [PATCH 256/910] Support hard links in tarballs Fixes #10395. --- src/libfetchers/git-utils.cc | 48 +++++++++++++++++++++++++++++++++-- src/libfetchers/git-utils.hh | 2 +- src/libutil/fs-sink.hh | 13 ++++++++++ src/libutil/tarfile.cc | 11 +++++--- src/libutil/tarfile.hh | 2 +- tests/functional/tarball.sh | 9 +++++++ tests/functional/tree.tar.gz | Bin 0 -> 298 bytes 7 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 tests/functional/tree.tar.gz diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 2ea1e15ed..32b35931a 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -115,10 +115,10 @@ git_oid hashToOID(const Hash & hash) return oid; } -Object lookupObject(git_repository * repo, const git_oid & oid) +Object lookupObject(git_repository * repo, const git_oid & oid, git_object_t type = GIT_OBJECT_ANY) { Object obj; - if (git_object_lookup(Setter(obj), repo, &oid, GIT_OBJECT_ANY)) { + if (git_object_lookup(Setter(obj), repo, &oid, type)) { auto err = git_error_last(); throw Error("getting Git object '%s': %s", oid, err->message); } @@ -909,6 +909,50 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink addToTree(*pathComponents.rbegin(), oid, GIT_FILEMODE_LINK); } + void createHardlink(const Path & path, const CanonPath & target) override + { + auto pathComponents = tokenizeString>(path, "/"); + if (!prepareDirs(pathComponents, false)) return; + + auto relTarget = CanonPath(path).parent()->makeRelative(target); + + auto dir = pendingDirs.rbegin(); + + // For each ../ component at the start, go up one directory. + std::string_view relTargetLeft(relTarget); + while (hasPrefix(relTargetLeft, "../")) { + if (dir == pendingDirs.rend()) + throw Error("invalid hard link target '%s'", target); + ++dir; + relTargetLeft = relTargetLeft.substr(3); + } + + // Look up the remainder of the target, starting at the + // top-most `git_treebuilder`. + std::variant curDir{dir->builder.get()}; + Object tree; // needed to keep `entry` alive + const git_tree_entry * entry = nullptr; + + for (auto & c : CanonPath(relTargetLeft)) { + if (auto builder = std::get_if(&curDir)) { + if (!(entry = git_treebuilder_get(*builder, std::string(c).c_str()))) + throw Error("cannot find hard link target '%s'", target); + curDir = *git_tree_entry_id(entry); + } else if (auto oid = std::get_if(&curDir)) { + tree = lookupObject(*repo, *oid, GIT_OBJECT_TREE); + if (!(entry = git_tree_entry_byname((const git_tree *) &*tree, std::string(c).c_str()))) + throw Error("cannot find hard link target '%s'", target); + curDir = *git_tree_entry_id(entry); + } + } + + assert(entry); + + addToTree(*pathComponents.rbegin(), + *git_tree_entry_id(entry), + git_tree_entry_filemode(entry)); + } + Hash sync() override { updateBuilders({}); diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 29d799554..495916f62 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -7,7 +7,7 @@ namespace nix { namespace fetchers { struct PublicKey; } -struct GitFileSystemObjectSink : FileSystemObjectSink +struct GitFileSystemObjectSink : ExtendedFileSystemObjectSink { /** * Flush builder and return a final Git hash. diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index ae577819a..994f19960 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -41,6 +41,19 @@ struct FileSystemObjectSink virtual void createSymlink(const Path & path, const std::string & target) = 0; }; +/** + * An extension of `FileSystemObjectSink` that supports file types + * that are not supported by Nix's FSO model. + */ +struct ExtendedFileSystemObjectSink : FileSystemObjectSink +{ + /** + * Create a hard link. The target must be the path of a previously + * encountered file relative to the root of the FSO. + */ + virtual void createHardlink(const Path & path, const CanonPath & target) = 0; +}; + /** * Recursively copy file system objects from the source into the sink. */ diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 6bb2bd2f3..e45cfaf85 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -163,7 +163,7 @@ void unpackTarfile(const Path & tarFile, const Path & destDir) extract_archive(archive, destDir); } -time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSink) +time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & parseSink) { time_t lastModified = 0; @@ -183,7 +183,12 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin lastModified = std::max(lastModified, archive_entry_mtime(entry)); - switch (archive_entry_filetype(entry)) { + if (auto target = archive_entry_hardlink(entry)) { + parseSink.createHardlink(path, CanonPath(target)); + continue; + } + + switch (auto type = archive_entry_filetype(entry)) { case AE_IFDIR: parseSink.createDirectory(path); @@ -220,7 +225,7 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin } default: - throw Error("file '%s' in tarball has unsupported file type", path); + throw Error("file '%s' in tarball has unsupported file type %d", path, type); } } diff --git a/src/libutil/tarfile.hh b/src/libutil/tarfile.hh index 705d211e4..0517177db 100644 --- a/src/libutil/tarfile.hh +++ b/src/libutil/tarfile.hh @@ -41,6 +41,6 @@ void unpackTarfile(Source & source, const Path & destDir); void unpackTarfile(const Path & tarFile, const Path & destDir); -time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSink); +time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & parseSink); } diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index ce162ddce..5d4749eb2 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -59,3 +59,12 @@ test_tarball() { test_tarball '' cat test_tarball .xz xz test_tarball .gz gzip + +# Test hard links. +path="$(nix flake prefetch --json "tarball+file://$(pwd)/tree.tar.gz" | jq -r .storePath)" +[[ $(cat "$path/a/b/foo") = bar ]] +[[ $(cat "$path/a/b/xyzzy") = bar ]] +[[ $(cat "$path/a/yyy") = bar ]] +[[ $(cat "$path/a/zzz") = bar ]] +[[ $(cat "$path/c/aap") = bar ]] +[[ $(cat "$path/fnord") = bar ]] diff --git a/tests/functional/tree.tar.gz b/tests/functional/tree.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..f1f1d996d8494c930ede3d0b66574c8e6efb014c GIT binary patch literal 298 zcmV+_0oDE=iwFP!000001MQdHYQ!KAMtzjLLC2rb=W)00RcVTwLX)R&bY%;LZb_DL z3;oWG1caG*bVjF~(vy;fRswSwbzrLB+POM5ly=@4Vr!jOq%~Qs1{Th%@_wFT9tM@t z%W=FpFXeNOg!(cS|50`aZ1K;Ui+|$+{P&>wUzSBKMiJ~UzJKswt0k+hC6HKZ9E)eQ}53c@Cj`>+G#*Y3^#PAOQ00000000000KmO`0`*Phd;ll_0G_ItkN^Mx literal 0 HcmV?d00001 From b130d2f2e335994cad555a77d242804b65a87062 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 11 Jun 2024 17:52:33 +0200 Subject: [PATCH 257/910] add more context on the README (#9871) the thesis is still the defining document with all the motivation and explanations. adding it here for greater visibility. also more emphasis and clarity around the community aspect. the hydra build job seems a bit arbitrary right there. may be better for the contributing guide. --- README.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e1cace3b4..931a60bba 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Visit [nix.dev](https://nix.dev) for [installation instructions](https://nix.dev Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual). -## Building And Developing +## Building and developing See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to set up a development environment and build Nix from source. @@ -22,12 +22,17 @@ See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacki Check the [contributing guide](./CONTRIBUTING.md) if you want to get involved with developing Nix. -## Additional Resources +## Additional resources -- [Nix manual](https://nixos.org/nix/manual) -- [Nix jobsets on hydra.nixos.org](https://hydra.nixos.org/project/nix) -- [NixOS Discourse](https://discourse.nixos.org/) -- [Matrix - #nix:nixos.org](https://matrix.to/#/#nix:nixos.org) +Nix was created by Eelco Dolstra and developed as the subject of his PhD thesis [The Purely Functional Software Deployment Model](https://edolstra.github.io/pubs/phd-thesis.pdf), published 2006. +Today, a world-wide developer community contributes to Nix and the ecosystem that has grown around it. + +- [The Nix, Nixpkgs, NixOS Community on nixos.org](https://nixos.org/) +- [Official documentation on nix.dev](https://nix.dev) +- [Nixpkgs](https://github.com/NixOS/nixpkgs) is [the largest, most up-to-date free software repository in the world](https://repology.org/repositories/graphs) +- [NixOS](https://github.com/NixOS/nixpkgs/tree/master/nixos) is a Linux distribution that can be configured fully declaratively +- [Discourse](https://discourse.nixos.org/) +- [Matrix](https://matrix.to/#/#nix:nixos.org) ## License From bd37a70d8f0ed43c21ce3cf746e9a20bede221ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 11 Jun 2024 19:39:42 +0200 Subject: [PATCH 258/910] Update tests/functional/tarball.sh Co-authored-by: Robert Hensing --- tests/functional/tarball.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index 5d4749eb2..86b8ef2f5 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -61,6 +61,9 @@ test_tarball .xz xz test_tarball .gz gzip # Test hard links. +# All entries in tree.tar.gz refer to the same file, and all have the same inode when unpacked by GNU tar. +# We don't preserve the hard links, because that's an optimization we think is not worth the complexity, +# so we only make sure that the contents are copied correctly. path="$(nix flake prefetch --json "tarball+file://$(pwd)/tree.tar.gz" | jq -r .storePath)" [[ $(cat "$path/a/b/foo") = bar ]] [[ $(cat "$path/a/b/xyzzy") = bar ]] From efd4bf653325ce2a3f243923f39092e77af29fe3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 12 Jun 2024 14:41:35 +0200 Subject: [PATCH 259/910] Update src/libfetchers/git-utils.cc Co-authored-by: Robert Hensing --- src/libfetchers/git-utils.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 32b35931a..ca02fbc89 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -914,11 +914,16 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink auto pathComponents = tokenizeString>(path, "/"); if (!prepareDirs(pathComponents, false)) return; + // We can't just look up the path from the start of the root, since + // some parent directories may not have finished yet, so we compute + // a relative path that helps us find the right git_tree_builder or object. auto relTarget = CanonPath(path).parent()->makeRelative(target); auto dir = pendingDirs.rbegin(); // For each ../ component at the start, go up one directory. + // CanonPath::makeRelative() always puts all .. elements at the start, + // so they're all handled by this loop: std::string_view relTargetLeft(relTarget); while (hasPrefix(relTargetLeft, "../")) { if (dir == pendingDirs.rend()) From 258e2a32ec5f9661d79c0016e2510c0057f9ddd3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 12 Jun 2024 14:57:40 +0200 Subject: [PATCH 260/910] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index e9763f6bf..ad2261920 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.23.0 +2.24.0 From e74ce01b7f0920026502824922cfd71fca5eb525 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 10:50:07 -0400 Subject: [PATCH 261/910] Fix precompiled headers building with clang Since 24.05 (I think) we need to pass `-c` or Clang thinks we want to compile *both* a final executable and precompiled header file, and complains that we cannot use `-o` with multiple outputs. `-c` seems fine with GCC too, so I just put it in there conditionally. --- mk/precompiled-headers.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mk/precompiled-headers.mk b/mk/precompiled-headers.mk index cdd3daecd..f2803eb79 100644 --- a/mk/precompiled-headers.mk +++ b/mk/precompiled-headers.mk @@ -8,7 +8,7 @@ GCH = $(buildprefix)precompiled-headers.h.gch $(GCH): precompiled-headers.h @rm -f $@ @mkdir -p "$(dir $@)" - $(trace-gen) $(CXX) -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(GCH_CXXFLAGS) + $(trace-gen) $(CXX) -c -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(GCH_CXXFLAGS) clean-files += $(GCH) From 7c2981fc559e93131c845164907cec6cae11ce48 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 10:02:20 -0400 Subject: [PATCH 262/910] Fix FreeBSD build This restores some CPP'd code that was added in c18911602eb4260d59acf8c17f1c3b4c7fcf7cee and accidentally lost in 2477e4e3b84b23b091befa1869a1fc6186fe74dc. Co-authored-by: Eelco Dolstra --- configure.ac | 8 +++++++ src/libexpr/eval.cc | 29 +++++++++++++++++++------- src/libstore/build/derivation-goal.cc | 6 +++++- tests/unit/libstore/worker-protocol.cc | 1 + 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 90a6d45d5..8211bec0b 100644 --- a/configure.ac +++ b/configure.ac @@ -369,6 +369,14 @@ if test "$gc" = yes; then PKG_CHECK_MODULES([BDW_GC], [bdw-gc]) CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS" AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.]) + + # See `fixupBoehmStackPointer`, for the integration between Boehm GC + # and Boost coroutines. + old_CFLAGS="$CFLAGS" + # Temporary set `-pthread` just for the next check + CFLAGS="$CFLAGS -pthread" + AC_CHECK_FUNCS([pthread_attr_get_np pthread_getattr_np]) + CFLAGS="$old_CFLAGS" fi AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f441977a5..42b26834c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -48,6 +48,9 @@ #define GC_INCLUDE_NEW #include +#if __FreeBSD__ +# include +#endif #include #include @@ -264,30 +267,42 @@ static BoehmGCStackAllocator boehmGCStackAllocator; * However, the implementation is quite lean, and usually we don't have active * coroutines during evaluation, so this is acceptable. */ -void fixupBoehmStackPointer(void ** sp_ptr, void * pthread_id) { +void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) { void *& sp = *sp_ptr; + auto pthread_id = reinterpret_cast(_pthread_id); pthread_attr_t pattr; size_t osStackSize; void * osStackLow; void * osStackBase; - #ifdef __APPLE__ - osStackSize = pthread_get_stacksize_np((pthread_t)pthread_id); - osStackLow = pthread_get_stackaddr_np((pthread_t)pthread_id); - #else +# ifdef __APPLE__ + osStackSize = pthread_get_stacksize_np(pthread_id); + osStackLow = pthread_get_stackaddr_np(pthread_id); +# else if (pthread_attr_init(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); } - if (pthread_getattr_np((pthread_t)pthread_id, &pattr)) { +# ifdef HAVE_PTHREAD_GETATTR_NP + if (pthread_getattr_np(pthread_id, &pattr)) { throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); } +# elif HAVE_PTHREAD_ATTR_GET_NP + if (!pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } + if (!pthread_attr_get_np(pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed"); + } +# else +# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" +# endif if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); } if (pthread_attr_destroy(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); } - #endif +# endif osStackBase = (char *)osStackLow + osStackSize; // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 4226fb61a..e72ef4cf9 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -25,6 +25,10 @@ #include #include +#ifndef _WIN32 // TODO abstract over proc exit status +# include +#endif + #include namespace nix { @@ -1033,7 +1037,7 @@ void DerivationGoal::buildDone() BuildResult::Status st = BuildResult::MiscFailure; -#ifndef _WIN32 +#ifndef _WIN32 // TODO abstract over proc exit status if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) st = BuildResult::TimedOut; diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 5907ea5a4..70e03a8ab 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -1,4 +1,5 @@ #include +#include #include #include From 5b53d8fec3149937d44b2302d51967aa95253d7a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 10:17:39 -0400 Subject: [PATCH 263/910] Factor out GC initialization code This is not really part of the evaluator: it is just an integration between Boehm GC and Boost coroutines usable for any purpose. The evaluator (merely) optionally uses it. --- src/libexpr/eval-gc.cc | 226 +++++++++++++++++++++++++++++++++++++++++ src/libexpr/eval-gc.hh | 16 +++ src/libexpr/eval.cc | 210 ++------------------------------------ src/libexpr/eval.hh | 7 +- 4 files changed, 249 insertions(+), 210 deletions(-) create mode 100644 src/libexpr/eval-gc.cc create mode 100644 src/libexpr/eval-gc.hh diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc new file mode 100644 index 000000000..baf9df332 --- /dev/null +++ b/src/libexpr/eval-gc.cc @@ -0,0 +1,226 @@ +#include "error.hh" +#include "environment-variables.hh" +#include "serialise.hh" +#include "eval-gc.hh" + +#if HAVE_BOEHMGC + +# include +# if __FreeBSD__ +# include +# endif + +# include +# include +# include + +# include +# include +# include + +#endif + +namespace nix { + +#if HAVE_BOEHMGC +/* Called when the Boehm GC runs out of memory. */ +static void * oomHandler(size_t requested) +{ + /* Convert this to a proper C++ exception. */ + throw std::bad_alloc(); +} + +class BoehmGCStackAllocator : public StackAllocator +{ + boost::coroutines2::protected_fixedsize_stack stack{ + // We allocate 8 MB, the default max stack size on NixOS. + // A smaller stack might be quicker to allocate but reduces the stack + // depth available for source filter expressions etc. + std::max(boost::context::stack_traits::default_size(), static_cast(8 * 1024 * 1024))}; + + // This is specific to boost::coroutines2::protected_fixedsize_stack. + // The stack protection page is included in sctx.size, so we have to + // subtract one page size from the stack size. + std::size_t pfss_usable_stack_size(boost::context::stack_context & sctx) + { + return sctx.size - boost::context::stack_traits::page_size(); + } + +public: + boost::context::stack_context allocate() override + { + auto sctx = stack.allocate(); + + // Stacks generally start at a high address and grow to lower addresses. + // Architectures that do the opposite are rare; in fact so rare that + // boost_routine does not implement it. + // So we subtract the stack size. + GC_add_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); + return sctx; + } + + void deallocate(boost::context::stack_context sctx) override + { + GC_remove_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); + stack.deallocate(sctx); + } +}; + +static BoehmGCStackAllocator boehmGCStackAllocator; + +/** + * When a thread goes into a coroutine, we lose its original sp until + * control flow returns to the thread. + * While in the coroutine, the sp points outside the thread stack, + * so we can detect this and push the entire thread stack instead, + * as an approximation. + * The coroutine's stack is covered by `BoehmGCStackAllocator`. + * This is not an optimal solution, because the garbage is scanned when a + * coroutine is active, for both the coroutine and the original thread stack. + * However, the implementation is quite lean, and usually we don't have active + * coroutines during evaluation, so this is acceptable. + */ +void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) +{ + void *& sp = *sp_ptr; + auto pthread_id = reinterpret_cast(_pthread_id); + pthread_attr_t pattr; + size_t osStackSize; + void * osStackLow; + void * osStackBase; + +# ifdef __APPLE__ + osStackSize = pthread_get_stacksize_np(pthread_id); + osStackLow = pthread_get_stackaddr_np(pthread_id); +# else + if (pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } +# ifdef HAVE_PTHREAD_GETATTR_NP + if (pthread_getattr_np(pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); + } +# elif HAVE_PTHREAD_ATTR_GET_NP + if (!pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } + if (!pthread_attr_get_np(pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed"); + } +# else +# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" +# endif + if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { + throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); + } + if (pthread_attr_destroy(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); + } +# endif + osStackBase = (char *) osStackLow + osStackSize; + // NOTE: We assume the stack grows down, as it does on all architectures we support. + // Architectures that grow the stack up are rare. + if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack + sp = osStackBase; + } +} + +/* 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(); + }; +}; + +static inline void initGCReal() +{ + /* Initialise the Boehm garbage collector. */ + + /* Don't look for interior pointers. This reduces the odds of + misdetection a bit. */ + GC_set_all_interior_pointers(0); + + /* We don't have any roots in data segments, so don't scan from + there. */ + GC_set_no_dls(1); + + GC_INIT(); + + GC_set_oom_fn(oomHandler); + + StackAllocator::defaultAllocator = &boehmGCStackAllocator; + +// TODO: Remove __APPLE__ condition. +// Comment suggests an implementation that works on darwin and windows +// https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 +# if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__) + GC_set_sp_corrector(&fixupBoehmStackPointer); + + if (!GC_get_sp_corrector()) { + printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); + /* Used to disable GC when entering coroutines on macOS */ + create_coro_gc_hook = []() -> std::shared_ptr { return std::make_shared(); }; + } +# else +# warning \ + "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later." +# 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 + fairly significant overhead.) The heap size can be overridden + through libgc's GC_INITIAL_HEAP_SIZE environment variable. We + should probably also provide a nix.conf setting for this. Note + that GC_expand_hp() causes a lot of virtual, but not physical + (resident) memory to be allocated. This might be a problem on + systems that don't overcommit. */ + if (!getEnv("GC_INITIAL_HEAP_SIZE")) { + size_t size = 32 * 1024 * 1024; +# if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) + size_t maxSize = 384 * 1024 * 1024; + long pageSize = sysconf(_SC_PAGESIZE); + long pages = sysconf(_SC_PHYS_PAGES); + if (pageSize != -1) + size = (pageSize * pages) / 4; // 25% of RAM + if (size > maxSize) + size = maxSize; +# endif + debug("setting initial heap size to %1% bytes", size); + GC_expand_hp(size); + } +} + +#endif + +static bool gcInitialised = false; + +void initGC() +{ + if (gcInitialised) + return; + +#if HAVE_BOEHMGC + initGCReal(); +#endif + + gcInitialised = true; +} + +void assertGCInitialized() +{ + assert(gcInitialised); +} + +} diff --git a/src/libexpr/eval-gc.hh b/src/libexpr/eval-gc.hh new file mode 100644 index 000000000..cd4ea914d --- /dev/null +++ b/src/libexpr/eval-gc.hh @@ -0,0 +1,16 @@ +#pragma once +///@file + +namespace nix { + +/** + * Initialise the Boehm GC, if applicable. + */ +void initGC(); + +/** + * Make sure `initGC` has already been called. + */ +void assertGCInitialized(); + +} diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 42b26834c..301b81a62 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -40,25 +40,16 @@ #include #ifndef _WIN32 // TODO use portable implementation -# include +# include #endif #if HAVE_BOEHMGC -#define GC_INCLUDE_NEW +# define GC_INCLUDE_NEW -#include -#if __FreeBSD__ -# include -#endif - -#include -#include -#include - -#include -#include -#include +# include +# include +# include #endif @@ -211,109 +202,6 @@ bool Value::isTrivial() const } -#if HAVE_BOEHMGC -/* Called when the Boehm GC runs out of memory. */ -static void * oomHandler(size_t requested) -{ - /* Convert this to a proper C++ exception. */ - throw std::bad_alloc(); -} - -class BoehmGCStackAllocator : public StackAllocator { - boost::coroutines2::protected_fixedsize_stack stack { - // We allocate 8 MB, the default max stack size on NixOS. - // A smaller stack might be quicker to allocate but reduces the stack - // depth available for source filter expressions etc. - std::max(boost::context::stack_traits::default_size(), static_cast(8 * 1024 * 1024)) - }; - - // This is specific to boost::coroutines2::protected_fixedsize_stack. - // The stack protection page is included in sctx.size, so we have to - // subtract one page size from the stack size. - std::size_t pfss_usable_stack_size(boost::context::stack_context &sctx) { - return sctx.size - boost::context::stack_traits::page_size(); - } - - public: - boost::context::stack_context allocate() override { - auto sctx = stack.allocate(); - - // Stacks generally start at a high address and grow to lower addresses. - // Architectures that do the opposite are rare; in fact so rare that - // boost_routine does not implement it. - // So we subtract the stack size. - GC_add_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); - return sctx; - } - - void deallocate(boost::context::stack_context sctx) override { - GC_remove_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); - stack.deallocate(sctx); - } - -}; - -static BoehmGCStackAllocator boehmGCStackAllocator; - -/** - * When a thread goes into a coroutine, we lose its original sp until - * control flow returns to the thread. - * While in the coroutine, the sp points outside the thread stack, - * so we can detect this and push the entire thread stack instead, - * as an approximation. - * The coroutine's stack is covered by `BoehmGCStackAllocator`. - * This is not an optimal solution, because the garbage is scanned when a - * coroutine is active, for both the coroutine and the original thread stack. - * However, the implementation is quite lean, and usually we don't have active - * coroutines during evaluation, so this is acceptable. - */ -void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) { - void *& sp = *sp_ptr; - auto pthread_id = reinterpret_cast(_pthread_id); - pthread_attr_t pattr; - size_t osStackSize; - void * osStackLow; - void * osStackBase; - -# ifdef __APPLE__ - osStackSize = pthread_get_stacksize_np(pthread_id); - osStackLow = pthread_get_stackaddr_np(pthread_id); -# else - if (pthread_attr_init(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); - } -# ifdef HAVE_PTHREAD_GETATTR_NP - if (pthread_getattr_np(pthread_id, &pattr)) { - throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); - } -# elif HAVE_PTHREAD_ATTR_GET_NP - if (!pthread_attr_init(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); - } - if (!pthread_attr_get_np(pthread_id, &pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed"); - } -# else -# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" -# endif - if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { - throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); - } - if (pthread_attr_destroy(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); - } -# endif - osStackBase = (char *)osStackLow + osStackSize; - // NOTE: We assume the stack grows down, as it does on all architectures we support. - // Architectures that grow the stack up are rare. - if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack - sp = osStackBase; - } -} - -#endif - - static Symbol getName(const AttrName & name, EvalState & state, Env & env) { if (name.symbol) { @@ -326,92 +214,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; - -void initGC() -{ - if (gcInitialised) return; - -#if HAVE_BOEHMGC - /* Initialise the Boehm garbage collector. */ - - /* Don't look for interior pointers. This reduces the odds of - misdetection a bit. */ - GC_set_all_interior_pointers(0); - - /* We don't have any roots in data segments, so don't scan from - there. */ - GC_set_no_dls(1); - - GC_INIT(); - - GC_set_oom_fn(oomHandler); - - StackAllocator::defaultAllocator = &boehmGCStackAllocator; - - // TODO: Remove __APPLE__ condition. - // Comment suggests an implementation that works on darwin and windows - // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 - #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__) - GC_set_sp_corrector(&fixupBoehmStackPointer); - - if (!GC_get_sp_corrector()) { - printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); - /* Used to disable GC when entering coroutines on macOS */ - create_coro_gc_hook = []() -> std::shared_ptr { - return std::make_shared(); - }; - } - #else - #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later." - #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 - fairly significant overhead.) The heap size can be overridden - through libgc's GC_INITIAL_HEAP_SIZE environment variable. We - should probably also provide a nix.conf setting for this. Note - that GC_expand_hp() causes a lot of virtual, but not physical - (resident) memory to be allocated. This might be a problem on - systems that don't overcommit. */ - if (!getEnv("GC_INITIAL_HEAP_SIZE")) { - size_t size = 32 * 1024 * 1024; -#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) - size_t maxSize = 384 * 1024 * 1024; - long pageSize = sysconf(_SC_PAGESIZE); - long pages = sysconf(_SC_PHYS_PAGES); - if (pageSize != -1) - size = (pageSize * pages) / 4; // 25% of RAM - if (size > maxSize) size = maxSize; -#endif - debug("setting initial heap size to %1% bytes", size); - GC_expand_hp(size); - } - -#endif - - gcInitialised = true; -} - static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( @@ -508,7 +310,7 @@ EvalState::EvalState( countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0"; - assert(gcInitialised); + assertGCInitialized(); static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index dac763268..0e5e0433e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -3,6 +3,7 @@ #include "attr-set.hh" #include "eval-error.hh" +#include "eval-gc.hh" #include "types.hh" #include "value.hh" #include "nixexpr.hh" @@ -146,12 +147,6 @@ std::string printValue(EvalState & state, Value & v); std::ostream & operator << (std::ostream & os, const ValueType t); -/** - * Initialise the Boehm GC, if applicable. - */ -void initGC(); - - struct RegexCache; std::shared_ptr makeRegexCache(); From 7738b295e54e385d6ec54c805362403f0df25e41 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:18:42 -0700 Subject: [PATCH 264/910] housekeeping: shellcheck for tests/functional/bash-profile.sh --- maintainers/flake-module.nix | 1 - tests/functional/bash-profile.sh | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 3006d5e30..261da1766 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -507,7 +507,6 @@ ''^scripts/install-nix-from-closure\.sh$'' ''^scripts/install-systemd-multi-user\.sh$'' ''^src/nix/get-env\.sh$'' - ''^tests/functional/bash-profile\.sh$'' ''^tests/functional/binary-cache-build-remote\.sh$'' ''^tests/functional/binary-cache\.sh$'' ''^tests/functional/brotli\.sh$'' diff --git a/tests/functional/bash-profile.sh b/tests/functional/bash-profile.sh index 6cfa5bd9c..4228d4a20 100755 --- a/tests/functional/bash-profile.sh +++ b/tests/functional/bash-profile.sh @@ -2,10 +2,10 @@ source common.sh -sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh +sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > "$TEST_ROOT"/nix-profile.sh user=$(whoami) -rm -rf $TEST_HOME $TEST_ROOT/profile-var -mkdir -p $TEST_HOME +rm -rf "$TEST_HOME" "$TEST_ROOT/profile-var" +mkdir -p "$TEST_HOME" USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh; set" USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh" # test idempotency From 4a28ba7877f8c35cbf370fc45a1128d25c7ffe5b Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:18:53 -0700 Subject: [PATCH 265/910] housekeeping: shellcheck for tests/functional/binary-cache-build-remote.sh --- tests/functional/binary-cache-build-remote.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh index 0303e9410..4edda85b6 100755 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -12,7 +12,7 @@ clearCacheCache outPath=$(nix-build --store "file://$cacheDir" --builders 'auto - - 1 1' -j0 dependencies.nix) # Test that the path exactly exists in the destination store. -nix path-info --store "file://$cacheDir" $outPath +nix path-info --store "file://$cacheDir" "$outPath" # Succeeds without any build capability because no-op nix-build --store "file://$cacheDir" -j0 dependencies.nix From aeed835a2ef8421b01d70d8e44a60fdade01eec4 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:39:01 -0700 Subject: [PATCH 266/910] housekeeping: shellcheck for tests/functional/binary-cache.sh Co-authored-by: Sandro --- tests/functional/binary-cache.sh | 163 ++++++++++++++++--------------- 1 file changed, 85 insertions(+), 78 deletions(-) diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh index 54a3687ca..23135359b 100755 --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -14,9 +14,9 @@ clearStore clearCache outPath=$(nix-build dependencies.nix --no-out-link) -nix copy --to file://$cacheDir $outPath +nix copy --to file://"$cacheDir" "$outPath" -readarray -t paths < <(nix path-info --all --json --store file://$cacheDir | jq 'keys|sort|.[]' -r) +readarray -t paths < <(nix path-info --all --json --store file://"$cacheDir" | jq 'keys|sort|.[]' -r) [[ "${#paths[@]}" -eq 3 ]] for path in "${paths[@]}"; do [[ "$path" =~ -dependencies-input-0$ ]] \ @@ -25,16 +25,16 @@ for path in "${paths[@]}"; do done # Test copying build logs to the binary cache. -expect 1 nix log --store file://$cacheDir $outPath 2>&1 | grep 'is not available' -nix store copy-log --to file://$cacheDir $outPath -nix log --store file://$cacheDir $outPath | grep FOO -rm -rf $TEST_ROOT/var/log/nix -expect 1 nix log $outPath 2>&1 | grep 'is not available' -nix log --substituters file://$cacheDir $outPath | grep FOO +expect 1 nix log --store file://"$cacheDir" "$outPath" 2>&1 | grep 'is not available' +nix store copy-log --to file://"$cacheDir" "$outPath" +nix log --store file://"$cacheDir" "$outPath" | grep FOO +rm -rf "$TEST_ROOT/var/log/nix" +expect 1 nix log "$outPath" 2>&1 | grep 'is not available' +nix log --substituters file://"$cacheDir" "$outPath" | grep FOO # Test copying build logs from the binary cache. -nix store copy-log --from file://$cacheDir $(nix-store -qd $outPath)^'*' -nix log $outPath | grep FOO +nix store copy-log --from file://"$cacheDir" "$(nix-store -qd "$outPath")"^'*' +nix log "$outPath" | grep FOO basicDownloadTests() { # No uploading tests bcause upload with force HTTP doesn't work. @@ -46,15 +46,15 @@ basicDownloadTests() { nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---" - nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath + nix-store --substituters "file://$cacheDir" --no-require-sigs -r "$outPath" - [ -x $outPath/program ] + [ -x "$outPath/program" ] # But with the right configuration, "nix-env -qas" should also work. clearStore clearCacheCache - echo "WantMassQuery: 1" >> $cacheDir/nix-cache-info + echo "WantMassQuery: 1" >> "$cacheDir/nix-cache-info" nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S" nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S" @@ -62,12 +62,12 @@ basicDownloadTests() { x=$(nix-env -f dependencies.nix -qas \* --prebuilt-only) [ -z "$x" ] - nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath + nix-store --substituters "file://$cacheDir" --no-require-sigs -r "$outPath" - nix-store --check-validity $outPath - nix-store -qR $outPath | grep input-2 + nix-store --check-validity "$outPath" + nix-store -qR "$outPath" | grep input-2 - echo "WantMassQuery: 0" >> $cacheDir/nix-cache-info + echo "WantMassQuery: 0" >> "$cacheDir/nix-cache-info" } @@ -83,22 +83,22 @@ basicDownloadTests # Test whether Nix notices if the NAR doesn't match the hash in the NAR info. clearStore -nar=$(ls $cacheDir/nar/*.nar.xz | head -n1) -mv $nar $nar.good -mkdir -p $TEST_ROOT/empty -nix-store --dump $TEST_ROOT/empty | xz > $nar +nar=$(find "$cacheDir/nar/" -type f -name "*.nar.xz" | head -n1) +mv "$nar" "$nar".good +mkdir -p "$TEST_ROOT/empty" +nix-store --dump "$TEST_ROOT/empty" | xz > "$nar" -expect 1 nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log -grepQuiet "hash mismatch" $TEST_ROOT/log +expect 1 nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" 2>&1 | tee "$TEST_ROOT/log" +grepQuiet "hash mismatch" "$TEST_ROOT/log" -mv $nar.good $nar +mv "$nar".good "$nar" # Test whether this unsigned cache is rejected if the user requires signed caches. clearStore clearCacheCache -if nix-store --substituters "file://$cacheDir" -r $outPath; then +if nix-store --substituters "file://$cacheDir" -r "$outPath"; then echo "unsigned binary cache incorrectly accepted" exit 1 fi @@ -107,131 +107,134 @@ fi # Test whether fallback works if a NAR has disappeared. This does not require --fallback. clearStore -mv $cacheDir/nar $cacheDir/nar2 +mv "$cacheDir/nar" "$cacheDir/nar2" -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" -mv $cacheDir/nar2 $cacheDir/nar +mv "$cacheDir/nar2" "$cacheDir/nar" # Test whether fallback works if a NAR is corrupted. This does require --fallback. clearStore -mv $cacheDir/nar $cacheDir/nar2 -mkdir $cacheDir/nar -for i in $(cd $cacheDir/nar2 && echo *); do touch $cacheDir/nar/$i; done +mv "$cacheDir/nar" "$cacheDir/nar2" +mkdir "$cacheDir/nar" +for i in $(cd "$cacheDir/nar2" && echo *); do touch "$cacheDir"/nar/"$i"; done -(! nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result) +(! nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result") -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result --fallback +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" --fallback -rm -rf $cacheDir/nar -mv $cacheDir/nar2 $cacheDir/nar +rm -rf "$cacheDir/nar" +mv "$cacheDir/nar2" "$cacheDir/nar" # Test whether building works if the binary cache contains an # incomplete closure. clearStore -rm -v $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo) +rm -v "$(grep -l "StorePath:.*dependencies-input-2" "$cacheDir"/*.narinfo)" -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log -grepQuiet "copying path.*input-0" $TEST_ROOT/log -grepQuiet "copying path.*input-2" $TEST_ROOT/log -grepQuiet "copying path.*top" $TEST_ROOT/log +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" 2>&1 | tee "$TEST_ROOT/log" +grepQuiet "copying path.*input-0" "$TEST_ROOT/log" +grepQuiet "copying path.*input-2" "$TEST_ROOT/log" +grepQuiet "copying path.*top" "$TEST_ROOT/log" # Idem, but without cached .narinfo. clearStore clearCacheCache -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log -grepQuiet "don't know how to build" $TEST_ROOT/log -grepQuiet "building.*input-1" $TEST_ROOT/log -grepQuiet "building.*input-2" $TEST_ROOT/log -grepQuiet "copying path.*input-0" $TEST_ROOT/log -grepQuiet "copying path.*top" $TEST_ROOT/log +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" 2>&1 | tee "$TEST_ROOT/log" +grepQuiet "don't know how to build" "$TEST_ROOT/log" +grepQuiet "building.*input-1" "$TEST_ROOT/log" +grepQuiet "building.*input-2" "$TEST_ROOT/log" +grepQuiet "copying path.*input-0" "$TEST_ROOT/log" +grepQuiet "copying path.*top" "$TEST_ROOT/log" # Create a signed binary cache. clearCache clearCacheCache -nix key generate-secret --key-name test.nixos.org-1 > $TEST_ROOT/sk1 -publicKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk1) +nix key generate-secret --key-name test.nixos.org-1 > "$TEST_ROOT/sk1" +publicKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk1") -nix key generate-secret --key-name test.nixos.org-1 > $TEST_ROOT/sk2 -badKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk2) +nix key generate-secret --key-name test.nixos.org-1 > "$TEST_ROOT/sk2" +badKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk2") -nix key generate-secret --key-name foo.nixos.org-1 > $TEST_ROOT/sk3 -otherKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk3) +nix key generate-secret --key-name foo.nixos.org-1 > "$TEST_ROOT/sk3" +otherKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk3") -_NIX_FORCE_HTTP= nix copy --to file://$cacheDir?secret-key=$TEST_ROOT/sk1 $outPath +_NIX_FORCE_HTTP='' nix copy --to file://"$cacheDir"?secret-key="$TEST_ROOT"/sk1 "$outPath" # Downloading should fail if we don't provide a key. clearStore clearCacheCache -(! nix-store -r $outPath --substituters "file://$cacheDir") +(! nix-store -r "$outPath" --substituters "file://$cacheDir") # And it should fail if we provide an incorrect key. clearStore clearCacheCache -(! nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$badKey") +(! nix-store -r "$outPath" --substituters "file://$cacheDir" --trusted-public-keys "$badKey") # It should succeed if we provide the correct key. -nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$otherKey $publicKey" +nix-store -r "$outPath" --substituters "file://$cacheDir" --trusted-public-keys "$otherKey $publicKey" # It should fail if we corrupt the .narinfo. clearStore cacheDir2=$TEST_ROOT/binary-cache-2 -rm -rf $cacheDir2 -cp -r $cacheDir $cacheDir2 +rm -rf "$cacheDir2" +cp -r "$cacheDir" "$cacheDir2" -for i in $cacheDir2/*.narinfo; do - grep -v References $i > $i.tmp - mv $i.tmp $i +for i in "$cacheDir2"/*.narinfo; do + grep -v References "$i" > "$i".tmp + mv "$i".tmp "$i" done clearCacheCache -(! nix-store -r $outPath --substituters "file://$cacheDir2" --trusted-public-keys "$publicKey") +(! nix-store -r "$outPath" --substituters "file://$cacheDir2" --trusted-public-keys "$publicKey") # If we provide a bad and a good binary cache, it should succeed. -nix-store -r $outPath --substituters "file://$cacheDir2 file://$cacheDir" --trusted-public-keys "$publicKey" +nix-store -r "$outPath" --substituters "file://$cacheDir2 file://$cacheDir" --trusted-public-keys "$publicKey" unset _NIX_FORCE_HTTP # Test 'nix verify --all' on a binary cache. -nix store verify -vvvvv --all --store file://$cacheDir --no-trust +nix store verify -vvvvv --all --store file://"$cacheDir" --no-trust # Test local NAR caching. narCache=$TEST_ROOT/nar-cache -rm -rf $narCache -mkdir $narCache +rm -rf "$narCache" +mkdir "$narCache" -[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" $outPath/foobar) = FOOBAR ]] +[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" "$outPath/foobar") = FOOBAR ]] rm -rfv "$cacheDir/nar" -[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" $outPath/foobar) = FOOBAR ]] +[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" "$outPath/foobar") = FOOBAR ]] -(! nix store cat --store file://$cacheDir $outPath/foobar) +(! nix store cat --store file://"$cacheDir" "$outPath/foobar") # Test NAR listing generation. clearCache + +# preserve quotes variables in the single-quoted string +# shellcheck disable=SC2016 outPath=$(nix-build --no-out-link -E ' with import ./config.nix; mkDerivation { @@ -240,16 +243,18 @@ outPath=$(nix-build --no-out-link -E ' } ') -nix copy --to file://$cacheDir?write-nar-listing=1 $outPath +nix copy --to file://"$cacheDir"?write-nar-listing=1 "$outPath" diff -u \ - <(jq -S < $cacheDir/$(basename $outPath | cut -c1-32).ls) \ + <(jq -S < "$cacheDir/$(basename "$outPath" | cut -c1-32).ls") \ <(echo '{"version":1,"root":{"type":"directory","entries":{"bar":{"type":"regular","size":4,"narOffset":232},"link":{"type":"symlink","target":"xyzzy"}}}}' | jq -S) # Test debug info index generation. clearCache +# preserve quotes variables in the single-quoted string +# shellcheck disable=SC2016 outPath=$(nix-build --no-out-link -E ' with import ./config.nix; mkDerivation { @@ -258,14 +263,16 @@ outPath=$(nix-build --no-out-link -E ' } ') -nix copy --to "file://$cacheDir?index-debug-info=1&compression=none" $outPath +nix copy --to "file://$cacheDir?index-debug-info=1&compression=none" "$outPath" diff -u \ - <(cat $cacheDir/debuginfo/02623eda209c26a59b1a8638ff7752f6b945c26b.debug | jq -S) \ + <(jq -S < "$cacheDir"/debuginfo/02623eda209c26a59b1a8638ff7752f6b945c26b.debug) \ <(echo '{"archive":"../nar/100vxs724qr46phz8m24iswmg9p3785hsyagz0kchf6q6gf06sw6.nar","member":"lib/debug/.build-id/02/623eda209c26a59b1a8638ff7752f6b945c26b.debug"}' | jq -S) # Test against issue https://github.com/NixOS/nix/issues/3964 -# + +# preserve quotes variables in the single-quoted string +# shellcheck disable=SC2016 expr=' with import ./config.nix; mkDerivation { @@ -275,22 +282,22 @@ expr=' } ' outPath=$(nix-build --no-out-link -E "$expr") -docPath=$(nix-store -q --references $outPath) +docPath=$(nix-store -q --references "$outPath") # $ nix-store -q --tree $outPath # ...-multi-output # +---...-multi-output-doc -nix copy --to "file://$cacheDir" $outPath +nix copy --to "file://$cacheDir" "$outPath" hashpart() { basename "$1" | cut -c1-32 } # break the closure of out by removing doc -rm $cacheDir/$(hashpart $docPath).narinfo +rm "$cacheDir/$(hashpart "$docPath")".narinfo -nix-store --delete $outPath $docPath +nix-store --delete "$outPath" "$docPath" # -vvv is the level that logs during the loop timeout 60 nix-build --no-out-link -E "$expr" --option substituters "file://$cacheDir" \ --option trusted-binary-caches "file://$cacheDir" --no-require-sigs From 7186c68f75530f9590c8b6e7c14dd10d1bf6ad81 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:47:35 -0700 Subject: [PATCH 267/910] housekeeping: shellcheck for tests/functional/brotli.sh Co-authored-by: Sandro --- tests/functional/brotli.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/brotli.sh b/tests/functional/brotli.sh index 02a2a0875..672e771c2 100755 --- a/tests/functional/brotli.sh +++ b/tests/functional/brotli.sh @@ -9,15 +9,15 @@ cacheURI="file://$cacheDir?compression=br" outPath=$(nix-build dependencies.nix --no-out-link) -nix copy --to $cacheURI $outPath +nix copy --to "$cacheURI" "$outPath" -HASH=$(nix hash path $outPath) +HASH=$(nix hash path "$outPath") clearStore clearCacheCache -nix copy --from $cacheURI $outPath --no-check-sigs +nix copy --from "$cacheURI" "$outPath" --no-check-sigs -HASH2=$(nix hash path $outPath) +HASH2=$(nix hash path "$outPath") -[[ $HASH = $HASH2 ]] +[[ $HASH == "$HASH2" ]] From f615489e0edd5113979d8506a8d0d8b54d5b8217 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:47:41 -0700 Subject: [PATCH 268/910] housekeeping: shellcheck for tests/functional/build-delete.sh --- tests/functional/build-delete.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh index 2ef3008f6..59cf95bd2 100755 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -6,25 +6,25 @@ clearStore # https://github.com/NixOS/nix/issues/6572 issue_6572_independent_outputs() { - nix build -f multiple-outputs.nix --json independent --no-link > $TEST_ROOT/independent.json + nix build -f multiple-outputs.nix --json independent --no-link > "$TEST_ROOT"/independent.json # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation. p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths) nix-store --delete "$p" # Clean up for next test # Make sure that 'nix build' tracks input-outputs correctly when a single output is already present. - nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.first)" + nix-store --delete "$(jq -r <"$TEST_ROOT"/independent.json .[0].outputs.first)" p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths) - cmp $p < $TEST_ROOT/a.json + nix build -f multiple-outputs.nix --json a --no-link > "$TEST_ROOT"/a.json # # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation. p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths) nix-store --delete "$p" # Clean up for next test # Make sure that 'nix build' tracks input-outputs correctly when a single output is already present. - nix-store --delete "$(jq -r <$TEST_ROOT/a.json .[0].outputs.second)" + nix-store --delete "$(jq -r <"$TEST_ROOT"/a.json .[0].outputs.second)" p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths) - cmp $p < Date: Mon, 3 Jun 2024 13:47:43 -0700 Subject: [PATCH 269/910] housekeeping: shellcheck for tests/functional/build-dry.sh --- tests/functional/build-dry.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/functional/build-dry.sh b/tests/functional/build-dry.sh index 9336cf745..dca5888a6 100755 --- a/tests/functional/build-dry.sh +++ b/tests/functional/build-dry.sh @@ -35,17 +35,17 @@ clearStore clearCache RESULT=$TEST_ROOT/result-link -rm -f $RESULT +rm -f "$RESULT" -nix-build dependencies.nix -o $RESULT --dry-run +nix-build dependencies.nix -o "$RESULT" --dry-run [[ ! -h $RESULT ]] || fail "nix-build --dry-run created output link" -nix build -f dependencies.nix -o $RESULT --dry-run +nix build -f dependencies.nix -o "$RESULT" --dry-run [[ ! -h $RESULT ]] || fail "nix build --dry-run created output link" -nix build -f dependencies.nix -o $RESULT +nix build -f dependencies.nix -o "$RESULT" [[ -h $RESULT ]] @@ -58,12 +58,12 @@ RES=$(nix build -f dependencies.nix --dry-run --json) if [[ -z "${NIX_TESTS_CA_BY_DEFAULT-}" ]]; then echo "$RES" | jq '.[0] | [ - (.drvPath | test("'$NIX_STORE_DIR'.*\\.drv")), - (.outputs.out | test("'$NIX_STORE_DIR'")) + (.drvPath | test("'"$NIX_STORE_DIR"'.*\\.drv")), + (.outputs.out | test("'"$NIX_STORE_DIR"'")) ] | all' else echo "$RES" | jq '.[0] | [ - (.drvPath | test("'$NIX_STORE_DIR'.*\\.drv")), + (.drvPath | test("'"$NIX_STORE_DIR"'.*\\.drv")), .outputs.out == null ] | all' fi From 80c44138cb62f41f6b198d8a3a317a650f11f64e Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:50:30 -0700 Subject: [PATCH 270/910] housekeeping: shellcheck for tests/functional/ca/build-cache.sh Co-authored-by: Sandro --- .shellcheckrc | 2 ++ tests/functional/binary-cache.sh | 22 +++++++++++----------- tests/functional/ca/build-cache.sh | 7 ++++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.shellcheckrc b/.shellcheckrc index 662e2045c..de98055f7 100644 --- a/.shellcheckrc +++ b/.shellcheckrc @@ -1,2 +1,4 @@ external-sources=true source-path=SCRIPTDIR +# Hack for scripts in e.g. tests/functional/ca +source-path=SCRIPTDIR/.. diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh index 23135359b..5ef6d89d4 100755 --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -14,9 +14,9 @@ clearStore clearCache outPath=$(nix-build dependencies.nix --no-out-link) -nix copy --to file://"$cacheDir" "$outPath" +nix copy --to "file://$cacheDir" "$outPath" -readarray -t paths < <(nix path-info --all --json --store file://"$cacheDir" | jq 'keys|sort|.[]' -r) +readarray -t paths < <(nix path-info --all --json --store "file://$cacheDir" | jq 'keys|sort|.[]' -r) [[ "${#paths[@]}" -eq 3 ]] for path in "${paths[@]}"; do [[ "$path" =~ -dependencies-input-0$ ]] \ @@ -25,15 +25,15 @@ for path in "${paths[@]}"; do done # Test copying build logs to the binary cache. -expect 1 nix log --store file://"$cacheDir" "$outPath" 2>&1 | grep 'is not available' -nix store copy-log --to file://"$cacheDir" "$outPath" -nix log --store file://"$cacheDir" "$outPath" | grep FOO +expect 1 nix log --store "file://$cacheDir" "$outPath" 2>&1 | grep 'is not available' +nix store copy-log --to "file://$cacheDir" "$outPath" +nix log --store "file://$cacheDir" "$outPath" | grep FOO rm -rf "$TEST_ROOT/var/log/nix" expect 1 nix log "$outPath" 2>&1 | grep 'is not available' -nix log --substituters file://"$cacheDir" "$outPath" | grep FOO +nix log --substituters "file://$cacheDir" "$outPath" | grep FOO # Test copying build logs from the binary cache. -nix store copy-log --from file://"$cacheDir" "$(nix-store -qd "$outPath")"^'*' +nix store copy-log --from "file://$cacheDir" "$(nix-store -qd "$outPath")"^'*' nix log "$outPath" | grep FOO basicDownloadTests() { @@ -166,7 +166,7 @@ badKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk2") nix key generate-secret --key-name foo.nixos.org-1 > "$TEST_ROOT/sk3" otherKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk3") -_NIX_FORCE_HTTP='' nix copy --to file://"$cacheDir"?secret-key="$TEST_ROOT"/sk1 "$outPath" +_NIX_FORCE_HTTP='' nix copy --to "file://$cacheDir"?secret-key="$TEST_ROOT"/sk1 "$outPath" # Downloading should fail if we don't provide a key. @@ -212,7 +212,7 @@ unset _NIX_FORCE_HTTP # Test 'nix verify --all' on a binary cache. -nix store verify -vvvvv --all --store file://"$cacheDir" --no-trust +nix store verify -vvvvv --all --store "file://$cacheDir" --no-trust # Test local NAR caching. @@ -226,7 +226,7 @@ rm -rfv "$cacheDir/nar" [[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" "$outPath/foobar") = FOOBAR ]] -(! nix store cat --store file://"$cacheDir" "$outPath/foobar") +(! nix store cat --store "file://$cacheDir" "$outPath/foobar") # Test NAR listing generation. @@ -243,7 +243,7 @@ outPath=$(nix-build --no-out-link -E ' } ') -nix copy --to file://"$cacheDir"?write-nar-listing=1 "$outPath" +nix copy --to "file://$cacheDir"?write-nar-listing=1 "$outPath" diff -u \ <(jq -S < "$cacheDir/$(basename "$outPath" | cut -c1-32).ls") \ diff --git a/tests/functional/ca/build-cache.sh b/tests/functional/ca/build-cache.sh index 6a4080fec..5cc71823e 100644 --- a/tests/functional/ca/build-cache.sh +++ b/tests/functional/ca/build-cache.sh @@ -26,7 +26,8 @@ copyAttr () { # Note: to copy CA derivations, we need to copy the realisations, which # currently requires naming the installables, not just the derivation output # path. - nix copy --to file://$cacheDir "${args[@]}" + + nix copy --to "file://$cacheDir" "${args[@]}" } testRemoteCacheFor () { @@ -35,7 +36,7 @@ testRemoteCacheFor () { copyAttr "$derivationPath" 1 clearStore # Check nothing gets built. - buildAttr "$derivationPath" 1 --option substituters file://$cacheDir --no-require-sigs |& grepQuietInverse " will be built:" + buildAttr "$derivationPath" 1 --option substituters "file://$cacheDir" --no-require-sigs |& grepQuietInverse " will be built:" } testRemoteCache () { @@ -48,4 +49,4 @@ testRemoteCache () { } clearStore -testRemoteCache \ No newline at end of file +testRemoteCache From 627176fd54ebbd7a8411fd736f5ca6c7cd02e197 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:50:50 -0700 Subject: [PATCH 271/910] housekeeping: shellcheck for tests/functional/ca/build.sh --- tests/functional/ca/build.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/ca/build.sh b/tests/functional/ca/build.sh index e1a8a7625..e5ad9d2a0 100644 --- a/tests/functional/ca/build.sh +++ b/tests/functional/ca/build.sh @@ -20,11 +20,11 @@ testDeterministicCA () { testCutoffFor () { local out1 out2 - out1=$(buildAttr $1 1) + out1=$(buildAttr "$1" 1) # The seed only changes the root derivation, and not it's output, so the # dependent derivations should only need to be built once. buildAttr rootCA 2 - out2=$(buildAttr $1 2 -j0) + out2=$(buildAttr "$1" 2 -j0) test "$out1" == "$out2" } @@ -41,7 +41,7 @@ testGC () { nix-instantiate ./content-addressed.nix -A rootCA --arg seed 5 nix-collect-garbage --option keep-derivations true clearStore - buildAttr rootCA 1 --out-link $TEST_ROOT/rootCA + buildAttr rootCA 1 --out-link "$TEST_ROOT"/rootCA nix-collect-garbage buildAttr rootCA 1 -j0 } @@ -55,7 +55,7 @@ testNixCommand () { testNormalization () { clearStore outPath=$(buildAttr rootCA 1) - test "$(stat -c %Y $outPath)" -eq 1 + test "$(stat -c %Y "$outPath")" -eq 1 } clearStore From 2dfbba3e5e075ecbf6ddd3fb90ecee3003db85ec Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:52:29 -0700 Subject: [PATCH 272/910] housekeeping: shellcheck for tests/functional/ca/derivation-json.sh --- tests/functional/ca/derivation-json.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/functional/ca/derivation-json.sh b/tests/functional/ca/derivation-json.sh index c1480fd17..1e2a8fe35 100644 --- a/tests/functional/ca/derivation-json.sh +++ b/tests/functional/ca/derivation-json.sh @@ -1,29 +1,31 @@ +#!/usr/bin/env bash +# source common.sh export NIX_TESTS_CA_BY_DEFAULT=1 drvPath=$(nix-instantiate ../simple.nix) -nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json +nix derivation show "$drvPath" | jq .[] > "$TEST_HOME"/simple.json -drvPath2=$(nix derivation add < $TEST_HOME/simple.json) +drvPath2=$(nix derivation add < "$TEST_HOME"/simple.json) [[ "$drvPath" = "$drvPath2" ]] # Content-addressed derivations can be renamed. -jq '.name = "foo"' < $TEST_HOME/simple.json > $TEST_HOME/foo.json -drvPath3=$(nix derivation add --dry-run < $TEST_HOME/foo.json) +jq '.name = "foo"' < "$TEST_HOME"/simple.json > "$TEST_HOME"/foo.json +drvPath3=$(nix derivation add --dry-run < "$TEST_HOME"/foo.json) # With --dry-run nothing is actually written [[ ! -e "$drvPath3" ]] # But the JSON is rejected without the experimental feature -expectStderr 1 nix derivation add < $TEST_HOME/foo.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'ca-derivations' is disabled" +expectStderr 1 nix derivation add < "$TEST_HOME"/foo.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'ca-derivations' is disabled" # Without --dry-run it is actually written -drvPath4=$(nix derivation add < $TEST_HOME/foo.json) +drvPath4=$(nix derivation add < "$TEST_HOME"/foo.json) [[ "$drvPath4" = "$drvPath3" ]] [[ -e "$drvPath3" ]] # The modified derivation read back as JSON matches -nix derivation show $drvPath3 | jq .[] > $TEST_HOME/foo-read.json -diff $TEST_HOME/foo.json $TEST_HOME/foo-read.json +nix derivation show "$drvPath3" | jq .[] > "$TEST_HOME"/foo-read.json +diff "$TEST_HOME"/foo.json "$TEST_HOME"/foo-read.json From 195c0da849e924ac0eee3583cace931eca158a3f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:54:17 -0700 Subject: [PATCH 273/910] housekeeping: shellcheck for tests/functional/ca/duplicate-realisation-in-closure.sh --- tests/functional/ca/duplicate-realisation-in-closure.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/ca/duplicate-realisation-in-closure.sh b/tests/functional/ca/duplicate-realisation-in-closure.sh index da9cd8fb4..0baf15cc2 100644 --- a/tests/functional/ca/duplicate-realisation-in-closure.sh +++ b/tests/functional/ca/duplicate-realisation-in-closure.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireDaemonNewerThan "2.4pre20210625" @@ -5,7 +7,7 @@ requireDaemonNewerThan "2.4pre20210625" export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" export REMOTE_STORE="file://$REMOTE_STORE_DIR" -rm -rf $REMOTE_STORE_DIR +rm -rf "$REMOTE_STORE_DIR" clearStore # Build dep1 and push that to the binary cache. From deacc421eb5bc53260ece90ce8cd1593ea7f7769 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:54:41 -0700 Subject: [PATCH 274/910] housekeeping: shellcheck for tests/functional/ca/nix-copy.sh --- tests/functional/ca/nix-copy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/ca/nix-copy.sh b/tests/functional/ca/nix-copy.sh index 7a8307a4e..f77b00030 100755 --- a/tests/functional/ca/nix-copy.sh +++ b/tests/functional/ca/nix-copy.sh @@ -15,13 +15,13 @@ testOneCopy () { rm -rf "$REMOTE_STORE_DIR" attrPath="$1" - nix copy --to $REMOTE_STORE "$attrPath" --file ./content-addressed.nix + nix copy --to "$REMOTE_STORE" "$attrPath" --file ./content-addressed.nix ensureCorrectlyCopied "$attrPath" # Ensure that we can copy back what we put in the store clearStore - nix copy --from $REMOTE_STORE \ + nix copy --from "$REMOTE_STORE" \ --file ./content-addressed.nix "$attrPath" \ --no-check-sigs } From 8f8553762960de9b1ff0d8add46ab9bc18cd76c5 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:54:42 -0700 Subject: [PATCH 275/910] housekeeping: shellcheck for tests/functional/ca/nix-run.sh --- tests/functional/ca/nix-run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/ca/nix-run.sh b/tests/functional/ca/nix-run.sh index 5f46518e8..920950c11 100755 --- a/tests/functional/ca/nix-run.sh +++ b/tests/functional/ca/nix-run.sh @@ -4,4 +4,4 @@ source common.sh FLAKE_PATH=path:$PWD -nix run --no-write-lock-file $FLAKE_PATH#runnable +nix run --no-write-lock-file "$FLAKE_PATH#runnable" From 04876c39e46758a7445d390aa679b090248f7c4f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:57:25 -0700 Subject: [PATCH 276/910] housekeeping: shellcheck for tests/functional/ca/signatures.sh --- tests/functional/ca/signatures.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/functional/ca/signatures.sh b/tests/functional/ca/signatures.sh index eb18a4130..f69a205d2 100644 --- a/tests/functional/ca/signatures.sh +++ b/tests/functional/ca/signatures.sh @@ -1,10 +1,12 @@ +#!/usr/bin/env bash + source common.sh clearStore clearCache -nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 -pk1=$(cat $TEST_ROOT/pk1) +nix-store --generate-binary-cache-key cache1.example.org "$TEST_ROOT/sk1" "$TEST_ROOT/pk1" +pk1=$(cat "$TEST_ROOT/pk1") export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" export REMOTE_STORE="file://$REMOTE_STORE_DIR" @@ -19,16 +21,16 @@ testOneCopy () { rm -rf "$REMOTE_STORE_DIR" attrPath="$1" - nix copy -vvvv --to $REMOTE_STORE "$attrPath" --file ./content-addressed.nix \ + nix copy -vvvv --to "$REMOTE_STORE" "$attrPath" --file ./content-addressed.nix \ --secret-key-files "$TEST_ROOT/sk1" --show-trace ensureCorrectlyCopied "$attrPath" # Ensure that we can copy back what we put in the store clearStore - nix copy --from $REMOTE_STORE \ + nix copy --from "$REMOTE_STORE" \ --file ./content-addressed.nix "$attrPath" \ - --trusted-public-keys $pk1 + --trusted-public-keys "$pk1" } for attrPath in rootCA dependentCA transitivelyDependentCA dependentNonCA dependentFixedOutput; do From 259b502773849afe385b473c48e852eb7bb7e9bd Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:14:23 -0700 Subject: [PATCH 277/910] housekeeping: shellcheck for tests/functional/ca/substitute.sh --- tests/functional/ca/substitute.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/functional/ca/substitute.sh b/tests/functional/ca/substitute.sh index ea981adc4..76cedb4bc 100644 --- a/tests/functional/ca/substitute.sh +++ b/tests/functional/ca/substitute.sh @@ -4,9 +4,10 @@ source common.sh +# shellcheck disable=SC1111 needLocalStore "“--no-require-sigs” can’t be used with the daemon" -rm -rf $TEST_ROOT/binary_cache +rm -rf "$TEST_ROOT"/binary_cache export REMOTE_STORE_DIR=$TEST_ROOT/binary_cache export REMOTE_STORE=file://$REMOTE_STORE_DIR @@ -17,11 +18,11 @@ buildDrvs () { # Populate the remote cache clearStore -nix copy --to $REMOTE_STORE --file ./content-addressed.nix +nix copy --to "$REMOTE_STORE" --file ./content-addressed.nix # Restart the build on an empty store, ensuring that we don't build clearStore -buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 transitivelyDependentCA +buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 transitivelyDependentCA # Check that the thing we’ve just substituted has its realisation stored nix realisation info --file ./content-addressed.nix transitivelyDependentCA # Check that its dependencies have it too @@ -63,9 +64,9 @@ clearStore # Add the realisations of rootCA to the cachecache clearCacheCache export _NIX_FORCE_HTTP=1 -buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 +buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 # Try rebuilding, but remove the realisations from the remote cache to force # using the cachecache clearStore -rm $REMOTE_STORE_DIR/realisations/* -buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 +rm "$REMOTE_STORE_DIR"/realisations/* +buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 From d7bb5bde48acbc9be46933344cc6c7d47c43a1df Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:14:46 -0700 Subject: [PATCH 278/910] housekeeping: shellcheck for tests/functional/check-refs.sh --- tests/functional/check-refs.sh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh index 2cebdd84d..6534e55c6 100755 --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -6,42 +6,42 @@ clearStore RESULT=$TEST_ROOT/result -dep=$(nix-build -o $RESULT check-refs.nix -A dep) +dep=$(nix-build -o "$RESULT" check-refs.nix -A dep) # test1 references dep, not itself. -test1=$(nix-build -o $RESULT check-refs.nix -A test1) -nix-store -q --references $test1 | grepQuietInverse $test1 -nix-store -q --references $test1 | grepQuiet $dep +test1=$(nix-build -o "$RESULT" check-refs.nix -A test1) +nix-store -q --references "$test1" | grepQuietInverse "$test1" +nix-store -q --references "$test1" | grepQuiet "$dep" # test2 references src, not itself nor dep. -test2=$(nix-build -o $RESULT check-refs.nix -A test2) -nix-store -q --references $test2 | grepQuietInverse $test2 -nix-store -q --references $test2 | grepQuietInverse $dep -nix-store -q --references $test2 | grepQuiet aux-ref +test2=$(nix-build -o "$RESULT" check-refs.nix -A test2) +nix-store -q --references "$test2" | grepQuietInverse "$test2" +nix-store -q --references "$test2" | grepQuietInverse "$dep" +nix-store -q --references "$test2" | grepQuiet aux-ref # test3 should fail (unallowed ref). -(! nix-build -o $RESULT check-refs.nix -A test3) +(! nix-build -o "$RESULT" check-refs.nix -A test3) # test4 should succeed. -nix-build -o $RESULT check-refs.nix -A test4 +nix-build -o "$RESULT" check-refs.nix -A test4 # test5 should succeed. -nix-build -o $RESULT check-refs.nix -A test5 +nix-build -o "$RESULT" check-refs.nix -A test5 # test6 should fail (unallowed self-ref). -(! nix-build -o $RESULT check-refs.nix -A test6) +(! nix-build -o "$RESULT" check-refs.nix -A test6) # test7 should succeed (allowed self-ref). -nix-build -o $RESULT check-refs.nix -A test7 +nix-build -o "$RESULT" check-refs.nix -A test7 # test8 should fail (toFile depending on derivation output). -(! nix-build -o $RESULT check-refs.nix -A test8) +(! nix-build -o "$RESULT" check-refs.nix -A test8) # test9 should fail (disallowed reference). -(! nix-build -o $RESULT check-refs.nix -A test9) +(! nix-build -o "$RESULT" check-refs.nix -A test9) # test10 should succeed (no disallowed references). -nix-build -o $RESULT check-refs.nix -A test10 +nix-build -o "$RESULT" check-refs.nix -A test10 if isDaemonNewer 2.12pre20230103; then if ! isDaemonNewer 2.16.0; then @@ -50,6 +50,6 @@ if isDaemonNewer 2.12pre20230103; then fi # test11 should succeed. - test11=$(nix-build -o $RESULT check-refs.nix -A test11) + test11=$(nix-build -o "$RESULT" check-refs.nix -A test11) [[ -z $(nix-store -q --references "$test11") ]] fi From 4f04006bc14c865acae67ee1890b72af2bd430a6 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:14:48 -0700 Subject: [PATCH 279/910] housekeeping: shellcheck for tests/functional/check-reqs.sh --- tests/functional/check-reqs.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh index 2bcd558fd..4d795391e 100755 --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -6,13 +6,13 @@ clearStore RESULT=$TEST_ROOT/result -nix-build -o $RESULT check-reqs.nix -A test1 +nix-build -o "$RESULT" check-reqs.nix -A test1 -(! nix-build -o $RESULT check-reqs.nix -A test2) -(! nix-build -o $RESULT check-reqs.nix -A test3) -(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep1' -(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep2' -(! nix-build -o $RESULT check-reqs.nix -A test5) -(! nix-build -o $RESULT check-reqs.nix -A test6) +(! nix-build -o "$RESULT" check-reqs.nix -A test2) +(! nix-build -o "$RESULT" check-reqs.nix -A test3) +(! nix-build -o "$RESULT" check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep1' +(! nix-build -o "$RESULT" check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep2' +(! nix-build -o "$RESULT" check-reqs.nix -A test5) +(! nix-build -o "$RESULT" check-reqs.nix -A test6) -nix-build -o $RESULT check-reqs.nix -A test7 +nix-build -o "$RESULT" check-reqs.nix -A test7 From 63272235e282ad0f075677883eb2812a9c68cec2 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:51:07 -0700 Subject: [PATCH 280/910] housekeeping: shellcheck for tests/functional/case-hacks.sh Co-authored-by: Sandro --- tests/functional/case-hack.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/functional/case-hack.sh b/tests/functional/case-hack.sh index fbc8242ff..48a2ab13f 100755 --- a/tests/functional/case-hack.sh +++ b/tests/functional/case-hack.sh @@ -4,18 +4,19 @@ source common.sh clearStore -rm -rf $TEST_ROOT/case +rm -rf "$TEST_ROOT/case" -opts="--option use-case-hack true" +opts=("--option" "use-case-hack" "true") # Check whether restoring and dumping a NAR that contains case # collisions is round-tripping, even on a case-insensitive system. -nix-store $opts --restore $TEST_ROOT/case < case.nar -nix-store $opts --dump $TEST_ROOT/case > $TEST_ROOT/case.nar -cmp case.nar $TEST_ROOT/case.nar -[ "$(nix-hash $opts --type sha256 $TEST_ROOT/case)" = "$(nix-hash --flat --type sha256 case.nar)" ] + +nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case.nar +nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > "$TEST_ROOT/case.nar" +cmp case.nar "$TEST_ROOT/case.nar" +[ "$(nix-hash "${opts[@]}" --type sha256 "$TEST_ROOT/case")" = "$(nix-hash --flat --type sha256 case.nar)" ] # Check whether we detect true collisions (e.g. those remaining after # removal of the suffix). touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3" -(! nix-store $opts --dump $TEST_ROOT/case > /dev/null) +(! nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > /dev/null) From 48520cb71ee2b068e66b26b37554cba8ff3b69cc Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:23:17 -0700 Subject: [PATCH 281/910] housekeeping: shellcheck for tests/functional/chroot-store.sh --- tests/functional/chroot-store.sh | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/functional/chroot-store.sh b/tests/functional/chroot-store.sh index 60b9c50a7..741907fca 100755 --- a/tests/functional/chroot-store.sh +++ b/tests/functional/chroot-store.sh @@ -2,34 +2,34 @@ source common.sh -echo example > $TEST_ROOT/example.txt -mkdir -p $TEST_ROOT/x +echo example > "$TEST_ROOT"/example.txt +mkdir -p "$TEST_ROOT/x" export NIX_STORE_DIR=/nix2/store -CORRECT_PATH=$(cd $TEST_ROOT && nix-store --store ./x --add example.txt) +CORRECT_PATH=$(cd "$TEST_ROOT" && nix-store --store ./x --add example.txt) [[ $CORRECT_PATH =~ ^/nix2/store/.*-example.txt$ ]] -PATH1=$(cd $TEST_ROOT && nix path-info --store ./x $CORRECT_PATH) -[ $CORRECT_PATH == $PATH1 ] +PATH1=$(cd "$TEST_ROOT" && nix path-info --store ./x "$CORRECT_PATH") +[ "$CORRECT_PATH" == "$PATH1" ] -PATH2=$(nix path-info --store "$TEST_ROOT/x" $CORRECT_PATH) -[ $CORRECT_PATH == $PATH2 ] +PATH2=$(nix path-info --store "$TEST_ROOT/x" "$CORRECT_PATH") +[ "$CORRECT_PATH" == "$PATH2" ] -PATH3=$(nix path-info --store "local?root=$TEST_ROOT/x" $CORRECT_PATH) -[ $CORRECT_PATH == $PATH3 ] +PATH3=$(nix path-info --store "local?root=$TEST_ROOT/x" "$CORRECT_PATH") +[ "$CORRECT_PATH" == "$PATH3" ] # Ensure store info trusted works with local store -nix --store $TEST_ROOT/x store info --json | jq -e '.trusted' +nix --store "$TEST_ROOT/x" store info --json | jq -e '.trusted' # Test building in a chroot store. if canUseSandbox; then flakeDir=$TEST_ROOT/flake - mkdir -p $flakeDir + mkdir -p "$flakeDir" - cat > $flakeDir/flake.nix < "$flakeDir"/flake.nix < Date: Tue, 4 Jun 2024 13:32:30 -0700 Subject: [PATCH 282/910] housekeeping: shellcheck for tests/functional/compression-levels.sh --- tests/functional/compression-levels.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh index 34f66c531..6a2111f10 100755 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -9,16 +9,16 @@ outPath=$(nix-build dependencies.nix --no-out-link) cacheURI="file://$cacheDir?compression=xz&compression-level=0" -nix copy --to $cacheURI $outPath +nix copy --to "$cacheURI" "$outPath" -FILESIZES=$(cat ${cacheDir}/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') +FILESIZES=$(cat "${cacheDir}"/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') clearCache cacheURI="file://$cacheDir?compression=xz&compression-level=5" -nix copy --to $cacheURI $outPath +nix copy --to "$cacheURI" "$outPath" -FILESIZES2=$(cat ${cacheDir}/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') +FILESIZES2=$(cat "${cacheDir}"/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') [[ $FILESIZES -gt $FILESIZES2 ]] From 847842c4bbb33dcde0fbdad557b3776482a28a87 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:33 -0700 Subject: [PATCH 283/910] housekeeping: shellcheck for tests/functional/derivation-json.sh --- tests/functional/derivation-json.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/derivation-json.sh b/tests/functional/derivation-json.sh index 59c77e6c5..06f934cfe 100755 --- a/tests/functional/derivation-json.sh +++ b/tests/functional/derivation-json.sh @@ -4,11 +4,11 @@ source common.sh drvPath=$(nix-instantiate simple.nix) -nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json +nix derivation show "$drvPath" | jq .[] > "$TEST_HOME"/simple.json -drvPath2=$(nix derivation add < $TEST_HOME/simple.json) +drvPath2=$(nix derivation add < "$TEST_HOME"/simple.json) [[ "$drvPath" = "$drvPath2" ]] # Input addressed derivations cannot be renamed. -jq '.name = "foo"' < $TEST_HOME/simple.json | expectStderr 1 nix derivation add | grepQuiet "has incorrect output" +jq '.name = "foo"' < "$TEST_HOME"/simple.json | expectStderr 1 nix derivation add | grepQuiet "has incorrect output" From 1c1abefdd2c7da4873f78c8f71583294a94ae187 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:35 -0700 Subject: [PATCH 284/910] housekeeping: shellcheck for tests/functional/dyn-drv/text-hashed-output.sh --- tests/functional/dyn-drv/text-hashed-output.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/dyn-drv/text-hashed-output.sh b/tests/functional/dyn-drv/text-hashed-output.sh index f3e5aa93b..2cc877219 100644 --- a/tests/functional/dyn-drv/text-hashed-output.sh +++ b/tests/functional/dyn-drv/text-hashed-output.sh @@ -20,7 +20,7 @@ nix show-derivation "$drvProducingDrv" out1=$(nix-build ./text-hashed-output.nix -A producingDrv --no-out-link) -nix path-info $drv --derivation --json | jq -nix path-info $out1 --derivation --json | jq +nix path-info "$drv" --derivation --json | jq +nix path-info "$out1" --derivation --json | jq -test $out1 == $drv +test "$out1" == "$drv" From 823d53c643d195f1663cc3dc5a0aa8e6184505d4 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:40 -0700 Subject: [PATCH 285/910] housekeeping: shellcheck for tests/functional/experimental-features.sh --- tests/functional/experimental-features.sh | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/functional/experimental-features.sh b/tests/functional/experimental-features.sh index 38f198eee..d7216992d 100755 --- a/tests/functional/experimental-features.sh +++ b/tests/functional/experimental-features.sh @@ -33,35 +33,35 @@ source common.sh NIX_CONFIG=' experimental-features = nix-command accept-flake-config = true -' expect 1 nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -[[ $(cat $TEST_ROOT/stdout) = '' ]] -grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr -grepQuiet "error: could not find setting 'accept-flake-config'" $TEST_ROOT/stderr +' expect 1 nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +[[ $(cat "$TEST_ROOT/stdout") = '' ]] +grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" "$TEST_ROOT/stderr" +grepQuiet "error: could not find setting 'accept-flake-config'" "$TEST_ROOT/stderr" # 'flakes' experimental-feature is disabled after, ignore and warn NIX_CONFIG=' accept-flake-config = true experimental-features = nix-command -' expect 1 nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -[[ $(cat $TEST_ROOT/stdout) = '' ]] -grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr -grepQuiet "error: could not find setting 'accept-flake-config'" $TEST_ROOT/stderr +' expect 1 nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +[[ $(cat "$TEST_ROOT/stdout") = '' ]] +grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" "$TEST_ROOT/stderr" +grepQuiet "error: could not find setting 'accept-flake-config'" "$TEST_ROOT/stderr" # 'flakes' experimental-feature is enabled before, process NIX_CONFIG=' experimental-features = nix-command flakes accept-flake-config = true -' nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -grepQuiet "true" $TEST_ROOT/stdout -grepQuietInverse "Ignoring setting 'accept-flake-config'" $TEST_ROOT/stderr +' nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +grepQuiet "true" "$TEST_ROOT/stdout" +grepQuietInverse "Ignoring setting 'accept-flake-config'" "$TEST_ROOT/stderr" # 'flakes' experimental-feature is enabled after, process NIX_CONFIG=' accept-flake-config = true experimental-features = nix-command flakes -' nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -grepQuiet "true" $TEST_ROOT/stdout -grepQuietInverse "Ignoring setting 'accept-flake-config'" $TEST_ROOT/stderr +' nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +grepQuiet "true" "$TEST_ROOT/stdout" +grepQuietInverse "Ignoring setting 'accept-flake-config'" "$TEST_ROOT/stderr" function exit_code_both_ways { expect 1 nix --experimental-features 'nix-command' "$@" 1>/dev/null From f0492a61976ba41063565e903370c0c73e85a8ae Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:57 -0700 Subject: [PATCH 286/910] housekeeping: shellcheck for tests/functional/fetchPath.sh --- tests/functional/fetchPath.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/fetchPath.sh b/tests/functional/fetchPath.sh index e466e4494..560a270c1 100755 --- a/tests/functional/fetchPath.sh +++ b/tests/functional/fetchPath.sh @@ -2,7 +2,7 @@ source common.sh -touch $TEST_ROOT/foo -t 202211111111 +touch "$TEST_ROOT/foo" -t 202211111111 # We only check whether 2022-11-1* **:**:** is the last modified date since # `lastModified` is transformed into UTC in `builtins.fetchTarball`. [[ "$(nix eval --impure --raw --expr "(builtins.fetchTree \"path://$TEST_ROOT/foo\").lastModifiedDate")" =~ 2022111.* ]] From 224f5515b91075e368259102cff40ba1a6b6494d Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:58 -0700 Subject: [PATCH 287/910] housekeeping: shellcheck for tests/functional/fetchTree-file.sh Co-authored-by: Sandro --- tests/functional/fetchTree-file.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/fetchTree-file.sh b/tests/functional/fetchTree-file.sh index 9c9532876..6faccd282 100755 --- a/tests/functional/fetchTree-file.sh +++ b/tests/functional/fetchTree-file.sh @@ -90,7 +90,7 @@ EOF EOF # Test tarball URLs on the command line. - [[ $(nix flake metadata --json file://$PWD/test_input_no_ext | jq -r .resolved.type) = tarball ]] + [[ $(nix flake metadata --json "file://$PWD/test_input_no_ext" | jq -r .resolved.type) = tarball ]] popd From ae6a842c550e3fbb9c75e3ee93a78cf387fe4ec3 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:59 -0700 Subject: [PATCH 288/910] housekeeping: shellcheck for tests/functional/filter-source.sh --- tests/functional/filter-source.sh | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/functional/filter-source.sh b/tests/functional/filter-source.sh index c5e10be93..b32f5b59d 100755 --- a/tests/functional/filter-source.sh +++ b/tests/functional/filter-source.sh @@ -2,26 +2,26 @@ source common.sh -rm -rf $TEST_ROOT/filterin -mkdir $TEST_ROOT/filterin -mkdir $TEST_ROOT/filterin/foo -touch $TEST_ROOT/filterin/foo/bar -touch $TEST_ROOT/filterin/xyzzy -touch $TEST_ROOT/filterin/b -touch $TEST_ROOT/filterin/bak -touch $TEST_ROOT/filterin/bla.c.bak -ln -s xyzzy $TEST_ROOT/filterin/link +rm -rf "$TEST_ROOT/filterin" +mkdir "$TEST_ROOT/filterin" +mkdir "$TEST_ROOT/filterin/foo" +touch "$TEST_ROOT/filterin/foo/bar" +touch "$TEST_ROOT/filterin/xyzzy" +touch "$TEST_ROOT/filterin/b" +touch "$TEST_ROOT/filterin/bak" +touch "$TEST_ROOT"/filterin/bla.c.bak +ln -s xyzzy "$TEST_ROOT/filterin/link" checkFilter() { - test ! -e $1/foo/bar - test -e $1/xyzzy - test -e $1/bak - test ! -e $1/bla.c.bak - test ! -L $1/link + test ! -e "$1/foo/bar" + test -e "$1/xyzzy" + test -e "$1/bak" + test ! -e "$1"/bla.c.bak + test ! -L "$1/link" } -nix-build ./filter-source.nix -o $TEST_ROOT/filterout1 -checkFilter $TEST_ROOT/filterout1 +nix-build ./filter-source.nix -o "$TEST_ROOT/filterout1" +checkFilter "$TEST_ROOT/filterout1" -nix-build ./path.nix -o $TEST_ROOT/filterout2 -checkFilter $TEST_ROOT/filterout2 +nix-build ./path.nix -o "$TEST_ROOT/filterout2" +checkFilter "$TEST_ROOT/filterout2" From d81fd4a1c38a7a06cfbad1728aa9bfcc3753dc6b Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:09 -0700 Subject: [PATCH 289/910] housekeeping: shellcheck for tests/functional/flakes/absolute-attr-paths.sh --- tests/functional/flakes/absolute-attr-paths.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/flakes/absolute-attr-paths.sh b/tests/functional/flakes/absolute-attr-paths.sh index 8ed1755c4..b0e6225d8 100755 --- a/tests/functional/flakes/absolute-attr-paths.sh +++ b/tests/functional/flakes/absolute-attr-paths.sh @@ -4,8 +4,8 @@ source ./common.sh flake1Dir=$TEST_ROOT/flake1 -mkdir -p $flake1Dir -cat > $flake1Dir/flake.nix < "$flake1Dir"/flake.nix < $flake1Dir/flake.nix < Date: Tue, 4 Jun 2024 13:33:10 -0700 Subject: [PATCH 290/910] housekeeping: shellcheck for tests/functional/flakes/build-paths.sh --- tests/functional/flakes/build-paths.sh | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh index a336471f0..f8486528b 100755 --- a/tests/functional/flakes/build-paths.sh +++ b/tests/functional/flakes/build-paths.sh @@ -5,15 +5,15 @@ source ./common.sh flake1Dir=$TEST_ROOT/flake1 flake2Dir=$TEST_ROOT/flake2 -mkdir -p $flake1Dir $flake2Dir +mkdir -p "$flake1Dir" "$flake2Dir" -writeSimpleFlake $flake2Dir -tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake2 -hash=$(nix hash path $flake2Dir) +writeSimpleFlake "$flake2Dir" +tar cfz "$TEST_ROOT"/flake.tar.gz -C "$TEST_ROOT" flake2 +hash=$(nix hash path "$flake2Dir") dep=$(nix store add-path ./common.sh) -cat > $flake1Dir/flake.nix < "$flake1Dir"/flake.nix < $flake1Dir/flake.nix < $flake1Dir/foo +echo bar > "$flake1Dir/foo" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a1 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a1" [[ -e $TEST_ROOT/result/simple.nix ]] -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a2 -[[ $(cat $TEST_ROOT/result) = bar ]] +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a2" +[[ $(cat "$TEST_ROOT/result") = bar ]] -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a3 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a3" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a4 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a4" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a6 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a6" [[ -e $TEST_ROOT/result/simple.nix ]] -nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a8 -diff common.sh $TEST_ROOT/result +nix build --impure --json --out-link "$TEST_ROOT/result" "$flake1Dir#a8" +diff common.sh "$TEST_ROOT/result" -expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a9 \ +expectStderr 1 nix build --impure --json --out-link "$TEST_ROOT/result" "$flake1Dir#a9" \ | grepQuiet "has 0 entries in its context. It should only have exactly one entry" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a10 -[[ $(readlink -e $TEST_ROOT/result) = *simple.drv ]] +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir"#a10 +[[ $(readlink -e "$TEST_ROOT/result") = *simple.drv ]] -expectStderr 1 nix build --json --out-link $TEST_ROOT/result $flake1Dir#a11 \ +expectStderr 1 nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a11" \ | grepQuiet "has a context which refers to a complete source and binary closure" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a12 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a12" [[ -e $TEST_ROOT/result/hello ]] -expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a13 \ +expectStderr 1 nix build --impure --json --out-link "$TEST_ROOT/result" "$flake1Dir#a13" \ | grepQuiet "has 2 entries in its context. It should only have exactly one entry" # Test accessing output in installables with `.` (foobarbaz.) -nix build --json --no-link $flake1Dir#a14.foo | jq --exit-status ' +nix build --json --no-link "$flake1Dir"#a14.foo | jq --exit-status ' (.[0] | (.drvPath | match(".*dot-installable.drv")) and (.outputs | keys == ["foo"])) From ece86b719150fe5122f78b24e3f46602b6656973 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:11 -0700 Subject: [PATCH 291/910] housekeeping: shellcheck for tests/functional/flakes/bundle.sh --- tests/functional/flakes/bundle.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/flakes/bundle.sh b/tests/functional/flakes/bundle.sh index 711691e0b..5e185cbf6 100755 --- a/tests/functional/flakes/bundle.sh +++ b/tests/functional/flakes/bundle.sh @@ -2,9 +2,9 @@ source common.sh -cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME +cp ../simple.nix ../simple.builder.sh ../config.nix "$TEST_HOME" -cd $TEST_HOME +cd "$TEST_HOME" cat < flake.nix { @@ -27,8 +27,8 @@ EOF nix build .# nix bundle --bundler .# .# -nix bundle --bundler .#bundlers.$system.default .#packages.$system.default -nix bundle --bundler .#bundlers.$system.simple .#packages.$system.default +nix bundle --bundler .#bundlers."$system".default .#packages."$system".default +nix bundle --bundler .#bundlers."$system".simple .#packages."$system".default -nix bundle --bundler .#bundlers.$system.default .#apps.$system.default -nix bundle --bundler .#bundlers.$system.simple .#apps.$system.default +nix bundle --bundler .#bundlers."$system".default .#apps."$system".default +nix bundle --bundler .#bundlers."$system".simple .#apps."$system".default From 3b853e795beab3f5f7117b47ae0b3be42882fac4 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:12 -0700 Subject: [PATCH 292/910] housekeeping: shellcheck for tests/functional/flakes/circular.sh --- tests/functional/flakes/circular.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/functional/flakes/circular.sh b/tests/functional/flakes/circular.sh index 6cab3a72b..5304496ba 100755 --- a/tests/functional/flakes/circular.sh +++ b/tests/functional/flakes/circular.sh @@ -8,10 +8,10 @@ requireGit flakeA=$TEST_ROOT/flakeA flakeB=$TEST_ROOT/flakeB -createGitRepo $flakeA -createGitRepo $flakeB +createGitRepo "$flakeA" +createGitRepo "$flakeB" -cat > $flakeA/flake.nix < "$flakeA"/flake.nix < $flakeA/flake.nix < $flakeB/flake.nix < "$flakeB"/flake.nix < $flakeB/flake.nix < Date: Tue, 4 Jun 2024 13:33:15 -0700 Subject: [PATCH 293/910] housekeeping: shellcheck for tests/functional/flakes/flake-in-submodule.sh --- tests/functional/flakes/flake-in-submodule.sh | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/functional/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh index 2988352a9..08f751216 100755 --- a/tests/functional/flakes/flake-in-submodule.sh +++ b/tests/functional/flakes/flake-in-submodule.sh @@ -27,8 +27,8 @@ rootRepo=$TEST_ROOT/rootRepo subRepo=$TEST_ROOT/submodule -createGitRepo $subRepo -cat > $subRepo/flake.nix < "$subRepo"/flake.nix < $subRepo/flake.nix < $subRepo/sub.nix -git -C $subRepo add flake.nix sub.nix -git -C $subRepo commit -m Initial +echo '"expression in submodule"' > "$subRepo"/sub.nix +git -C "$subRepo" add flake.nix sub.nix +git -C "$subRepo" commit -m Initial -createGitRepo $rootRepo +createGitRepo "$rootRepo" -git -C $rootRepo submodule init -git -C $rootRepo submodule add $subRepo submodule -echo '"expression in root repo"' > $rootRepo/root.nix -git -C $rootRepo add root.nix -git -C $rootRepo commit -m "Add root.nix" +git -C "$rootRepo" submodule init +git -C "$rootRepo" submodule add "$subRepo" submodule +echo '"expression in root repo"' > "$rootRepo"/root.nix +git -C "$rootRepo" add root.nix +git -C "$rootRepo" commit -m "Add root.nix" flakeref=git+file://$rootRepo\?submodules=1\&dir=submodule # Flake can live inside a submodule and can be accessed via ?dir=submodule -[[ $(nix eval --json $flakeref#sub ) = '"expression in submodule"' ]] +[[ $(nix eval --json "$flakeref#sub" ) = '"expression in submodule"' ]] # The flake can access content outside of the submodule -[[ $(nix eval --json $flakeref#root ) = '"expression in root repo"' ]] +[[ $(nix eval --json "$flakeref#root" ) = '"expression in root repo"' ]] # Check that dirtying a submodule makes the entire thing dirty. -[[ $(nix flake metadata --json $flakeref | jq -r .locked.rev) != null ]] -echo '"foo"' > $rootRepo/submodule/sub.nix -[[ $(nix eval --json $flakeref#sub ) = '"foo"' ]] -[[ $(nix flake metadata --json $flakeref | jq -r .locked.rev) = null ]] +[[ $(nix flake metadata --json "$flakeref" | jq -r .locked.rev) != null ]] +echo '"foo"' > "$rootRepo"/submodule/sub.nix +[[ $(nix eval --json "$flakeref#sub" ) = '"foo"' ]] +[[ $(nix flake metadata --json "$flakeref" | jq -r .locked.rev) = null ]] From d95adb531ec55970c3eeae964c734015985e19b2 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:23 -0700 Subject: [PATCH 294/910] housekeeping: shellcheck for tests/functional/flakes/init.sh --- tests/functional/flakes/init.sh | 58 ++++++++++++++++----------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh index f8d51e819..6787b9e79 100755 --- a/tests/functional/flakes/init.sh +++ b/tests/functional/flakes/init.sh @@ -8,16 +8,16 @@ templatesDir=$TEST_ROOT/templates flakeDir=$TEST_ROOT/flake nixpkgsDir=$TEST_ROOT/nixpkgs -nix registry add --registry $registry templates git+file://$templatesDir -nix registry add --registry $registry nixpkgs git+file://$nixpkgsDir +nix registry add --registry "$registry" templates git+file://"$templatesDir" +nix registry add --registry "$registry" nixpkgs git+file://"$nixpkgsDir" -createGitRepo $nixpkgsDir -createSimpleGitFlake $nixpkgsDir +createGitRepo "$nixpkgsDir" +createSimpleGitFlake "$nixpkgsDir" # Test 'nix flake init'. -createGitRepo $templatesDir +createGitRepo "$templatesDir" -cat > $templatesDir/flake.nix < "$templatesDir"/flake.nix < $templatesDir/flake.nix < $templatesDir/trivial/flake.nix < "$templatesDir"/trivial/flake.nix < $templatesDir/trivial/flake.nix < $templatesDir/trivial/a -echo b > $templatesDir/trivial/b +echo a > "$templatesDir/trivial/a" +echo b > "$templatesDir/trivial/b" -git -C $templatesDir add flake.nix trivial/ -git -C $templatesDir commit -m 'Initial' +git -C "$templatesDir" add flake.nix trivial/ +git -C "$templatesDir" commit -m 'Initial' nix flake check templates nix flake show templates nix flake show templates --json | jq -createGitRepo $flakeDir -(cd $flakeDir && nix flake init) -(cd $flakeDir && nix flake init) # check idempotence -git -C $flakeDir add flake.nix -nix flake check $flakeDir -nix flake show $flakeDir -nix flake show $flakeDir --json | jq -git -C $flakeDir commit -a -m 'Initial' +createGitRepo "$flakeDir" +(cd "$flakeDir" && nix flake init) +(cd "$flakeDir" && nix flake init) # check idempotence +git -C "$flakeDir" add flake.nix +nix flake check "$flakeDir" +nix flake show "$flakeDir" +nix flake show "$flakeDir" --json | jq +git -C "$flakeDir" commit -a -m 'Initial' # Test 'nix flake init' with benign conflicts createGitRepo "$flakeDir" -echo a > $flakeDir/a -(cd $flakeDir && nix flake init) # check idempotence +echo a > "$flakeDir/a" +(cd "$flakeDir" && nix flake init) # check idempotence # Test 'nix flake init' with conflicts createGitRepo "$flakeDir" -echo b > $flakeDir/a -pushd $flakeDir +echo b > "$flakeDir/a" +pushd "$flakeDir" (! nix flake init) |& grep "refusing to overwrite existing file '$flakeDir/a'" popd -git -C $flakeDir commit -a -m 'Changed' +git -C "$flakeDir" commit -a -m 'Changed' # Test 'nix flake new'. -rm -rf $flakeDir -nix flake new -t templates#trivial $flakeDir -nix flake new -t templates#trivial $flakeDir # check idempotence -nix flake check $flakeDir +rm -rf "$flakeDir" +nix flake new -t templates#trivial "$flakeDir" +nix flake new -t templates#trivial "$flakeDir" # check idempotence +nix flake check "$flakeDir" From c7b3468968c56e34a38096419d59be5e71941e6f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:24 -0700 Subject: [PATCH 295/910] housekeeping: shellcheck for tests/functional/flakes/inputs.sh --- tests/functional/flakes/inputs.sh | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/functional/flakes/inputs.sh b/tests/functional/flakes/inputs.sh index 0327a3e9e..bc0603f1b 100755 --- a/tests/functional/flakes/inputs.sh +++ b/tests/functional/flakes/inputs.sh @@ -8,12 +8,12 @@ requireGit test_subdir_self_path() { baseDir=$TEST_ROOT/$RANDOM flakeDir=$baseDir/b-low - mkdir -p $flakeDir - writeSimpleFlake $baseDir - writeSimpleFlake $flakeDir + mkdir -p "$flakeDir" + writeSimpleFlake "$baseDir" + writeSimpleFlake "$flakeDir" - echo all good > $flakeDir/message - cat > $flakeDir/flake.nix < "$flakeDir/message" + cat > "$flakeDir"/flake.nix < $flakeDir/message - cat > $flakeDir/flake.nix < "$flakeDir/message" + cat > "$flakeDir"/flake.nix < $clientDir/flake.nix < "$clientDir"/flake.nix < Date: Tue, 4 Jun 2024 13:33:29 -0700 Subject: [PATCH 296/910] housekeeping: shellcheck for tests/functional/flakes/mercurial.sh --- tests/functional/flakes/mercurial.sh | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh index 0e9f2d626..1b095790b 100755 --- a/tests/functional/flakes/mercurial.sh +++ b/tests/functional/flakes/mercurial.sh @@ -5,41 +5,41 @@ source ./common.sh [[ $(type -p hg) ]] || skipTest "Mercurial not installed" flake1Dir=$TEST_ROOT/flake-hg1 -mkdir -p $flake1Dir -writeSimpleFlake $flake1Dir -hg init $flake1Dir +mkdir -p "$flake1Dir" +writeSimpleFlake "$flake1Dir" +hg init "$flake1Dir" -nix registry add --registry $registry flake1 hg+file://$flake1Dir +nix registry add --registry "$registry" flake1 hg+file://"$flake1Dir" flake2Dir=$TEST_ROOT/flake-hg2 -mkdir -p $flake2Dir -writeDependentFlake $flake2Dir -hg init $flake2Dir +mkdir -p "$flake2Dir" +writeDependentFlake "$flake2Dir" +hg init "$flake2Dir" -hg add $flake1Dir/* -hg commit --config ui.username=foobar@example.org $flake1Dir -m 'Initial commit' +hg add "$flake1Dir"/* +hg commit --config ui.username=foobar@example.org "$flake1Dir" -m 'Initial commit' -hg add $flake2Dir/flake.nix -hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Initial commit' +hg add "$flake2Dir"/flake.nix +hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Initial commit' -nix build -o $TEST_ROOT/result hg+file://$flake2Dir +nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" [[ -e $TEST_ROOT/result/hello ]] -(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) +(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) -nix eval hg+file://$flake2Dir#expr +nix eval hg+file://"$flake2Dir"#expr -nix eval hg+file://$flake2Dir#expr +nix eval hg+file://"$flake2Dir"#expr -(! nix eval hg+file://$flake2Dir#expr --no-allow-dirty) +(! nix eval hg+file://"$flake2Dir"#expr --no-allow-dirty) -(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) +(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) -hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Add lock file' +hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Add lock file' -nix flake metadata --json hg+file://$flake2Dir --refresh | jq -e -r .revision -nix flake metadata --json hg+file://$flake2Dir -[[ $(nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revCount) = 1 ]] +nix flake metadata --json hg+file://"$flake2Dir" --refresh | jq -e -r .revision +nix flake metadata --json hg+file://"$flake2Dir" +[[ $(nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revCount) = 1 ]] -nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-registries --no-allow-dirty -nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-use-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-use-registries --no-allow-dirty From e1ce349d05608ea4acdcf02385f7fb634caa6a4b Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:30 -0700 Subject: [PATCH 297/910] housekeeping: shellcheck for tests/functional/flakes/search-root.sh --- tests/functional/flakes/search-root.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/flakes/search-root.sh b/tests/functional/flakes/search-root.sh index c2337edc0..600dcf937 100755 --- a/tests/functional/flakes/search-root.sh +++ b/tests/functional/flakes/search-root.sh @@ -4,8 +4,8 @@ source common.sh clearStore -writeSimpleFlake $TEST_HOME -cd $TEST_HOME +writeSimpleFlake "$TEST_HOME" +cd "$TEST_HOME" mkdir -p foo/subdir echo '{ outputs = _: {}; }' > foo/flake.nix @@ -27,11 +27,11 @@ success=("" . .# .#test ../subdir ../subdir#test "$PWD") failure=("path:$PWD" "../simple.nix") for i in "${success[@]}"; do - nix build $i || fail "flake should be found by searching up directories" + nix build "$i" || fail "flake should be found by searching up directories" done for i in "${failure[@]}"; do - ! nix build $i || fail "flake should not search up directories when using 'path:'" + ! nix build "$i" || fail "flake should not search up directories when using 'path:'" done popd @@ -45,7 +45,7 @@ if [[ -n $(type -p git) ]]; then pushd subdir git init for i in "${success[@]}" "${failure[@]}"; do - ! nix build $i || fail "flake should not search past a git repository" + ! nix build "$i" || fail "flake should not search past a git repository" done rm -rf .git popd From b764dd9aa40f3dcc5664d920393d277b97db3923 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:31 -0700 Subject: [PATCH 298/910] housekeeping: shellcheck for tests/functional/flakes/unlocked-override.sh --- tests/functional/flakes/unlocked-override.sh | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/functional/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh index 680a1505c..a17a0c2af 100755 --- a/tests/functional/flakes/unlocked-override.sh +++ b/tests/functional/flakes/unlocked-override.sh @@ -7,26 +7,26 @@ requireGit flake1Dir=$TEST_ROOT/flake1 flake2Dir=$TEST_ROOT/flake2 -createGitRepo $flake1Dir -cat > $flake1Dir/flake.nix < "$flake1Dir"/flake.nix < $flake1Dir/x.nix -git -C $flake1Dir add flake.nix x.nix -git -C $flake1Dir commit -m Initial +echo 123 > "$flake1Dir"/x.nix +git -C "$flake1Dir" add flake.nix x.nix +git -C "$flake1Dir" commit -m Initial -createGitRepo $flake2Dir -cat > $flake2Dir/flake.nix < "$flake2Dir"/flake.nix < $flake1Dir/x.nix +echo 456 > "$flake1Dir"/x.nix -[[ $(nix eval --json $flake2Dir#x --override-input flake1 $TEST_ROOT/flake1) = 456 ]] +[[ $(nix eval --json "$flake2Dir#x" --override-input flake1 "$TEST_ROOT/flake1") = 456 ]] From cd46ec17f9bc353ba3b604e5291202e8c5aa175d Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:32 -0700 Subject: [PATCH 299/910] housekeeping: shellcheck for tests/functional/function-trace.sh --- tests/functional/function-trace.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/function-trace.sh b/tests/functional/function-trace.sh index 71f18b67f..7524afdf2 100755 --- a/tests/functional/function-trace.sh +++ b/tests/functional/function-trace.sh @@ -21,12 +21,12 @@ expect_trace() { <(echo "$expect") \ <(echo "$actual") ) && result=0 || result=$? - if [ $result -eq 0 ]; then + if [ "$result" -eq 0 ]; then echo " ok." else echo " failed. difference:" echo "$msg" - return $result + return "$result" fi } From d1c476865a3cab6527f8f04aba8e285ff0ddd48c Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:49 -0700 Subject: [PATCH 300/910] housekeeping: shellcheck for tests/functional/gc-runtime.sh --- tests/functional/gc-runtime.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/gc-runtime.sh b/tests/functional/gc-runtime.sh index 2ee72b61e..b5f6f769c 100755 --- a/tests/functional/gc-runtime.sh +++ b/tests/functional/gc-runtime.sh @@ -12,27 +12,27 @@ esac set -m # enable job control, needed for kill profiles="$NIX_STATE_DIR"/profiles -rm -rf $profiles +rm -rf "$profiles" -nix-env -p $profiles/test -f ./gc-runtime.nix -i gc-runtime +nix-env -p "$profiles/test" -f ./gc-runtime.nix -i gc-runtime -outPath=$(nix-env -p $profiles/test -q --no-name --out-path gc-runtime) -echo $outPath +outPath=$(nix-env -p "$profiles/test" -q --no-name --out-path gc-runtime) +echo "$outPath" echo "backgrounding program..." -$profiles/test/program & +"$profiles"/test/program & sleep 2 # hack - wait for the program to get started child=$! echo PID=$child -nix-env -p $profiles/test -e gc-runtime -nix-env -p $profiles/test --delete-generations old +nix-env -p "$profiles/test" -e gc-runtime +nix-env -p "$profiles/test" --delete-generations old nix-store --gc kill -- -$child -if ! test -e $outPath; then +if ! test -e "$outPath"; then echo "running program was garbage collected!" exit 1 fi From 1c9336098916d23d1510efb848ecdec8ce6eb70f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:54 -0700 Subject: [PATCH 301/910] housekeeping: shellcheck for tests/functional/hash-path.sh --- tests/functional/hash-path.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/functional/hash-path.sh b/tests/functional/hash-path.sh index 12605ef71..86d782a95 100755 --- a/tests/functional/hash-path.sh +++ b/tests/functional/hash-path.sh @@ -3,7 +3,7 @@ source common.sh try () { - printf "%s" "$2" > $TEST_ROOT/vector + printf "%s" "$2" > "$TEST_ROOT/vector" hash="$(nix-hash --flat ${FORMAT+--$FORMAT} --type "$1" "$TEST_ROOT/vector")" if ! (( "${NO_TEST_CLASSIC-}" )) && test "$hash" != "$3"; then echo "try nix-hash: hash $1, expected $3, got $hash" @@ -61,7 +61,7 @@ NO_TEST_NIX_COMMAND=1 try sha512 "abc" "ddaf35a193617abacc417349ae20413112e6fa4e NO_TEST_CLASSIC=1 try sha512 "abc" "sha512-3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw==" try2 () { - hash=$(nix-hash --type "$1" $TEST_ROOT/hash-path) + hash=$(nix-hash --type "$1" "$TEST_ROOT/hash-path") if test "$hash" != "$2"; then echo "try nix-hash; hash $1, expected $2, got $hash" exit 1 @@ -73,22 +73,22 @@ try2 () { fi } -rm -rf $TEST_ROOT/hash-path -mkdir $TEST_ROOT/hash-path -echo "Hello World" > $TEST_ROOT/hash-path/hello +rm -rf "$TEST_ROOT/hash-path" +mkdir "$TEST_ROOT/hash-path" +echo "Hello World" > "$TEST_ROOT/hash-path/hello" try2 md5 "ea9b55537dd4c7e104515b2ccfaf4100" # Execute bit matters. -chmod +x $TEST_ROOT/hash-path/hello +chmod +x "$TEST_ROOT/hash-path/hello" try2 md5 "20f3ffe011d4cfa7d72bfabef7882836" # Mtime and other bits don't. -touch -r . $TEST_ROOT/hash-path/hello -chmod 744 $TEST_ROOT/hash-path/hello +touch -r . "$TEST_ROOT/hash-path/hello" +chmod 744 "$TEST_ROOT/hash-path/hello" try2 md5 "20f3ffe011d4cfa7d72bfabef7882836" # File type (e.g., symlink) does. -rm $TEST_ROOT/hash-path/hello -ln -s x $TEST_ROOT/hash-path/hello +rm "$TEST_ROOT/hash-path/hello" +ln -s x "$TEST_ROOT/hash-path/hello" try2 md5 "f78b733a68f5edbdf9413899339eaa4a" From 2d467b4731365065b22456066bcdf4cc1d548c13 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:59 -0700 Subject: [PATCH 302/910] housekeeping: shellcheck for tests/functional/import-derivation.sh --- tests/functional/import-derivation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/import-derivation.sh b/tests/functional/import-derivation.sh index 53efa1f5d..96cc30646 100755 --- a/tests/functional/import-derivation.sh +++ b/tests/functional/import-derivation.sh @@ -11,4 +11,4 @@ fi outPath=$(nix-build ./import-derivation.nix --no-out-link) -[ "$(cat $outPath)" = FOO579 ] +[ "$(cat "$outPath")" = FOO579 ] From 1afac8fbbc49dabe793e5f6c62ffa6ee334a169e Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 23:37:45 -0700 Subject: [PATCH 303/910] remove tests from pre-commit excludes --- maintainers/flake-module.nix | 38 ------------------------------------ 1 file changed, 38 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 261da1766..b1d21e8fe 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -507,49 +507,30 @@ ''^scripts/install-nix-from-closure\.sh$'' ''^scripts/install-systemd-multi-user\.sh$'' ''^src/nix/get-env\.sh$'' - ''^tests/functional/binary-cache-build-remote\.sh$'' - ''^tests/functional/binary-cache\.sh$'' - ''^tests/functional/brotli\.sh$'' - ''^tests/functional/build-delete\.sh$'' - ''^tests/functional/build-dry\.sh$'' ''^tests/functional/build\.sh$'' - ''^tests/functional/ca/build-cache\.sh$'' ''^tests/functional/ca/build-dry\.sh$'' ''^tests/functional/ca/build-with-garbage-path\.sh$'' - ''^tests/functional/ca/build\.sh$'' ''^tests/functional/ca/common\.sh$'' ''^tests/functional/ca/concurrent-builds\.sh$'' - ''^tests/functional/ca/derivation-json\.sh$'' - ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' ''^tests/functional/ca/eval-store\.sh$'' ''^tests/functional/ca/gc\.sh$'' ''^tests/functional/ca/import-derivation\.sh$'' ''^tests/functional/ca/new-build-cmd\.sh$'' - ''^tests/functional/ca/nix-copy\.sh$'' - ''^tests/functional/ca/nix-run\.sh$'' ''^tests/functional/ca/nix-shell\.sh$'' ''^tests/functional/ca/post-hook\.sh$'' ''^tests/functional/ca/recursive\.sh$'' ''^tests/functional/ca/repl\.sh$'' ''^tests/functional/ca/selfref-gc\.sh$'' - ''^tests/functional/ca/signatures\.sh$'' - ''^tests/functional/ca/substitute\.sh$'' ''^tests/functional/ca/why-depends\.sh$'' - ''^tests/functional/case-hack\.sh$'' - ''^tests/functional/check-refs\.sh$'' - ''^tests/functional/check-reqs\.sh$'' ''^tests/functional/check\.sh$'' - ''^tests/functional/chroot-store\.sh$'' ''^tests/functional/common/vars-and-functions\.sh$'' ''^tests/functional/completions\.sh$'' - ''^tests/functional/compression-levels\.sh$'' ''^tests/functional/compute-levels\.sh$'' ''^tests/functional/config\.sh$'' ''^tests/functional/db-migration\.sh$'' ''^tests/functional/debugger\.sh$'' ''^tests/functional/dependencies\.builder0\.sh$'' ''^tests/functional/dependencies\.sh$'' - ''^tests/functional/derivation-json\.sh$'' ''^tests/functional/dump-db\.sh$'' ''^tests/functional/dyn-drv/build-built-drv\.sh$'' ''^tests/functional/dyn-drv/common\.sh$'' @@ -557,10 +538,8 @@ ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' - ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' ''^tests/functional/eval-store\.sh$'' ''^tests/functional/eval\.sh$'' - ''^tests/functional/experimental-features\.sh$'' ''^tests/functional/export-graph\.sh$'' ''^tests/functional/export\.sh$'' ''^tests/functional/extra-sandbox-profile\.sh$'' @@ -570,49 +549,32 @@ ''^tests/functional/fetchGitSubmodules\.sh$'' ''^tests/functional/fetchGitVerification\.sh$'' ''^tests/functional/fetchMercurial\.sh$'' - ''^tests/functional/fetchPath\.sh$'' - ''^tests/functional/fetchTree-file\.sh$'' ''^tests/functional/fetchurl\.sh$'' - ''^tests/functional/filter-source\.sh$'' ''^tests/functional/fixed\.builder1\.sh$'' ''^tests/functional/fixed\.builder2\.sh$'' ''^tests/functional/fixed\.sh$'' - ''^tests/functional/flakes/absolute-attr-paths\.sh$'' ''^tests/functional/flakes/absolute-paths\.sh$'' - ''^tests/functional/flakes/build-paths\.sh$'' - ''^tests/functional/flakes/bundle\.sh$'' ''^tests/functional/flakes/check\.sh$'' - ''^tests/functional/flakes/circular\.sh$'' ''^tests/functional/flakes/common\.sh$'' ''^tests/functional/flakes/config\.sh$'' ''^tests/functional/flakes/develop\.sh$'' - ''^tests/functional/flakes/flake-in-submodule\.sh$'' ''^tests/functional/flakes/flakes\.sh$'' ''^tests/functional/flakes/follow-paths\.sh$'' - ''^tests/functional/flakes/init\.sh$'' - ''^tests/functional/flakes/inputs\.sh$'' - ''^tests/functional/flakes/mercurial\.sh$'' ''^tests/functional/flakes/prefetch\.sh$'' ''^tests/functional/flakes/run\.sh$'' - ''^tests/functional/flakes/search-root\.sh$'' ''^tests/functional/flakes/show\.sh$'' - ''^tests/functional/flakes/unlocked-override\.sh$'' ''^tests/functional/fmt\.sh$'' ''^tests/functional/fmt\.simple\.sh$'' - ''^tests/functional/function-trace\.sh$'' ''^tests/functional/gc-auto\.sh$'' ''^tests/functional/gc-concurrent\.builder\.sh$'' ''^tests/functional/gc-concurrent\.sh$'' ''^tests/functional/gc-concurrent2\.builder\.sh$'' ''^tests/functional/gc-non-blocking\.sh$'' - ''^tests/functional/gc-runtime\.sh$'' ''^tests/functional/gc\.sh$'' ''^tests/functional/git-hashing/common\.sh$'' ''^tests/functional/git-hashing/simple\.sh$'' ''^tests/functional/hash-convert\.sh$'' - ''^tests/functional/hash-path\.sh$'' ''^tests/functional/help\.sh$'' - ''^tests/functional/import-derivation\.sh$'' ''^tests/functional/impure-derivations\.sh$'' ''^tests/functional/impure-env\.sh$'' ''^tests/functional/impure-eval\.sh$'' From d8ae28617db90fb500103fccb7df0b3499d22099 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 12:42:38 -0400 Subject: [PATCH 304/910] Try to fix quotes that don't go to end with sed --- tests/functional/ca/substitute.sh | 2 +- tests/functional/common/init.sh | 2 +- tests/functional/dependencies.sh | 2 +- tests/functional/fetchGitRefs.sh | 2 +- tests/functional/gc-concurrent.sh | 4 ++-- tests/functional/gc.sh | 6 +++--- tests/functional/local-overlay-store/gc-inner.sh | 6 +++--- tests/functional/multiple-outputs.sh | 2 +- tests/functional/readfile-context.sh | 2 +- tests/functional/referrers.sh | 2 +- tests/functional/simple.sh | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/functional/ca/substitute.sh b/tests/functional/ca/substitute.sh index 76cedb4bc..9728470f0 100644 --- a/tests/functional/ca/substitute.sh +++ b/tests/functional/ca/substitute.sh @@ -7,7 +7,7 @@ source common.sh # shellcheck disable=SC1111 needLocalStore "“--no-require-sigs” can’t be used with the daemon" -rm -rf "$TEST_ROOT"/binary_cache +rm -rf "$TEST_ROOT/binary_cache" export REMOTE_STORE_DIR=$TEST_ROOT/binary_cache export REMOTE_STORE=file://$REMOTE_STORE_DIR diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index dda1ecd41..4f2a393af 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -13,7 +13,7 @@ mkdir "$TEST_HOME" mkdir "$NIX_STORE_DIR" mkdir "$NIX_LOCALSTATE_DIR" -mkdir -p "$NIX_LOG_DIR"/drvs +mkdir -p "$NIX_LOG_DIR/drvs" mkdir "$NIX_STATE_DIR" mkdir "$NIX_CONF_DIR" diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh index 5922a1f98..1b266935d 100755 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -33,7 +33,7 @@ nix-store -q --tree "$outPath" | grep '───.*dependencies-input-2' echo "output path is $outPath" -text=$(cat "$outPath"/foobar) +text=$(cat "$outPath/foobar") if test "$text" != "FOOBAR"; then exit 1; fi deps=$(nix-store -quR "$drvPath") diff --git a/tests/functional/fetchGitRefs.sh b/tests/functional/fetchGitRefs.sh index b17cc2090..9373146cd 100755 --- a/tests/functional/fetchGitRefs.sh +++ b/tests/functional/fetchGitRefs.sh @@ -14,7 +14,7 @@ git init "$repo" git -C "$repo" config user.email "foobar@example.com" git -C "$repo" config user.name "Foobar" -echo utrecht > "$repo"/hello +echo utrecht > "$repo/hello" git -C "$repo" add hello git -C "$repo" commit -m 'Bla1' diff --git a/tests/functional/gc-concurrent.sh b/tests/functional/gc-concurrent.sh index 67ea3dc74..128271287 100755 --- a/tests/functional/gc-concurrent.sh +++ b/tests/functional/gc-concurrent.sh @@ -20,8 +20,8 @@ outPath3=$(nix-store -r $drvPath3) touch $outPath3.lock rm -f "$NIX_STATE_DIR"/gcroots/foo* -ln -s $drvPath2 "$NIX_STATE_DIR"/gcroots/foo -ln -s $outPath3 "$NIX_STATE_DIR"/gcroots/foo2 +ln -s $drvPath2 "$NIX_STATE_DIR/gcroots/foo" +ln -s $outPath3 "$NIX_STATE_DIR/gcroots/foo2" # Start build #1 in the background. It starts immediately. nix-store -rvv "$drvPath1" & diff --git a/tests/functional/gc.sh b/tests/functional/gc.sh index 1f216ebc7..7594312bb 100755 --- a/tests/functional/gc.sh +++ b/tests/functional/gc.sh @@ -8,8 +8,8 @@ drvPath=$(nix-instantiate dependencies.nix) outPath=$(nix-store -rvv "$drvPath") # Set a GC root. -rm -f "$NIX_STATE_DIR"/gcroots/foo -ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo +rm -f "$NIX_STATE_DIR/gcroots/foo" +ln -sf $outPath "$NIX_STATE_DIR/gcroots/foo" [ "$(nix-store -q --roots $outPath)" = "$NIX_STATE_DIR/gcroots/foo -> $outPath" ] @@ -42,7 +42,7 @@ cat $outPath/reference-to-input-2/bar # Check that the derivation has been GC'd. if test -e $drvPath; then false; fi -rm "$NIX_STATE_DIR"/gcroots/foo +rm "$NIX_STATE_DIR/gcroots/foo" nix-collect-garbage diff --git a/tests/functional/local-overlay-store/gc-inner.sh b/tests/functional/local-overlay-store/gc-inner.sh index ea92154d2..687fed897 100644 --- a/tests/functional/local-overlay-store/gc-inner.sh +++ b/tests/functional/local-overlay-store/gc-inner.sh @@ -20,8 +20,8 @@ outPath=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg # Set a GC root. mkdir -p "$stateB" -rm -f "$stateB"/gcroots/foo -ln -sf $outPath "$stateB"/gcroots/foo +rm -f "$stateB/gcroots/foo" +ln -sf $outPath "$stateB/gcroots/foo" [ "$(nix-store -q --roots $outPath)" = "$stateB/gcroots/foo -> $outPath" ] @@ -46,7 +46,7 @@ nix-collect-garbage # Check that the root and its dependencies haven't been deleted. cat "$storeBRoot/$outPath" -rm "$stateB"/gcroots/foo +rm "$stateB/gcroots/foo" nix-collect-garbage diff --git a/tests/functional/multiple-outputs.sh b/tests/functional/multiple-outputs.sh index af9f8af72..31ce2a3a4 100755 --- a/tests/functional/multiple-outputs.sh +++ b/tests/functional/multiple-outputs.sh @@ -35,7 +35,7 @@ outPath=$(nix-store -q $drvPath) echo "building b..." outPath=$(nix-build multiple-outputs.nix -A b --no-out-link) echo "output path is $outPath" -[ "$(cat "$outPath"/file)" = "success" ] +[ "$(cat "$outPath/file")" = "success" ] # Test nix-build on a derivation with multiple outputs. outPath1=$(nix-build multiple-outputs.nix -A a -o $TEST_ROOT/result) diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh index 76fad9349..d0644471d 100755 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -7,7 +7,7 @@ clearStore outPath=$(nix-build --no-out-link readfile-context.nix) # Set a GC root. -ln -s $outPath "$NIX_STATE_DIR"/gcroots/foo +ln -s $outPath "$NIX_STATE_DIR/gcroots/foo" # Check that file exists. [ "$(cat $(cat $outPath))" = "Hello World!" ] diff --git a/tests/functional/referrers.sh b/tests/functional/referrers.sh index 898032e42..0fda97378 100755 --- a/tests/functional/referrers.sh +++ b/tests/functional/referrers.sh @@ -31,7 +31,7 @@ echo "registering..." nix-store --register-validity < $TEST_ROOT/reg_info echo "collecting garbage..." -ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref +ln -sfn $reference "$NIX_STATE_DIR/gcroots/ref" nix-store --gc if [ -n "$(type -p sqlite3)" -a "$(sqlite3 $NIX_STATE_DIR/db/db.sqlite 'select count(*) from Refs')" -ne 0 ]; then diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh index 846738cbd..4e7d37f59 100755 --- a/tests/functional/simple.sh +++ b/tests/functional/simple.sh @@ -14,7 +14,7 @@ echo "output path is $outPath" (! [ -w $outPath ]) -text=$(cat "$outPath"/hello) +text=$(cat "$outPath/hello") if test "$text" != "Hello World!"; then exit 1; fi # Directed delete: $outPath is not reachable from a root, so it should From 33241887d1d30fb1d5b679162b3f26e67567d7fb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 17:47:54 -0400 Subject: [PATCH 305/910] More quote coalescing --- tests/functional/flakes/init.sh | 4 ++-- tests/functional/flakes/mercurial.sh | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh index 6787b9e79..9e484f71c 100755 --- a/tests/functional/flakes/init.sh +++ b/tests/functional/flakes/init.sh @@ -8,8 +8,8 @@ templatesDir=$TEST_ROOT/templates flakeDir=$TEST_ROOT/flake nixpkgsDir=$TEST_ROOT/nixpkgs -nix registry add --registry "$registry" templates git+file://"$templatesDir" -nix registry add --registry "$registry" nixpkgs git+file://"$nixpkgsDir" +nix registry add --registry "$registry" templates "git+file://$templatesDir" +nix registry add --registry "$registry" nixpkgs "git+file://$nixpkgsDir" createGitRepo "$nixpkgsDir" createSimpleGitFlake "$nixpkgsDir" diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh index 1b095790b..b9045bf6b 100755 --- a/tests/functional/flakes/mercurial.sh +++ b/tests/functional/flakes/mercurial.sh @@ -9,7 +9,7 @@ mkdir -p "$flake1Dir" writeSimpleFlake "$flake1Dir" hg init "$flake1Dir" -nix registry add --registry "$registry" flake1 hg+file://"$flake1Dir" +nix registry add --registry "$registry" flake1 "hg+file://$flake1Dir" flake2Dir=$TEST_ROOT/flake-hg2 mkdir -p "$flake2Dir" @@ -22,24 +22,24 @@ hg commit --config ui.username=foobar@example.org "$flake1Dir" -m 'Initial commi hg add "$flake2Dir"/flake.nix hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Initial commit' -nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" +nix build -o "$TEST_ROOT/result" "hg+file://$flake2Dir" [[ -e $TEST_ROOT/result/hello ]] -(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) +(! nix flake metadata --json "hg+file://$flake2Dir" | jq -e -r .revision) -nix eval hg+file://"$flake2Dir"#expr +nix eval "hg+file://$flake2Dir"#expr -nix eval hg+file://"$flake2Dir"#expr +nix eval "hg+file://$flake2Dir"#expr -(! nix eval hg+file://"$flake2Dir"#expr --no-allow-dirty) +(! nix eval "hg+file://$flake2Dir"#expr --no-allow-dirty) -(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) +(! nix flake metadata --json "hg+file://$flake2Dir" | jq -e -r .revision) hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Add lock file' -nix flake metadata --json hg+file://"$flake2Dir" --refresh | jq -e -r .revision -nix flake metadata --json hg+file://"$flake2Dir" -[[ $(nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revCount) = 1 ]] +nix flake metadata --json "hg+file://$flake2Dir" --refresh | jq -e -r .revision +nix flake metadata --json "hg+file://$flake2Dir" +[[ $(nix flake metadata --json "hg+file://$flake2Dir" | jq -e -r .revCount) = 1 ]] -nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-registries --no-allow-dirty -nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-use-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" "hg+file://$flake2Dir" --no-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" "hg+file://$flake2Dir" --no-use-registries --no-allow-dirty From 28d2af4ea69fbc7f1803de4a3ab1e6dd49dfe519 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 09:28:27 -0400 Subject: [PATCH 306/910] Build `nix-util` with Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The idea is two-fold: - Replace autotools with Meson - Build each library in its own derivation The interaction of these two features is that Meson's "subprojects" feature (https://mesonbuild.com/Subprojects) allows us to have single dev shell for building all libraries still, while also building things separately. This allows us to break up the build without a huge productivity lost. I tested the Linux native build, and NetBSD and Windows cross builds. Also do some clean ups of the Flake in the process of supporting new jobs. Special thanks to everyone that has worked on a Meson port so far, @p01arst0rm and @Qyriad in particular. Co-Authored-By: p01arst0rm Co-Authored-By: Artemis Tosini Co-Authored-By: Artemis Tosini Co-Authored-By: Felix Uhl Co-Authored-By: Jade Lovelace Co-Authored-By: Lunaphied Co-Authored-By: Maximilian Bosch Co-Authored-By: Pierre Bourdon Co-Authored-By: Qyriad Co-Authored-By: Rebecca Turner Co-Authored-By: Winter Co-Authored-By: eldritch horrors Co-Authored-By: jade Co-Authored-By: julia Co-Authored-By: rebecca “wiggles” turner Co-Authored-By: wiggles dog Co-Authored-By: fricklerhandwerk Co-authored-By: Eli Schwartz Co-authored-by: Robert Hensing --- .gitignore | 2 + flake.nix | 75 +++++--- {build => maintainers}/hydra.nix | 12 +- meson.build | 9 + package.nix | 3 +- src/libutil/.version | 1 + src/libutil/linux/meson.build | 11 ++ src/libutil/meson.build | 288 +++++++++++++++++++++++++++++++ src/libutil/meson.options | 5 + src/libutil/package.nix | 144 ++++++++++++++++ src/libutil/unix/meson.build | 17 ++ src/libutil/windows/meson.build | 19 ++ 12 files changed, 553 insertions(+), 33 deletions(-) rename {build => maintainers}/hydra.nix (93%) create mode 100644 meson.build create mode 120000 src/libutil/.version create mode 100644 src/libutil/linux/meson.build create mode 100644 src/libutil/meson.build create mode 100644 src/libutil/meson.options create mode 100644 src/libutil/package.nix create mode 100644 src/libutil/unix/meson.build create mode 100644 src/libutil/windows/meson.build diff --git a/.gitignore b/.gitignore index 28f853715..1bf540ba2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ perl/Makefile.config /svn-revision /libtool /config/config.* +# Default meson build dir +/build # /doc/manual/ /doc/manual/*.1 diff --git a/flake.nix b/flake.nix index 5dbc554fc..0e29c6e6f 100644 --- a/flake.nix +++ b/flake.nix @@ -160,21 +160,23 @@ }; }); - nix = - let - officialRelease = false; - versionSuffix = - if officialRelease - then "" - else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}"; + nix-util = final.callPackage ./src/libutil/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; - in final.callPackage ./package.nix { + nix = + final.callPackage ./package.nix { inherit fileset stdenv + officialRelease versionSuffix ; - officialRelease = false; boehmgc = final.boehmgc-nix; libgit2 = final.libgit2-nix; libseccomp = final.libseccomp-nix; @@ -203,7 +205,7 @@ # 'nix.perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - hydraJobs = import ./build/hydra.nix { + hydraJobs = import ./maintainers/hydra.nix { inherit inputs binaryTarball @@ -236,11 +238,29 @@ } // devFlake.checks.${system} or {} ); - packages = forAllSystems (system: rec { - inherit (nixpkgsFor.${system}.native) nix changelog-d; - default = nix; - } // (lib.optionalAttrs (builtins.elem system linux64BitSystems) { - nix-static = nixpkgsFor.${system}.static.nix; + packages = forAllSystems (system: { + inherit (nixpkgsFor.${system}.native) + changelog-d; + default = self.packages.${system}.nix; + } // lib.concatMapAttrs + (pkgName: {}: { + "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; + "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; + } // lib.concatMapAttrs + (crossSystem: {}: { + "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; + }) + (lib.genAttrs crossSystems (_: { })) + // lib.concatMapAttrs + (stdenvName: {}: { + "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; + }) + (lib.genAttrs stdenvs (_: { }))) + { + "nix" = { }; + "nix-util" = { }; + } + // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = let pkgs = nixpkgsFor.${system}.native; @@ -255,18 +275,7 @@ ln -s ${image} $image echo "file binary-dist $image" >> $out/nix-support/hydra-build-products ''; - } // builtins.listToAttrs (map - (crossSystem: { - name = "nix-${crossSystem}"; - value = nixpkgsFor.${system}.cross.${crossSystem}.nix; - }) - crossSystems) - // builtins.listToAttrs (map - (stdenvName: { - name = "nix-${stdenvName}"; - value = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nix; - }) - stdenvs))); + }); devShells = let makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: @@ -274,6 +283,11 @@ modular = devFlake.getSystem stdenv.buildPlatform.system; in { pname = "shell-for-" + attrs.pname; + + # Remove the version suffix to avoid unnecessary attempts to substitute in nix develop + version = lib.fileContents ./.version; + name = attrs.pname; + installFlags = "sysconfdir=$(out)/etc"; shellHook = '' PATH=$prefix/bin:$PATH @@ -288,12 +302,19 @@ src = null; env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev pkgs.boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib pkgs.boost}/lib"; # For `make format`, to work without installing pre-commit _NIX_PRE_COMMIT_HOOKS_CONFIG = "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; }; + inherit (pkgs.nix-util) mesonFlags; + nativeBuildInputs = attrs.nativeBuildInputs or [] + ++ pkgs.nix-util.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/build/hydra.nix b/maintainers/hydra.nix similarity index 93% rename from build/hydra.nix rename to maintainers/hydra.nix index 857b7f1f0..2541b061e 100644 --- a/build/hydra.nix +++ b/maintainers/hydra.nix @@ -32,6 +32,8 @@ let doBuild = false; }; + + forAllPackages = lib.genAttrs [ "nix" "nix-util" ]; in { # Binary package for various platforms. @@ -39,10 +41,12 @@ in shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); - buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); + buildStatic = forAllPackages (pkgName: + lib.genAttrs linux64BitSystems (system: nixpkgsFor.${system}.static.${pkgName})); - buildCross = forAllCrossSystems (crossSystem: - lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); + buildCross = forAllPackages (pkgName: + forAllCrossSystems (crossSystem: + lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}))); buildNoGc = forAllSystems (system: self.packages.${system}.nix.override { enableGC = false; } @@ -76,7 +80,7 @@ in binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: forAllCrossSystems (crossSystem: binaryTarball - self.packages.${system}."nix-${crossSystem}" + nixpkgsFor.${system}.cross.${crossSystem}.nix nixpkgsFor.${system}.cross.${crossSystem})); # The first half of the installation script. This is uploaded diff --git a/meson.build b/meson.build new file mode 100644 index 000000000..fc6441877 --- /dev/null +++ b/meson.build @@ -0,0 +1,9 @@ +# This is just a stub project to include all the others as subprojects +# for development shell purposes + +project('nix-dev-shell', 'cpp', + version : files('.version'), + subproject_dir : 'src', +) + +subproject('libutil') diff --git a/package.nix b/package.nix index cf1654c6a..677ee73c7 100644 --- a/package.nix +++ b/package.nix @@ -385,8 +385,7 @@ in { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 strictDeps = !withCoverageChecks; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; diff --git a/src/libutil/.version b/src/libutil/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libutil/linux/meson.build b/src/libutil/linux/meson.build new file mode 100644 index 000000000..a1ded76ca --- /dev/null +++ b/src/libutil/linux/meson.build @@ -0,0 +1,11 @@ +sources += files( + 'cgroup.cc', + 'namespaces.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'cgroup.hh', + 'namespaces.hh', +) diff --git a/src/libutil/meson.build b/src/libutil/meson.build new file mode 100644 index 000000000..8644c69f4 --- /dev/null +++ b/src/libutil/meson.build @@ -0,0 +1,288 @@ +project('nix-util', 'cpp', + version : run_command('cat', './.version', check : true).stdout().strip(), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 0.64.0', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +deps_private = [ ] +deps_public = [ ] +deps_other = [ ] + +configdata = configuration_data() + +# Check for each of these functions, and create a define like `#define HAVE_LUTIMES 1`. +check_funcs = [ + # Optionally used for changing the mtime of symlinks. + 'lutimes', + 'pipe2', + 'posix_fallocate', + 'strsignal', + 'sysconf', +] +foreach funcspec : check_funcs + define_name = 'HAVE_' + funcspec.underscorify().to_upper() + define_value = cxx.has_function(funcspec).to_int() + configdata.set(define_name, define_value) +endforeach + +# Conditional to work around https://github.com/mesonbuild/meson/issues/13293 +if host_machine.system() != 'windows' and cxx.get_id() == 'gcc' + deps_private += dependency('threads') +endif + +if host_machine.system() == 'windows' + socket = cxx.find_library('ws2_32') + deps_other += socket +elif host_machine.system() == 'sunos' + socket = cxx.find_library('socket') + network_service_library = cxx.find_library('nsl') + deps_other += [socket, network_service_library] +endif + +boost = dependency( + 'boost', + modules : ['context', 'coroutine'], +) +# Actually public, but wrong type of dep for pkg config +deps_other += boost + +openssl = dependency( + 'libcrypto', + 'openssl', + version : '>= 1.1.1', +) +deps_private += openssl + +libarchive = dependency('libarchive', version : '>= 3.1.2') +deps_public += libarchive +if get_option('default_library') == 'static' + # Workaround until https://github.com/libarchive/libarchive/issues/1446 is fixed + add_project_arguments('-lz', language : 'cpp') +endif + +sodium = dependency('libsodium', 'sodium') +deps_private += sodium + +brotli = [ + dependency('libbrotlicommon'), + dependency('libbrotlidec'), + dependency('libbrotlienc'), +] +deps_private += brotli + +# cpuid only makes sense on x86_64 +cpuid_required = host_machine.cpu_family() == 'x86_64' ? get_option('cpuid') : false +cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required) +configdata.set('HAVE_LIBCPUID', cpuid.found().to_int()) +deps_private += cpuid + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +config_util_h = configure_file( + configuration : configdata, + output : 'config-util.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'archive.cc', + 'args.cc', + 'canon-path.cc', + 'compression.cc', + 'compute-levels.cc', + 'config.cc', + 'current-process.cc', + 'english.cc', + 'environment-variables.cc', + 'error.cc', + 'exit.cc', + 'experimental-features.cc', + 'file-content-address.cc', + 'file-descriptor.cc', + 'file-system.cc', + 'fs-sink.cc', + 'git.cc', + 'hash.cc', + 'hilite.cc', + 'json-utils.cc', + 'logging.cc', + 'memory-source-accessor.cc', + 'position.cc', + 'posix-source-accessor.cc', + 'references.cc', + 'serialise.cc', + 'signature/local-keys.cc', + 'signature/signer.cc', + 'source-accessor.cc', + 'source-path.cc', + 'suggestions.cc', + 'tarfile.cc', + 'terminal.cc', + 'thread-pool.cc', + 'unix-domain-socket.cc', + 'url.cc', + 'users.cc', + 'util.cc', + 'xml-writer.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_util_h] + files( + 'abstract-setting-to-json.hh', + 'ansicolor.hh', + 'archive.hh', + 'args.hh', + 'args/root.hh', + 'callback.hh', + 'canon-path.hh', + 'chunked-vector.hh', + 'closure.hh', + 'comparator.hh', + 'compression.hh', + 'compute-levels.hh', + 'config-impl.hh', + 'config.hh', + 'current-process.hh', + 'english.hh', + 'environment-variables.hh', + 'error.hh', + 'exit.hh', + 'experimental-features.hh', + 'file-content-address.hh', + 'file-descriptor.hh', + 'file-path-impl.hh', + 'file-path.hh', + 'file-system.hh', + 'finally.hh', + 'fmt.hh', + 'fs-sink.hh', + 'git.hh', + 'hash.hh', + 'hilite.hh', + 'json-impls.hh', + 'json-utils.hh', + 'logging.hh', + 'lru-cache.hh', + 'memory-source-accessor.hh', + 'muxable-pipe.hh', + 'pool.hh', + 'position.hh', + 'posix-source-accessor.hh', + 'processes.hh', + 'ref.hh', + 'references.hh', + 'regex-combinators.hh', + 'repair-flag.hh', + 'serialise.hh', + 'signals.hh', + 'signature/local-keys.hh', + 'signature/signer.hh', + 'source-accessor.hh', + 'source-path.hh', + 'split.hh', + 'suggestions.hh', + 'sync.hh', + 'tarfile.hh', + 'terminal.hh', + 'thread-pool.hh', + 'topo-sort.hh', + 'types.hh', + 'unix-domain-socket.hh', + 'url-parts.hh', + 'url.hh', + 'users.hh', + 'util.hh', + 'variant-wrapper.hh', + 'xml-writer.hh', +) + +if host_machine.system() == 'linux' + subdir('linux') +endif + +if host_machine.system() == 'windows' + subdir('windows') +else + subdir('unix') +endif + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +libutil = library( + 'nixutil', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = ['-lboost_context', '-lboost_coroutine'] +if host_machine.system() == 'windows' + # `libraries_private` cannot contain ad-hoc dependencies (from + # `find_library), so we need to do this manually + libraries_private += ['-lws2_32'] +endif + +import('pkgconfig').generate( + libutil, + filebase : 'nix-util', + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : deps_public, + requires_private : deps_private, + # avoid absolute paths, use vendored ones + libraries_private : libraries_private, +) + +nix_util = declare_dependency( + include_directories : include_dirs, + link_with : libutil, + compile_args : ['-std=c++2a'], + dependencies : [], +) +meson.override_dependency('nix-util', nix_util) diff --git a/src/libutil/meson.options b/src/libutil/meson.options new file mode 100644 index 000000000..21883af01 --- /dev/null +++ b/src/libutil/meson.options @@ -0,0 +1,5 @@ +# vim: filetype=meson + +option('cpuid', type : 'feature', + description : 'determine microarchitecture levels with libcpuid (only relevant on x86_64)', +) diff --git a/src/libutil/package.nix b/src/libutil/package.nix new file mode 100644 index 000000000..6575b37a6 --- /dev/null +++ b/src/libutil/package.nix @@ -0,0 +1,144 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, boost +, brotli +, libarchive +, libcpuid +, libsodium +, nlohmann_json +, openssl + +# Configuration Options + +, versionSuffix ? "" +, officialRelease ? false + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + boost + brotli + libsodium + openssl + ] ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid + ; + + propagatedBuildInputs = [ + libarchive + nlohmann_json + ]; + + disallowedReferences = [ boost ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + '' + # Copy libboost_context so we don't get all of Boost in our closure. + + # https://github.com/NixOS/nixpkgs/issues/45462 + + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' + mkdir -p $out/lib + cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib + rm -f $out/lib/*.a + '' + lib.optionalString stdenv.hostPlatform.isLinux '' + chmod u+w $out/lib/*.so.* + patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* + '' + lib.optionalString stdenv.hostPlatform.isDarwin '' + for LIB in $out/lib/*.dylib; do + chmod u+w $LIB + install_name_tool -id $LIB $LIB + install_name_tool -delete_rpath ${boost}/lib/ $LIB || true + done + install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib + '' + ); + + env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib boost}/lib"; + } // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs + '' + sed -i "$out/lib/pkgconfig/nix-util.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' + '' + + lib.optionalString stdenv.isDarwin '' + install_name_tool \ + -change ${boost}/lib/libboost_context.dylib \ + $out/lib/libboost_context.dylib \ + $out/lib/libnixutil.dylib + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libutil/unix/meson.build b/src/libutil/unix/meson.build new file mode 100644 index 000000000..38e5cd3aa --- /dev/null +++ b/src/libutil/unix/meson.build @@ -0,0 +1,17 @@ +sources += files( + 'environment-variables.cc', + 'file-descriptor.cc', + 'file-path.cc', + 'file-system.cc', + 'muxable-pipe.cc', + 'processes.cc', + 'signals.cc', + 'users.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'monitor-fd.hh', + 'signals-impl.hh', +) diff --git a/src/libutil/windows/meson.build b/src/libutil/windows/meson.build new file mode 100644 index 000000000..00320877f --- /dev/null +++ b/src/libutil/windows/meson.build @@ -0,0 +1,19 @@ +sources += files( + 'environment-variables.cc', + 'file-descriptor.cc', + 'file-path.cc', + 'file-system.cc', + 'muxable-pipe.cc', + 'processes.cc', + 'users.cc', + 'windows-async-pipe.cc', + 'windows-error.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'signals-impl.hh', + 'windows-async-pipe.hh', + 'windows-error.hh', +) From 25a98949431af72bfdc00cd7e4e6bab18cdd1ec5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 18:33:28 -0400 Subject: [PATCH 307/910] hash: Compare hash algo second for back compat Previously (in cfc18a77395b5ef49763f77c3cc67a95f762a0eb), we forgot to compare the algo at all. This means we keep the same ordering as before by making the stuff we always have compared take priority. --- src/libutil/hash.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 2f2ed8138..7064e96e6 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -52,11 +52,11 @@ bool Hash::operator == (const Hash & h2) const std::strong_ordering Hash::operator <=> (const Hash & h) const { - if (auto cmp = algo <=> h.algo; cmp != 0) return cmp; if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp; for (unsigned int i = 0; i < hashSize; i++) { if (auto cmp = hash[i] <=> h.hash[i]; cmp != 0) return cmp; } + if (auto cmp = algo <=> h.algo; cmp != 0) return cmp; return std::strong_ordering::equivalent; } From 1dc7c8e59991b15a78504be52931f8561dcff32b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 13 Jun 2024 13:55:42 +0200 Subject: [PATCH 308/910] eval-fail-infinite-recursion-lambda: Reduce recursion depth This prevents the test from failing in environments with a smaller configured stack size. --- .../functional/lang/eval-fail-infinite-recursion-lambda.err.exp | 2 +- tests/functional/lang/eval-fail-infinite-recursion-lambda.flags | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 tests/functional/lang/eval-fail-infinite-recursion-lambda.flags diff --git a/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp b/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp index 5d843d827..712dd75a8 100644 --- a/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp +++ b/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp @@ -29,7 +29,7 @@ error: | ^ 2| - (19997 duplicate frames omitted) + (197 duplicate frames omitted) error: stack overflow; max-call-depth exceeded at /pwd/lang/eval-fail-infinite-recursion-lambda.nix:1:14: diff --git a/tests/functional/lang/eval-fail-infinite-recursion-lambda.flags b/tests/functional/lang/eval-fail-infinite-recursion-lambda.flags new file mode 100644 index 000000000..59e20ec9c --- /dev/null +++ b/tests/functional/lang/eval-fail-infinite-recursion-lambda.flags @@ -0,0 +1 @@ +--max-call-depth 100 \ No newline at end of file From ff87c1a318c8ea5ed8714e2d363d4e080fc93f02 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 10:34:44 -0400 Subject: [PATCH 309/910] Put some file descriptor functions in unix and windows namespaces It is misleading when platform-specific functions are in the overall `nix` namespace. More namespaces also makes for nicer doxygen. --- src/libstore/unix/build/local-derivation-goal.cc | 4 ++-- src/libutil/file-descriptor.hh | 10 +++++++--- src/libutil/file-system.cc | 2 +- src/libutil/unix-domain-socket.cc | 2 +- src/libutil/unix/file-descriptor.cc | 8 ++++---- src/libutil/windows/file-descriptor.cc | 4 ++-- src/nix/unix/daemon.cc | 4 ++-- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 16095cf5d..a99439738 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1500,7 +1500,7 @@ void LocalDerivationGoal::startDaemon() throw SysError("accepting connection"); } - closeOnExec(remote.get()); + unix::closeOnExec(remote.get()); debug("received daemon connection"); @@ -1961,7 +1961,7 @@ void LocalDerivationGoal::runChild() throw SysError("changing into '%1%'", tmpDir); /* Close all other file descriptors. */ - closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}); + unix::closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}); #if __linux__ linux::setPersonality(drv->platform); diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 84786e95a..492b67d74 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -140,6 +140,7 @@ public: }; #ifndef _WIN32 // Not needed on Windows, where we don't fork +namespace unix { /** * Close all file descriptors except those listed in the given set. @@ -152,13 +153,16 @@ void closeMostFDs(const std::set & exceptions); */ void closeOnExec(Descriptor fd); +} // namespace unix #endif -#ifdef _WIN32 -# if _WIN32_WINNT >= 0x0600 +#if defined(_WIN32) && _WIN32_WINNT >= 0x0600 +namespace windows ( + Path handleToPath(Descriptor handle); std::wstring handleToFileName(Descriptor handle); -# endif + +} // namespace windows #endif MakeError(EndOfFile, Error); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 919bf5d50..9d6142556 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -546,7 +546,7 @@ std::pair createTempFile(const Path & prefix) if (!fd) throw SysError("creating temporary file '%s'", tmpl); #ifndef _WIN32 - closeOnExec(fd.get()); + unix::closeOnExec(fd.get()); #endif return {std::move(fd), tmpl}; } diff --git a/src/libutil/unix-domain-socket.cc b/src/libutil/unix-domain-socket.cc index 87914bb83..1707fdb75 100644 --- a/src/libutil/unix-domain-socket.cc +++ b/src/libutil/unix-domain-socket.cc @@ -24,7 +24,7 @@ AutoCloseFD createUnixDomainSocket() if (!fdSocket) throw SysError("cannot create Unix domain socket"); #ifndef _WIN32 - closeOnExec(fdSocket.get()); + unix::closeOnExec(fdSocket.get()); #endif return fdSocket; } diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index a74f16ce1..963e75785 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -110,8 +110,8 @@ void Pipe::create() if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); #else if (pipe(fds) != 0) throw SysError("creating pipe"); - closeOnExec(fds[0]); - closeOnExec(fds[1]); + unix::closeOnExec(fds[0]); + unix::closeOnExec(fds[1]); #endif readSide = fds[0]; writeSide = fds[1]; @@ -120,7 +120,7 @@ void Pipe::create() ////////////////////////////////////////////////////////////////////// -void closeMostFDs(const std::set & exceptions) +void unix::closeMostFDs(const std::set & exceptions) { #if __linux__ try { @@ -146,7 +146,7 @@ void closeMostFDs(const std::set & exceptions) } -void closeOnExec(int fd) +void unix::closeOnExec(int fd) { int prev; if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc index b5c21ad32..16773e3ea 100644 --- a/src/libutil/windows/file-descriptor.cc +++ b/src/libutil/windows/file-descriptor.cc @@ -122,7 +122,7 @@ void Pipe::create() #if _WIN32_WINNT >= 0x0600 -std::wstring handleToFileName(HANDLE handle) { +std::wstring windows::handleToFileName(HANDLE handle) { std::vector buf(0x100); DWORD dw = GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED); if (dw == 0) { @@ -141,7 +141,7 @@ std::wstring handleToFileName(HANDLE handle) { } -Path handleToPath(HANDLE handle) { +Path windows::handleToPath(HANDLE handle) { return os_string_to_string(handleToFileName(handle)); } diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index de77a7b6b..f1fc51682 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -295,7 +295,7 @@ static void daemonLoop(std::optional forceTrustClientOpt) if (getEnv("LISTEN_PID") != std::to_string(getpid()) || listenFds != "1") throw Error("unexpected systemd environment variables"); fdSocket = SD_LISTEN_FDS_START; - closeOnExec(fdSocket.get()); + unix::closeOnExec(fdSocket.get()); } // Otherwise, create and bind to a Unix domain socket. @@ -323,7 +323,7 @@ static void daemonLoop(std::optional forceTrustClientOpt) throw SysError("accepting connection"); } - closeOnExec(remote.get()); + unix::closeOnExec(remote.get()); PeerInfo peer { .pidKnown = false }; TrustedFlag trusted; From 56f0b5304f14212b111ca51c27f1c216caf3acbf Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 10:50:15 -0400 Subject: [PATCH 310/910] Document the `nix-util` Meson build system more extensively I hope this will make it easier to maintain, and also make it easier for others to assist with porting the rest of the build system to Meson. Co-authored-by: Robert Hensing --- flake.nix | 1 + src/libutil/meson.build | 82 ++++++++++++++++++++++------- src/libutil/package.nix | 9 ++-- src/libutil/unix/file-descriptor.cc | 2 + 4 files changed, 71 insertions(+), 23 deletions(-) diff --git a/flake.nix b/flake.nix index 0e29c6e6f..f3660b8bf 100644 --- a/flake.nix +++ b/flake.nix @@ -243,6 +243,7 @@ changelog-d; default = self.packages.${system}.nix; } // lib.concatMapAttrs + # We need to "flatten" packages we care about to pass `flake check`. (pkgName: {}: { "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 8644c69f4..2259d4e22 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -1,5 +1,5 @@ project('nix-util', 'cpp', - version : run_command('cat', './.version', check : true).stdout().strip(), + version : files('.version'), default_options : [ 'cpp_std=c++2a', # TODO(Qyriad): increase the warning level @@ -8,25 +8,61 @@ project('nix-util', 'cpp', 'optimization=2', 'errorlogs=true', # Please print logs for tests that fail ], - meson_version : '>= 0.64.0', + meson_version : '>= 1.1', license : 'LGPL-2.1-or-later', ) cxx = meson.get_compiler('cpp') +# These are private dependencies with pkg-config files. What private +# means is that the dependencies are used by the library but they are +# *not* used (e.g. `#include`-ed) in any installed header file, and only +# in regular source code (`*.cc`) or private, uninstalled headers. They +# are thus part of the *implementation* of the library, but not its +# *interface*. +# +# See `man pkg-config` for some details. deps_private = [ ] + +# These are public dependencies with pkg-config files. Public is the +# opposite of private: these dependencies are used in installed header +# files. They are part of the interface (and implementation) of the +# library. +# +# N.B. This concept is mostly unrelated to our own concept of a public +# (stable) API, for consumption outside of the Nix repository. +# `libnixutil` is an unstable C++ library, whose public interface is +# likewise unstable. `libutilc` conversely is a hopefully-soon stable +# C library, whose public interface --- including public but not private +# dependencies --- will also likewise soon be stable. +# +# N.B. For distributions that care about "ABI" stablity and not just +# "API" stability, the private dependencies also matter as they can +# potentially affect the public ABI. deps_public = [ ] + +# These are dependencencies without pkg-config files. Ideally they are +# just private, but they may also be public (e.g. boost). deps_other = [ ] configdata = configuration_data() -# Check for each of these functions, and create a define like `#define HAVE_LUTIMES 1`. +# Check for each of these functions, and create a define like `#define +# HAVE_LUTIMES 1`. The `#define` is unconditional, 0 for not found and 1 +# for found. One therefore uses it with `#if` not `#ifdef`. check_funcs = [ - # Optionally used for changing the mtime of symlinks. + # Optionally used for changing the mtime of symlinks. 'lutimes', + # Optionally used for creating pipes on Unix 'pipe2', + # Optionally used to preallocate files to be large enough before + # writing to them. 'posix_fallocate', + # Optionally used to get more information about processes failing due + # to a signal on Unix. 'strsignal', + # Optionally used to try to close more file descriptors (e.g. before + # forking) on Unix. 'sysconf', ] foreach funcspec : check_funcs @@ -35,8 +71,10 @@ foreach funcspec : check_funcs configdata.set(define_name, define_value) endforeach -# Conditional to work around https://github.com/mesonbuild/meson/issues/13293 -if host_machine.system() != 'windows' and cxx.get_id() == 'gcc' +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') deps_private += dependency('threads') endif @@ -53,7 +91,8 @@ boost = dependency( 'boost', modules : ['context', 'coroutine'], ) -# Actually public, but wrong type of dep for pkg config +# boost is a public dependency, but not a pkg-config dependency unfortunately, so we +# put in `deps_other`. deps_other += boost openssl = dependency( @@ -80,8 +119,10 @@ brotli = [ ] deps_private += brotli -# cpuid only makes sense on x86_64 -cpuid_required = host_machine.cpu_family() == 'x86_64' ? get_option('cpuid') : false +cpuid_required = get_option('cpuid') +if host_machine.cpu_family() != 'x86_64' and cpuid_required.enabled() + warning('Force-enabling seccomp on non-x86_64 does not make sense') +endif cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required) configdata.set('HAVE_LIBCPUID', cpuid.found().to_int()) deps_private += cpuid @@ -89,7 +130,7 @@ deps_private += cpuid nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json -config_util_h = configure_file( +config_h = configure_file( configuration : configdata, output : 'config-util.h', ) @@ -157,7 +198,7 @@ sources = files( include_dirs = [include_directories('.')] -headers = [config_util_h] + files( +headers = [config_h] + files( 'abstract-setting-to-json.hh', 'ansicolor.hh', 'archive.hh', @@ -248,7 +289,7 @@ else linker_export_flags = [] endif -libutil = library( +this_library = library( 'nixutil', sources, dependencies : deps_public + deps_private + deps_other, @@ -259,6 +300,11 @@ libutil = library( install_headers(headers, subdir : 'nix', preserve_path : true) +# Part of how we copy boost libraries to a separate installation to +# reduce closure size. These libraries will be copied to our `$out/bin`, +# and these `-l` flags will pick them up there. +# +# https://github.com/NixOS/nixpkgs/issues/45462 libraries_private = ['-lboost_context', '-lboost_coroutine'] if host_machine.system() == 'windows' # `libraries_private` cannot contain ad-hoc dependencies (from @@ -267,22 +313,20 @@ if host_machine.system() == 'windows' endif import('pkgconfig').generate( - libutil, - filebase : 'nix-util', + this_library, + filebase : meson.project_name(), name : 'Nix', description : 'Nix Package Manager', subdirs : ['nix'], extra_cflags : ['-std=c++2a'], requires : deps_public, requires_private : deps_private, - # avoid absolute paths, use vendored ones libraries_private : libraries_private, ) -nix_util = declare_dependency( +meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, - link_with : libutil, + link_with : this_library, compile_args : ['-std=c++2a'], dependencies : [], -) -meson.override_dependency('nix-util', nix_util) +)) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 6575b37a6..a461dccb8 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -83,9 +83,8 @@ mkDerivation (finalAttrs: { '' echo ${version} > .version '' - # Copy libboost_context so we don't get all of Boost in our closure. - - # https://github.com/NixOS/nixpkgs/issues/45462 + # Copy some boost libraries so we don't get all of Boost in our + # closure. https://github.com/NixOS/nixpkgs/issues/45462 + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' mkdir -p $out/lib cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib @@ -115,7 +114,9 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; postInstall = - # Remove absolute path to boost libs + # Remove absolute path to boost libs that ends up in `Libs.private` + # by default, and would clash with out `disallowedReferences`. Part + # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. '' sed -i "$out/lib/pkgconfig/nix-util.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' '' diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index a74f16ce1..bc2b2d614 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -139,7 +139,9 @@ void closeMostFDs(const std::set & exceptions) #endif int maxFD = 0; +#if HAVE_SYSCONF maxFD = sysconf(_SC_OPEN_MAX); +#endif for (int fd = 0; fd < maxFD; ++fd) if (!exceptions.count(fd)) close(fd); /* ignore result */ From 7a5ee5d5973160293fe6b0dcfafc87699a75bd49 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 11:42:17 -0400 Subject: [PATCH 311/910] Apply suggestions from code review Co-authored-by: Valentin Gagarin --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index f3660b8bf..599c6dfbe 100644 --- a/flake.nix +++ b/flake.nix @@ -243,7 +243,7 @@ changelog-d; default = self.packages.${system}.nix; } // lib.concatMapAttrs - # We need to "flatten" packages we care about to pass `flake check`. + # We need to flatten recursive attribute sets of derivations to pass `flake check`. (pkgName: {}: { "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; From 0b56c98b1c10c3ea16b8cda9644e5b656b9d7b67 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:18:36 +0200 Subject: [PATCH 312/910] C API: Value -> nix_value --- src/libexpr-c/nix_api_expr.h | 3 ++- src/libexpr-c/nix_api_expr_internal.h | 5 +++++ src/libexpr-c/nix_api_value.cc | 11 ++++++++--- src/libexpr-c/nix_api_value.h | 5 ++++- tests/unit/libexpr/nix_api_value.cc | 10 ++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index cb6c00385..140da9e52 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -36,7 +36,8 @@ typedef struct EvalState EvalState; // nix::EvalState * @struct Value * @see value_manip */ -typedef void Value; // nix::Value +typedef struct nix_value nix_value; +[[deprecated("use nix_value instead")]] typedef nix_value Value; // Function prototypes /** diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index 7743849fd..5a875ef39 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -20,6 +20,11 @@ struct ListBuilder nix::ListBuilder builder; }; +struct nix_value +{ + nix::Value value; +}; + struct nix_string_return { std::string str; diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 978cf7f43..dd8382133 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -64,6 +64,11 @@ static nix::Value & check_value_out(Value * value) return v; } +static inline nix_value * as_nix_value_ptr(nix::Value * v) +{ + return reinterpret_cast(v); +} + /** * Helper function to convert calls from nix into C API. * @@ -159,7 +164,7 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state) if (context) context->last_err_code = NIX_OK; try { - Value * res = state->state.allocValue(); + nix_value * res = as_nix_value_ptr(state->state.allocValue()); nix_gc_incref(nullptr, res); return res; } @@ -345,7 +350,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt if (attr) { nix_gc_incref(nullptr, attr->value); state->state.forceValue(*attr->value, nix::noPos); - return attr->value; + return as_nix_value_ptr(attr->value); } nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); return nullptr; @@ -380,7 +385,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); nix_gc_incref(nullptr, a.value); state->state.forceValue(*a.value, nix::noPos); - return a.value; + return as_nix_value_ptr(a.value); } NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index 244860707..acc4a1969 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -35,8 +35,11 @@ typedef enum { } ValueType; // forward declarations -typedef void Value; +typedef struct nix_value nix_value; typedef struct EvalState EvalState; + +[[deprecated("use nix_value instead")]] typedef nix_value Value; + // type defs /** @brief Stores an under-construction set of bindings * @ingroup value_manip diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index c71593c85..b674d8681 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -4,16 +4,26 @@ #include "nix_api_util_internal.h" #include "nix_api_expr.h" #include "nix_api_value.h" +#include "nix_api_expr_internal.h" #include "tests/nix_api_expr.hh" #include "tests/string_callback.hh" #include "gmock/gmock.h" +#include #include #include namespace nixC { +TEST_F(nix_api_expr_test, as_nix_value_ptr) +{ + // nix_alloc_value casts nix::Value to nix_value + // It should be obvious from the decl that that works, but if it doesn't, + // the whole implementation would be utterly broken. + ASSERT_EQ(sizeof(nix::Value), sizeof(nix_value)); +} + TEST_F(nix_api_expr_test, nix_value_get_int_invalid) { ASSERT_EQ(0, nix_get_int(ctx, nullptr)); From c50db4e58c6f18b9e7948de9d87f5af48b07fa9b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:21:04 +0200 Subject: [PATCH 313/910] C API: Add nix_value_{inc,dec}ref - Can be implemented more easily by more eval architectures. - Better types in generated bindings remove some uncertainty and doubt. --- src/libexpr-c/nix_api_expr.cc | 9 +++++++++ src/libexpr-c/nix_api_expr.h | 5 +++++ src/libexpr-c/nix_api_value.h | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index b86d745db..28b8922ce 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -181,6 +181,15 @@ nix_err nix_gc_decref(nix_c_context * context, const void *) void nix_gc_now() {} #endif +nix_err nix_value_incref(nix_c_context * context, nix_value *x) +{ + return nix_gc_incref(context, (const void *) x); +} +nix_err nix_value_decref(nix_c_context * context, nix_value *x) +{ + return nix_gc_decref(context, (const void *) x); +} + void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * obj, void * cd)) { #ifdef HAVE_BOEHMGC diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 140da9e52..ce00246c6 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -189,6 +189,11 @@ void nix_state_free(EvalState * state); * you're done with a value returned by the evaluator. * @{ */ + +// TODO: Deprecate nix_gc_incref in favor of the type-specific reference counting functions? +// e.g. nix_value_incref. +// It gives implementors more flexibility, and adds safety, so that generated +// bindings can be used without fighting the host type system (where applicable). /** * @brief Increment the garbage collector reference counter for the given object. * diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index acc4a1969..26044bd57 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -147,6 +147,25 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); */ Value * nix_alloc_value(nix_c_context * context, EvalState * state); +/** + * @brief Increment the garbage collector reference counter for the given `nix_value`. + * + * The Nix language evaluator C API keeps track of alive objects by reference counting. + * When you're done with a refcounted pointer, call nix_value_decref(). + * + * @param[out] context Optional, stores error information + * @param[in] value The object to keep alive + */ +nix_err nix_value_incref(nix_c_context * context, nix_value * value); + +/** + * @brief Decrement the garbage collector reference counter for the given object + * + * @param[out] context Optional, stores error information + * @param[in] value The object to stop referencing + */ +nix_err nix_value_decref(nix_c_context * context, nix_value * value); + /** @addtogroup value_manip Manipulating values * @brief Functions to inspect and change Nix language values, represented by Value. * @{ From 5d8118d9cb6a2c349fd96bee5a9bdd1c4b65fae9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:23:21 +0200 Subject: [PATCH 314/910] C API: Docs --- src/libexpr-c/nix_api_expr.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index ce00246c6..783176bfa 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -29,12 +29,20 @@ extern "C" { * @see nix_state_create */ typedef struct EvalState EvalState; // nix::EvalState -/** - * @brief Represents a value in the Nix language. + +/** @brief A Nix language value, or thunk that may evaluate to a value. + * + * Values are the primary objects manipulated in the Nix language. + * They are considered to be immutable from a user's perspective, but the process of evaluating a Value changes its + * ValueType if it was a thunk. After a Value has been evaluated, its ValueType does not change. + * + * Evaluation in this context refers to the process of evaluating a single Value object, also called "forcing" the + * Value; see `nix_value_force`. + * + * The evaluator manages its own memory, but your use of the C API must follow the reference counting rules. * - * Owned by the garbage collector. - * @struct Value * @see value_manip + * @see nix_value_incref, nix_value_decref */ typedef struct nix_value nix_value; [[deprecated("use nix_value instead")]] typedef nix_value Value; @@ -128,7 +136,7 @@ nix_err nix_value_call_multi( * The Nix interpreter is lazy, and not-yet-evaluated Values can be * of type NIX_TYPE_THUNK instead of their actual value. * - * This function converts these Values into their final type. + * This function mutates such a Value, so that, if successful, it has its final type. * * @note This function is mainly needed before calling @ref getters, but not for API calls that return a `Value`. * From b94e1d62182114ab5198fd58869a9af29d54cff5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:51:58 +0200 Subject: [PATCH 315/910] C API: Value -> nix_value See issue https://github.com/NixOS/nix/issues/10434 --- src/libexpr-c/nix_api_expr.cc | 26 +++--- src/libexpr-c/nix_api_expr.h | 26 +++--- src/libexpr-c/nix_api_value.cc | 93 ++++++++++--------- src/libexpr-c/nix_api_value.h | 71 +++++++------- .../libexpr-support/tests/nix_api_expr.hh | 2 +- tests/unit/libexpr/nix_api_expr.cc | 62 +++++++------ tests/unit/libexpr/nix_api_external.cc | 4 +- tests/unit/libexpr/nix_api_value.cc | 32 +++---- 8 files changed, 161 insertions(+), 155 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 28b8922ce..bdf7a1e63 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -42,56 +42,56 @@ nix_err nix_libexpr_init(nix_c_context * context) } nix_err nix_expr_eval_from_string( - nix_c_context * context, EvalState * state, const char * expr, const char * path, Value * value) + nix_c_context * context, EvalState * state, const char * expr, const char * path, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path))); - state->state.eval(parsedExpr, *(nix::Value *) value); - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.eval(parsedExpr, value->value); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value) +nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, nix_value * arg, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.callFunction(*(nix::Value *) fn, *(nix::Value *) arg, *(nix::Value *) value, nix::noPos); - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.callFunction(fn->value, arg->value, value->value, nix::noPos); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value) +nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, nix_value * fn, size_t nargs, nix_value ** args, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.callFunction(*(nix::Value *) fn, nargs, (nix::Value * *)args, *(nix::Value *) value, nix::noPos); - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.callFunction(fn->value, nargs, (nix::Value * *)args, value->value, nix::noPos); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value) +nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * value) +nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.forceValueDeep(*(nix::Value *) value); + state->state.forceValueDeep(value->value); } NIXC_CATCH_ERRS } diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 783176bfa..adf8b65b1 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -33,11 +33,11 @@ typedef struct EvalState EvalState; // nix::EvalState /** @brief A Nix language value, or thunk that may evaluate to a value. * * Values are the primary objects manipulated in the Nix language. - * They are considered to be immutable from a user's perspective, but the process of evaluating a Value changes its - * ValueType if it was a thunk. After a Value has been evaluated, its ValueType does not change. + * They are considered to be immutable from a user's perspective, but the process of evaluating a value changes its + * ValueType if it was a thunk. After a value has been evaluated, its ValueType does not change. * - * Evaluation in this context refers to the process of evaluating a single Value object, also called "forcing" the - * Value; see `nix_value_force`. + * Evaluation in this context refers to the process of evaluating a single value object, also called "forcing" the + * value; see `nix_value_force`. * * The evaluator manages its own memory, but your use of the C API must follow the reference counting rules. * @@ -74,7 +74,7 @@ nix_err nix_libexpr_init(nix_c_context * context); * @return NIX_OK if the evaluation was successful, an error code otherwise. */ nix_err nix_expr_eval_from_string( - nix_c_context * context, EvalState * state, const char * expr, const char * path, Value * value); + nix_c_context * context, EvalState * state, const char * expr, const char * path, nix_value * value); /** * @brief Calls a Nix function with an argument. @@ -88,7 +88,7 @@ nix_err nix_expr_eval_from_string( * @see nix_init_apply() for a similar function that does not performs the call immediately, but stores it as a thunk. * Note the different argument order. */ -nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value); +nix_err nix_value_call(nix_c_context * context, EvalState * state, nix_value * fn, nix_value * arg, nix_value * value); /** * @brief Calls a Nix function with multiple arguments. @@ -107,7 +107,7 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, V * @see NIX_VALUE_CALL For a macro that wraps this function for convenience. */ nix_err nix_value_call_multi( - nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value); + nix_c_context * context, EvalState * state, nix_value * fn, size_t nargs, nix_value ** args, nix_value * value); /** * @brief Calls a Nix function with multiple arguments. @@ -125,7 +125,7 @@ nix_err nix_value_call_multi( */ #define NIX_VALUE_CALL(context, state, value, fn, ...) \ do { \ - Value * args_array[] = {__VA_ARGS__}; \ + nix_value * args_array[] = {__VA_ARGS__}; \ size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ nix_value_call_multi(context, state, fn, nargs, args_array, value); \ } while (0) @@ -133,12 +133,10 @@ nix_err nix_value_call_multi( /** * @brief Forces the evaluation of a Nix value. * - * The Nix interpreter is lazy, and not-yet-evaluated Values can be + * The Nix interpreter is lazy, and not-yet-evaluated values can be * of type NIX_TYPE_THUNK instead of their actual value. * - * This function mutates such a Value, so that, if successful, it has its final type. - * - * @note This function is mainly needed before calling @ref getters, but not for API calls that return a `Value`. + * This function mutates such a `nix_value`, so that, if successful, it has its final type. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. @@ -147,7 +145,7 @@ nix_err nix_value_call_multi( * @return NIX_OK if the force operation was successful, an error code * otherwise. */ -nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value); +nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value * value); /** * @brief Forces the deep evaluation of a Nix value. @@ -163,7 +161,7 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * valu * @return NIX_OK if the deep force operation was successful, an error code * otherwise. */ -nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * value); +nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value); /** * @brief Create a new Nix language evaluator state. diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index dd8382133..2f2f99617 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -21,45 +21,45 @@ #endif // Internal helper functions to check [in] and [out] `Value *` parameters -static const nix::Value & check_value_not_null(const Value * value) +static const nix::Value & check_value_not_null(const nix_value * value) { if (!value) { - throw std::runtime_error("Value is null"); + throw std::runtime_error("nix_value is null"); } return *((const nix::Value *) value); } -static nix::Value & check_value_not_null(Value * value) +static nix::Value & check_value_not_null(nix_value * value) { if (!value) { - throw std::runtime_error("Value is null"); + throw std::runtime_error("nix_value is null"); } - return *((nix::Value *) value); + return value->value; } -static const nix::Value & check_value_in(const Value * value) +static const nix::Value & check_value_in(const nix_value * value) { auto & v = check_value_not_null(value); if (!v.isValid()) { - throw std::runtime_error("Uninitialized Value"); + throw std::runtime_error("Uninitialized nix_value"); } return v; } -static nix::Value & check_value_in(Value * value) +static nix::Value & check_value_in(nix_value * value) { auto & v = check_value_not_null(value); if (!v.isValid()) { - throw std::runtime_error("Uninitialized Value"); + throw std::runtime_error("Uninitialized nix_value"); } return v; } -static nix::Value & check_value_out(Value * value) +static nix::Value & check_value_out(nix_value * value) { auto & v = check_value_not_null(value); if (v.isValid()) { - throw std::runtime_error("Value already initialized. Variables are immutable"); + throw std::runtime_error("nix_value already initialized. Variables are immutable"); } return v; } @@ -92,7 +92,7 @@ static void nix_c_primop_wrapper( // or maybe something to make blackholes work better; we don't know). nix::Value vTmp; - f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &vTmp); + f(userdata, &ctx, (EvalState *) &state, (nix_value **) args, (nix_value *) &vTmp); if (ctx.last_err_code != NIX_OK) { /* TODO: Throw different errors depending on the error code */ @@ -159,7 +159,7 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp) NIXC_CATCH_ERRS } -Value * nix_alloc_value(nix_c_context * context, EvalState * state) +nix_value * nix_alloc_value(nix_c_context * context, EvalState * state) { if (context) context->last_err_code = NIX_OK; @@ -171,7 +171,7 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state) NIXC_CATCH_ERRS_NULL } -ValueType nix_get_type(nix_c_context * context, const Value * value) +ValueType nix_get_type(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -207,7 +207,7 @@ ValueType nix_get_type(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(NIX_TYPE_NULL); } -const char * nix_get_typename(nix_c_context * context, const Value * value) +const char * nix_get_typename(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -219,7 +219,7 @@ const char * nix_get_typename(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_NULL } -bool nix_get_bool(nix_c_context * context, const Value * value) +bool nix_get_bool(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -231,7 +231,8 @@ bool nix_get_bool(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(false); } -nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data) +nix_err +nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -243,7 +244,7 @@ nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_str NIXC_CATCH_ERRS } -const char * nix_get_path_string(nix_c_context * context, const Value * value) +const char * nix_get_path_string(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -262,7 +263,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_NULL } -unsigned int nix_get_list_size(nix_c_context * context, const Value * value) +unsigned int nix_get_list_size(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -274,7 +275,7 @@ unsigned int nix_get_list_size(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0); } -unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) +unsigned int nix_get_attrs_size(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -286,7 +287,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0); } -double nix_get_float(nix_c_context * context, const Value * value) +double nix_get_float(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -298,7 +299,7 @@ double nix_get_float(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0.0); } -int64_t nix_get_int(nix_c_context * context, const Value * value) +int64_t nix_get_int(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -310,7 +311,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0); } -ExternalValue * nix_get_external(nix_c_context * context, Value * value) +ExternalValue * nix_get_external(nix_c_context * context, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -322,7 +323,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value) NIXC_CATCH_ERRS_NULL; } -Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int ix) +nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix) { if (context) context->last_err_code = NIX_OK; @@ -333,12 +334,12 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta nix_gc_incref(nullptr, p); if (p != nullptr) state->state.forceValue(*p, nix::noPos); - return (Value *) p; + return as_nix_value_ptr(p); } NIXC_CATCH_ERRS_NULL } -Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name) +nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name) { if (context) context->last_err_code = NIX_OK; @@ -358,7 +359,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt NIXC_CATCH_ERRS_NULL } -bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name) +bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name) { if (context) context->last_err_code = NIX_OK; @@ -374,8 +375,8 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState NIXC_CATCH_ERRS_RES(false); } -Value * -nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i, const char ** name) +nix_value * nix_get_attr_byidx( + nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i, const char ** name) { if (context) context->last_err_code = NIX_OK; @@ -390,7 +391,8 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta NIXC_CATCH_ERRS_NULL } -const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i) +const char * +nix_get_attr_name_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i) { if (context) context->last_err_code = NIX_OK; @@ -402,7 +404,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu NIXC_CATCH_ERRS_NULL } -nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) +nix_err nix_init_bool(nix_c_context * context, nix_value * value, bool b) { if (context) context->last_err_code = NIX_OK; @@ -414,7 +416,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) } // todo string context -nix_err nix_init_string(nix_c_context * context, Value * value, const char * str) +nix_err nix_init_string(nix_c_context * context, nix_value * value, const char * str) { if (context) context->last_err_code = NIX_OK; @@ -425,7 +427,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str NIXC_CATCH_ERRS } -nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * value, const char * str) +nix_err nix_init_path_string(nix_c_context * context, EvalState * s, nix_value * value, const char * str) { if (context) context->last_err_code = NIX_OK; @@ -436,7 +438,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val NIXC_CATCH_ERRS } -nix_err nix_init_float(nix_c_context * context, Value * value, double d) +nix_err nix_init_float(nix_c_context * context, nix_value * value, double d) { if (context) context->last_err_code = NIX_OK; @@ -447,7 +449,7 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d) NIXC_CATCH_ERRS } -nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) +nix_err nix_init_int(nix_c_context * context, nix_value * value, int64_t i) { if (context) context->last_err_code = NIX_OK; @@ -458,7 +460,7 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) NIXC_CATCH_ERRS } -nix_err nix_init_null(nix_c_context * context, Value * value) +nix_err nix_init_null(nix_c_context * context, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -469,7 +471,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value) NIXC_CATCH_ERRS } -nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg) +nix_err nix_init_apply(nix_c_context * context, nix_value * value, nix_value * fn, nix_value * arg) { if (context) context->last_err_code = NIX_OK; @@ -482,7 +484,7 @@ nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value NIXC_CATCH_ERRS } -nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val) +nix_err nix_init_external(nix_c_context * context, nix_value * value, ExternalValue * val) { if (context) context->last_err_code = NIX_OK; @@ -509,7 +511,8 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, NIXC_CATCH_ERRS_NULL } -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, Value * value) +nix_err +nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -529,7 +532,7 @@ void nix_list_builder_free(ListBuilder * list_builder) #endif } -nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value * value) +nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -540,7 +543,7 @@ nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value NIXC_CATCH_ERRS } -nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) +nix_err nix_init_primop(nix_c_context * context, nix_value * value, PrimOp * p) { if (context) context->last_err_code = NIX_OK; @@ -551,7 +554,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) NIXC_CATCH_ERRS } -nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source) +nix_err nix_copy_value(nix_c_context * context, nix_value * value, const nix_value * source) { if (context) context->last_err_code = NIX_OK; @@ -563,7 +566,7 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * sou NIXC_CATCH_ERRS } -nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * b) +nix_err nix_make_attrs(nix_c_context * context, nix_value * value, BindingsBuilder * b) { if (context) context->last_err_code = NIX_OK; @@ -589,7 +592,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * NIXC_CATCH_ERRS_NULL } -nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * bb, const char * name, Value * value) +nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * bb, const char * name, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -610,7 +613,7 @@ void nix_bindings_builder_free(BindingsBuilder * bb) #endif } -nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD) +nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, nix_value * value, bool isIFD) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index 26044bd57..044f68c9e 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -93,7 +93,8 @@ typedef struct nix_realised_string nix_realised_string; * @param[out] ret return value * @see nix_alloc_primop, nix_init_primop */ -typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret); +typedef void (*PrimOpFun)( + void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret); /** @brief Allocate a PrimOp * @@ -145,7 +146,7 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); * @return value, or null in case of errors * */ -Value * nix_alloc_value(nix_c_context * context, EvalState * state); +nix_value * nix_alloc_value(nix_c_context * context, EvalState * state); /** * @brief Increment the garbage collector reference counter for the given `nix_value`. @@ -167,7 +168,7 @@ nix_err nix_value_incref(nix_c_context * context, nix_value * value); nix_err nix_value_decref(nix_c_context * context, nix_value * value); /** @addtogroup value_manip Manipulating values - * @brief Functions to inspect and change Nix language values, represented by Value. + * @brief Functions to inspect and change Nix language values, represented by nix_value. * @{ */ /** @anchor getters @@ -179,7 +180,7 @@ nix_err nix_value_decref(nix_c_context * context, nix_value * value); * @param[in] value Nix value to inspect * @return type of nix value */ -ValueType nix_get_type(nix_c_context * context, const Value * value); +ValueType nix_get_type(nix_c_context * context, const nix_value * value); /** @brief Get type name of value as defined in the evaluator * @param[out] context Optional, stores error information @@ -187,14 +188,14 @@ ValueType nix_get_type(nix_c_context * context, const Value * value); * @return type name, owned string * @todo way to free the result */ -const char * nix_get_typename(nix_c_context * context, const Value * value); +const char * nix_get_typename(nix_c_context * context, const nix_value * value); /** @brief Get boolean value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return true or false, error info via context */ -bool nix_get_bool(nix_c_context * context, const Value * value); +bool nix_get_bool(nix_c_context * context, const nix_value * value); /** @brief Get the raw string * @@ -208,7 +209,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value); * @return error code, NIX_OK on success. */ nix_err -nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data); +nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_callback callback, void * user_data); /** @brief Get path as string * @param[out] context Optional, stores error information @@ -216,42 +217,42 @@ nix_get_string(nix_c_context * context, const Value * value, nix_get_string_call * @return string * @return NULL in case of error. */ -const char * nix_get_path_string(nix_c_context * context, const Value * value); +const char * nix_get_path_string(nix_c_context * context, const nix_value * value); /** @brief Get the length of a list * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return length of list, error info via context */ -unsigned int nix_get_list_size(nix_c_context * context, const Value * value); +unsigned int nix_get_list_size(nix_c_context * context, const nix_value * value); /** @brief Get the element count of an attrset * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return attrset element count, error info via context */ -unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value); +unsigned int nix_get_attrs_size(nix_c_context * context, const nix_value * value); /** @brief Get float value in 64 bits * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return float contents, error info via context */ -double nix_get_float(nix_c_context * context, const Value * value); +double nix_get_float(nix_c_context * context, const nix_value * value); /** @brief Get int value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return int contents, error info via context */ -int64_t nix_get_int(nix_c_context * context, const Value * value); +int64_t nix_get_int(nix_c_context * context, const nix_value * value); /** @brief Get external reference * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return reference to external, NULL in case of error */ -ExternalValue * nix_get_external(nix_c_context * context, Value *); +ExternalValue * nix_get_external(nix_c_context * context, nix_value *); /** @brief Get the ix'th element of a list * @@ -262,7 +263,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value *); * @param[in] ix list element to get * @return value, NULL in case of errors */ -Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int ix); +nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix); /** @brief Get an attr by name * @@ -273,7 +274,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta * @param[in] name attribute name * @return value, NULL in case of errors */ -Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name); +nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name); /** @brief Check if an attribute name exists on a value * @param[out] context Optional, stores error information @@ -282,7 +283,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt * @param[in] name attribute name * @return value, error info via context */ -bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name); +bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name); /** @brief Get an attribute by index in the sorted bindings * @@ -296,8 +297,8 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * @param[out] name will store a pointer to the attribute name * @return value, NULL in case of errors */ -Value * -nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i, const char ** name); +nix_value * nix_get_attr_byidx( + nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i, const char ** name); /** @brief Get an attribute name by index in the sorted bindings * @@ -310,7 +311,8 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta * @param[in] i attribute index * @return name, NULL in case of errors */ -const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i); +const char * +nix_get_attr_name_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i); /**@}*/ /** @name Initializers @@ -327,7 +329,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu * @param[in] b the boolean value * @return error code, NIX_OK on success. */ -nix_err nix_init_bool(nix_c_context * context, Value * value, bool b); +nix_err nix_init_bool(nix_c_context * context, nix_value * value, bool b); /** @brief Set a string * @param[out] context Optional, stores error information @@ -335,7 +337,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b); * @param[in] str the string, copied * @return error code, NIX_OK on success. */ -nix_err nix_init_string(nix_c_context * context, Value * value, const char * str); +nix_err nix_init_string(nix_c_context * context, nix_value * value, const char * str); /** @brief Set a path * @param[out] context Optional, stores error information @@ -343,7 +345,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str * @param[in] str the path string, copied * @return error code, NIX_OK on success. */ -nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * value, const char * str); +nix_err nix_init_path_string(nix_c_context * context, EvalState * s, nix_value * value, const char * str); /** @brief Set a float * @param[out] context Optional, stores error information @@ -351,7 +353,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val * @param[in] d the float, 64-bits * @return error code, NIX_OK on success. */ -nix_err nix_init_float(nix_c_context * context, Value * value, double d); +nix_err nix_init_float(nix_c_context * context, nix_value * value, double d); /** @brief Set an int * @param[out] context Optional, stores error information @@ -360,13 +362,13 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d); * @return error code, NIX_OK on success. */ -nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i); +nix_err nix_init_int(nix_c_context * context, nix_value * value, int64_t i); /** @brief Set null * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ -nix_err nix_init_null(nix_c_context * context, Value * value); +nix_err nix_init_null(nix_c_context * context, nix_value * value); /** @brief Set the value to a thunk that will perform a function application when needed. * @@ -382,7 +384,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value); * @see nix_value_call() for a similar function that performs the call immediately and only stores the return value. * Note the different argument order. */ -nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg); +nix_err nix_init_apply(nix_c_context * context, nix_value * value, nix_value * fn, nix_value * arg); /** @brief Set an external value * @param[out] context Optional, stores error information @@ -390,7 +392,7 @@ nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * @param[in] val the external value to set. Will be GC-referenced by the value. * @return error code, NIX_OK on success. */ -nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val); +nix_err nix_init_external(nix_c_context * context, nix_value * value, ExternalValue * val); /** @brief Create a list from a list builder * @param[out] context Optional, stores error information @@ -398,7 +400,7 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ -nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value * value); +nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, nix_value * value); /** @brief Create a list builder * @param[out] context Optional, stores error information @@ -415,7 +417,8 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, * @param[in] value value to insert * @return error code, NIX_OK on success. */ -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, Value * value); +nix_err +nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, nix_value * value); /** @brief Free a list builder * @@ -430,7 +433,7 @@ void nix_list_builder_free(ListBuilder * list_builder); * @param[in] b bindings builder to use. Make sure to unref this afterwards. * @return error code, NIX_OK on success. */ -nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * b); +nix_err nix_make_attrs(nix_c_context * context, nix_value * value, BindingsBuilder * b); /** @brief Set primop * @param[out] context Optional, stores error information @@ -439,14 +442,14 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * * @see nix_alloc_primop * @return error code, NIX_OK on success. */ -nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * op); +nix_err nix_init_primop(nix_c_context * context, nix_value * value, PrimOp * op); /** @brief Copy from another value * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] source value to copy from * @return error code, NIX_OK on success. */ -nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source); +nix_err nix_copy_value(nix_c_context * context, nix_value * value, const nix_value * source); /**@}*/ /** @brief Create a bindings builder @@ -466,7 +469,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * * @return error code, NIX_OK on success. */ nix_err -nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder, const char * name, Value * value); +nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder, const char * name, nix_value * value); /** @brief Free a bindings builder * @@ -493,7 +496,7 @@ void nix_bindings_builder_free(BindingsBuilder * builder); You should set this to false when building for your application's purpose. * @return NULL if failed, are a new nix_realised_string, which must be freed with nix_realised_string_free */ -nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD); +nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, nix_value * value, bool isIFD); /** @brief Start of the string * @param[in] realised_string diff --git a/tests/unit/libexpr-support/tests/nix_api_expr.hh b/tests/unit/libexpr-support/tests/nix_api_expr.hh index d1840d034..6ddca0d14 100644 --- a/tests/unit/libexpr-support/tests/nix_api_expr.hh +++ b/tests/unit/libexpr-support/tests/nix_api_expr.hh @@ -25,7 +25,7 @@ protected: } EvalState * state; - Value * value; + nix_value * value; }; } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 92a6a1175..8b97d6923 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -39,12 +39,12 @@ TEST_F(nix_api_expr_test, nix_expr_eval_drv) ASSERT_EQ(NIX_TYPE_ATTRS, nix_get_type(nullptr, value)); EvalState * stateFn = nix_state_create(nullptr, nullptr, store); - Value * valueFn = nix_alloc_value(nullptr, state); + nix_value * valueFn = nix_alloc_value(nullptr, state); nix_expr_eval_from_string(nullptr, stateFn, "builtins.toString", ".", valueFn); ASSERT_EQ(NIX_TYPE_FUNCTION, nix_get_type(nullptr, valueFn)); EvalState * stateResult = nix_state_create(nullptr, nullptr, store); - Value * valueResult = nix_alloc_value(nullptr, stateResult); + nix_value * valueResult = nix_alloc_value(nullptr, stateResult); nix_value_call(ctx, stateResult, valueFn, value, valueResult); ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(nullptr, valueResult)); @@ -70,7 +70,7 @@ TEST_F(nix_api_expr_test, nix_build_drv) })"; nix_expr_eval_from_string(nullptr, state, expr, ".", value); - Value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); + nix_value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); std::string drvPath; nix_get_string(nullptr, drvPathValue, OBSERVE_STRING(drvPath)); @@ -84,7 +84,7 @@ TEST_F(nix_api_expr_test, nix_build_drv) StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath.c_str()); ASSERT_EQ(true, nix_store_is_valid_path(ctx, store, drvStorePath)); - Value * outPathValue = nix_get_attr_byname(ctx, value, state, "outPath"); + nix_value * outPathValue = nix_get_attr_byname(ctx, value, state, "outPath"); std::string outPath; nix_get_string(ctx, outPathValue, OBSERVE_STRING(outPath)); @@ -193,7 +193,8 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context) const char * SAMPLE_USER_DATA = "whatever"; -static void primop_square(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +static void +primop_square(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { assert(context); assert(state); @@ -207,17 +208,17 @@ TEST_F(nix_api_expr_test, nix_expr_primop) PrimOp * primop = nix_alloc_primop(ctx, primop_square, 1, "square", nullptr, "square an integer", (void *) SAMPLE_USER_DATA); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, primopValue, three, result); assert_ctx_ok(); @@ -226,7 +227,8 @@ TEST_F(nix_api_expr_test, nix_expr_primop) ASSERT_EQ(9, r); } -static void primop_repeat(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +static void +primop_repeat(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { assert(context); assert(state); @@ -255,27 +257,27 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_multiple_calls) PrimOp * primop = nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * hello = nix_alloc_value(ctx, state); + nix_value * hello = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_string(ctx, hello, "hello"); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * partial = nix_alloc_value(ctx, state); + nix_value * partial = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, primopValue, hello, partial); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, partial, three, result); assert_ctx_ok(); @@ -290,22 +292,22 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call) PrimOp * primop = nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * hello = nix_alloc_value(ctx, state); + nix_value * hello = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_string(ctx, hello, "hello"); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); NIX_VALUE_CALL(ctx, state, result, primopValue, hello, three); assert_ctx_ok(); @@ -318,7 +320,7 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call) } static void -primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { } @@ -327,17 +329,17 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) PrimOp * primop = nix_alloc_primop(ctx, primop_bad_no_return, 1, "badNoReturn", nullptr, "a broken primop", nullptr); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, primopValue, three, result); ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); @@ -348,8 +350,8 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badNoReturn"))); } -static void -primop_bad_return_thunk(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +static void primop_bad_return_thunk( + void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { nix_init_apply(context, ret, args[0], args[1]); } @@ -358,22 +360,22 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) PrimOp * primop = nix_alloc_primop(ctx, primop_bad_return_thunk, 2, "badReturnThunk", nullptr, "a broken primop", nullptr); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * toString = nix_alloc_value(ctx, state); + nix_value * toString = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_expr_eval_from_string(ctx, state, "builtins.toString", ".", toString); assert_ctx_ok(); - Value * four = nix_alloc_value(ctx, state); + nix_value * four = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, four, 4); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); NIX_VALUE_CALL(ctx, state, result, primopValue, toString, four); @@ -387,11 +389,11 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) TEST_F(nix_api_expr_test, nix_value_call_multi_no_args) { - Value * n = nix_alloc_value(ctx, state); + nix_value * n = nix_alloc_value(ctx, state); nix_init_int(ctx, n, 3); assert_ctx_ok(); - Value * r = nix_alloc_value(ctx, state); + nix_value * r = nix_alloc_value(ctx, state); nix_value_call_multi(ctx, state, n, 0, nullptr, r); assert_ctx_ok(); diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc index 2391f8317..81ff285a4 100644 --- a/tests/unit/libexpr/nix_api_external.cc +++ b/tests/unit/libexpr/nix_api_external.cc @@ -49,10 +49,10 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external) nix_init_external(ctx, value, val); EvalState * stateResult = nix_state_create(nullptr, nullptr, store); - Value * valueResult = nix_alloc_value(nullptr, stateResult); + nix_value * valueResult = nix_alloc_value(nullptr, stateResult); EvalState * stateFn = nix_state_create(nullptr, nullptr, store); - Value * valueFn = nix_alloc_value(nullptr, stateFn); + nix_value * valueFn = nix_alloc_value(nullptr, stateFn); nix_expr_eval_from_string(nullptr, state, "builtins.typeOf", ".", valueFn); diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index b674d8681..7fc8b4f64 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -148,8 +148,8 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) int size = 10; ListBuilder * builder = nix_make_list_builder(ctx, state, size); - Value * intValue = nix_alloc_value(ctx, state); - Value * intValue2 = nix_alloc_value(ctx, state); + nix_value * intValue = nix_alloc_value(ctx, state); + nix_value * intValue2 = nix_alloc_value(ctx, state); // `init` and `insert` can be called in any order nix_init_int(ctx, intValue, 42); @@ -204,10 +204,10 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr) BindingsBuilder * builder = nix_make_bindings_builder(ctx, state, size); - Value * intValue = nix_alloc_value(ctx, state); + nix_value * intValue = nix_alloc_value(ctx, state); nix_init_int(ctx, intValue, 42); - Value * stringValue = nix_alloc_value(ctx, state); + nix_value * stringValue = nix_alloc_value(ctx, state); nix_init_string(ctx, stringValue, "foo"); nix_bindings_builder_insert(ctx, builder, "a", intValue); @@ -217,7 +217,7 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr) ASSERT_EQ(2, nix_get_attrs_size(ctx, value)); - Value * out_value = nix_get_attr_byname(ctx, value, state, "a"); + nix_value * out_value = nix_get_attr_byname(ctx, value, state, "a"); ASSERT_EQ(42, nix_get_int(ctx, out_value)); nix_gc_decref(ctx, out_value); @@ -261,10 +261,10 @@ TEST_F(nix_api_expr_test, nix_value_init) // two = 2; // f = a: a * a; - Value * two = nix_alloc_value(ctx, state); + nix_value * two = nix_alloc_value(ctx, state); nix_init_int(ctx, two, 2); - Value * f = nix_alloc_value(ctx, state); + nix_value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( ctx, state, @@ -278,7 +278,7 @@ TEST_F(nix_api_expr_test, nix_value_init) // r = f two; - Value * r = nix_alloc_value(ctx, state); + nix_value * r = nix_alloc_value(ctx, state); nix_init_apply(ctx, r, f, two); assert_ctx_ok(); @@ -307,11 +307,11 @@ TEST_F(nix_api_expr_test, nix_value_init) TEST_F(nix_api_expr_test, nix_value_init_apply_error) { - Value * some_string = nix_alloc_value(ctx, state); + nix_value * some_string = nix_alloc_value(ctx, state); nix_init_string(ctx, some_string, "some string"); assert_ctx_ok(); - Value * v = nix_alloc_value(ctx, state); + nix_value * v = nix_alloc_value(ctx, state); nix_init_apply(ctx, v, some_string, some_string); assert_ctx_ok(); @@ -336,7 +336,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) // r = f e // r should not throw an exception, because e is not evaluated - Value * f = nix_alloc_value(ctx, state); + nix_value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( ctx, state, @@ -347,9 +347,9 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) f); assert_ctx_ok(); - Value * e = nix_alloc_value(ctx, state); + nix_value * e = nix_alloc_value(ctx, state); { - Value * g = nix_alloc_value(ctx, state); + nix_value * g = nix_alloc_value(ctx, state); nix_expr_eval_from_string( ctx, state, @@ -365,7 +365,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) nix_gc_decref(ctx, g); } - Value * r = nix_alloc_value(ctx, state); + nix_value * r = nix_alloc_value(ctx, state); nix_init_apply(ctx, r, f, e); assert_ctx_ok(); @@ -377,7 +377,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) ASSERT_EQ(1, n); // nix_get_attr_byname isn't lazy (it could have been) so it will throw the exception - Value * foo = nix_get_attr_byname(ctx, r, state, "foo"); + nix_value * foo = nix_get_attr_byname(ctx, r, state, "foo"); ASSERT_EQ(nullptr, foo); ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("error message for test case nix_value_init_apply_lazy_arg")); @@ -388,7 +388,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) TEST_F(nix_api_expr_test, nix_copy_value) { - Value * source = nix_alloc_value(ctx, state); + nix_value * source = nix_alloc_value(ctx, state); nix_init_int(ctx, source, 42); nix_copy_value(ctx, value, source); From 03883f0d1d3a35e95a5f30c911503c53bfc5693a Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Thu, 13 Jun 2024 14:13:21 -0700 Subject: [PATCH 316/910] fix: copy in `install-multi-user.sh` (#10902) --- scripts/install-multi-user.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index ad3ee8881..6aee073e3 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -754,7 +754,7 @@ I will: (if it does, I will tell you how to clean them up.) - create local users (see the list above for the users I'll make) - create a local group ($NIX_BUILD_GROUP_NAME) - - install Nix in to $NIX_ROOT + - install Nix in $NIX_ROOT - create a configuration file in /etc/nix - set up the "default profile" by creating some Nix-related files in $ROOT_HOME From ea8e49bea5c2fdc93b87f52cbb58001e30e5f94d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 17:46:10 -0400 Subject: [PATCH 317/910] Force the cpuid option for libutil rather than relying on detection This is more robust, and match's Nixpkgs policy to force enable flags statically by default (a common distro thing). --- src/libutil/package.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index a461dccb8..dd93e5663 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -102,6 +102,10 @@ mkDerivation (finalAttrs: { '' ); + mesonFlags = [ + (lib.mesonEnable "cpuid" stdenv.hostPlatform.isx86_64) + ]; + env = { # Needed for Meson to find Boost. # https://github.com/NixOS/nixpkgs/issues/86131. From 81004a05c60d579c935bd300146f4a0c918bdbeb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 09:28:27 -0400 Subject: [PATCH 318/910] Build `nix-store` with Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Special thanks to everyone that has worked on a Meson port so far, @p01arst0rm and @Qyriad in particular. Co-Authored-By: p01arst0rm Co-Authored-By: Artemis Tosini Co-Authored-By: Artemis Tosini Co-Authored-By: Felix Uhl Co-Authored-By: Jade Lovelace Co-Authored-By: Lunaphied Co-Authored-By: Maximilian Bosch Co-Authored-By: Pierre Bourdon Co-Authored-By: Qyriad Co-Authored-By: Rebecca Turner Co-Authored-By: Winter Co-Authored-By: eldritch horrors Co-Authored-By: jade Co-Authored-By: julia Co-Authored-By: rebecca “wiggles” turner Co-Authored-By: wiggles dog Co-Authored-By: fricklerhandwerk Co-authored-by: Eli Schwartz --- flake.nix | 15 +- maintainers/hydra.nix | 6 +- meson.build | 1 + src/libstore/.version | 1 + src/libstore/build/derivation-goal.cc | 2 +- src/libstore/linux/meson.build | 10 + src/libstore/meson.build | 452 ++++++++++++++++++++++++++ src/libstore/meson.options | 25 ++ src/libstore/package.nix | 142 ++++++++ src/libstore/unix/meson.build | 19 ++ src/libstore/windows/meson.build | 11 + 11 files changed, 681 insertions(+), 3 deletions(-) create mode 120000 src/libstore/.version create mode 100644 src/libstore/linux/meson.build create mode 100644 src/libstore/meson.build create mode 100644 src/libstore/meson.options create mode 100644 src/libstore/package.nix create mode 100644 src/libstore/unix/meson.build create mode 100644 src/libstore/windows/meson.build diff --git a/flake.nix b/flake.nix index 599c6dfbe..377247cb8 100644 --- a/flake.nix +++ b/flake.nix @@ -169,6 +169,17 @@ ; }; + nix-store = final.callPackage ./src/libstore/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + libseccomp = final.libseccomp-nix; + busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; + }; + nix = final.callPackage ./package.nix { inherit @@ -260,6 +271,7 @@ { "nix" = { }; "nix-util" = { }; + "nix-store" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = @@ -312,10 +324,11 @@ "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; }; - inherit (pkgs.nix-util) mesonFlags; + mesonFlags = pkgs.nix-util.mesonFlags ++ pkgs.nix-store.mesonFlags; nativeBuildInputs = attrs.nativeBuildInputs or [] ++ pkgs.nix-util.nativeBuildInputs + ++ pkgs.nix-store.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 2541b061e..d53647549 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -33,7 +33,11 @@ let doBuild = false; }; - forAllPackages = lib.genAttrs [ "nix" "nix-util" ]; + forAllPackages = lib.genAttrs [ + "nix" + "nix-util" + "nix-store" + ]; in { # Binary package for various platforms. diff --git a/meson.build b/meson.build index fc6441877..10883d832 100644 --- a/meson.build +++ b/meson.build @@ -7,3 +7,4 @@ project('nix-dev-shell', 'cpp', ) subproject('libutil') +subproject('libstore') diff --git a/src/libstore/.version b/src/libstore/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index e72ef4cf9..146a060f3 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -26,7 +26,7 @@ #include #ifndef _WIN32 // TODO abstract over proc exit status -# include +# include #endif #include diff --git a/src/libstore/linux/meson.build b/src/libstore/linux/meson.build new file mode 100644 index 000000000..0c494b5d6 --- /dev/null +++ b/src/libstore/linux/meson.build @@ -0,0 +1,10 @@ +sources += files( + 'personality.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'fchmodat2-compat.hh', + 'personality.hh', +) diff --git a/src/libstore/meson.build b/src/libstore/meson.build new file mode 100644 index 000000000..c9c6f66f1 --- /dev/null +++ b/src/libstore/meson.build @@ -0,0 +1,452 @@ +project('nix-store', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +configdata.set_quoted('SYSTEM', host_machine.system()) + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +run_command('ln', '-s', + meson.project_build_root() / '__nothing_link_target', + meson.project_build_root() / '__nothing_symlink', + check : true, +) +can_link_symlink = run_command('ln', + meson.project_build_root() / '__nothing_symlink', + meson.project_build_root() / '__nothing_hardlink', + check : false, +).returncode() == 0 +run_command('rm', '-f', + meson.project_build_root() / '__nothing_symlink', + meson.project_build_root() / '__nothing_hardlink', + check : true, +) +summary('can hardlink to symlink', can_link_symlink, bool_yn : true) +configdata.set('CAN_LINK_SYMLINK', can_link_symlink.to_int()) + +# Check for each of these functions, and create a define like `#define HAVE_LCHOWN 1`. +# +# Only need to do functions that deps (like `libnixutil`) didn't already +# check for. +check_funcs = [ + # Optionally used for canonicalising files from the build + 'lchown', +] +foreach funcspec : check_funcs + define_name = 'HAVE_' + funcspec.underscorify().to_upper() + define_value = cxx.has_function(funcspec).to_int() + configdata.set(define_name, define_value) +endforeach + +has_acl_support = cxx.has_header('sys/xattr.h') \ + and cxx.has_function('llistxattr') \ + and cxx.has_function('lremovexattr') +configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int()) + +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') + deps_private += dependency('threads') +endif + +boost = dependency( + 'boost', + modules : ['container'], +) +# boost is a public dependency, but not a pkg-config dependency unfortunately, so we +# put in `deps_other`. +deps_other += boost + +curl = dependency('libcurl', 'curl') +deps_private += curl + +# seccomp only makes sense on Linux +is_linux = host_machine.system() == 'linux' +seccomp_required = get_option('seccomp-sandboxing') +if not is_linux and seccomp_required.enabled() + warning('Force-enabling seccomp on non-Linux does not make sense') +endif +seccomp = dependency('libseccomp', 'seccomp', required : seccomp_required, version : '>=2.5.5') +if is_linux and not seccomp.found() + warning('Sandbox security is reduced because libseccomp has not been found! Please provide libseccomp if it supports your CPU architecture.') +endif +configdata.set('HAVE_SECCOMP', seccomp.found().to_int()) +deps_private += seccomp + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') +deps_private += sqlite + + +enable_embedded_sandbox_shell = get_option('embedded-sandbox-shell') +if enable_embedded_sandbox_shell + # This one goes in config.h + # The path to busybox is passed as a -D flag when compiling this_library. + # Idk why, ask the old buildsystem. + configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) +endif + +generated_headers = [] +foreach header : [ 'schema.sql', 'ca-specific-schema.sql' ] + generated_headers += custom_target( + command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + input : header, + output : '@PLAINNAME@.gen.hh', + install : true, + install_dir : get_option('includedir') / 'nix' + ) +endforeach + +if enable_embedded_sandbox_shell + hexdump = find_program('hexdump', native : true) + embedded_sandbox_shell_gen = custom_target( + 'embedded-sandbox-shell.gen.hh', + command : [ + hexdump, + '-v', + '-e', + '1/1 "0x%x," "\n"' + ], + input : busybox.full_path(), + output : 'embedded-sandbox-shell.gen.hh', + capture : true, + feed : true, + ) + generated_headers += embedded_sandbox_shell_gen +endif + +config_h = configure_file( + configuration : configdata, + output : 'config-store.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'binary-cache-store.cc', + 'build-result.cc', + 'build/derivation-goal.cc', + 'build/drv-output-substitution-goal.cc', + 'build/entry-points.cc', + 'build/goal.cc', + 'build/substitution-goal.cc', + 'build/worker.cc', + 'builtins/buildenv.cc', + 'builtins/fetchurl.cc', + 'builtins/unpack-channel.cc', + 'common-protocol.cc', + 'content-address.cc', + 'daemon.cc', + 'derivations.cc', + 'derived-path-map.cc', + 'derived-path.cc', + 'downstream-placeholder.cc', + 'dummy-store.cc', + 'export-import.cc', + 'filetransfer.cc', + 'gc.cc', + 'globals.cc', + 'http-binary-cache-store.cc', + 'indirect-root-store.cc', + 'keys.cc', + 'legacy-ssh-store.cc', + 'local-binary-cache-store.cc', + 'local-fs-store.cc', + 'local-overlay-store.cc', + 'local-store.cc', + 'log-store.cc', + 'machines.cc', + 'make-content-addressed.cc', + 'misc.cc', + 'names.cc', + 'nar-accessor.cc', + 'nar-info-disk-cache.cc', + 'nar-info.cc', + 'optimise-store.cc', + 'outputs-spec.cc', + 'parsed-derivations.cc', + 'path-info.cc', + 'path-references.cc', + 'path-with-outputs.cc', + 'path.cc', + 'pathlocks.cc', + 'posix-fs-canonicalise.cc', + 'profiles.cc', + 'realisation.cc', + 'remote-fs-accessor.cc', + 'remote-store.cc', + 's3-binary-cache-store.cc', + 'serve-protocol-connection.cc', + 'serve-protocol.cc', + 'sqlite.cc', + 'ssh-store-config.cc', + 'ssh-store.cc', + 'ssh.cc', + 'store-api.cc', + 'store-reference.cc', + 'uds-remote-store.cc', + 'worker-protocol-connection.cc', + 'worker-protocol.cc', +) + +include_dirs = [ + include_directories('.'), + include_directories('build'), +] + +headers = [config_h] +files( + 'binary-cache-store.hh', + 'build-result.hh', + 'build/derivation-goal.hh', + 'build/drv-output-substitution-goal.hh', + 'build/goal.hh', + 'build/substitution-goal.hh', + 'build/worker.hh', + 'builtins.hh', + 'builtins/buildenv.hh', + 'common-protocol-impl.hh', + 'common-protocol.hh', + 'content-address.hh', + 'daemon.hh', + 'derivations.hh', + 'derived-path-map.hh', + 'derived-path.hh', + 'downstream-placeholder.hh', + 'filetransfer.hh', + 'gc-store.hh', + 'globals.hh', + 'indirect-root-store.hh', + 'keys.hh', + 'legacy-ssh-store.hh', + 'length-prefixed-protocol-helper.hh', + 'local-fs-store.hh', + 'local-overlay-store.hh', + 'local-store.hh', + 'log-store.hh', + 'machines.hh', + 'make-content-addressed.hh', + 'names.hh', + 'nar-accessor.hh', + 'nar-info-disk-cache.hh', + 'nar-info.hh', + 'outputs-spec.hh', + 'parsed-derivations.hh', + 'path-info.hh', + 'path-references.hh', + 'path-regex.hh', + 'path-with-outputs.hh', + 'path.hh', + 'pathlocks.hh', + 'posix-fs-canonicalise.hh', + 'profiles.hh', + 'realisation.hh', + 'remote-fs-accessor.hh', + 'remote-store-connection.hh', + 'remote-store.hh', + 's3-binary-cache-store.hh', + 's3.hh', + 'serve-protocol-connection.hh', + 'serve-protocol-impl.hh', + 'serve-protocol.hh', + 'sqlite.hh', + 'ssh-store-config.hh', + 'ssh.hh', + 'store-api.hh', + 'store-cast.hh', + 'store-dir-config.hh', + 'store-reference.hh', + 'uds-remote-store.hh', + 'worker-protocol-connection.hh', + 'worker-protocol-impl.hh', + 'worker-protocol.hh', +) + +if host_machine.system() == 'linux' + subdir('linux') +endif + +if host_machine.system() == 'windows' + subdir('windows') +else + subdir('unix') +endif + +fs = import('fs') + +prefix = get_option('prefix') +# For each of these paths, assume that it is relative to the prefix unless +# it is already an absolute path (which is the default for store-dir, state-dir, and log-dir). +path_opts = [ + # Meson built-ins. + 'datadir', + 'bindir', + 'mandir', + 'libdir', + 'includedir', + 'libexecdir', + # Homecooked Nix directories. + 'store-dir', + 'state-dir', + 'log-dir', +] +# For your grepping pleasure, this loop sets the following variables that aren't mentioned +# literally above: +# store_dir +# state_dir +# log_dir +# profile_dir +foreach optname : path_opts + varname = optname.replace('-', '_') + path = get_option(optname) + if fs.is_absolute(path) + set_variable(varname, path) + else + set_variable(varname, prefix / path) + endif +endforeach + +# sysconfdir doesn't get anything installed to directly, and is only used to +# tell Nix where to look for nix.conf, so it doesn't get appended to prefix. +sysconfdir = get_option('sysconfdir') +if not fs.is_absolute(sysconfdir) + sysconfdir = '/' / sysconfdir +endif + +lsof = find_program('lsof', required : false) + +# Aside from prefix itself, each of these was made into an absolute path +# by joining it with prefix, unless it was already an absolute path +# (which is the default for store-dir, state-dir, and log-dir). +cpp_str_defines = { + 'NIX_PREFIX': prefix, + 'NIX_STORE_DIR': store_dir, + 'NIX_DATA_DIR': datadir, + 'NIX_STATE_DIR': state_dir / 'nix', + 'NIX_LOG_DIR': log_dir, + 'NIX_CONF_DIR': sysconfdir / 'nix', + 'NIX_BIN_DIR': bindir, + 'NIX_MAN_DIR': mandir, +} + +if lsof.found() + lsof_path = lsof.full_path() +else + # Just look up on the PATH + lsof_path = 'lsof' +endif +cpp_str_defines += { + 'LSOF': lsof_path +} + +#if busybox.found() + cpp_str_defines += { +# 'SANDBOX_SHELL': busybox.full_path() + } +#endif + +cpp_args = [] + +foreach name, value : cpp_str_defines + cpp_args += [ + '-D' + name + '=' + '"' + value + '"' + ] +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # See note in `../nix-util/meson.build` + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nixstore', + generated_headers, + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + cpp_args : cpp_args, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = deps_public +if nix_util.type_name() == 'internal' + # `requires` cannot contain declared dependencies (from the + # subproject), so we need to do this manually + requires = [ 'nix-util' ] + requires +endif + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, + libraries_private : ['-lboost_container'], +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [nix_util], +)) diff --git a/src/libstore/meson.options b/src/libstore/meson.options new file mode 100644 index 000000000..723a8e020 --- /dev/null +++ b/src/libstore/meson.options @@ -0,0 +1,25 @@ +# vim: filetype=meson + +option('embedded-sandbox-shell', type : 'boolean', value : false, + description : 'include the sandbox shell in the Nix binary', +) + +option('seccomp-sandboxing', type : 'feature', + description : 'build support for seccomp sandboxing (recommended unless your arch doesn\'t support libseccomp, only relevant on Linux)', +) + +option('sandbox-shell', type : 'string', value : 'busybox', + description : 'path to a statically-linked shell to use as /bin/sh in sandboxes (usually busybox)', +) + +option('store-dir', type : 'string', value : '/nix/store', + description : 'path of the Nix store', +) + +option('state-dir', type : 'string', value : '/nix/var', + description : 'path to store state in for Nix', +) + +option('log-dir', type : 'string', value : '/nix/var/log/nix', + description : 'path to store logs in for Nix', +) diff --git a/src/libstore/package.nix b/src/libstore/package.nix new file mode 100644 index 000000000..e54dfe597 --- /dev/null +++ b/src/libstore/package.nix @@ -0,0 +1,142 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util +, boost +, curl +, aws-sdk-cpp +, libseccomp +, nlohmann_json +, man +, sqlite + +, busybox-sandbox-shell ? null + +# Configuration Options + +, versionSuffix ? "" +, officialRelease ? false + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +# Avoid setting things that would interfere with a functioning devShell +, forDevShell ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "sb") ./.) + (fileset.fileFilter (file: file.hasExt "md") ./.) + (fileset.fileFilter (file: file.hasExt "sql") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + boost + curl + sqlite + ] ++ lib.optional stdenv.hostPlatform.isLinux libseccomp + # There have been issues building these dependencies + ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) + (aws-sdk-cpp.override { + apis = ["s3" "transfer"]; + customMemoryManagement = false; + }) + ; + + propagatedBuildInputs = [ + nix-util + nlohmann_json + ]; + + disallowedReferences = [ boost ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + (lib.mesonEnable "seccomp-sandboxing" stdenv.hostPlatform.isLinux) + (lib.mesonBool "embedded-sandbox-shell" stdenv.hostPlatform.isStatic) + ] ++ lib.optionals stdenv.hostPlatform.isLinux [ + (lib.mesonOption "sandbox-shell" "${busybox-sandbox-shell}/bin/busybox") + ]; + + env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib boost}/lib"; + } // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs that ends up in `Libs.private` + # by default, and would clash with out `disallowedReferences`. Part + # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. + '' + sed -i "$out/lib/pkgconfig/nix-store.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore/unix/meson.build b/src/libstore/unix/meson.build new file mode 100644 index 000000000..d9d190131 --- /dev/null +++ b/src/libstore/unix/meson.build @@ -0,0 +1,19 @@ +sources += files( + 'build/child.cc', + 'build/hook-instance.cc', + 'build/local-derivation-goal.cc', + 'pathlocks.cc', + 'user-lock.cc', +) + +include_dirs += include_directories( + '.', + 'build', +) + +headers += files( + 'build/child.hh', + 'build/hook-instance.hh', + 'build/local-derivation-goal.hh', + 'user-lock.hh', +) diff --git a/src/libstore/windows/meson.build b/src/libstore/windows/meson.build new file mode 100644 index 000000000..b81c5b2af --- /dev/null +++ b/src/libstore/windows/meson.build @@ -0,0 +1,11 @@ +sources += files( + 'pathlocks.cc', +) + +include_dirs += include_directories( + '.', + #'build', +) + +headers += files( +) From 2dc7598779178188fddf87e6da940636c9d67b2e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 16:30:34 +0200 Subject: [PATCH 319/910] C API: Add nix_clear_err --- src/libutil-c/nix_api_util.cc | 6 ++++++ src/libutil-c/nix_api_util.h | 29 ++++++++++++++++++++++++++--- tests/unit/libutil/nix_api_util.cc | 3 +++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/libutil-c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc index 0a9b49345..c976b1815 100644 --- a/src/libutil-c/nix_api_util.cc +++ b/src/libutil-c/nix_api_util.cc @@ -57,6 +57,12 @@ nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg) return err; } +void nix_clear_err(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; +} + const char * nix_version_get() { return PACKAGE_VERSION; diff --git a/src/libutil-c/nix_api_util.h b/src/libutil-c/nix_api_util.h index e0ca04e69..9a9f1d98d 100644 --- a/src/libutil-c/nix_api_util.h +++ b/src/libutil-c/nix_api_util.h @@ -221,7 +221,9 @@ const char * nix_version_get(); * @param[out] n optional: a pointer to an unsigned int that is set to the * length of the error. * @return nullptr if no error message was ever set, - * a borrowed pointer to the error message otherwise. + * a borrowed pointer to the error message otherwise, which is valid + * until the next call to a Nix function, or until the context is + * destroyed. */ const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, unsigned int * n); @@ -282,13 +284,34 @@ nix_err nix_err_code(const nix_c_context * read_context); * * All other use is internal to the API. * - * @param context context to write the error message to, or NULL + * @param context context to write the error message to, required unless C++ exceptions are supported * @param err The error code to set and return - * @param msg The error message to set. + * @param msg The error message to set. This string is copied. * @returns the error code set */ nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg); +/** + * @brief Clear the error message from a nix context. + * + * This is performed implicitly by all functions that accept a context, so + * this won't be necessary in most cases. + * However, if you want to clear the error message without calling another + * function, you can use this. + * + * Example use case: a higher order function that helps with error handling, + * to make it more robust in the following scenario: + * + * 1. A previous call failed, and the error was caught and handled. + * 2. The context is reused with our error handling helper function. + * 3. The callback passed to the helper function doesn't actually make a call to + * a Nix function. + * 4. The handled error is raised again, from an unrelated call. + * + * This failure can be avoided by clearing the error message after handling it. + */ +void nix_clear_err(nix_c_context * context); + /** * @} */ diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index d2999f55b..052d0c533 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -31,6 +31,9 @@ TEST_F(nix_api_util_context, nix_context_error) } ASSERT_EQ(ctx->last_err_code, NIX_ERR_UNKNOWN); ASSERT_EQ(*ctx->last_err, err_msg_ref); + + nix_clear_err(ctx); + ASSERT_EQ(ctx->last_err_code, NIX_OK); } TEST_F(nix_api_util_context, nix_set_err_msg) From 61381c9964c69afd720264272a0f42d298d6616c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 14 Jun 2024 16:36:23 +0200 Subject: [PATCH 320/910] C API: Make nix_err_msg treat NIX_OK as having no message The documentation "solved" this by specifying a precondition, but let's just make it more robust, and not leak irrelevant messages that might linger. We don't clear the message when clearing the status, in order to keep clearing fast; see last_err field doc. --- src/libutil-c/nix_api_util.cc | 2 +- src/libutil-c/nix_api_util_internal.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libutil-c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc index c976b1815..cf468c8cd 100644 --- a/src/libutil-c/nix_api_util.cc +++ b/src/libutil-c/nix_api_util.cc @@ -112,7 +112,7 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_con { if (context) context->last_err_code = NIX_OK; - if (read_context->last_err) { + if (read_context->last_err && read_context->last_err_code != NIX_OK) { if (n) *n = read_context->last_err->size(); return read_context->last_err->c_str(); diff --git a/src/libutil-c/nix_api_util_internal.h b/src/libutil-c/nix_api_util_internal.h index aa829feaf..7fa4252ac 100644 --- a/src/libutil-c/nix_api_util_internal.h +++ b/src/libutil-c/nix_api_util_internal.h @@ -10,6 +10,7 @@ struct nix_c_context { nix_err last_err_code = NIX_OK; + /** The last error message. Always check last_err_code. This may not have been cleared, so that clearing is fast. */ std::optional last_err = {}; std::optional info = {}; std::string name = ""; From 5a9e1c0d20e2332c79fb0fd7570315a5d93041f2 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Sat, 15 Jun 2024 14:02:17 +0200 Subject: [PATCH 321/910] Restrict supported tarball formats to actual Tarballs The documentation is clear about the supported formats (with at least `builtins.fetchTarball`). The way the code was written previously it supported all the formats that libarchive supported. That is a surprisingly large amount of formats that are likely not on the radar of the Nix developers and users. Before people end up relying on this (or if they do) it is better to break it now before it becomes a widespread "feature". Zip file support has been retained as (at least to my knowledge) historically that has been used to fetch nixpkgs in some shell expressions *many* years back. Fixes https://github.com/NixOS/nix/issues/10917 --- src/libutil/tarfile.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 6bb2bd2f3..f0e24e937 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -79,7 +79,8 @@ TarArchive::TarArchive(Source & source, bool raw, std::optional com } if (!raw) { - archive_read_support_format_all(archive); + archive_read_support_format_tar(archive); + archive_read_support_format_zip(archive); } else { archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); @@ -96,7 +97,8 @@ TarArchive::TarArchive(const Path & path) , buffer(defaultBufferSize) { archive_read_support_filter_all(archive); - archive_read_support_format_all(archive); + archive_read_support_format_tar(archive); + archive_read_support_format_zip(archive); archive_read_set_option(archive, NULL, "mac-ext", NULL); check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s"); } From 2894c1b38e961fe2cc22df2a8247773cd012dcac Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 16:34:49 +0200 Subject: [PATCH 322/910] WIP add testresults output --- package.nix | 8 +++++++- tests/unit/libexpr/local.mk | 2 +- tests/unit/libfetchers/local.mk | 2 +- tests/unit/libstore/local.mk | 2 +- tests/unit/libutil/local.mk | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/package.nix b/package.nix index 677ee73c7..5986edeb4 100644 --- a/package.nix +++ b/package.nix @@ -208,7 +208,9 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && (enableManual || enableInternalAPIDocs || enableExternalAPIDocs)) "doc" - ++ lib.optional installUnitTests "check"; + ++ lib.optional installUnitTests "check" + ++ lib.optional doCheck "testresults" + ; nativeBuildInputs = [ autoconf-archive @@ -317,6 +319,10 @@ in { makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1"; + preCheck = '' + mkdir $testresults + ''; + installTargets = lib.optional doBuild "install" ++ lib.optional enableInternalAPIDocs "internal-api-html" ++ lib.optional enableExternalAPIDocs "external-api-html"; diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index 09a7dfca1..1617e2823 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -4,7 +4,7 @@ programs += libexpr-tests libexpr-tests_NAME := libnixexpr-tests -libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml libexpr-tests_DIR := $(d) diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk index d576d28f3..286a59030 100644 --- a/tests/unit/libfetchers/local.mk +++ b/tests/unit/libfetchers/local.mk @@ -4,7 +4,7 @@ programs += libfetchers-tests libfetchers-tests_NAME = libnixfetchers-tests -libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml libfetchers-tests_DIR := $(d) diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk index 0af1d2622..8d3d6b0af 100644 --- a/tests/unit/libstore/local.mk +++ b/tests/unit/libstore/local.mk @@ -4,7 +4,7 @@ programs += libstore-tests libstore-tests_NAME = libnixstore-tests -libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml libstore-tests_DIR := $(d) diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk index b9bddc24d..404f35cf1 100644 --- a/tests/unit/libutil/local.mk +++ b/tests/unit/libutil/local.mk @@ -4,7 +4,7 @@ programs += libutil-tests libutil-tests_NAME = libnixutil-tests -libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml libutil-tests_DIR := $(d) From de639ceafe75ae25293f49f170ccad0c3e4a133f Mon Sep 17 00:00:00 2001 From: Jared Baur Date: Sat, 15 Jun 2024 09:06:35 +0000 Subject: [PATCH 323/910] Don't chown when local-store is read-only If the local-store is using the read-only flag, the underlying filesystem might be read-only, thus an attempt to `chown` would always fail. --- src/libstore/local-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index b44879cc9..676a035fa 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -233,7 +233,7 @@ LocalStore::LocalStore(const Params & params) struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); if (!gr) printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup); - else { + else if (!readOnly) { struct stat st; if (stat(realStoreDir.get().c_str(), &st)) throw SysError("getting attributes of path '%1%'", realStoreDir); From b0cfac8f93b3dc22df6bff14b12edc9d00a1d439 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 00:03:50 -0700 Subject: [PATCH 324/910] Fix compile error on windows --- src/libutil/file-descriptor.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 492b67d74..be61375f6 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -157,7 +157,7 @@ void closeOnExec(Descriptor fd); #endif #if defined(_WIN32) && _WIN32_WINNT >= 0x0600 -namespace windows ( +namespace windows { Path handleToPath(Descriptor handle); std::wstring handleToFileName(Descriptor handle); From 5e806673c3b7dda4a6dcf4fc45a0aa292751e6cb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 17 Jun 2024 08:29:02 -0400 Subject: [PATCH 325/910] Make `hydraJobs.build` include the constituent packages We were only doing that for the more exotic builds, just forgot. --- maintainers/hydra.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index d53647549..8ccf2f951 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -41,7 +41,8 @@ let in { # Binary package for various platforms. - build = forAllSystems (system: self.packages.${system}.nix); + build = forAllPackages (pkgName: + forAllSystems (system: self.packages.${system}.${pkgName})); shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); From c9cdc2423acb84714f2434333dbadfd1acaa9d26 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 17 Jun 2024 09:04:41 -0400 Subject: [PATCH 326/910] Temporarily remove the Meson builds from `packages` in the flake This will avoid some out-of-memory issues in GitHub actions that result from num jobs > 1 and num cores = 4. Once we only have the Meson build system, this problem should go away, and we can reenable these jobs. --- flake.nix | 7 +++++-- maintainers/hydra.nix | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 377247cb8..9f494cb15 100644 --- a/flake.nix +++ b/flake.nix @@ -270,8 +270,11 @@ (lib.genAttrs stdenvs (_: { }))) { "nix" = { }; - "nix-util" = { }; - "nix-store" = { }; + # Temporarily disabled because GitHub Actions OOM issues. Once + # the old build system is gone and we are back to one build + # system, we should reenable these. + #"nix-util" = { }; + #"nix-store" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 8ccf2f951..cc0dadac9 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -42,7 +42,7 @@ in { # Binary package for various platforms. build = forAllPackages (pkgName: - forAllSystems (system: self.packages.${system}.${pkgName})); + forAllSystems (system: nixpkgsFor.${system}.native.${pkgName})); shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); From 68b8a28bc4b09cdd88f28bf0ba102a274a461b0c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 11:39:06 +0200 Subject: [PATCH 327/910] tests/run.sh: Check that env is mostly unmodified --- tests/functional/flakes/run.sh | 21 +++++++++++++++++++++ tests/functional/shell-hello.nix | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/tests/functional/flakes/run.sh b/tests/functional/flakes/run.sh index 4d8b512b9..7fc78b3aa 100755 --- a/tests/functional/flakes/run.sh +++ b/tests/functional/flakes/run.sh @@ -27,5 +27,26 @@ nix run --no-write-lock-file .#pkgAsPkg ! nix run --no-write-lock-file .#pkgAsApp || fail "'nix run' shouldn’t accept an 'app' defined under 'packages'" ! nix run --no-write-lock-file .#appAsPkg || fail "elements of 'apps' should be of type 'app'" +# Test that we're not setting any more environment variables than necessary. +# For instance, we might set an environment variable temporarily to affect some +# initialization or whatnot, but this must not leak into the environment of the +# command being run. +env > $TEST_ROOT/expected-env +nix run -f shell-hello.nix env > $TEST_ROOT/actual-env +# Remove/reset variables we expect to be different. +# - PATH is modified by nix shell +# - _ is set by bash and is expected to differ because it contains the original command +# - __CF_USER_TEXT_ENCODING is set by macOS and is beyond our control +sed -i \ + -e 's/PATH=.*/PATH=.../' \ + -e 's/_=.*/_=.../' \ + -e '/^__CF_USER_TEXT_ENCODING=.*$/d' \ + $TEST_ROOT/expected-env $TEST_ROOT/actual-env +sort $TEST_ROOT/expected-env | uniq > $TEST_ROOT/expected-env.sorted +# nix run appears to clear _. I don't understand why. Is this ok? +echo "_=..." >> $TEST_ROOT/actual-env +sort $TEST_ROOT/actual-env | uniq > $TEST_ROOT/actual-env.sorted +diff $TEST_ROOT/expected-env.sorted $TEST_ROOT/actual-env.sorted + clearStore diff --git a/tests/functional/shell-hello.nix b/tests/functional/shell-hello.nix index c46fdec8a..c920d7cb4 100644 --- a/tests/functional/shell-hello.nix +++ b/tests/functional/shell-hello.nix @@ -55,4 +55,26 @@ rec { chmod +x $out/bin/hello ''; }; + + # execs env from PATH, so that we can probe the environment + # does not allow arguments, because we don't need them + env = mkDerivation { + name = "env"; + outputs = [ "out" ]; + buildCommand = + '' + mkdir -p $out/bin + + cat > $out/bin/env <&2 + exit 1 + fi + exec env + EOF + chmod +x $out/bin/env + ''; + }; + } From 316b58dd5fcf6df5931c53c85a30018fe9d7d2ff Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 11:39:33 +0200 Subject: [PATCH 328/910] tests/shell.sh: Check that env is mostly unmodified --- tests/functional/shell.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index 1760eefff..fd0020a0f 100755 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -21,6 +21,25 @@ nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World' # Test that symlinks outside of the store don't work. expect 1 nix shell -f shell-hello.nix forbidden-symlink -c hello 2>&1 | grepQuiet "is not in the Nix store" +# Test that we're not setting any more environment variables than necessary. +# For instance, we might set an environment variable temporarily to affect some +# initialization or whatnot, but this must not leak into the environment of the +# command being run. +env > $TEST_ROOT/expected-env +nix shell -f shell-hello.nix hello -c env > $TEST_ROOT/actual-env +# Remove/reset variables we expect to be different. +# - PATH is modified by nix shell +# - _ is set by bash and is expectedf to differ because it contains the original command +# - __CF_USER_TEXT_ENCODING is set by macOS and is beyond our control +sed -i \ + -e 's/PATH=.*/PATH=.../' \ + -e 's/_=.*/_=.../' \ + -e '/^__CF_USER_TEXT_ENCODING=.*$/d' \ + $TEST_ROOT/expected-env $TEST_ROOT/actual-env +sort $TEST_ROOT/expected-env > $TEST_ROOT/expected-env.sorted +sort $TEST_ROOT/actual-env > $TEST_ROOT/actual-env.sorted +diff $TEST_ROOT/expected-env.sorted $TEST_ROOT/actual-env.sorted + if isDaemonNewer "2.20.0pre20231220"; then # Test that command line attribute ordering is reflected in the PATH # https://github.com/NixOS/nix/issues/7905 From 4f340213bbfe9e372eea1eb02230720a5b5ba2b8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 17 Jun 2024 17:49:09 +0200 Subject: [PATCH 329/910] use separate paragraphs inside list items --- doc/manual/src/command-ref/nix-channel.md | 18 ++++--- .../src/command-ref/nix-collect-garbage.md | 6 ++- .../command-ref/nix-env/delete-generations.md | 9 ++-- .../src/command-ref/nix-env/env-common.md | 3 +- .../src/command-ref/nix-env/opt-common.md | 12 +++-- doc/manual/src/command-ref/nix-env/query.md | 54 ++++++++++++------- doc/manual/src/command-ref/nix-env/upgrade.md | 18 ++++--- doc/manual/src/command-ref/nix-hash.md | 33 ++++++++---- doc/manual/src/command-ref/nix-instantiate.md | 24 ++++++--- .../src/command-ref/nix-prefetch-url.md | 15 ++++-- doc/manual/src/command-ref/nix-shell.md | 24 ++++++--- .../src/command-ref/nix-store/add-fixed.md | 3 +- doc/manual/src/command-ref/nix-store/gc.md | 12 +++-- doc/manual/src/command-ref/nix-store/query.md | 48 +++++++++++------ .../src/command-ref/nix-store/realise.md | 9 ++-- doc/manual/src/command-ref/nix-store/serve.md | 3 +- .../src/command-ref/nix-store/verify.md | 6 ++- 17 files changed, 198 insertions(+), 99 deletions(-) diff --git a/doc/manual/src/command-ref/nix-channel.md b/doc/manual/src/command-ref/nix-channel.md index cebbc7b00..99f6e37cf 100644 --- a/doc/manual/src/command-ref/nix-channel.md +++ b/doc/manual/src/command-ref/nix-channel.md @@ -27,7 +27,8 @@ The moving parts of channels are: This command has the following operations: - - `--add` *url* \[*name*\]\ + - `--add` *url* \[*name*\] + Add a channel *name* located at *url* to the list of subscribed channels. If *name* is omitted, default to the last component of *url*, with the suffixes `-stable` or `-unstable` removed. @@ -39,17 +40,21 @@ This command has the following operations: A channel URL must point to a directory containing a file `nixexprs.tar.gz`. At the top level, that tarball must contain a single directory with a `default.nix` file that serves as the channel’s entry point. - - `--remove` *name*\ + - `--remove` *name* + Remove the channel *name* from the list of subscribed channels. - - `--list`\ + - `--list` + Print the names and URLs of all subscribed channels on standard output. - - `--update` \[*names*…\]\ + - `--update` \[*names*…\] + Download the Nix expressions of subscribed channels and create a new generation. Update all channels if none is specified, and only those included in *names* otherwise. - - `--list-generations`\ + - `--list-generations` + Prints a list of all the current existing generations for the channel profile. @@ -58,7 +63,8 @@ This command has the following operations: nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations ``` - - `--rollback` \[*generation*\]\ + - `--rollback` \[*generation*\] + Revert channels to the state before the last call to `nix-channel --update`. Optionally, you can specify a specific channel *generation* number to restore. diff --git a/doc/manual/src/command-ref/nix-collect-garbage.md b/doc/manual/src/command-ref/nix-collect-garbage.md index 8e1307c48..2136d28e9 100644 --- a/doc/manual/src/command-ref/nix-collect-garbage.md +++ b/doc/manual/src/command-ref/nix-collect-garbage.md @@ -48,12 +48,14 @@ Instead, it looks in a few locations, and acts on all profiles it finds there: These options are for deleting old [profiles] prior to deleting unreachable [store objects]. -- [`--delete-old`](#opt-delete-old) / `-d`\ +- [`--delete-old`](#opt-delete-old) / `-d` + Delete all old generations of profiles. This is the equivalent of invoking [`nix-env --delete-generations old`](@docroot@/command-ref/nix-env/delete-generations.md#generations-old) on each found profile. -- [`--delete-older-than`](#opt-delete-older-than) *period*\ +- [`--delete-older-than`](#opt-delete-older-than) *period* + Delete all generations of profiles older than the specified amount (except for the generations that were active at that point in time). *period* is a value such as `30d`, which would mean 30 days. diff --git a/doc/manual/src/command-ref/nix-env/delete-generations.md b/doc/manual/src/command-ref/nix-env/delete-generations.md index ae618b2c6..b1ff0bb69 100644 --- a/doc/manual/src/command-ref/nix-env/delete-generations.md +++ b/doc/manual/src/command-ref/nix-env/delete-generations.md @@ -12,7 +12,8 @@ This operation deletes the specified generations of the current profile. *generations* can be a one of the following: -- [`...`](#generations-list):\ +- [`...`](#generations-list) + A list of generation numbers, each one a separate command-line argument. Delete exactly the profile generations given by their generation number. @@ -30,7 +31,8 @@ This operation deletes the specified generations of the current profile. > Because one can roll back to a previous generation, it is possible to have generations newer than the current one. > They will also be deleted. -- [`d`](#generations-time):\ +- [`d`](#generations-time) + The last *number* days *Example*: `30d` @@ -38,7 +40,8 @@ This operation deletes the specified generations of the current profile. Delete all generations created more than *number* days ago, except the most recent one of them. This allows rolling back to generations that were available within the specified period. -- [`+`](#generations-count):\ +- [`+`](#generations-count) + The last *number* generations up to the present *Example*: `+5` diff --git a/doc/manual/src/command-ref/nix-env/env-common.md b/doc/manual/src/command-ref/nix-env/env-common.md index 735817959..200da7219 100644 --- a/doc/manual/src/command-ref/nix-env/env-common.md +++ b/doc/manual/src/command-ref/nix-env/env-common.md @@ -1,6 +1,7 @@ # Environment variables -- `NIX_PROFILE`\ +- `NIX_PROFILE` + Location of the Nix profile. Defaults to the target of the symlink `~/.nix-profile`, if it exists, or `/nix/var/nix/profiles/default` otherwise. diff --git a/doc/manual/src/command-ref/nix-env/opt-common.md b/doc/manual/src/command-ref/nix-env/opt-common.md index 636281b6d..3ece3e881 100644 --- a/doc/manual/src/command-ref/nix-env/opt-common.md +++ b/doc/manual/src/command-ref/nix-env/opt-common.md @@ -2,7 +2,8 @@ The following options are allowed for all `nix-env` operations, but may not always have an effect. - - `--file` / `-f` *path*\ + - `--file` / `-f` *path* + Specifies the Nix expression (designated below as the *active Nix expression*) used by the `--install`, `--upgrade`, and `--query --available` operations to obtain derivations. The default is @@ -13,13 +14,15 @@ The following options are allowed for all `nix-env` operations, but may not alwa unpacked to a temporary location. The tarball must include a single top-level directory containing at least a file named `default.nix`. - - `--profile` / `-p` *path*\ + - `--profile` / `-p` *path* + Specifies the profile to be used by those operations that operate on a profile (designated below as the *active profile*). A profile is a sequence of user environments called *generations*, one of which is the *current generation*. - - `--dry-run`\ + - `--dry-run` + For the `--install`, `--upgrade`, `--uninstall`, `--switch-generation`, `--delete-generations` and `--rollback` operations, this flag will cause `nix-env` to print what *would* be @@ -29,7 +32,8 @@ The following options are allowed for all `nix-env` operations, but may not alwa [substituted](@docroot@/glossary.md) (i.e., downloaded) and which paths will be built from source (because no substitute is available). - - `--system-filter` *system*\ + - `--system-filter` *system* + By default, operations such as `--query --available` show derivations matching any platform. This option allows you to use derivations for the specified platform *system*. diff --git a/doc/manual/src/command-ref/nix-env/query.md b/doc/manual/src/command-ref/nix-env/query.md index c9b4d8513..c67794ed5 100644 --- a/doc/manual/src/command-ref/nix-env/query.md +++ b/doc/manual/src/command-ref/nix-env/query.md @@ -35,11 +35,13 @@ The derivations are sorted by their `name` attributes. The following flags specify the set of things on which the query operates. - - `--installed`\ + - `--installed` + The query operates on the store paths that are installed in the current generation of the active profile. This is the default. - - `--available`; `-a`\ + - `--available` / `-a` + The query operates on the derivations that are available in the active Nix expression. @@ -50,24 +52,28 @@ selected derivations. Multiple flags may be specified, in which case the information is shown in the order given here. Note that the name of the derivation is shown unless `--no-name` is specified. - - `--xml`\ + - `--xml` + Print the result in an XML representation suitable for automatic processing by other tools. The root element is called `items`, which contains a `item` element for each available or installed derivation. The fields discussed below are all stored in attributes of the `item` elements. - - `--json`\ + - `--json` + Print the result in a JSON representation suitable for automatic processing by other tools. - - `--prebuilt-only` / `-b`\ + - `--prebuilt-only` / `-b` + Show only derivations for which a substitute is registered, i.e., there is a pre-built binary available that can be downloaded in lieu of building the derivation. Thus, this shows all packages that probably can be installed quickly. - - `--status`; `-s`\ + - `--status` / `-s` + Print the *status* of the derivation. The status consists of three characters. The first is `I` or `-`, indicating whether the derivation is currently installed in the current generation of the @@ -78,49 +84,61 @@ derivation is shown unless `--no-name` is specified. derivation to be built. The third is `S` or `-`, indicating whether a substitute is available for the derivation. - - `--attr-path`; `-P`\ + - `--attr-path` / `-P` + Print the *attribute path* of the derivation, which can be used to unambiguously select it using the `--attr` option available in commands that install derivations like `nix-env --install`. This option only works together with `--available` - - `--no-name`\ + - `--no-name` + Suppress printing of the `name` attribute of each derivation. - - `--compare-versions` / `-c`\ + - `--compare-versions` / `-c` + Compare installed versions to available versions, or vice versa (if `--available` is given). This is useful for quickly seeing whether upgrades for installed packages are available in a Nix expression. A column is added with the following meaning: - - `<` *version*\ + - `<` *version* + A newer version of the package is available or installed. - - `=` *version*\ + - `=` *version* + At most the same version of the package is available or installed. - - `>` *version*\ + - `>` *version* + Only older versions of the package are available or installed. - - `- ?`\ + - `- ?` + No version of the package is available or installed. - - `--system`\ + - `--system` + Print the `system` attribute of the derivation. - - `--drv-path`\ + - `--drv-path` + Print the path of the [store derivation](@docroot@/glossary.md#gloss-store-derivation). - - `--out-path`\ + - `--out-path` + Print the output path of the derivation. - - `--description`\ + - `--description` + Print a short (one-line) description of the derivation, if available. The description is taken from the `meta.description` attribute of the derivation. - - `--meta`\ + - `--meta` + Print all of the meta-attributes of the derivation. This option is only available with `--xml` or `--json`. diff --git a/doc/manual/src/command-ref/nix-env/upgrade.md b/doc/manual/src/command-ref/nix-env/upgrade.md index 322dfbda2..dc99064b9 100644 --- a/doc/manual/src/command-ref/nix-env/upgrade.md +++ b/doc/manual/src/command-ref/nix-env/upgrade.md @@ -28,36 +28,42 @@ version is installed. # Flags - - `--lt`\ + - `--lt` + Only upgrade a derivation to newer versions. This is the default. - - `--leq`\ + - `--leq` + In addition to upgrading to newer versions, also “upgrade” to derivations that have the same version. Version are not a unique identification of a derivation, so there may be many derivations that have the same version. This flag may be useful to force “synchronisation” between the installed and available derivations. - - `--eq`\ + - `--eq` + *Only* “upgrade” to derivations that have the same version. This may not seem very useful, but it actually is, e.g., when there is a new release of Nixpkgs and you want to replace installed applications with the same versions built against newer dependencies (to reduce the number of dependencies floating around on your system). - - `--always`\ + - `--always` + In addition to upgrading to newer versions, also “upgrade” to derivations that have the same or a lower version. I.e., derivations may actually be downgraded depending on what is available in the active Nix expression. - - `--prebuilt-only` / `-b`\ + - `--prebuilt-only` / `-b` + Use only derivations for which a substitute is registered, i.e., there is a pre-built binary available that can be downloaded in lieu of building the derivation. Thus, no packages will be built from source. - - `--preserve-installed` / `-P`\ + - `--preserve-installed` / `-P` + Do not remove derivations with a name matching one of the derivations being installed. Usually, trying to have two versions of the same package installed in the same generation of a profile will diff --git a/doc/manual/src/command-ref/nix-hash.md b/doc/manual/src/command-ref/nix-hash.md index 24e91df12..4762600c2 100644 --- a/doc/manual/src/command-ref/nix-hash.md +++ b/doc/manual/src/command-ref/nix-hash.md @@ -29,7 +29,8 @@ md5sum`. # Options - - `--flat`\ + - `--flat` + Print the cryptographic hash of the contents of each regular file *path*. That is, instead of computing the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, @@ -38,43 +39,53 @@ md5sum`. The result is identical to that produced by the GNU commands `md5sum` and `sha1sum`. - - `--base16`\ + - `--base16` + Print the hash in a hexadecimal representation (default). - - `--base32`\ + - `--base32` + Print the hash in a base-32 representation rather than hexadecimal. This base-32 representation is more compact and can be used in Nix expressions (such as in calls to `fetchurl`). - - `--base64`\ + - `--base64` + Similar to --base32, but print the hash in a base-64 representation, which is more compact than the base-32 one. - - `--sri`\ + - `--sri` + Print the hash in SRI format with base-64 encoding. The type of hash algorithm will be prepended to the hash string, followed by a hyphen (-) and the base-64 hash body. - - `--truncate`\ + - `--truncate` + Truncate hashes longer than 160 bits (such as SHA-256) to 160 bits. - - `--type` *hashAlgo*\ + - `--type` *hashAlgo* + Use the specified cryptographic hash algorithm, which can be one of `md5`, `sha1`, `sha256`, and `sha512`. - - `--to-base16`\ + - `--to-base16` + Don’t hash anything, but convert the base-32 hash representation *hash* to hexadecimal. - - `--to-base32`\ + - `--to-base32` + Don’t hash anything, but convert the hexadecimal hash representation *hash* to base-32. - - `--to-base64`\ + - `--to-base64` + Don’t hash anything, but convert the hexadecimal hash representation *hash* to base-64. - - `--to-sri`\ + - `--to-sri` + Don’t hash anything, but convert the hexadecimal hash representation *hash* to SRI. diff --git a/doc/manual/src/command-ref/nix-instantiate.md b/doc/manual/src/command-ref/nix-instantiate.md index dffbb2d70..554784b63 100644 --- a/doc/manual/src/command-ref/nix-instantiate.md +++ b/doc/manual/src/command-ref/nix-instantiate.md @@ -30,14 +30,17 @@ standard input. # Options - - `--add-root` *path*\ + - `--add-root` *path* + See the [corresponding option](nix-store.md) in `nix-store`. - - `--parse`\ + - `--parse` + Just parse the input files, and print their abstract syntax trees on standard output as a Nix expression. - - `--eval`\ + - `--eval` + Just parse and evaluate the input files, and print the resulting values on standard output. No instantiation of store derivations takes place. @@ -80,14 +83,16 @@ standard input. > > ``` - - `--find-file`\ + - `--find-file` + Look up the given files in Nix’s search path (as specified by the `NIX_PATH` environment variable). If found, print the corresponding absolute paths on standard output. For instance, if `NIX_PATH` is `nixpkgs=/home/alice/nixpkgs`, then `nix-instantiate --find-file nixpkgs/default.nix` will print `/home/alice/nixpkgs/default.nix`. - - `--strict`\ + - `--strict` + When used with `--eval`, recursively evaluate list elements and attributes. Normally, such sub-expressions are left unevaluated (since the Nix language is lazy). @@ -97,17 +102,20 @@ standard input. > This option can cause non-termination, because lazy data > structures can be infinitely large. - - `--json`\ + - `--json` + When used with `--eval`, print the resulting value as an JSON representation of the abstract syntax tree rather than as a Nix expression. - - `--xml`\ + - `--xml` + When used with `--eval`, print the resulting value as an XML representation of the abstract syntax tree rather than as a Nix expression. The schema is the same as that used by the [`toXML` built-in](../language/builtins.md). - - `--read-write-mode`\ + - `--read-write-mode` + When used with `--eval`, perform evaluation in read/write mode so nix language features that require it will still work (at the cost of needing to do instantiation of every evaluated derivation). If diff --git a/doc/manual/src/command-ref/nix-prefetch-url.md b/doc/manual/src/command-ref/nix-prefetch-url.md index 45ef01e02..309738113 100644 --- a/doc/manual/src/command-ref/nix-prefetch-url.md +++ b/doc/manual/src/command-ref/nix-prefetch-url.md @@ -39,23 +39,28 @@ the path of the downloaded file in the Nix store is also printed. # Options - - `--type` *hashAlgo*\ + - `--type` *hashAlgo* + Use the specified cryptographic hash algorithm, which can be one of `md5`, `sha1`, `sha256`, and `sha512`. The default is `sha256`. - - `--print-path`\ + - `--print-path` + Print the store path of the downloaded file on standard output. - - `--unpack`\ + - `--unpack` + Unpack the archive (which must be a tarball or zip file) and add the result to the Nix store. The resulting hash can be used with functions such as Nixpkgs’s `fetchzip` or `fetchFromGitHub`. - - `--executable`\ + - `--executable` + Set the executable bit on the downloaded file. - - `--name` *name*\ + - `--name` *name* + Override the name of the file in the Nix store. By default, this is `hash-basename`, where *basename* is the last component of *url*. Overriding the name is necessary when *basename* contains characters diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index a4dd051fc..16b270de7 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -60,7 +60,8 @@ All options not listed here are passed to `nix-store --realise`, except for `--arg` and `--attr` / `-A` which are passed to `nix-instantiate`. - - `--command` *cmd*\ + - `--command` *cmd* + In the environment of the derivation, run the shell command *cmd*. This command is executed in an interactive shell. (Use `--run` to use a non-interactive shell instead.) However, a call to `exit` is @@ -70,34 +71,40 @@ All options not listed here are passed to `nix-store drop you into the interactive shell. This can be useful for doing any additional initialisation. - - `--run` *cmd*\ + - `--run` *cmd* + Like `--command`, but executes the command in a non-interactive shell. This means (among other things) that if you hit Ctrl-C while the command is running, the shell exits. - - `--exclude` *regexp*\ + - `--exclude` *regexp* + Do not build any dependencies whose store path matches the regular expression *regexp*. This option may be specified multiple times. - - `--pure`\ + - `--pure` + If this flag is specified, the environment is almost entirely cleared before the interactive shell is started, so you get an environment that more closely corresponds to the “real” Nix build. A few variables, in particular `HOME`, `USER` and `DISPLAY`, are retained. - - `--packages` / `-p` *packages*…\ + - `--packages` / `-p` *packages*… + Set up an environment in which the specified packages are present. The command line arguments are interpreted as attribute names inside the Nix Packages collection. Thus, `nix-shell --packages libjpeg openjdk` will start a shell in which the packages denoted by the attribute names `libjpeg` and `openjdk` are present. - - `-i` *interpreter*\ + - `-i` *interpreter* + The chained script interpreter to be invoked by `nix-shell`. Only applicable in `#!`-scripts (described below). - - `--keep` *name*\ + - `--keep` *name* + When a `--pure` shell is started, keep the listed environment variables. @@ -105,7 +112,8 @@ All options not listed here are passed to `nix-store # Environment variables - - `NIX_BUILD_SHELL`\ + - `NIX_BUILD_SHELL` + Shell used to start the interactive environment. Defaults to the `bash` found in ``, falling back to the `bash` found in `PATH` if not found. diff --git a/doc/manual/src/command-ref/nix-store/add-fixed.md b/doc/manual/src/command-ref/nix-store/add-fixed.md index d25db091c..3de25194c 100644 --- a/doc/manual/src/command-ref/nix-store/add-fixed.md +++ b/doc/manual/src/command-ref/nix-store/add-fixed.md @@ -16,7 +16,8 @@ public url or broke since the download expression was written. This operation has the following options: - - `--recursive`\ + - `--recursive` + Use recursive instead of flat hashing mode, used when adding directories to the store. diff --git a/doc/manual/src/command-ref/nix-store/gc.md b/doc/manual/src/command-ref/nix-store/gc.md index 7be0d559a..07fd452ce 100644 --- a/doc/manual/src/command-ref/nix-store/gc.md +++ b/doc/manual/src/command-ref/nix-store/gc.md @@ -14,18 +14,21 @@ reachable via file system references from a set of “roots”, are deleted. The following suboperations may be specified: - - `--print-roots`\ + - `--print-roots` + This operation prints on standard output the set of roots used by the garbage collector. - - `--print-live`\ + - `--print-live` + This operation prints on standard output the set of “live” store paths, which are all the store paths reachable from the roots. Live paths should never be deleted, since that would break consistency — it would become possible that applications are installed that reference things that are no longer present in the store. - - `--print-dead`\ + - `--print-dead` + This operation prints out on standard output the set of “dead” store paths, which is just the opposite of the set of live paths: any path in the store that is not live (with respect to the roots) is dead. @@ -33,7 +36,8 @@ The following suboperations may be specified: By default, all unreachable paths are deleted. The following options control what gets deleted and in what order: - - `--max-freed` *bytes*\ + - `--max-freed` *bytes* + Keep deleting paths until at least *bytes* bytes have been deleted, then stop. The argument *bytes* can be followed by the multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB diff --git a/doc/manual/src/command-ref/nix-store/query.md b/doc/manual/src/command-ref/nix-store/query.md index 0bcacfe0c..a703b002b 100644 --- a/doc/manual/src/command-ref/nix-store/query.md +++ b/doc/manual/src/command-ref/nix-store/query.md @@ -24,25 +24,29 @@ symlink. # Common query options - - `--use-output`; `-u`\ + - `--use-output` / `-u` + For each argument to the query that is a [store derivation], apply the query to the output path of the derivation instead. - - `--force-realise`; `-f`\ + - `--force-realise` / `-f` + Realise each argument to the query first (see [`nix-store --realise`](./realise.md)). [store derivation]: @docroot@/glossary.md#gloss-store-derivation # Queries - - `--outputs`\ + - `--outputs` + Prints out the [output paths] of the store derivations *paths*. These are the paths that will be produced when the derivation is built. [output paths]: @docroot@/glossary.md#gloss-output-path - - `--requisites`; `-R`\ + - `--requisites` / `-R` + Prints out the [closure] of the store path *paths*. [closure]: @docroot@/glossary.md#gloss-closure @@ -61,27 +65,31 @@ symlink. dependencies) is obtained by distributing the closure of a store derivation and specifying the option `--include-outputs`. - - `--references`\ + - `--references` + Prints the set of [references] of the store paths *paths*, that is, their immediate dependencies. (For *all* dependencies, use `--requisites`.) [references]: @docroot@/glossary.md#gloss-reference - - `--referrers`\ + - `--referrers` + Prints the set of *referrers* of the store paths *paths*, that is, the store paths currently existing in the Nix store that refer to one of *paths*. Note that contrary to the references, the set of referrers is not constant; it can change as store paths are added or removed. - - `--referrers-closure`\ + - `--referrers-closure` + Prints the closure of the set of store paths *paths* under the referrers relation; that is, all store paths that directly or indirectly refer to one of *paths*. These are all the path currently in the Nix store that are dependent on *paths*. - - `--deriver`; `-d`\ + - `--deriver` / `-d` + Prints the [deriver] that was used to build the store paths *paths*. If the path has no deriver (e.g., if it is a source file), or if the deriver is not known (e.g., in the case of a binary-only @@ -92,12 +100,14 @@ symlink. [deriver]: @docroot@/glossary.md#gloss-deriver - - `--valid-derivers`\ + - `--valid-derivers` + Prints a set of derivation files (`.drv`) which are supposed produce said paths when realized. Might print nothing, for example for source paths or paths subsituted from a binary cache. - - `--graph`\ + - `--graph` + Prints the references graph of the store paths *paths* in the format of the `dot` tool of AT\&T's [Graphviz package](http://www.graphviz.org/). This can be used to visualise @@ -105,39 +115,45 @@ symlink. this to a store derivation. To obtain a runtime dependency graph, apply it to an output path. - - `--tree`\ + - `--tree` + Prints the references graph of the store paths *paths* as a nested ASCII tree. References are ordered by descending closure size; this tends to flatten the tree, making it more readable. The query only recurses into a store path when it is first encountered; this prevents a blowup of the tree representation of the graph. - - `--graphml`\ + - `--graphml` + Prints the references graph of the store paths *paths* in the [GraphML](http://graphml.graphdrawing.org/) file format. This can be used to visualise dependency graphs. To obtain a build-time dependency graph, apply this to a [store derivation]. To obtain a runtime dependency graph, apply it to an output path. - - `--binding` *name*; `-b` *name*\ + - `--binding` *name* / `-b` *name* + Prints the value of the attribute *name* (i.e., environment variable) of the [store derivation]s *paths*. It is an error for a derivation to not have the specified attribute. - - `--hash`\ + - `--hash` + Prints the SHA-256 hash of the contents of the store paths *paths* (that is, the hash of the output of `nix-store --dump` on the given paths). Since the hash is stored in the Nix database, this is a fast operation. - - `--size`\ + - `--size` + Prints the size in bytes of the contents of the store paths *paths* — to be precise, the size of the output of `nix-store --dump` on the given paths. Note that the actual disk space required by the store paths may be higher, especially on filesystems with large cluster sizes. - - `--roots`\ + - `--roots` + Prints the garbage collector roots that point, directly or indirectly, at the store paths *paths*. diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index 6e56387eb..6288b24e0 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -42,15 +42,18 @@ For non-derivation arguments, the argument itself is printed. # Options - - `--dry-run`\ + - `--dry-run` + Print on standard error a description of what packages would be built or downloaded, without actually performing the operation. - - `--ignore-unknown`\ + - `--ignore-unknown` + If a non-derivation path does not have a substitute, then silently ignore it. - - `--check`\ + - `--check` + This option allows you to check whether a derivation is deterministic. It rebuilds the specified derivation and checks whether the result is bitwise-identical with the existing outputs, diff --git a/doc/manual/src/command-ref/nix-store/serve.md b/doc/manual/src/command-ref/nix-store/serve.md index 0f90f65ae..dd9b93fbf 100644 --- a/doc/manual/src/command-ref/nix-store/serve.md +++ b/doc/manual/src/command-ref/nix-store/serve.md @@ -14,7 +14,8 @@ access to a restricted ssh user. The following flags are available: - - `--write`\ + - `--write` + Allow the connected client to request the realization of derivations. In effect, this can be used to make the host act as a remote builder. diff --git a/doc/manual/src/command-ref/nix-store/verify.md b/doc/manual/src/command-ref/nix-store/verify.md index 2695b3361..1b1b6f529 100644 --- a/doc/manual/src/command-ref/nix-store/verify.md +++ b/doc/manual/src/command-ref/nix-store/verify.md @@ -16,14 +16,16 @@ being modified by non-Nix tools, or of bugs in Nix itself. This operation has the following options: - - `--check-contents`\ + - `--check-contents` + Checks that the contents of every valid store path has not been altered by computing a SHA-256 hash of the contents and comparing it with the hash stored in the Nix database at build time. Paths that have been modified are printed out. For large stores, `--check-contents` is obviously quite slow. - - `--repair`\ + - `--repair` + If any valid path is missing from the store, or (if `--check-contents` is given) the contents of a valid path has been modified, then try to repair the path by redownloading it. See From a58ca342cae1d6bee488a42b6fc054c1072cfb8f Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 11:01:29 -0700 Subject: [PATCH 330/910] Initial runProgram implementation for Windows This is incomplete; proper shell escaping needs to be done --- src/libutil/processes.hh | 17 +- src/libutil/windows/processes.cc | 322 ++++++++++++++++++++++++++++++- 2 files changed, 331 insertions(+), 8 deletions(-) diff --git a/src/libutil/processes.hh b/src/libutil/processes.hh index 168fcaa55..bbbe7dcab 100644 --- a/src/libutil/processes.hh +++ b/src/libutil/processes.hh @@ -3,6 +3,7 @@ #include "types.hh" #include "error.hh" +#include "file-descriptor.hh" #include "logging.hh" #include "ansicolor.hh" @@ -23,26 +24,36 @@ namespace nix { struct Sink; struct Source; -#ifndef _WIN32 class Pid { +#ifndef _WIN32 pid_t pid = -1; bool separatePG = false; int killSignal = SIGKILL; +#else + AutoCloseFD pid = INVALID_DESCRIPTOR; +#endif public: Pid(); +#ifndef _WIN32 Pid(pid_t pid); - ~Pid(); void operator =(pid_t pid); operator pid_t(); +#else + Pid(AutoCloseFD pid); + void operator =(AutoCloseFD pid); +#endif + ~Pid(); int kill(); int wait(); + // TODO: Implement for Windows +#ifndef _WIN32 void setSeparatePG(bool separatePG); void setKillSignal(int signal); pid_t release(); -}; #endif +}; #ifndef _WIN32 diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 44a32f6a1..bb796ce2e 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -1,9 +1,15 @@ #include "current-process.hh" #include "environment-variables.hh" +#include "error.hh" +#include "file-descriptor.hh" +#include "file-path.hh" #include "signals.hh" #include "processes.hh" #include "finally.hh" #include "serialise.hh" +#include "file-system.hh" +#include "util.hh" +#include "windows-error.hh" #include #include @@ -16,25 +22,332 @@ #include #include +#define WIN32_LEAN_AND_MEAN +#include + namespace nix { +using namespace nix::windows; + +Pid::Pid() {} + +Pid::Pid(AutoCloseFD pid) + : pid(std::move(pid)) +{ +} + +Pid::~Pid() +{ + if (pid.get() != INVALID_DESCRIPTOR) + kill(); +} + +void Pid::operator=(AutoCloseFD pid) +{ + if (this->pid.get() != INVALID_DESCRIPTOR && this->pid.get() != pid.get()) + kill(); + this->pid = std::move(pid); +} + +// TODO: Implement (not needed for process spawning yet) +int Pid::kill() +{ + assert(pid.get() != INVALID_DESCRIPTOR); + + debug("killing process %1%", pid.get()); + + throw UnimplementedError("Pid::kill unimplemented"); +} + +int Pid::wait() +{ + // https://github.com/nix-windows/nix/blob/windows-meson/src/libutil/util.cc#L1938 + assert(pid.get() != INVALID_DESCRIPTOR); + DWORD status = WaitForSingleObject(pid.get(), INFINITE); + if (status != WAIT_OBJECT_0) { + debug("WaitForSingleObject returned %1%", status); + } + + DWORD exitCode = 0; + if (GetExitCodeProcess(pid.get(), &exitCode) == FALSE) { + debug("GetExitCodeProcess failed on pid %1%", pid.get()); + } + + pid.close(); + return exitCode; +} + +// TODO: Merge this with Unix's runProgram since it's identical logic. std::string runProgram(Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) { - throw UnimplementedError("Cannot shell out to git on Windows yet"); + auto res = runProgram(RunOptions{ + .program = program, .lookupPath = lookupPath, .args = args, .input = input, .isInteractive = isInteractive}); + + if (!statusOk(res.first)) + throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first)); + + return res.second; +} +// Looks at the $PATH environment variable to find the program. +// Adapted from https://github.com/nix-windows/nix/blob/windows/src/libutil/util.cc#L2276 +Path lookupPathForProgram(const Path & program) +{ + if (program.find('/') != program.npos || program.find('\\') != program.npos) { + throw Error("program '%1%' partially specifies its path", program); + } + + // Possible extensions. + // TODO: This should actually be sourced from $PATHEXT, not hardcoded. + static constexpr const char * exts[] = {"", ".exe", ".cmd", ".bat"}; + + auto path = getEnv("PATH"); + if (!path.has_value()) { + throw WinError("couldn't find PATH environment variable"); + } + + // Look through each directory listed in $PATH. + for (const std::string & dir : tokenizeString(*getEnv("PATH"), ";")) { + Path candidate = canonPath(dir) + '/' + program; + for (const auto ext : exts) { + if (pathExists(candidate + ext)) { + return candidate; + } + } + } + + throw WinError("program '%1%' not found on PATH", program); } +std::optional getProgramInterpreter(const Path & program) +{ + // These extensions are automatically handled by Windows and don't require an interpreter. + static constexpr const char * exts[] = {".exe", ".cmd", ".bat"}; + for (const auto ext : exts) { + if (hasSuffix(program, ext)) { + return {}; + } + } + // TODO: Open file and read the shebang + throw UnimplementedError("getProgramInterpreter unimplemented"); +} +// TODO: Not sure if this is needed in the unix version but it might be useful as a member func +void setFDInheritable(AutoCloseFD & fd, bool inherit) +{ + if (fd.get() != INVALID_DESCRIPTOR) { + if (!SetHandleInformation(fd.get(), HANDLE_FLAG_INHERIT, inherit ? HANDLE_FLAG_INHERIT : 0)) { + throw WinError("Couldn't disable inheriting of handle"); + } + } +} + +AutoCloseFD nullFD() +{ + // Create null handle to discard reads / writes + // https://stackoverflow.com/a/25609668 + // https://github.com/nix-windows/nix/blob/windows-meson/src/libutil/util.cc#L2228 + AutoCloseFD nul = CreateFileW( + L"NUL", + GENERIC_READ | GENERIC_WRITE, + // We don't care who reads / writes / deletes this file since it's NUL anyways + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (!nul.get()) { + throw WinError("Couldn't open NUL device"); + } + // Let this handle be inheritable by child processes + setFDInheritable(nul, true); + return nul; +} + +Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & out, Pipe & in) +{ + // Setup pipes. + if (options.standardOut) { + // Don't inherit the read end of the output pipe + setFDInheritable(out.readSide, false); + } else { + out.writeSide = nullFD(); + } + if (options.standardIn) { + // Don't inherit the write end of the input pipe + setFDInheritable(in.writeSide, false); + } else { + in.readSide = nullFD(); + } + + STARTUPINFOW startInfo = {0}; + startInfo.cb = sizeof(startInfo); + startInfo.dwFlags = STARTF_USESTDHANDLES; + startInfo.hStdInput = in.readSide.get(); + startInfo.hStdOutput = out.writeSide.get(); + startInfo.hStdError = out.writeSide.get(); + + std::string envline; + // Retain the current processes' environment variables. + for (const auto & envVar : getEnv()) { + envline += (envVar.first + '=' + envVar.second + '\0'); + } + // Also add new ones specified in options. + if (options.environment) { + for (const auto & envVar : *options.environment) { + envline += (envVar.first + '=' + envVar.second + '\0'); + } + } + + std::string cmdline = realProgram; + for (const auto & arg : options.args) { + // TODO: This isn't the right way to escape windows command + // See https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw + cmdline += ' ' + shellEscape(arg); + } + + PROCESS_INFORMATION procInfo = {0}; + if (CreateProcessW( + string_to_os_string(realProgram).c_str(), + string_to_os_string(cmdline).data(), + NULL, + NULL, + TRUE, + CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED, + string_to_os_string(envline).data(), + options.chdir.has_value() ? string_to_os_string(*options.chdir).data() : NULL, + &startInfo, + &procInfo) + == 0) { + throw WinError("CreateProcessW failed (%1%)", cmdline); + } + + // Convert these to use RAII + AutoCloseFD process = procInfo.hProcess; + AutoCloseFD thread = procInfo.hThread; + + // Add current process and child to job object so child terminates when parent terminates + // TODO: This spawns one job per child process. We can probably keep this as a global, and + // add children a single job so we don't use so many jobs at once. + Descriptor job = CreateJobObjectW(NULL, NULL); + if (job == NULL) { + TerminateProcess(procInfo.hProcess, 0); + throw WinError("Couldn't create job object for child process"); + } + if (AssignProcessToJobObject(job, procInfo.hProcess) == FALSE) { + TerminateProcess(procInfo.hProcess, 0); + throw WinError("Couldn't assign child process to job object"); + } + if (ResumeThread(procInfo.hThread) == -1) { + TerminateProcess(procInfo.hProcess, 0); + throw WinError("Couldn't resume child process thread"); + } + + return process; +} + +// TODO: Merge this with Unix's runProgram since it's identical logic. // Output = error code + "standard out" output stream std::pair runProgram(RunOptions && options) { - throw UnimplementedError("Cannot shell out to git on Windows yet"); -} + StringSink sink; + options.standardOut = &sink; + int status = 0; + + try { + runProgram2(options); + } catch (ExecError & e) { + status = e.status; + } + + return {status, std::move(sink.s)}; +} void runProgram2(const RunOptions & options) { - throw UnimplementedError("Cannot shell out to git on Windows yet"); + checkInterrupt(); + + assert(!(options.standardIn && options.input)); + + std::unique_ptr source_; + Source * source = options.standardIn; + + if (options.input) { + source_ = std::make_unique(*options.input); + source = source_.get(); + } + + /* Create a pipe. */ + Pipe out, in; + // TODO: I copied this from unix but this is handled again in spawnProcess, so might be weird to split it up like + // this + if (options.standardOut) + out.create(); + if (source) + in.create(); + + Path realProgram = options.program; + if (options.lookupPath) { + realProgram = lookupPathForProgram(realProgram); + } + // TODO: Implement shebang / program interpreter lookup on Windows + auto interpreter = getProgramInterpreter(realProgram); + + std::optional>> resumeLoggerDefer; + if (options.isInteractive) { + logger->pause(); + resumeLoggerDefer.emplace([]() { logger->resume(); }); + } + + Pid pid = spawnProcess(interpreter.has_value() ? *interpreter : realProgram, options, out, in); + + // TODO: This is identical to unix, deduplicate? + out.writeSide.close(); + + std::thread writerThread; + + std::promise promise; + + Finally doJoin([&] { + if (writerThread.joinable()) + writerThread.join(); + }); + + if (source) { + in.readSide.close(); + writerThread = std::thread([&] { + try { + std::vector buf(8 * 1024); + while (true) { + size_t n; + try { + n = source->read(buf.data(), buf.size()); + } catch (EndOfFile &) { + break; + } + writeFull(in.writeSide.get(), {buf.data(), n}); + } + promise.set_value(); + } catch (...) { + promise.set_exception(std::current_exception()); + } + in.writeSide.close(); + }); + } + + if (options.standardOut) + drainFD(out.readSide.get(), *options.standardOut); + + /* Wait for the child to finish. */ + int status = pid.wait(); + + /* Wait for the writer thread to finish. */ + if (source) + promise.get_future().get(); + + if (status) + throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status)); } std::string statusToString(int status) @@ -45,7 +358,6 @@ std::string statusToString(int status) return "succeeded"; } - bool statusOk(int status) { return status == 0; From a83d95e26ebdbe996a9cafc106538e5ada283fe5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 18:10:25 -0400 Subject: [PATCH 331/910] Integrate perl with the other meson builds One big dev shell! --- flake.nix | 38 ++++++++++++++----- maintainers/hydra.nix | 2 +- meson.build | 1 + package.nix | 4 +- src/perl/.version | 1 + {perl => src/perl}/.yath.rc.in | 0 {perl => src/perl}/MANIFEST | 0 {perl => src/perl}/lib/Nix/Config.pm.in | 0 {perl => src/perl}/lib/Nix/CopyClosure.pm | 0 {perl => src/perl}/lib/Nix/Manifest.pm | 0 {perl => src/perl}/lib/Nix/SSH.pm | 0 {perl => src/perl}/lib/Nix/Store.pm | 0 {perl => src/perl}/lib/Nix/Store.xs | 3 +- {perl => src/perl}/lib/Nix/Utils.pm | 0 {perl => src/perl}/lib/Nix/meson.build | 0 {perl => src/perl}/meson.build | 8 ++-- .../perl/meson.options | 5 --- perl/default.nix => src/perl/package.nix | 17 ++++++--- {perl => src/perl}/t/init.t | 0 {perl => src/perl}/t/meson.build | 0 20 files changed, 52 insertions(+), 27 deletions(-) create mode 120000 src/perl/.version rename {perl => src/perl}/.yath.rc.in (100%) rename {perl => src/perl}/MANIFEST (100%) rename {perl => src/perl}/lib/Nix/Config.pm.in (100%) rename {perl => src/perl}/lib/Nix/CopyClosure.pm (100%) rename {perl => src/perl}/lib/Nix/Manifest.pm (100%) rename {perl => src/perl}/lib/Nix/SSH.pm (100%) rename {perl => src/perl}/lib/Nix/Store.pm (100%) rename {perl => src/perl}/lib/Nix/Store.xs (99%) rename {perl => src/perl}/lib/Nix/Utils.pm (100%) rename {perl => src/perl}/lib/Nix/meson.build (100%) rename {perl => src/perl}/meson.build (96%) rename perl/meson_options.txt => src/perl/meson.options (88%) rename perl/default.nix => src/perl/package.nix (79%) rename {perl => src/perl}/t/init.t (100%) rename {perl => src/perl}/t/meson.build (100%) diff --git a/flake.nix b/flake.nix index 9f494cb15..2b0e3c613 100644 --- a/flake.nix +++ b/flake.nix @@ -192,14 +192,14 @@ libgit2 = final.libgit2-nix; libseccomp = final.libseccomp-nix; busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; - } // { - # this is a proper separate downstream package, but put - # here also for back compat reasons. - perl-bindings = final.nix-perl-bindings; }; - nix-perl-bindings = final.callPackage ./perl { - inherit fileset stdenv; + nix-perl-bindings = final.callPackage ./src/perl/package.nix { + inherit + fileset + stdenv + versionSuffix + ; }; # See https://github.com/NixOS/nixpkgs/pull/214409 @@ -213,7 +213,7 @@ in { # A Nixpkgs overlay that overrides the 'nix' and - # 'nix.perl-bindings' packages. + # 'nix-perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); hydraJobs = import ./maintainers/hydra.nix { @@ -245,7 +245,11 @@ # Some perl dependencies are broken on i686-linux. # Since the support is only best-effort there, disable the perl # bindings - perlBindings = self.hydraJobs.perlBindings.${system}; + + # Temporarily disabled because GitHub Actions OOM issues. Once + # the old build system is gone and we are back to one build + # system, we should reenable this. + #perlBindings = self.hydraJobs.perlBindings.${system}; } // devFlake.checks.${system} or {} ); @@ -297,6 +301,13 @@ makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: let modular = devFlake.getSystem stdenv.buildPlatform.system; + transformFlag = prefix: flag: + assert builtins.isString flag; + let + rest = builtins.substring 2 (builtins.stringLength flag) flag; + in + "-D${prefix}:${rest}"; + havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix; in { pname = "shell-for-" + attrs.pname; @@ -327,11 +338,16 @@ "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; }; - mesonFlags = pkgs.nix-util.mesonFlags ++ pkgs.nix-store.mesonFlags; + mesonFlags = + map (transformFlag "libutil") pkgs.nix-util.mesonFlags + ++ map (transformFlag "libstore") pkgs.nix-store.mesonFlags + ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nix-perl-bindings.mesonFlags) + ; nativeBuildInputs = attrs.nativeBuildInputs or [] ++ pkgs.nix-util.nativeBuildInputs ++ pkgs.nix-store.nativeBuildInputs + ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" @@ -341,6 +357,10 @@ # https://github.com/NixOS/nixpkgs/pull/291814 is available ++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear ++ lib.optional (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) pkgs.buildPackages.clang-tools; + + buildInputs = attrs.buildInputs or [] + ++ lib.optional havePerl pkgs.perl + ; }); in forAllSystems (system: diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index cc0dadac9..6c07e65d4 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -75,7 +75,7 @@ in ); # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix-perl-bindings); # Binary tarball for various platforms, containing a Nix store # with the closure of 'nix' package, and the second half of diff --git a/meson.build b/meson.build index 10883d832..f1ee7a571 100644 --- a/meson.build +++ b/meson.build @@ -8,3 +8,4 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') +subproject('perl') diff --git a/package.nix b/package.nix index 5986edeb4..cb9554235 100644 --- a/package.nix +++ b/package.nix @@ -180,7 +180,7 @@ in { ./doc ./misc ./precompiled-headers.h - ./src + (fileset.difference ./src ./src/perl) ./COPYING ./scripts/local.mk ] ++ lib.optionals buildUnitTests [ @@ -192,7 +192,7 @@ in { ] ++ lib.optionals (enableInternalAPIDocs || enableExternalAPIDocs) [ # Source might not be compiled, but still must be available # for Doxygen to gather comments. - ./src + (fileset.difference ./src ./src/perl) ./tests/unit ] ++ lib.optionals buildUnitTests [ ./tests/unit diff --git a/src/perl/.version b/src/perl/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/perl/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/perl/.yath.rc.in b/src/perl/.yath.rc.in similarity index 100% rename from perl/.yath.rc.in rename to src/perl/.yath.rc.in diff --git a/perl/MANIFEST b/src/perl/MANIFEST similarity index 100% rename from perl/MANIFEST rename to src/perl/MANIFEST diff --git a/perl/lib/Nix/Config.pm.in b/src/perl/lib/Nix/Config.pm.in similarity index 100% rename from perl/lib/Nix/Config.pm.in rename to src/perl/lib/Nix/Config.pm.in diff --git a/perl/lib/Nix/CopyClosure.pm b/src/perl/lib/Nix/CopyClosure.pm similarity index 100% rename from perl/lib/Nix/CopyClosure.pm rename to src/perl/lib/Nix/CopyClosure.pm diff --git a/perl/lib/Nix/Manifest.pm b/src/perl/lib/Nix/Manifest.pm similarity index 100% rename from perl/lib/Nix/Manifest.pm rename to src/perl/lib/Nix/Manifest.pm diff --git a/perl/lib/Nix/SSH.pm b/src/perl/lib/Nix/SSH.pm similarity index 100% rename from perl/lib/Nix/SSH.pm rename to src/perl/lib/Nix/SSH.pm diff --git a/perl/lib/Nix/Store.pm b/src/perl/lib/Nix/Store.pm similarity index 100% rename from perl/lib/Nix/Store.pm rename to src/perl/lib/Nix/Store.pm diff --git a/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs similarity index 99% rename from perl/lib/Nix/Store.xs rename to src/perl/lib/Nix/Store.xs index e751c2be1..15eb5c4f8 100644 --- a/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -1,4 +1,5 @@ -#include "nix/config.h" +#include "config-util.h" +#include "config-store.h" #include "EXTERN.h" #include "perl.h" diff --git a/perl/lib/Nix/Utils.pm b/src/perl/lib/Nix/Utils.pm similarity index 100% rename from perl/lib/Nix/Utils.pm rename to src/perl/lib/Nix/Utils.pm diff --git a/perl/lib/Nix/meson.build b/src/perl/lib/Nix/meson.build similarity index 100% rename from perl/lib/Nix/meson.build rename to src/perl/lib/Nix/meson.build diff --git a/perl/meson.build b/src/perl/meson.build similarity index 96% rename from perl/meson.build rename to src/perl/meson.build index 350e5bd67..06abb4f2e 100644 --- a/perl/meson.build +++ b/src/perl/meson.build @@ -7,17 +7,17 @@ project ( 'nix-perl', 'cpp', - meson_version : '>= 0.64.0', + version : files('.version'), + meson_version : '>= 1.1', license : 'LGPL-2.1-or-later', ) # setup env #------------------------------------------------- fs = import('fs') -nix_version = get_option('version') cpp = meson.get_compiler('cpp') nix_perl_conf = configuration_data() -nix_perl_conf.set('PACKAGE_VERSION', nix_version) +nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) # set error arguments @@ -64,7 +64,7 @@ yath = find_program('yath', required : false) bzip2_dep = dependency('bzip2') curl_dep = dependency('libcurl') libsodium_dep = dependency('libsodium') -# nix_util_dep = dependency('nix-util') + nix_store_dep = dependency('nix-store') diff --git a/perl/meson_options.txt b/src/perl/meson.options similarity index 88% rename from perl/meson_options.txt rename to src/perl/meson.options index 82ca52f37..9b5b6b1d9 100644 --- a/perl/meson_options.txt +++ b/src/perl/meson.options @@ -5,11 +5,6 @@ # compiler args #============================================================================ -option( - 'version', - type : 'string', - description : 'nix-perl version') - option( 'tests', type : 'feature', diff --git a/perl/default.nix b/src/perl/package.nix similarity index 79% rename from perl/default.nix rename to src/perl/package.nix index 45682381e..a31b1b66c 100644 --- a/perl/default.nix +++ b/src/perl/package.nix @@ -6,17 +6,19 @@ , meson , ninja , pkg-config -, nix +, nix-store , curl , bzip2 , xz , boost , libsodium , darwin +, versionSuffix ? "" }: perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { - name = "nix-perl-${nix.version}"; + pname = "nix-perl"; + version = lib.fileContents ./.version + versionSuffix; src = fileset.toSource { root = ./.; @@ -24,7 +26,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { ./MANIFEST ./lib ./meson.build - ./meson_options.txt + ./meson.options ] ++ lib.optionals finalAttrs.doCheck [ ./.yath.rc.in ./t @@ -38,7 +40,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { ]; buildInputs = [ - nix + nix-store curl bzip2 xz @@ -55,8 +57,13 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { perlPackages.Test2Harness ]; + preConfigure = + # "Inline" .version so its not a symlink, and includes the suffix + '' + echo ${finalAttrs.version} > .version + ''; + mesonFlags = [ - (lib.mesonOption "version" (builtins.readFile ../.version)) (lib.mesonOption "dbi_path" "${perlPackages.DBI}/${perl.libPrefix}") (lib.mesonOption "dbd_sqlite_path" "${perlPackages.DBDSQLite}/${perl.libPrefix}") (lib.mesonEnable "tests" finalAttrs.doCheck) diff --git a/perl/t/init.t b/src/perl/t/init.t similarity index 100% rename from perl/t/init.t rename to src/perl/t/init.t diff --git a/perl/t/meson.build b/src/perl/t/meson.build similarity index 100% rename from perl/t/meson.build rename to src/perl/t/meson.build From 6e34c6832796781667562e1e40e85fc09a085987 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 17 Jun 2024 15:50:59 +0200 Subject: [PATCH 332/910] Convert the internal API doc build to Meson --- Makefile | 11 ---- Makefile.config.in | 1 - configure.ac | 5 -- doc/internal-api/local.mk | 7 --- doc/manual/src/contributing/documentation.md | 12 ++-- flake.nix | 10 ++++ maintainers/hydra.nix | 6 +- meson.build | 1 + package.nix | 19 ++----- .../internal-api-docs}/.gitignore | 0 src/internal-api-docs/.version | 1 + .../internal-api-docs}/doxygen.cfg.in | 46 +++++++-------- src/internal-api-docs/meson.build | 31 ++++++++++ src/internal-api-docs/package.nix | 56 +++++++++++++++++++ 14 files changed, 136 insertions(+), 70 deletions(-) delete mode 100644 doc/internal-api/local.mk rename {doc/internal-api => src/internal-api-docs}/.gitignore (100%) create mode 120000 src/internal-api-docs/.version rename {doc/internal-api => src/internal-api-docs}/doxygen.cfg.in (82%) create mode 100644 src/internal-api-docs/meson.build create mode 100644 src/internal-api-docs/package.nix diff --git a/Makefile b/Makefile index 6c96ef5db..257560028 100644 --- a/Makefile +++ b/Makefile @@ -68,10 +68,6 @@ ifeq ($(ENABLE_DOC_GEN), yes) makefiles-late += doc/manual/local.mk endif -ifeq ($(ENABLE_INTERNAL_API_DOCS), yes) -makefiles-late += doc/internal-api/local.mk -endif - ifeq ($(ENABLE_EXTERNAL_API_DOCS), yes) makefiles-late += doc/external-api/local.mk endif @@ -132,13 +128,6 @@ manual-html manpages: @exit 1 endif -ifneq ($(ENABLE_INTERNAL_API_DOCS), yes) -.PHONY: internal-api-html -internal-api-html: - @echo "Internal API docs are disabled. Configure with '--enable-internal-api-docs', or avoid calling 'make internal-api-html'." - @exit 1 -endif - ifneq ($(ENABLE_EXTERNAL_API_DOCS), yes) .PHONY: external-api-html external-api-html: diff --git a/Makefile.config.in b/Makefile.config.in index 7f517898c..56e67e5cd 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -11,7 +11,6 @@ EDITLINE_LIBS = @EDITLINE_LIBS@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ -ENABLE_INTERNAL_API_DOCS = @ENABLE_INTERNAL_API_DOCS@ ENABLE_EXTERNAL_API_DOCS = @ENABLE_EXTERNAL_API_DOCS@ ENABLE_S3 = @ENABLE_S3@ ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ diff --git a/configure.ac b/configure.ac index 8211bec0b..2fefbe95a 100644 --- a/configure.ac +++ b/configure.ac @@ -171,11 +171,6 @@ AS_IF( [test "$ENABLE_BUILD" == "no" && test "$ENABLE_DOC_GEN" == "yes"], [AC_MSG_ERROR([Cannot enable generated docs when building overall is disabled. Please do not pass '--enable-doc-gen' or do not pass '--disable-build'.])]) -# Building without API docs is the default as Nix' C++ interfaces are internal and unstable. -AC_ARG_ENABLE(internal-api-docs, AS_HELP_STRING([--enable-internal-api-docs],[Build API docs for Nix's internal unstable C++ interfaces]), - ENABLE_INTERNAL_API_DOCS=$enableval, ENABLE_INTERNAL_API_DOCS=no) -AC_SUBST(ENABLE_INTERNAL_API_DOCS) - AC_ARG_ENABLE(external-api-docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's external unstable C interfaces]), ENABLE_EXTERNAL_API_DOCS=$enableval, ENABLE_EXTERNAL_API_DOCS=no) AC_SUBST(ENABLE_EXTERNAL_API_DOCS) diff --git a/doc/internal-api/local.mk b/doc/internal-api/local.mk deleted file mode 100644 index be9b7bb55..000000000 --- a/doc/internal-api/local.mk +++ /dev/null @@ -1,7 +0,0 @@ -$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg src/**/*.hh - mkdir -p $(docdir)/internal-api - { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/internal-api" ; } | doxygen - - -# Generate the HTML API docs for Nix's unstable internal interfaces. -.PHONY: internal-api-html -internal-api-html: $(docdir)/internal-api/html/index.html diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index 6e7c0a967..a5e2bfa83 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -196,15 +196,17 @@ You can also build and view it yourself: [Doxygen API documentation]: https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs ```console -# nix build .#hydraJobs.internal-api-docs -# xdg-open ./result/share/doc/nix/internal-api/html/index.html +$ nix build .#hydraJobs.internal-api-docs +$ xdg-open ./result/share/doc/nix/internal-api/html/index.html ``` or inside `nix-shell` or `nix develop`: -``` -# make internal-api-html -# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html +```console +$ mesonConfigurePhase +$ cd build +$ ninja src/internal-api-docs/html +$ xdg-open src/internal-api-docs/html/index.html ``` ## C API documentation diff --git a/flake.nix b/flake.nix index 2b0e3c613..77b0870a2 100644 --- a/flake.nix +++ b/flake.nix @@ -202,6 +202,15 @@ ; }; + + nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + # See https://github.com/NixOS/nixpkgs/pull/214409 # Remove when fixed in this flake's nixpkgs pre-commit = @@ -279,6 +288,7 @@ # system, we should reenable these. #"nix-util" = { }; #"nix-store" = { }; + "nix-internal-api-docs" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 6c07e65d4..235752979 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -124,11 +124,7 @@ in }; # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableInternalAPIDocs = true; - }; + internal-api-docs = nixpkgsFor.x86_64-linux.native.nix-internal-api-docs; # API docs for Nix's C bindings. external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { diff --git a/meson.build b/meson.build index f1ee7a571..95a6665a7 100644 --- a/meson.build +++ b/meson.build @@ -9,3 +9,4 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') subproject('perl') +subproject('internal-api-docs') diff --git a/package.nix b/package.nix index cb9554235..64096c7aa 100644 --- a/package.nix +++ b/package.nix @@ -93,9 +93,8 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to build the internal/external API docs, can be done separately from +# Whether to build the external API docs, can be done separately from # everything else. -, enableInternalAPIDocs ? forDevShell , enableExternalAPIDocs ? forDevShell # Whether to install unit tests. This is useful when cross compiling @@ -185,11 +184,9 @@ in { ./scripts/local.mk ] ++ lib.optionals buildUnitTests [ ./doc/manual - ] ++ lib.optionals enableInternalAPIDocs [ - ./doc/internal-api ] ++ lib.optionals enableExternalAPIDocs [ ./doc/external-api - ] ++ lib.optionals (enableInternalAPIDocs || enableExternalAPIDocs) [ + ] ++ lib.optionals enableExternalAPIDocs [ # Source might not be compiled, but still must be available # for Doxygen to gather comments. (fileset.difference ./src ./src/perl) @@ -207,7 +204,7 @@ in { ++ lib.optional doBuild "dev" # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. - ++ lib.optional (doBuild && (enableManual || enableInternalAPIDocs || enableExternalAPIDocs)) "doc" + ++ lib.optional (doBuild && (enableManual || enableExternalAPIDocs)) "doc" ++ lib.optional installUnitTests "check" ++ lib.optional doCheck "testresults" ; @@ -231,7 +228,7 @@ in { ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux - ++ lib.optional (enableInternalAPIDocs || enableExternalAPIDocs) doxygen + ++ lib.optional enableExternalAPIDocs doxygen ; buildInputs = lib.optionals doBuild [ @@ -294,7 +291,6 @@ in { (lib.enableFeature doBuild "build") (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") - (lib.enableFeature enableInternalAPIDocs "internal-api-docs") (lib.enableFeature enableExternalAPIDocs "external-api-docs") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") @@ -324,7 +320,6 @@ in { ''; installTargets = lib.optional doBuild "install" - ++ lib.optional enableInternalAPIDocs "internal-api-html" ++ lib.optional enableExternalAPIDocs "external-api-html"; installFlags = "sysconfdir=$(out)/etc"; @@ -349,11 +344,7 @@ in { ) + lib.optionalString enableManual '' mkdir -p ''${!outputDoc}/nix-support echo "doc manual ''${!outputDoc}/share/doc/nix/manual" >> ''${!outputDoc}/nix-support/hydra-build-products - '' + lib.optionalString enableInternalAPIDocs '' - mkdir -p ''${!outputDoc}/nix-support - echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products - '' - + lib.optionalString enableExternalAPIDocs '' + '' + lib.optionalString enableExternalAPIDocs '' mkdir -p ''${!outputDoc}/nix-support echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; diff --git a/doc/internal-api/.gitignore b/src/internal-api-docs/.gitignore similarity index 100% rename from doc/internal-api/.gitignore rename to src/internal-api-docs/.gitignore diff --git a/src/internal-api-docs/.version b/src/internal-api-docs/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/internal-api-docs/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/doc/internal-api/doxygen.cfg.in b/src/internal-api-docs/doxygen.cfg.in similarity index 82% rename from doc/internal-api/doxygen.cfg.in rename to src/internal-api-docs/doxygen.cfg.in index 6c6c325bd..9e7425581 100644 --- a/doc/internal-api/doxygen.cfg.in +++ b/src/internal-api-docs/doxygen.cfg.in @@ -12,7 +12,9 @@ PROJECT_NAME = "Nix" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = @PACKAGE_VERSION@ +PROJECT_NUMBER = @PROJECT_NUMBER@ + +OUTPUT_DIRECTORY = @OUTPUT_DIRECTORY@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -36,27 +38,27 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - src/libcmd \ - src/libexpr \ - src/libexpr/flake \ - tests/unit/libexpr \ - tests/unit/libexpr/value \ - tests/unit/libexpr/test \ - tests/unit/libexpr/test/value \ - src/libexpr/value \ - src/libfetchers \ - src/libmain \ - src/libstore \ - src/libstore/build \ - src/libstore/builtins \ - tests/unit/libstore \ - tests/unit/libstore/test \ - src/libutil \ - tests/unit/libutil \ - tests/unit/libutil/test \ - src/nix \ - src/nix-env \ - src/nix-store + @src@/src/libcmd \ + @src@/src/libexpr \ + @src@/src/libexpr/flake \ + @src@/tests/unit/libexpr \ + @src@/tests/unit/libexpr/value \ + @src@/tests/unit/libexpr/test \ + @src@/tests/unit/libexpr/test/value \ + @src@/src/libexpr/value \ + @src@/src/libfetchers \ + @src@/src/libmain \ + @src@/src/libstore \ + @src@/src/libstore/build \ + @src@/src/libstore/builtins \ + @src@/tests/unit/libstore \ + @src@/tests/unit/libstore/test \ + @src@/src/libutil \ + @src@/tests/unit/libutil \ + @src@/tests/unit/libutil/test \ + @src@/src/nix \ + @src@/src/nix-env \ + @src@/src/nix-store # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be diff --git a/src/internal-api-docs/meson.build b/src/internal-api-docs/meson.build new file mode 100644 index 000000000..2568c56cf --- /dev/null +++ b/src/internal-api-docs/meson.build @@ -0,0 +1,31 @@ +project('nix-internal-api-docs', + version : files('.version'), + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +fs = import('fs') + +doxygen_cfg = configure_file( + input : 'doxygen.cfg.in', + output : 'doxygen.cfg', + configuration : { + 'PROJECT_NUMBER': meson.project_version(), + 'OUTPUT_DIRECTORY' : meson.current_build_dir(), + 'src' : fs.parent(fs.parent(meson.project_source_root())), + }, +) + +doxygen = find_program('doxygen', native : true, required : true) + +custom_target( + 'internal-api-docs', + command : [ doxygen , doxygen_cfg ], + input : [ + doxygen_cfg, + ], + output : 'html', + install : true, + install_dir : get_option('datadir') / 'doc/nix/internal-api', + build_always_stale : true, +) diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix new file mode 100644 index 000000000..bb20a68d3 --- /dev/null +++ b/src/internal-api-docs/package.nix @@ -0,0 +1,56 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, doxygen + +# Configuration Options + +, versionSuffix ? "" +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "nix-internal-api-docs"; + version = lib.fileContents ./.version + versionSuffix; + + src = fileset.toSource { + root = ../..; + fileset = let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "hh"); + in fileset.unions [ + ./meson.build + ./doxygen.cfg.in + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../.) + (cpp ../../tests/unit) + ]; + }; + + nativeBuildInputs = [ + meson + ninja + doxygen + ]; + + postUnpack = '' + sourceRoot=$sourceRoot/src/internal-api-docs + ''; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${finalAttrs.version} > .version + ''; + + enableParallelBuilding = true; + + strictDeps = true; + + meta = { + platforms = lib.platforms.all; + }; +}) From b11cf8166f63fa061d98cd5812a94234fe974845 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 13:12:28 -0700 Subject: [PATCH 333/910] Format runProgram declaration --- src/libutil/windows/processes.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index bb796ce2e..1d8035c62 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -78,8 +78,8 @@ int Pid::wait() } // TODO: Merge this with Unix's runProgram since it's identical logic. -std::string runProgram(Path program, bool lookupPath, const Strings & args, - const std::optional & input, bool isInteractive) +std::string runProgram( + Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) { auto res = runProgram(RunOptions{ .program = program, .lookupPath = lookupPath, .args = args, .input = input, .isInteractive = isInteractive}); From 706edf26eb3ec1781afe16c925db5b7071494539 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Wed, 5 Jun 2024 21:36:18 -0400 Subject: [PATCH 334/910] build: meson for libfetchers --- flake.nix | 12 +++ maintainers/hydra.nix | 1 + meson.build | 1 + src/libfetchers/.version | 1 + src/libfetchers/meson.build | 141 ++++++++++++++++++++++++++++++++++++ src/libfetchers/package.nix | 106 +++++++++++++++++++++++++++ src/libstore/meson.build | 5 +- src/libutil/package.nix | 1 + 8 files changed, 266 insertions(+), 2 deletions(-) create mode 120000 src/libfetchers/.version create mode 100644 src/libfetchers/meson.build create mode 100644 src/libfetchers/package.nix diff --git a/flake.nix b/flake.nix index 77b0870a2..5c8d84a75 100644 --- a/flake.nix +++ b/flake.nix @@ -180,6 +180,15 @@ busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; }; + nix-fetchers = final.callPackage ./src/libfetchers/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; + nix = final.callPackage ./package.nix { inherit @@ -288,6 +297,7 @@ # system, we should reenable these. #"nix-util" = { }; #"nix-store" = { }; + #"nix-fetchers" = { }; "nix-internal-api-docs" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { @@ -351,12 +361,14 @@ mesonFlags = map (transformFlag "libutil") pkgs.nix-util.mesonFlags ++ map (transformFlag "libstore") pkgs.nix-store.mesonFlags + ++ map (transformFlag "libfetchers") pkgs.nix-fetchers.mesonFlags ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nix-perl-bindings.mesonFlags) ; nativeBuildInputs = attrs.nativeBuildInputs or [] ++ pkgs.nix-util.nativeBuildInputs ++ pkgs.nix-store.nativeBuildInputs + ++ pkgs.nix-fetchers.nativeBuildInputs ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs ++ [ modular.pre-commit.settings.package diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 235752979..293dee5cd 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -37,6 +37,7 @@ let "nix" "nix-util" "nix-store" + "nix-fetchers" ]; in { diff --git a/meson.build b/meson.build index 95a6665a7..ebad238b1 100644 --- a/meson.build +++ b/meson.build @@ -8,5 +8,6 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') +subproject('libfetchers') subproject('perl') subproject('internal-api-docs') diff --git a/src/libfetchers/.version b/src/libfetchers/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libfetchers/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build new file mode 100644 index 000000000..cab8d63eb --- /dev/null +++ b/src/libfetchers/meson.build @@ -0,0 +1,141 @@ +project('nix-fetchers', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +nix_store = dependency('nix-store') +if nix_store.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_store +else + deps_public += nix_store +endif + + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +libgit2 = dependency('libgit2') +deps_public += libgit2 + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + # '-include', 'config-fetchers.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'attrs.cc', + 'cache.cc', + 'fetch-settings.cc', + 'fetch-to-store.cc', + 'fetchers.cc', + 'filtering-source-accessor.cc', + 'git.cc', + 'git-utils.cc', + 'github.cc', + 'indirect.cc', + 'mercurial.cc', + 'mounted-source-accessor.cc', + 'path.cc', + 'store-path-accessor.cc', + 'registry.cc', + 'tarball.cc', +) + +headers = files( + 'attrs.hh', + 'cache.hh', + 'fetch-settings.hh', + 'fetch-to-store.hh', + 'filtering-source-accessor.hh', + 'git-utils.hh', + 'mounted-source-accessor.hh', + 'fetchers.hh', + 'registry.hh', + 'store-path-accessor.hh', + 'tarball.hh', +) + +this_library = library( + 'nixfetchers', + sources, + dependencies : deps_public + deps_private + deps_other, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +if nix_util.type_name() == 'internal' + # `requires` cannot contain declared dependencies (from the + # subproject), so we need to do this manually + requires += 'nix-util' +endif +if nix_store.type_name() == 'internal' + requires += 'nix-store' +endif +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_directories('.'), + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [nix_util, nix_store], +)) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix new file mode 100644 index 000000000..800256030 --- /dev/null +++ b/src/libfetchers/package.nix @@ -0,0 +1,106 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nlohmann_json +, libgit2 +, man + +# Configuration Options + +, versionSuffix ? "" +, officialRelease ? false + +# Check test coverage of Nix. Probably want to use with with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +# Avoid setting things that would interfere with a functioning devShell +, forDevShell ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-fetchers"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + libgit2 + ]; + + propagatedBuildInputs = [ + nix-store + nix-util + nlohmann_json + ]; + + preConfigure = + # "Inline" .version so its not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs + '' + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*-tab.*" ]; + + hardeningDisable = ["fortify"]; +}) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index c9c6f66f1..a605b43e1 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -425,12 +425,13 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = deps_public +requires = [] if nix_util.type_name() == 'internal' # `requires` cannot contain declared dependencies (from the # subproject), so we need to do this manually - requires = [ 'nix-util' ] + requires + requires += 'nix-util' endif +requires += deps_public import('pkgconfig').generate( this_library, diff --git a/src/libutil/package.nix b/src/libutil/package.nix index dd93e5663..b36e3879c 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -72,6 +72,7 @@ mkDerivation (finalAttrs: { ; propagatedBuildInputs = [ + boost.dev libarchive nlohmann_json ]; From 4662e7d8568f55f7f56c55e7976b68a25824d1a3 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 14:57:57 -0700 Subject: [PATCH 335/910] Implement windowsEscape --- src/libutil/windows/processes.cc | 48 +++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 1d8035c62..cf1949aa2 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -164,6 +164,52 @@ AutoCloseFD nullFD() return nul; } +// Adapted from +// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ +std::string windowsEscape(const std::string & str, bool cmd) +{ + // TODO: This doesn't handle cmd.exe escaping. + if (cmd) { + throw UnimplementedError("cmd.exe escaping is not implemented"); + } + + if (str.find_first_of(" \t\n\v\"") == str.npos && !str.empty()) { + // No need to escape this one, the nonempty contents don't have a special character + return str; + } + std::string buffer; + // Add the opening quote + buffer += '"'; + for (auto iter = str.begin();; ++iter) { + size_t backslashes = 0; + while (iter != str.end() && *iter == '\\') { + ++iter; + ++backslashes; + } + + // We only escape backslashes if: + // - They come immediately before the closing quote + // - They come immediately before a quote in the middle of the string + // Both of these cases break the escaping if not handled. Otherwise backslashes are fine as-is + if (iter == str.end()) { + // Need to escape each backslash + buffer.append(backslashes * 2, '\\'); + // Exit since we've reached the end of the string + break; + } else if (*iter == '"') { + // Need to escape each backslash and the intermediate quote character + buffer.append(backslashes * 2, '\\'); + buffer += "\\\""; + } else { + // Don't escape the backslashes since they won't break the delimiter + buffer.append(backslashes, '\\'); + buffer += *iter; + } + } + // Add the closing quote + return buffer + '"'; +} + Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & out, Pipe & in) { // Setup pipes. @@ -203,7 +249,7 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou for (const auto & arg : options.args) { // TODO: This isn't the right way to escape windows command // See https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw - cmdline += ' ' + shellEscape(arg); + cmdline += ' ' + windowsEscape(arg, false); } PROCESS_INFORMATION procInfo = {0}; From d7537f695515339f811da73c98b64bb4e2e7dc9c Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 14:58:17 -0700 Subject: [PATCH 336/910] Implement initial spawn tests (just testing windowsEscape for now) --- tests/unit/libutil/spawn.cc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/unit/libutil/spawn.cc diff --git a/tests/unit/libutil/spawn.cc b/tests/unit/libutil/spawn.cc new file mode 100644 index 000000000..6d9821f4e --- /dev/null +++ b/tests/unit/libutil/spawn.cc @@ -0,0 +1,35 @@ +#include + +#include "processes.hh" + +namespace nix { +/* +TEST(SpawnTest, spawnEcho) +{ +auto output = runProgram(RunOptions{.program = "echo", .args = {}}); +} +*/ + +#ifdef _WIN32 +std::string windowsEscape(const std::string & str, bool cmd); + +TEST(SpawnTest, windowsEscape) +{ + auto empty = windowsEscape("", false); + ASSERT_EQ(empty, R"("")"); + // There's no quotes in this argument so the input should equal the output + auto backslashStr = R"(\\\\)"; + auto backslashes = windowsEscape(backslashStr, false); + ASSERT_EQ(backslashes, backslashStr); + + auto nestedQuotes = windowsEscape(R"(he said: "hello there")", false); + ASSERT_EQ(nestedQuotes, R"("he said: \"hello there\"")"); + + auto middleQuote = windowsEscape(R"( \\\" )", false); + ASSERT_EQ(middleQuote, R"(" \\\\\\\" ")"); + + auto space = windowsEscape("hello world", false); + ASSERT_EQ(space, R"("hello world")"); +} +#endif +} From 4f6e3b940255053cd51e566416327c6120851075 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 18:44:23 -0700 Subject: [PATCH 337/910] Implement tests for lookupPathForProgram and fix bugs caught by tests --- src/libutil/windows/processes.cc | 8 ++++---- tests/unit/libutil/spawn.cc | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index cf1949aa2..a1104fa1e 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -94,7 +94,7 @@ std::string runProgram( Path lookupPathForProgram(const Path & program) { if (program.find('/') != program.npos || program.find('\\') != program.npos) { - throw Error("program '%1%' partially specifies its path", program); + throw UsageError("program '%1%' partially specifies its path", program); } // Possible extensions. @@ -107,8 +107,9 @@ Path lookupPathForProgram(const Path & program) } // Look through each directory listed in $PATH. - for (const std::string & dir : tokenizeString(*getEnv("PATH"), ";")) { - Path candidate = canonPath(dir) + '/' + program; + for (const std::string & dir : tokenizeString(*path, ";")) { + // TODO: This should actually be canonPath(dir), but that ends up appending two drive paths + Path candidate = dir + "/" + program; for (const auto ext : exts) { if (pathExists(candidate + ext)) { return candidate; @@ -408,5 +409,4 @@ bool statusOk(int status) { return status == 0; } - } diff --git a/tests/unit/libutil/spawn.cc b/tests/unit/libutil/spawn.cc index 6d9821f4e..e84de18f1 100644 --- a/tests/unit/libutil/spawn.cc +++ b/tests/unit/libutil/spawn.cc @@ -6,11 +6,19 @@ namespace nix { /* TEST(SpawnTest, spawnEcho) { -auto output = runProgram(RunOptions{.program = "echo", .args = {}}); +auto output = runProgram(RunOptions{.program = "cmd", .lookupPath = true, .args = {"/C", "echo \"hello world\""}}); +std::cout << output.second << std::endl; } */ #ifdef _WIN32 +Path lookupPathForProgram(const Path & program); +TEST(SpawnTest, pathSearch) +{ + ASSERT_NO_THROW(lookupPathForProgram("cmd")); + ASSERT_NO_THROW(lookupPathForProgram("cmd.exe")); + ASSERT_THROW(lookupPathForProgram("C:/System32/cmd.exe"), UsageError); +} std::string windowsEscape(const std::string & str, bool cmd); TEST(SpawnTest, windowsEscape) From ff1fc780d24b0916ac8ffaa884dd3096f1d9aac7 Mon Sep 17 00:00:00 2001 From: Mingye Wang Date: Tue, 18 Jun 2024 12:05:59 +0800 Subject: [PATCH 338/910] optimize-store.cc: Update macos exclusion comments #2230 broadened the scope of macOS hardlink exclusion but did not change the comments. This was a little confusing for me, so I figured the comments should be updated. --- src/libstore/optimise-store.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 2477cf0c0..e9b6d2d50 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -98,9 +98,10 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, #if __APPLE__ /* HFS/macOS has some undocumented security feature disabling hardlinking for - special files within .app dirs. *.app/Contents/PkgInfo and - *.app/Contents/Resources/\*.lproj seem to be the only paths affected. See - https://github.com/NixOS/nix/issues/1443 for more discussion. */ + special files within .app dirs. Known affected paths include + *.app/Contents/{PkgInfo,Resources/\*.lproj,_CodeSignature} and .DS_Store. + See https://github.com/NixOS/nix/issues/1443 and + https://github.com/NixOS/nix/pull/2230 for more discussion. */ if (std::regex_search(path, std::regex("\\.app/Contents/.+$"))) { From fcb92b4fa4f260086aafdb7d9e8e51a26360b46e Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 22:14:38 -0700 Subject: [PATCH 339/910] Fix DWORD vs. int comparison warning --- src/libutil/windows/processes.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index a1104fa1e..973a2cab9 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -285,7 +285,7 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou TerminateProcess(procInfo.hProcess, 0); throw WinError("Couldn't assign child process to job object"); } - if (ResumeThread(procInfo.hThread) == -1) { + if (ResumeThread(procInfo.hThread) == (DWORD) -1) { TerminateProcess(procInfo.hProcess, 0); throw WinError("Couldn't resume child process thread"); } From 8b81d083a72b714a5599c8243f8e05ed15d2d7c0 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Tue, 18 Jun 2024 00:55:47 -0700 Subject: [PATCH 340/910] Remove lookupPathForProgram and implement initial runProgram test Apparently, CreateProcessW already searches path, so manual path search isn't really necessary. --- src/libutil/windows/processes.cc | 38 +++----------------------------- tests/unit/libutil/spawn.cc | 17 +++++--------- 2 files changed, 8 insertions(+), 47 deletions(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 973a2cab9..9cd714f84 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -89,36 +89,6 @@ std::string runProgram( return res.second; } -// Looks at the $PATH environment variable to find the program. -// Adapted from https://github.com/nix-windows/nix/blob/windows/src/libutil/util.cc#L2276 -Path lookupPathForProgram(const Path & program) -{ - if (program.find('/') != program.npos || program.find('\\') != program.npos) { - throw UsageError("program '%1%' partially specifies its path", program); - } - - // Possible extensions. - // TODO: This should actually be sourced from $PATHEXT, not hardcoded. - static constexpr const char * exts[] = {"", ".exe", ".cmd", ".bat"}; - - auto path = getEnv("PATH"); - if (!path.has_value()) { - throw WinError("couldn't find PATH environment variable"); - } - - // Look through each directory listed in $PATH. - for (const std::string & dir : tokenizeString(*path, ";")) { - // TODO: This should actually be canonPath(dir), but that ends up appending two drive paths - Path candidate = dir + "/" + program; - for (const auto ext : exts) { - if (pathExists(candidate + ext)) { - return candidate; - } - } - } - - throw WinError("program '%1%' not found on PATH", program); -} std::optional getProgramInterpreter(const Path & program) { @@ -246,7 +216,7 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou } } - std::string cmdline = realProgram; + std::string cmdline = windowsEscape(realProgram, false); for (const auto & arg : options.args) { // TODO: This isn't the right way to escape windows command // See https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw @@ -255,7 +225,8 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou PROCESS_INFORMATION procInfo = {0}; if (CreateProcessW( - string_to_os_string(realProgram).c_str(), + // EXE path is provided in the cmdline + NULL, string_to_os_string(cmdline).data(), NULL, NULL, @@ -335,9 +306,6 @@ void runProgram2(const RunOptions & options) in.create(); Path realProgram = options.program; - if (options.lookupPath) { - realProgram = lookupPathForProgram(realProgram); - } // TODO: Implement shebang / program interpreter lookup on Windows auto interpreter = getProgramInterpreter(realProgram); diff --git a/tests/unit/libutil/spawn.cc b/tests/unit/libutil/spawn.cc index e84de18f1..c617acae0 100644 --- a/tests/unit/libutil/spawn.cc +++ b/tests/unit/libutil/spawn.cc @@ -3,22 +3,15 @@ #include "processes.hh" namespace nix { -/* -TEST(SpawnTest, spawnEcho) -{ -auto output = runProgram(RunOptions{.program = "cmd", .lookupPath = true, .args = {"/C", "echo \"hello world\""}}); -std::cout << output.second << std::endl; -} -*/ #ifdef _WIN32 -Path lookupPathForProgram(const Path & program); -TEST(SpawnTest, pathSearch) +TEST(SpawnTest, spawnEcho) { - ASSERT_NO_THROW(lookupPathForProgram("cmd")); - ASSERT_NO_THROW(lookupPathForProgram("cmd.exe")); - ASSERT_THROW(lookupPathForProgram("C:/System32/cmd.exe"), UsageError); + auto output = runProgram(RunOptions{.program = "cmd.exe", .args = {"/C", "echo", "hello world"}}); + ASSERT_EQ(output.first, 0); + ASSERT_EQ(output.second, "\"hello world\"\r\n"); } + std::string windowsEscape(const std::string & str, bool cmd); TEST(SpawnTest, windowsEscape) From b975151c090f9bf25c812e0e0b31e2ca6998123a Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 18 Jun 2024 11:26:08 +0200 Subject: [PATCH 341/910] dedent lists this indentation is unnecessary and probably an artefact from the migration off XML. --- doc/manual/src/command-ref/nix-build.md | 18 +- doc/manual/src/command-ref/nix-channel.md | 52 ++--- .../src/command-ref/nix-copy-closure.md | 32 +-- doc/manual/src/command-ref/nix-env/install.md | 184 +++++++++--------- .../src/command-ref/nix-env/opt-common.md | 51 +++-- .../src/command-ref/nix-env/set-flag.md | 32 +-- doc/manual/src/command-ref/nix-env/upgrade.md | 62 +++--- doc/manual/src/command-ref/nix-hash.md | 76 ++++---- doc/manual/src/command-ref/nix-instantiate.md | 148 +++++++------- .../src/command-ref/nix-prefetch-url.md | 34 ++-- doc/manual/src/command-ref/nix-shell.md | 76 ++++---- .../src/command-ref/nix-store/add-fixed.md | 6 +- doc/manual/src/command-ref/nix-store/gc.md | 36 ++-- doc/manual/src/command-ref/nix-store/query.md | 180 ++++++++--------- .../src/command-ref/nix-store/realise.md | 30 +-- doc/manual/src/command-ref/nix-store/serve.md | 8 +- .../src/command-ref/nix-store/verify.md | 22 +-- 17 files changed, 523 insertions(+), 524 deletions(-) diff --git a/doc/manual/src/command-ref/nix-build.md b/doc/manual/src/command-ref/nix-build.md index e4223b542..3bb59cbed 100644 --- a/doc/manual/src/command-ref/nix-build.md +++ b/doc/manual/src/command-ref/nix-build.md @@ -55,20 +55,20 @@ All options not listed here are passed to [`nix-store --realise`](nix-store/realise.md), except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](nix-instantiate.md). - - [`--no-out-link`](#opt-no-out-link) +- [`--no-out-link`](#opt-no-out-link) - Do not create a symlink to the output path. Note that as a result - the output does not become a root of the garbage collector, and so - might be deleted by `nix-store --gc`. + Do not create a symlink to the output path. Note that as a result + the output does not become a root of the garbage collector, and so + might be deleted by `nix-store --gc`. - - [`--dry-run`](#opt-dry-run) +- [`--dry-run`](#opt-dry-run) - Show what store paths would be built or downloaded. + Show what store paths would be built or downloaded. - - [`--out-link`](#opt-out-link) / `-o` *outlink* +- [`--out-link`](#opt-out-link) / `-o` *outlink* - Change the name of the symlink to the output path created from - `result` to *outlink*. + Change the name of the symlink to the output path created from + `result` to *outlink*. {{#include ./status-build-failure.md}} diff --git a/doc/manual/src/command-ref/nix-channel.md b/doc/manual/src/command-ref/nix-channel.md index 99f6e37cf..8b58392b7 100644 --- a/doc/manual/src/command-ref/nix-channel.md +++ b/doc/manual/src/command-ref/nix-channel.md @@ -27,46 +27,46 @@ The moving parts of channels are: This command has the following operations: - - `--add` *url* \[*name*\] +- `--add` *url* \[*name*\] - Add a channel *name* located at *url* to the list of subscribed channels. - If *name* is omitted, default to the last component of *url*, with the suffixes `-stable` or `-unstable` removed. + Add a channel *name* located at *url* to the list of subscribed channels. + If *name* is omitted, default to the last component of *url*, with the suffixes `-stable` or `-unstable` removed. - > **Note** - > - > `--add` does not automatically perform an update. - > Use `--update` explicitly. + > **Note** + > + > `--add` does not automatically perform an update. + > Use `--update` explicitly. - A channel URL must point to a directory containing a file `nixexprs.tar.gz`. - At the top level, that tarball must contain a single directory with a `default.nix` file that serves as the channel’s entry point. + A channel URL must point to a directory containing a file `nixexprs.tar.gz`. + At the top level, that tarball must contain a single directory with a `default.nix` file that serves as the channel’s entry point. - - `--remove` *name* +- `--remove` *name* - Remove the channel *name* from the list of subscribed channels. + Remove the channel *name* from the list of subscribed channels. - - `--list` +- `--list` - Print the names and URLs of all subscribed channels on standard output. + Print the names and URLs of all subscribed channels on standard output. - - `--update` \[*names*…\] +- `--update` \[*names*…\] - Download the Nix expressions of subscribed channels and create a new generation. - Update all channels if none is specified, and only those included in *names* otherwise. + Download the Nix expressions of subscribed channels and create a new generation. + Update all channels if none is specified, and only those included in *names* otherwise. - - `--list-generations` +- `--list-generations` - Prints a list of all the current existing generations for the - channel profile. + Prints a list of all the current existing generations for the + channel profile. - Works the same way as - ``` - nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations - ``` + Works the same way as + ``` + nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations + ``` - - `--rollback` \[*generation*\] +- `--rollback` \[*generation*\] - Revert channels to the state before the last call to `nix-channel --update`. - Optionally, you can specify a specific channel *generation* number to restore. + Revert channels to the state before the last call to `nix-channel --update`. + Optionally, you can specify a specific channel *generation* number to restore. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index d94bde3a3..8cfd6ebad 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -27,38 +27,38 @@ When using public key authentication, you can avoid typing the passphrase with ` # Options - - `--to` +- `--to` - Copy the closure of _paths_ from a Nix store accessible from the local machine to the Nix store on the remote _machine_. - This is the default behavior. + Copy the closure of _paths_ from a Nix store accessible from the local machine to the Nix store on the remote _machine_. + This is the default behavior. - - `--from` +- `--from` - Copy the closure of _paths_ from the Nix store on the remote _machine_ to the local machine's specified Nix store. + Copy the closure of _paths_ from the Nix store on the remote _machine_ to the local machine's specified Nix store. - - `--gzip` +- `--gzip` - Enable compression of the SSH connection. + Enable compression of the SSH connection. - - `--include-outputs` +- `--include-outputs` - Also copy the outputs of [store derivation]s included in the closure. + Also copy the outputs of [store derivation]s included in the closure. - [store derivation]: @docroot@/glossary.md#gloss-store-derivation + [store derivation]: @docroot@/glossary.md#gloss-store-derivation - - `--use-substitutes` / `-s` +- `--use-substitutes` / `-s` - Attempt to download missing store objects on the target from [substituters](@docroot@/command-ref/conf-file.md#conf-substituters). - Any store objects that cannot be substituted on the target are still copied normally from the source. - This is useful, for instance, if the connection between the source and target machine is slow, but the connection between the target machine and `cache.nixos.org` (the default binary cache server) is fast. + Attempt to download missing store objects on the target from [substituters](@docroot@/command-ref/conf-file.md#conf-substituters). + Any store objects that cannot be substituted on the target are still copied normally from the source. + This is useful, for instance, if the connection between the source and target machine is slow, but the connection between the target machine and `cache.nixos.org` (the default binary cache server) is fast. {{#include ./opt-common.md}} # Environment variables - - `NIX_SSHOPTS` +- `NIX_SSHOPTS` - Additional options to be passed to `ssh` on the command line. + Additional options to be passed to `ssh` on the command line. {{#include ./env-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index 85f37904f..76aa70f71 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -21,125 +21,125 @@ It is based on the current generation of the active [profile](@docroot@/command- The arguments *args* map to store paths in a number of possible ways: - - By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression]. - These are [realised], and the resulting output paths are installed. - Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified. +- By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression]. + These are [realised], and the resulting output paths are installed. + Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified. - [derivation]: @docroot@/glossary.md#gloss-derivation - [default Nix expression]: @docroot@/command-ref/files/default-nix-expression.md - [realised]: @docroot@/glossary.md#gloss-realise + [derivation]: @docroot@/glossary.md#gloss-derivation + [default Nix expression]: @docroot@/command-ref/files/default-nix-expression.md + [realised]: @docroot@/glossary.md#gloss-realise - If there are multiple derivations matching a name in *args* that - have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the - derivation with the highest *priority* is used. A derivation can - define a priority by declaring the `meta.priority` attribute. This - attribute should be a number, with a higher value denoting a lower - priority. The default priority is `5`. + If there are multiple derivations matching a name in *args* that + have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the + derivation with the highest *priority* is used. A derivation can + define a priority by declaring the `meta.priority` attribute. This + attribute should be a number, with a higher value denoting a lower + priority. The default priority is `5`. - If there are multiple matching derivations with the same priority, - then the derivation with the highest version will be installed. + If there are multiple matching derivations with the same priority, + then the derivation with the highest version will be installed. - You can force the installation of multiple derivations with the same - name by being specific about the versions. For instance, `nix-env --install - gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will - probably cause a user environment conflict\!). + You can force the installation of multiple derivations with the same + name by being specific about the versions. For instance, `nix-env --install + gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will + probably cause a user environment conflict\!). - - If [`--attr`](#opt-attr) / `-A` is specified, the arguments are *attribute paths* that select attributes from the [default Nix expression]. - This is faster than using derivation names and unambiguous. - Show the attribute paths of available packages with [`nix-env --query`](./query.md): +- If [`--attr`](#opt-attr) / `-A` is specified, the arguments are *attribute paths* that select attributes from the [default Nix expression]. + This is faster than using derivation names and unambiguous. + Show the attribute paths of available packages with [`nix-env --query`](./query.md): - ```console - nix-env --query --available --attr-path - ``` + ```console + nix-env --query --available --attr-path + ``` - - If `--from-profile` *path* is given, *args* is a set of names - denoting installed [store paths] in the profile *path*. This is an - easy way to copy user environment elements from one profile to - another. +- If `--from-profile` *path* is given, *args* is a set of names + denoting installed [store paths] in the profile *path*. This is an + easy way to copy user environment elements from one profile to + another. - - If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument. - The derivations returned by those function calls are installed. - This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. +- If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument. + The derivations returned by those function calls are installed. + This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. - - If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed. +- If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed. - - If *args* are [store paths] that are not store derivations, then these are [realised] and installed. +- If *args* are [store paths] that are not store derivations, then these are [realised] and installed. - - By default all [outputs](@docroot@/language/derivations.md#attr-outputs) are installed for each [derivation]. - This can be overridden by adding a `meta.outputsToInstall` attribute on the derivation listing a subset of the output names. +- By default all [outputs](@docroot@/language/derivations.md#attr-outputs) are installed for each [derivation]. + This can be overridden by adding a `meta.outputsToInstall` attribute on the derivation listing a subset of the output names. - Example: + Example: - The file `example.nix` defines a derivation with two outputs `foo` and `bar`, each containing a file. + The file `example.nix` defines a derivation with two outputs `foo` and `bar`, each containing a file. - ```nix - # example.nix - let - pkgs = import {}; - command = '' - ${pkgs.coreutils}/bin/mkdir -p $foo $bar - echo foo > $foo/foo-file - echo bar > $bar/bar-file - ''; - in - derivation { - name = "example"; - builder = "${pkgs.bash}/bin/bash"; - args = [ "-c" command ]; - outputs = [ "foo" "bar" ]; - system = builtins.currentSystem; - } - ``` + ```nix + # example.nix + let + pkgs = import {}; + command = '' + ${pkgs.coreutils}/bin/mkdir -p $foo $bar + echo foo > $foo/foo-file + echo bar > $bar/bar-file + ''; + in + derivation { + name = "example"; + builder = "${pkgs.bash}/bin/bash"; + args = [ "-c" command ]; + outputs = [ "foo" "bar" ]; + system = builtins.currentSystem; + } + ``` - Installing from this Nix expression will make files from both outputs appear in the current profile. + Installing from this Nix expression will make files from both outputs appear in the current profile. - ```console - $ nix-env --install --file example.nix - installing 'example' - $ ls ~/.nix-profile - foo-file - bar-file - manifest.nix - ``` + ```console + $ nix-env --install --file example.nix + installing 'example' + $ ls ~/.nix-profile + foo-file + bar-file + manifest.nix + ``` - Adding `meta.outputsToInstall` to that derivation will make `nix-env` only install files from the specified outputs. + Adding `meta.outputsToInstall` to that derivation will make `nix-env` only install files from the specified outputs. - ```nix - # example-outputs.nix - import ./example.nix // { meta.outputsToInstall = [ "bar" ]; } - ``` + ```nix + # example-outputs.nix + import ./example.nix // { meta.outputsToInstall = [ "bar" ]; } + ``` - ```console - $ nix-env --install --file example-outputs.nix - installing 'example' - $ ls ~/.nix-profile - bar-file - manifest.nix - ``` + ```console + $ nix-env --install --file example-outputs.nix + installing 'example' + $ ls ~/.nix-profile + bar-file + manifest.nix + ``` # Options - - `--prebuilt-only` / `-b` +- `--prebuilt-only` / `-b` - Use only derivations for which a substitute is registered, i.e., - there is a pre-built binary available that can be downloaded in lieu - of building the derivation. Thus, no packages will be built from - source. + Use only derivations for which a substitute is registered, i.e., + there is a pre-built binary available that can be downloaded in lieu + of building the derivation. Thus, no packages will be built from + source. - - `--preserve-installed` / `-P` +- `--preserve-installed` / `-P` - Do not remove derivations with a name matching one of the - derivations being installed. Usually, trying to have two versions of - the same package installed in the same generation of a profile will - lead to an error in building the generation, due to file name - clashes between the two versions. However, this is not the case for - all packages. + Do not remove derivations with a name matching one of the + derivations being installed. Usually, trying to have two versions of + the same package installed in the same generation of a profile will + lead to an error in building the generation, due to file name + clashes between the two versions. However, this is not the case for + all packages. - - `--remove-all` / `-r` +- `--remove-all` / `-r` - Remove all previously installed packages first. This is equivalent - to running `nix-env --uninstall '.*'` first, except that everything happens - in a single transaction. + Remove all previously installed packages first. This is equivalent + to running `nix-env --uninstall '.*'` first, except that everything happens + in a single transaction. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/opt-common.md b/doc/manual/src/command-ref/nix-env/opt-common.md index 3ece3e881..1479ca0bd 100644 --- a/doc/manual/src/command-ref/nix-env/opt-common.md +++ b/doc/manual/src/command-ref/nix-env/opt-common.md @@ -2,38 +2,37 @@ The following options are allowed for all `nix-env` operations, but may not always have an effect. - - `--file` / `-f` *path* +- `--file` / `-f` *path* - Specifies the Nix expression (designated below as the *active Nix - expression*) used by the `--install`, `--upgrade`, and `--query - --available` operations to obtain derivations. The default is - `~/.nix-defexpr`. + Specifies the Nix expression (designated below as the *active Nix + expression*) used by the `--install`, `--upgrade`, and `--query + --available` operations to obtain derivations. The default is + `~/.nix-defexpr`. - If the argument starts with `http://` or `https://`, it is - interpreted as the URL of a tarball that will be downloaded and - unpacked to a temporary location. The tarball must include a single - top-level directory containing at least a file named `default.nix`. + If the argument starts with `http://` or `https://`, it is + interpreted as the URL of a tarball that will be downloaded and + unpacked to a temporary location. The tarball must include a single + top-level directory containing at least a file named `default.nix`. - - `--profile` / `-p` *path* +- `--profile` / `-p` *path* - Specifies the profile to be used by those operations that operate on - a profile (designated below as the *active profile*). A profile is a - sequence of user environments called *generations*, one of which is - the *current generation*. + Specifies the profile to be used by those operations that operate on + a profile (designated below as the *active profile*). A profile is a + sequence of user environments called *generations*, one of which is + the *current generation*. - - `--dry-run` +- `--dry-run` - For the `--install`, `--upgrade`, `--uninstall`, - `--switch-generation`, `--delete-generations` and `--rollback` - operations, this flag will cause `nix-env` to print what *would* be - done if this flag had not been specified, without actually doing it. + For the `--install`, `--upgrade`, `--uninstall`, + `--switch-generation`, `--delete-generations` and `--rollback` + operations, this flag will cause `nix-env` to print what *would* be + done if this flag had not been specified, without actually doing it. - `--dry-run` also prints out which paths will be - [substituted](@docroot@/glossary.md) (i.e., downloaded) and which paths - will be built from source (because no substitute is available). + `--dry-run` also prints out which paths will be + [substituted](@docroot@/glossary.md) (i.e., downloaded) and which paths + will be built from source (because no substitute is available). - - `--system-filter` *system* +- `--system-filter` *system* - By default, operations such as `--query - --available` show derivations matching any platform. This option - allows you to use derivations for the specified platform *system*. + By default, operations such as `--query --available` show derivations matching any platform. This option + allows you to use derivations for the specified platform *system*. diff --git a/doc/manual/src/command-ref/nix-env/set-flag.md b/doc/manual/src/command-ref/nix-env/set-flag.md index e04b22a91..58a0248bb 100644 --- a/doc/manual/src/command-ref/nix-env/set-flag.md +++ b/doc/manual/src/command-ref/nix-env/set-flag.md @@ -13,24 +13,24 @@ to be modified. There are several attributes that can be usefully modified, because they affect the behaviour of `nix-env` or the user environment build script: - - `priority` can be changed to resolve filename clashes. The user - environment build script uses the `meta.priority` attribute of - derivations to resolve filename collisions between packages. Lower - priority values denote a higher priority. For instance, the GCC - wrapper package and the Binutils package in Nixpkgs both have a file - `bin/ld`, so previously if you tried to install both you would get a - collision. Now, on the other hand, the GCC wrapper declares a higher - priority than Binutils, so the former’s `bin/ld` is symlinked in the - user environment. +- `priority` can be changed to resolve filename clashes. The user + environment build script uses the `meta.priority` attribute of + derivations to resolve filename collisions between packages. Lower + priority values denote a higher priority. For instance, the GCC + wrapper package and the Binutils package in Nixpkgs both have a file + `bin/ld`, so previously if you tried to install both you would get a + collision. Now, on the other hand, the GCC wrapper declares a higher + priority than Binutils, so the former’s `bin/ld` is symlinked in the + user environment. - - `keep` can be set to `true` to prevent the package from being - upgraded or replaced. This is useful if you want to hang on to an - older version of a package. +- `keep` can be set to `true` to prevent the package from being + upgraded or replaced. This is useful if you want to hang on to an + older version of a package. - - `active` can be set to `false` to “disable” the package. That is, no - symlinks will be generated to the files of the package, but it - remains part of the profile (so it won’t be garbage-collected). It - can be set back to `true` to re-enable the package. +- `active` can be set to `false` to “disable” the package. That is, no + symlinks will be generated to the files of the package, but it + remains part of the profile (so it won’t be garbage-collected). It + can be set back to `true` to re-enable the package. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/upgrade.md b/doc/manual/src/command-ref/nix-env/upgrade.md index dc99064b9..2779363c3 100644 --- a/doc/manual/src/command-ref/nix-env/upgrade.md +++ b/doc/manual/src/command-ref/nix-env/upgrade.md @@ -28,48 +28,48 @@ version is installed. # Flags - - `--lt` +- `--lt` - Only upgrade a derivation to newer versions. This is the default. + Only upgrade a derivation to newer versions. This is the default. - - `--leq` +- `--leq` - In addition to upgrading to newer versions, also “upgrade” to - derivations that have the same version. Version are not a unique - identification of a derivation, so there may be many derivations - that have the same version. This flag may be useful to force - “synchronisation” between the installed and available derivations. + In addition to upgrading to newer versions, also “upgrade” to + derivations that have the same version. Version are not a unique + identification of a derivation, so there may be many derivations + that have the same version. This flag may be useful to force + “synchronisation” between the installed and available derivations. - - `--eq` +- `--eq` - *Only* “upgrade” to derivations that have the same version. This may - not seem very useful, but it actually is, e.g., when there is a new - release of Nixpkgs and you want to replace installed applications - with the same versions built against newer dependencies (to reduce - the number of dependencies floating around on your system). + *Only* “upgrade” to derivations that have the same version. This may + not seem very useful, but it actually is, e.g., when there is a new + release of Nixpkgs and you want to replace installed applications + with the same versions built against newer dependencies (to reduce + the number of dependencies floating around on your system). - - `--always` +- `--always` - In addition to upgrading to newer versions, also “upgrade” to - derivations that have the same or a lower version. I.e., derivations - may actually be downgraded depending on what is available in the - active Nix expression. + In addition to upgrading to newer versions, also “upgrade” to + derivations that have the same or a lower version. I.e., derivations + may actually be downgraded depending on what is available in the + active Nix expression. - - `--prebuilt-only` / `-b` +- `--prebuilt-only` / `-b` - Use only derivations for which a substitute is registered, i.e., - there is a pre-built binary available that can be downloaded in lieu - of building the derivation. Thus, no packages will be built from - source. + Use only derivations for which a substitute is registered, i.e., + there is a pre-built binary available that can be downloaded in lieu + of building the derivation. Thus, no packages will be built from + source. - - `--preserve-installed` / `-P` +- `--preserve-installed` / `-P` - Do not remove derivations with a name matching one of the - derivations being installed. Usually, trying to have two versions of - the same package installed in the same generation of a profile will - lead to an error in building the generation, due to file name - clashes between the two versions. However, this is not the case for - all packages. + Do not remove derivations with a name matching one of the + derivations being installed. Usually, trying to have two versions of + the same package installed in the same generation of a profile will + lead to an error in building the generation, due to file name + clashes between the two versions. However, this is not the case for + all packages. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-hash.md b/doc/manual/src/command-ref/nix-hash.md index 4762600c2..f249c2b84 100644 --- a/doc/manual/src/command-ref/nix-hash.md +++ b/doc/manual/src/command-ref/nix-hash.md @@ -29,65 +29,65 @@ md5sum`. # Options - - `--flat` +- `--flat` - Print the cryptographic hash of the contents of each regular file *path*. - That is, instead of computing - the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, - just [directly hash]((@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is. - This requires *path* to resolve to a regular file rather than directory. - The result is identical to that produced by the GNU commands - `md5sum` and `sha1sum`. + Print the cryptographic hash of the contents of each regular file *path*. + That is, instead of computing + the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, + just [directly hash]((@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is. + This requires *path* to resolve to a regular file rather than directory. + The result is identical to that produced by the GNU commands + `md5sum` and `sha1sum`. - - `--base16` +- `--base16` - Print the hash in a hexadecimal representation (default). + Print the hash in a hexadecimal representation (default). - - `--base32` +- `--base32` - Print the hash in a base-32 representation rather than hexadecimal. - This base-32 representation is more compact and can be used in Nix - expressions (such as in calls to `fetchurl`). + Print the hash in a base-32 representation rather than hexadecimal. + This base-32 representation is more compact and can be used in Nix + expressions (such as in calls to `fetchurl`). - - `--base64` +- `--base64` - Similar to --base32, but print the hash in a base-64 representation, - which is more compact than the base-32 one. + Similar to --base32, but print the hash in a base-64 representation, + which is more compact than the base-32 one. - - `--sri` +- `--sri` - Print the hash in SRI format with base-64 encoding. - The type of hash algorithm will be prepended to the hash string, - followed by a hyphen (-) and the base-64 hash body. + Print the hash in SRI format with base-64 encoding. + The type of hash algorithm will be prepended to the hash string, + followed by a hyphen (-) and the base-64 hash body. - - `--truncate` +- `--truncate` - Truncate hashes longer than 160 bits (such as SHA-256) to 160 bits. + Truncate hashes longer than 160 bits (such as SHA-256) to 160 bits. - - `--type` *hashAlgo* +- `--type` *hashAlgo* - Use the specified cryptographic hash algorithm, which can be one of - `md5`, `sha1`, `sha256`, and `sha512`. + Use the specified cryptographic hash algorithm, which can be one of + `md5`, `sha1`, `sha256`, and `sha512`. - - `--to-base16` +- `--to-base16` - Don’t hash anything, but convert the base-32 hash representation - *hash* to hexadecimal. + Don’t hash anything, but convert the base-32 hash representation + *hash* to hexadecimal. - - `--to-base32` +- `--to-base32` - Don’t hash anything, but convert the hexadecimal hash representation - *hash* to base-32. + Don’t hash anything, but convert the hexadecimal hash representation + *hash* to base-32. - - `--to-base64` +- `--to-base64` - Don’t hash anything, but convert the hexadecimal hash representation - *hash* to base-64. + Don’t hash anything, but convert the hexadecimal hash representation + *hash* to base-64. - - `--to-sri` +- `--to-sri` - Don’t hash anything, but convert the hexadecimal hash representation - *hash* to SRI. + Don’t hash anything, but convert the hexadecimal hash representation + *hash* to SRI. # Examples diff --git a/doc/manual/src/command-ref/nix-instantiate.md b/doc/manual/src/command-ref/nix-instantiate.md index 554784b63..6f6fcdc1f 100644 --- a/doc/manual/src/command-ref/nix-instantiate.md +++ b/doc/manual/src/command-ref/nix-instantiate.md @@ -30,97 +30,97 @@ standard input. # Options - - `--add-root` *path* +- `--add-root` *path* - See the [corresponding option](nix-store.md) in `nix-store`. + See the [corresponding option](nix-store.md) in `nix-store`. - - `--parse` +- `--parse` - Just parse the input files, and print their abstract syntax trees on - standard output as a Nix expression. + Just parse the input files, and print their abstract syntax trees on + standard output as a Nix expression. - - `--eval` +- `--eval` - Just parse and evaluate the input files, and print the resulting - values on standard output. No instantiation of store derivations - takes place. + Just parse and evaluate the input files, and print the resulting + values on standard output. No instantiation of store derivations + takes place. - > **Warning** - > - > This option produces output which can be parsed as a Nix expression which - > will produce a different result than the input expression when evaluated. - > For example, these two Nix expressions print the same result despite - > having different meaning: - > - > ```console - > $ nix-instantiate --eval --expr '{ a = {}; }' - > { a = ; } - > $ nix-instantiate --eval --expr '{ a = ; }' - > { a = ; } - > ``` - > - > For human-readable output, `nix eval` (experimental) is more informative: - > - > ```console - > $ nix-instantiate --eval --expr 'a: a' - > - > $ nix eval --expr 'a: a' - > «lambda @ «string»:1:1» - > ``` - > - > For machine-readable output, the `--xml` option produces unambiguous - > output: - > - > ```console - > $ nix-instantiate --eval --xml --expr '{ foo = ; }' - > - > - > - > - > - > - > - > - > ``` + > **Warning** + > + > This option produces output which can be parsed as a Nix expression which + > will produce a different result than the input expression when evaluated. + > For example, these two Nix expressions print the same result despite + > having different meaning: + > + > ```console + > $ nix-instantiate --eval --expr '{ a = {}; }' + > { a = ; } + > $ nix-instantiate --eval --expr '{ a = ; }' + > { a = ; } + > ``` + > + > For human-readable output, `nix eval` (experimental) is more informative: + > + > ```console + > $ nix-instantiate --eval --expr 'a: a' + > + > $ nix eval --expr 'a: a' + > «lambda @ «string»:1:1» + > ``` + > + > For machine-readable output, the `--xml` option produces unambiguous + > output: + > + > ```console + > $ nix-instantiate --eval --xml --expr '{ foo = ; }' + > + > + > + > + > + > + > + > + > ``` - - `--find-file` +- `--find-file` - Look up the given files in Nix’s search path (as specified by the - `NIX_PATH` environment variable). If found, print the corresponding - absolute paths on standard output. For instance, if `NIX_PATH` is - `nixpkgs=/home/alice/nixpkgs`, then `nix-instantiate --find-file - nixpkgs/default.nix` will print `/home/alice/nixpkgs/default.nix`. + Look up the given files in Nix’s search path (as specified by the + `NIX_PATH` environment variable). If found, print the corresponding + absolute paths on standard output. For instance, if `NIX_PATH` is + `nixpkgs=/home/alice/nixpkgs`, then `nix-instantiate --find-file + nixpkgs/default.nix` will print `/home/alice/nixpkgs/default.nix`. - - `--strict` +- `--strict` - When used with `--eval`, recursively evaluate list elements and - attributes. Normally, such sub-expressions are left unevaluated - (since the Nix language is lazy). + When used with `--eval`, recursively evaluate list elements and + attributes. Normally, such sub-expressions are left unevaluated + (since the Nix language is lazy). - > **Warning** - > - > This option can cause non-termination, because lazy data - > structures can be infinitely large. + > **Warning** + > + > This option can cause non-termination, because lazy data + > structures can be infinitely large. - - `--json` +- `--json` - When used with `--eval`, print the resulting value as an JSON - representation of the abstract syntax tree rather than as a Nix expression. + When used with `--eval`, print the resulting value as an JSON + representation of the abstract syntax tree rather than as a Nix expression. - - `--xml` +- `--xml` - When used with `--eval`, print the resulting value as an XML - representation of the abstract syntax tree rather than as a Nix expression. - The schema is the same as that used by the [`toXML` - built-in](../language/builtins.md). + When used with `--eval`, print the resulting value as an XML + representation of the abstract syntax tree rather than as a Nix expression. + The schema is the same as that used by the [`toXML` + built-in](../language/builtins.md). - - `--read-write-mode` +- `--read-write-mode` - When used with `--eval`, perform evaluation in read/write mode so - nix language features that require it will still work (at the cost - of needing to do instantiation of every evaluated derivation). If - this option is not enabled, there may be uninstantiated store paths - in the final output. + When used with `--eval`, perform evaluation in read/write mode so + nix language features that require it will still work (at the cost + of needing to do instantiation of every evaluated derivation). If + this option is not enabled, there may be uninstantiated store paths + in the final output. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-prefetch-url.md b/doc/manual/src/command-ref/nix-prefetch-url.md index 309738113..ffab94b8a 100644 --- a/doc/manual/src/command-ref/nix-prefetch-url.md +++ b/doc/manual/src/command-ref/nix-prefetch-url.md @@ -39,32 +39,32 @@ the path of the downloaded file in the Nix store is also printed. # Options - - `--type` *hashAlgo* +- `--type` *hashAlgo* - Use the specified cryptographic hash algorithm, - which can be one of `md5`, `sha1`, `sha256`, and `sha512`. - The default is `sha256`. + Use the specified cryptographic hash algorithm, + which can be one of `md5`, `sha1`, `sha256`, and `sha512`. + The default is `sha256`. - - `--print-path` +- `--print-path` - Print the store path of the downloaded file on standard output. + Print the store path of the downloaded file on standard output. - - `--unpack` +- `--unpack` - Unpack the archive (which must be a tarball or zip file) and add the - result to the Nix store. The resulting hash can be used with - functions such as Nixpkgs’s `fetchzip` or `fetchFromGitHub`. + Unpack the archive (which must be a tarball or zip file) and add the + result to the Nix store. The resulting hash can be used with + functions such as Nixpkgs’s `fetchzip` or `fetchFromGitHub`. - - `--executable` +- `--executable` - Set the executable bit on the downloaded file. + Set the executable bit on the downloaded file. - - `--name` *name* +- `--name` *name* - Override the name of the file in the Nix store. By default, this is - `hash-basename`, where *basename* is the last component of *url*. - Overriding the name is necessary when *basename* contains characters - that are not allowed in Nix store paths. + Override the name of the file in the Nix store. By default, this is + `hash-basename`, where *basename* is the last component of *url*. + Overriding the name is necessary when *basename* contains characters + that are not allowed in Nix store paths. # Examples diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index 16b270de7..ddec30f5b 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -60,63 +60,63 @@ All options not listed here are passed to `nix-store --realise`, except for `--arg` and `--attr` / `-A` which are passed to `nix-instantiate`. - - `--command` *cmd* +- `--command` *cmd* - In the environment of the derivation, run the shell command *cmd*. - This command is executed in an interactive shell. (Use `--run` to - use a non-interactive shell instead.) However, a call to `exit` is - implicitly added to the command, so the shell will exit after - running the command. To prevent this, add `return` at the end; - e.g. `--command "echo Hello; return"` will print `Hello` and then - drop you into the interactive shell. This can be useful for doing - any additional initialisation. + In the environment of the derivation, run the shell command *cmd*. + This command is executed in an interactive shell. (Use `--run` to + use a non-interactive shell instead.) However, a call to `exit` is + implicitly added to the command, so the shell will exit after + running the command. To prevent this, add `return` at the end; + e.g. `--command "echo Hello; return"` will print `Hello` and then + drop you into the interactive shell. This can be useful for doing + any additional initialisation. - - `--run` *cmd* +- `--run` *cmd* - Like `--command`, but executes the command in a non-interactive - shell. This means (among other things) that if you hit Ctrl-C while - the command is running, the shell exits. + Like `--command`, but executes the command in a non-interactive + shell. This means (among other things) that if you hit Ctrl-C while + the command is running, the shell exits. - - `--exclude` *regexp* +- `--exclude` *regexp* - Do not build any dependencies whose store path matches the regular - expression *regexp*. This option may be specified multiple times. + Do not build any dependencies whose store path matches the regular + expression *regexp*. This option may be specified multiple times. - - `--pure` +- `--pure` - If this flag is specified, the environment is almost entirely - cleared before the interactive shell is started, so you get an - environment that more closely corresponds to the “real” Nix build. A - few variables, in particular `HOME`, `USER` and `DISPLAY`, are - retained. + If this flag is specified, the environment is almost entirely + cleared before the interactive shell is started, so you get an + environment that more closely corresponds to the “real” Nix build. A + few variables, in particular `HOME`, `USER` and `DISPLAY`, are + retained. - - `--packages` / `-p` *packages*… +- `--packages` / `-p` *packages*… - Set up an environment in which the specified packages are present. - The command line arguments are interpreted as attribute names inside - the Nix Packages collection. Thus, `nix-shell --packages libjpeg openjdk` - will start a shell in which the packages denoted by the attribute - names `libjpeg` and `openjdk` are present. + Set up an environment in which the specified packages are present. + The command line arguments are interpreted as attribute names inside + the Nix Packages collection. Thus, `nix-shell --packages libjpeg openjdk` + will start a shell in which the packages denoted by the attribute + names `libjpeg` and `openjdk` are present. - - `-i` *interpreter* +- `-i` *interpreter* - The chained script interpreter to be invoked by `nix-shell`. Only - applicable in `#!`-scripts (described below). + The chained script interpreter to be invoked by `nix-shell`. Only + applicable in `#!`-scripts (described below). - - `--keep` *name* +- `--keep` *name* - When a `--pure` shell is started, keep the listed environment - variables. + When a `--pure` shell is started, keep the listed environment + variables. {{#include ./opt-common.md}} # Environment variables - - `NIX_BUILD_SHELL` +- `NIX_BUILD_SHELL` - Shell used to start the interactive environment. Defaults to the - `bash` found in ``, falling back to the `bash` found in - `PATH` if not found. + Shell used to start the interactive environment. Defaults to the + `bash` found in ``, falling back to the `bash` found in + `PATH` if not found. {{#include ./env-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/add-fixed.md b/doc/manual/src/command-ref/nix-store/add-fixed.md index 3de25194c..bebf15026 100644 --- a/doc/manual/src/command-ref/nix-store/add-fixed.md +++ b/doc/manual/src/command-ref/nix-store/add-fixed.md @@ -16,10 +16,10 @@ public url or broke since the download expression was written. This operation has the following options: - - `--recursive` +- `--recursive` - Use recursive instead of flat hashing mode, used when adding - directories to the store. + Use recursive instead of flat hashing mode, used when adding + directories to the store. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/gc.md b/doc/manual/src/command-ref/nix-store/gc.md index 07fd452ce..f432e00eb 100644 --- a/doc/manual/src/command-ref/nix-store/gc.md +++ b/doc/manual/src/command-ref/nix-store/gc.md @@ -14,34 +14,34 @@ reachable via file system references from a set of “roots”, are deleted. The following suboperations may be specified: - - `--print-roots` +- `--print-roots` - This operation prints on standard output the set of roots used by - the garbage collector. + This operation prints on standard output the set of roots used by + the garbage collector. - - `--print-live` +- `--print-live` - This operation prints on standard output the set of “live” store - paths, which are all the store paths reachable from the roots. Live - paths should never be deleted, since that would break consistency — - it would become possible that applications are installed that - reference things that are no longer present in the store. + This operation prints on standard output the set of “live” store + paths, which are all the store paths reachable from the roots. Live + paths should never be deleted, since that would break consistency — + it would become possible that applications are installed that + reference things that are no longer present in the store. - - `--print-dead` +- `--print-dead` - This operation prints out on standard output the set of “dead” store - paths, which is just the opposite of the set of live paths: any path - in the store that is not live (with respect to the roots) is dead. + This operation prints out on standard output the set of “dead” store + paths, which is just the opposite of the set of live paths: any path + in the store that is not live (with respect to the roots) is dead. By default, all unreachable paths are deleted. The following options control what gets deleted and in what order: - - `--max-freed` *bytes* +- `--max-freed` *bytes* - Keep deleting paths until at least *bytes* bytes have been deleted, - then stop. The argument *bytes* can be followed by the - multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB - or TiB units. + Keep deleting paths until at least *bytes* bytes have been deleted, + then stop. The argument *bytes* can be followed by the + multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB + or TiB units. The behaviour of the collector is also influenced by the `keep-outputs` and `keep-derivations` settings in the Nix diff --git a/doc/manual/src/command-ref/nix-store/query.md b/doc/manual/src/command-ref/nix-store/query.md index a703b002b..b4efa734e 100644 --- a/doc/manual/src/command-ref/nix-store/query.md +++ b/doc/manual/src/command-ref/nix-store/query.md @@ -24,138 +24,138 @@ symlink. # Common query options - - `--use-output` / `-u` +- `--use-output` / `-u` - For each argument to the query that is a [store derivation], apply the - query to the output path of the derivation instead. + For each argument to the query that is a [store derivation], apply the + query to the output path of the derivation instead. - - `--force-realise` / `-f` +- `--force-realise` / `-f` - Realise each argument to the query first (see [`nix-store --realise`](./realise.md)). + Realise each argument to the query first (see [`nix-store --realise`](./realise.md)). [store derivation]: @docroot@/glossary.md#gloss-store-derivation # Queries - - `--outputs` +- `--outputs` - Prints out the [output paths] of the store - derivations *paths*. These are the paths that will be produced when - the derivation is built. + Prints out the [output paths] of the store + derivations *paths*. These are the paths that will be produced when + the derivation is built. - [output paths]: @docroot@/glossary.md#gloss-output-path + [output paths]: @docroot@/glossary.md#gloss-output-path - - `--requisites` / `-R` +- `--requisites` / `-R` - Prints out the [closure] of the store path *paths*. + Prints out the [closure] of the store path *paths*. - [closure]: @docroot@/glossary.md#gloss-closure + [closure]: @docroot@/glossary.md#gloss-closure - This query has one option: + This query has one option: - - `--include-outputs` - Also include the existing output paths of [store derivation]s, - and their closures. + - `--include-outputs` + Also include the existing output paths of [store derivation]s, + and their closures. - This query can be used to implement various kinds of deployment. A - *source deployment* is obtained by distributing the closure of a - store derivation. A *binary deployment* is obtained by distributing - the closure of an output path. A *cache deployment* (combined - source/binary deployment, including binaries of build-time-only - dependencies) is obtained by distributing the closure of a store - derivation and specifying the option `--include-outputs`. + This query can be used to implement various kinds of deployment. A + *source deployment* is obtained by distributing the closure of a + store derivation. A *binary deployment* is obtained by distributing + the closure of an output path. A *cache deployment* (combined + source/binary deployment, including binaries of build-time-only + dependencies) is obtained by distributing the closure of a store + derivation and specifying the option `--include-outputs`. - - `--references` +- `--references` - Prints the set of [references] of the store paths - *paths*, that is, their immediate dependencies. (For *all* - dependencies, use `--requisites`.) + Prints the set of [references] of the store paths + *paths*, that is, their immediate dependencies. (For *all* + dependencies, use `--requisites`.) - [references]: @docroot@/glossary.md#gloss-reference + [references]: @docroot@/glossary.md#gloss-reference - - `--referrers` +- `--referrers` - Prints the set of *referrers* of the store paths *paths*, that is, - the store paths currently existing in the Nix store that refer to - one of *paths*. Note that contrary to the references, the set of - referrers is not constant; it can change as store paths are added or - removed. + Prints the set of *referrers* of the store paths *paths*, that is, + the store paths currently existing in the Nix store that refer to + one of *paths*. Note that contrary to the references, the set of + referrers is not constant; it can change as store paths are added or + removed. - - `--referrers-closure` +- `--referrers-closure` - Prints the closure of the set of store paths *paths* under the - referrers relation; that is, all store paths that directly or - indirectly refer to one of *paths*. These are all the path currently - in the Nix store that are dependent on *paths*. + Prints the closure of the set of store paths *paths* under the + referrers relation; that is, all store paths that directly or + indirectly refer to one of *paths*. These are all the path currently + in the Nix store that are dependent on *paths*. - - `--deriver` / `-d` +- `--deriver` / `-d` - Prints the [deriver] that was used to build the store paths *paths*. If - the path has no deriver (e.g., if it is a source file), or if the - deriver is not known (e.g., in the case of a binary-only - deployment), the string `unknown-deriver` is printed. - The returned deriver is not guaranteed to exist in the local store, for - example when *paths* were substituted from a binary cache. - Use `--valid-derivers` instead to obtain valid paths only. + Prints the [deriver] that was used to build the store paths *paths*. If + the path has no deriver (e.g., if it is a source file), or if the + deriver is not known (e.g., in the case of a binary-only + deployment), the string `unknown-deriver` is printed. + The returned deriver is not guaranteed to exist in the local store, for + example when *paths* were substituted from a binary cache. + Use `--valid-derivers` instead to obtain valid paths only. - [deriver]: @docroot@/glossary.md#gloss-deriver + [deriver]: @docroot@/glossary.md#gloss-deriver - - `--valid-derivers` +- `--valid-derivers` - Prints a set of derivation files (`.drv`) which are supposed produce - said paths when realized. Might print nothing, for example for source paths - or paths subsituted from a binary cache. + Prints a set of derivation files (`.drv`) which are supposed produce + said paths when realized. Might print nothing, for example for source paths + or paths subsituted from a binary cache. - - `--graph` +- `--graph` - Prints the references graph of the store paths *paths* in the format - of the `dot` tool of AT\&T's [Graphviz - package](http://www.graphviz.org/). This can be used to visualise - dependency graphs. To obtain a build-time dependency graph, apply - this to a store derivation. To obtain a runtime dependency graph, - apply it to an output path. + Prints the references graph of the store paths *paths* in the format + of the `dot` tool of AT\&T's [Graphviz + package](http://www.graphviz.org/). This can be used to visualise + dependency graphs. To obtain a build-time dependency graph, apply + this to a store derivation. To obtain a runtime dependency graph, + apply it to an output path. - - `--tree` +- `--tree` - Prints the references graph of the store paths *paths* as a nested - ASCII tree. References are ordered by descending closure size; this - tends to flatten the tree, making it more readable. The query only - recurses into a store path when it is first encountered; this - prevents a blowup of the tree representation of the graph. + Prints the references graph of the store paths *paths* as a nested + ASCII tree. References are ordered by descending closure size; this + tends to flatten the tree, making it more readable. The query only + recurses into a store path when it is first encountered; this + prevents a blowup of the tree representation of the graph. - - `--graphml` +- `--graphml` - Prints the references graph of the store paths *paths* in the - [GraphML](http://graphml.graphdrawing.org/) file format. This can be - used to visualise dependency graphs. To obtain a build-time - dependency graph, apply this to a [store derivation]. To obtain a - runtime dependency graph, apply it to an output path. + Prints the references graph of the store paths *paths* in the + [GraphML](http://graphml.graphdrawing.org/) file format. This can be + used to visualise dependency graphs. To obtain a build-time + dependency graph, apply this to a [store derivation]. To obtain a + runtime dependency graph, apply it to an output path. - - `--binding` *name* / `-b` *name* +- `--binding` *name* / `-b` *name* - Prints the value of the attribute *name* (i.e., environment - variable) of the [store derivation]s *paths*. It is an error for a - derivation to not have the specified attribute. + Prints the value of the attribute *name* (i.e., environment + variable) of the [store derivation]s *paths*. It is an error for a + derivation to not have the specified attribute. - - `--hash` +- `--hash` - Prints the SHA-256 hash of the contents of the store paths *paths* - (that is, the hash of the output of `nix-store --dump` on the given - paths). Since the hash is stored in the Nix database, this is a fast - operation. + Prints the SHA-256 hash of the contents of the store paths *paths* + (that is, the hash of the output of `nix-store --dump` on the given + paths). Since the hash is stored in the Nix database, this is a fast + operation. - - `--size` +- `--size` - Prints the size in bytes of the contents of the store paths *paths* - — to be precise, the size of the output of `nix-store --dump` on - the given paths. Note that the actual disk space required by the - store paths may be higher, especially on filesystems with large - cluster sizes. + Prints the size in bytes of the contents of the store paths *paths* + — to be precise, the size of the output of `nix-store --dump` on + the given paths. Note that the actual disk space required by the + store paths may be higher, especially on filesystems with large + cluster sizes. - - `--roots` +- `--roots` - Prints the garbage collector roots that point, directly or - indirectly, at the store paths *paths*. + Prints the garbage collector roots that point, directly or + indirectly, at the store paths *paths*. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index 6288b24e0..e30b351a4 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -42,26 +42,26 @@ For non-derivation arguments, the argument itself is printed. # Options - - `--dry-run` +- `--dry-run` - Print on standard error a description of what packages would be - built or downloaded, without actually performing the operation. + Print on standard error a description of what packages would be + built or downloaded, without actually performing the operation. - - `--ignore-unknown` +- `--ignore-unknown` - If a non-derivation path does not have a substitute, then silently - ignore it. + If a non-derivation path does not have a substitute, then silently + ignore it. - - `--check` +- `--check` - This option allows you to check whether a derivation is - deterministic. It rebuilds the specified derivation and checks - whether the result is bitwise-identical with the existing outputs, - printing an error if that’s not the case. The outputs of the - specified derivation must already exist. When used with `-K`, if an - output path is not identical to the corresponding output from the - previous build, the new output path is left in - `/nix/store/name.check.` + This option allows you to check whether a derivation is + deterministic. It rebuilds the specified derivation and checks + whether the result is bitwise-identical with the existing outputs, + printing an error if that’s not the case. The outputs of the + specified derivation must already exist. When used with `-K`, if an + output path is not identical to the corresponding output from the + previous build, the new output path is left in + `/nix/store/name.check.` {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/serve.md b/doc/manual/src/command-ref/nix-store/serve.md index dd9b93fbf..9a4cf5216 100644 --- a/doc/manual/src/command-ref/nix-store/serve.md +++ b/doc/manual/src/command-ref/nix-store/serve.md @@ -14,11 +14,11 @@ access to a restricted ssh user. The following flags are available: - - `--write` +- `--write` - Allow the connected client to request the realization of - derivations. In effect, this can be used to make the host act as a - remote builder. + Allow the connected client to request the realization of + derivations. In effect, this can be used to make the host act as a + remote builder. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/verify.md b/doc/manual/src/command-ref/nix-store/verify.md index 1b1b6f529..40c9180db 100644 --- a/doc/manual/src/command-ref/nix-store/verify.md +++ b/doc/manual/src/command-ref/nix-store/verify.md @@ -16,20 +16,20 @@ being modified by non-Nix tools, or of bugs in Nix itself. This operation has the following options: - - `--check-contents` +- `--check-contents` - Checks that the contents of every valid store path has not been - altered by computing a SHA-256 hash of the contents and comparing it - with the hash stored in the Nix database at build time. Paths that - have been modified are printed out. For large stores, - `--check-contents` is obviously quite slow. + Checks that the contents of every valid store path has not been + altered by computing a SHA-256 hash of the contents and comparing it + with the hash stored in the Nix database at build time. Paths that + have been modified are printed out. For large stores, + `--check-contents` is obviously quite slow. - - `--repair` +- `--repair` - If any valid path is missing from the store, or (if - `--check-contents` is given) the contents of a valid path has been - modified, then try to repair the path by redownloading it. See - `nix-store --repair-path` for details. + If any valid path is missing from the store, or (if + `--check-contents` is given) the contents of a valid path has been + modified, then try to repair the path by redownloading it. See + `nix-store --repair-path` for details. {{#include ./opt-common.md}} From 1c131ec2b71fa7ad6fd285ed2a9fcc4cf616b3a6 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 19 Jun 2024 22:43:54 +0200 Subject: [PATCH 342/910] Port C API docs to Meson (#10936) * Port C API docs to Meson * don't cross-compile the docs --- Makefile | 11 ---- Makefile.config.in | 1 - configure.ac | 9 --- doc/external-api/local.mk | 7 -- doc/manual/src/contributing/documentation.md | 10 +-- flake.nix | 14 +++- maintainers/hydra.nix | 6 +- meson.build | 1 + package.nix | 25 +------ .../external-api-docs}/.gitignore | 0 src/external-api-docs/.version | 1 + .../external-api-docs}/README.md | 0 .../external-api-docs}/doxygen.cfg.in | 13 ++-- src/external-api-docs/meson.build | 31 +++++++++ src/external-api-docs/package.nix | 65 +++++++++++++++++++ src/internal-api-docs/package.nix | 5 ++ 16 files changed, 131 insertions(+), 68 deletions(-) delete mode 100644 doc/external-api/local.mk rename {doc/external-api => src/external-api-docs}/.gitignore (100%) create mode 120000 src/external-api-docs/.version rename {doc/external-api => src/external-api-docs}/README.md (100%) rename {doc/external-api => src/external-api-docs}/doxygen.cfg.in (91%) create mode 100644 src/external-api-docs/meson.build create mode 100644 src/external-api-docs/package.nix diff --git a/Makefile b/Makefile index 257560028..227aaf6d9 100644 --- a/Makefile +++ b/Makefile @@ -68,10 +68,6 @@ ifeq ($(ENABLE_DOC_GEN), yes) makefiles-late += doc/manual/local.mk endif -ifeq ($(ENABLE_EXTERNAL_API_DOCS), yes) -makefiles-late += doc/external-api/local.mk -endif - # Miscellaneous global Flags OPTIMIZE = 1 @@ -127,10 +123,3 @@ manual-html manpages: @echo "Generated docs are disabled. Configure without '--disable-doc-gen', or avoid calling 'make manpages' and 'make manual-html'." @exit 1 endif - -ifneq ($(ENABLE_EXTERNAL_API_DOCS), yes) -.PHONY: external-api-html -external-api-html: - @echo "External API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." - @exit 1 -endif diff --git a/Makefile.config.in b/Makefile.config.in index 56e67e5cd..3100d2073 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -11,7 +11,6 @@ EDITLINE_LIBS = @EDITLINE_LIBS@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ -ENABLE_EXTERNAL_API_DOCS = @ENABLE_EXTERNAL_API_DOCS@ ENABLE_S3 = @ENABLE_S3@ ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ diff --git a/configure.ac b/configure.ac index 2fefbe95a..2b5cd115f 100644 --- a/configure.ac +++ b/configure.ac @@ -149,11 +149,6 @@ AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build th ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) AC_SUBST(ENABLE_UNIT_TESTS) -# Build external API docs by default -AC_ARG_ENABLE(external_api_docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's C interface]), - external_api_docs=$enableval, external_api_docs=yes) -AC_SUBST(external_api_docs) - AS_IF( [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) @@ -171,10 +166,6 @@ AS_IF( [test "$ENABLE_BUILD" == "no" && test "$ENABLE_DOC_GEN" == "yes"], [AC_MSG_ERROR([Cannot enable generated docs when building overall is disabled. Please do not pass '--enable-doc-gen' or do not pass '--disable-build'.])]) -AC_ARG_ENABLE(external-api-docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's external unstable C interfaces]), - ENABLE_EXTERNAL_API_DOCS=$enableval, ENABLE_EXTERNAL_API_DOCS=no) -AC_SUBST(ENABLE_EXTERNAL_API_DOCS) - AS_IF( [test "$ENABLE_FUNCTIONAL_TESTS" == "yes" || test "$ENABLE_DOC_GEN" == "yes"], [NEED_PROG(jq, jq)]) diff --git a/doc/external-api/local.mk b/doc/external-api/local.mk deleted file mode 100644 index ae2b44db8..000000000 --- a/doc/external-api/local.mk +++ /dev/null @@ -1,7 +0,0 @@ -$(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg src/lib*-c/*.h - mkdir -p $(docdir)/external-api - { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/external-api" ; } | doxygen - - -# Generate the HTML API docs for Nix's unstable C bindings -.PHONY: external-api-html -external-api-html: $(docdir)/external-api/html/index.html diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index a5e2bfa83..a14ecedd6 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -204,7 +204,6 @@ or inside `nix-shell` or `nix develop`: ```console $ mesonConfigurePhase -$ cd build $ ninja src/internal-api-docs/html $ xdg-open src/internal-api-docs/html/index.html ``` @@ -218,13 +217,14 @@ You can also build and view it yourself: [C API documentation]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs ```console -# nix build .#hydraJobs.external-api-docs -# xdg-open ./result/share/doc/nix/external-api/html/index.html +$ nix build .#hydraJobs.external-api-docs +$ xdg-open ./result/share/doc/nix/external-api/html/index.html ``` or inside `nix-shell` or `nix develop`: ``` -# make external-api-html -# xdg-open ./outputs/doc/share/doc/nix/external-api/html/index.html +$ mesonConfigurePhase +$ ninja src/external-api-docs/html +$ xdg-open src/external-api-docs/html/index.html ``` diff --git a/flake.nix b/flake.nix index 5c8d84a75..4e7363cd6 100644 --- a/flake.nix +++ b/flake.nix @@ -211,7 +211,6 @@ ; }; - nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { inherit fileset @@ -220,6 +219,14 @@ ; }; + nix-external-api-docs = final.callPackage ./src/external-api-docs/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + # See https://github.com/NixOS/nixpkgs/pull/214409 # Remove when fixed in this flake's nixpkgs pre-commit = @@ -275,6 +282,8 @@ inherit (nixpkgsFor.${system}.native) changelog-d; default = self.packages.${system}.nix; + nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; + nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; } // lib.concatMapAttrs # We need to flatten recursive attribute sets of derivations to pass `flake check`. (pkgName: {}: { @@ -298,7 +307,6 @@ #"nix-util" = { }; #"nix-store" = { }; #"nix-fetchers" = { }; - "nix-internal-api-docs" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = @@ -370,6 +378,8 @@ ++ pkgs.nix-store.nativeBuildInputs ++ pkgs.nix-fetchers.nativeBuildInputs ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs + ++ pkgs.nix-internal-api-docs.nativeBuildInputs + ++ pkgs.nix-external-api-docs.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 293dee5cd..abd44efef 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -128,11 +128,7 @@ in internal-api-docs = nixpkgsFor.x86_64-linux.native.nix-internal-api-docs; # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableExternalAPIDocs = true; - }; + external-api-docs = nixpkgsFor.x86_64-linux.native.nix-external-api-docs; # System tests. tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor self; } // { diff --git a/meson.build b/meson.build index ebad238b1..e67fd4a7a 100644 --- a/meson.build +++ b/meson.build @@ -11,3 +11,4 @@ subproject('libstore') subproject('libfetchers') subproject('perl') subproject('internal-api-docs') +subproject('external-api-docs') diff --git a/package.nix b/package.nix index 64096c7aa..f414b9a73 100644 --- a/package.nix +++ b/package.nix @@ -20,7 +20,6 @@ , git , gtest , jq -, doxygen , libarchive , libcpuid , libgit2 @@ -53,8 +52,7 @@ , versionSuffix ? "" , officialRelease ? false -# Whether to build Nix. Useful to skip for tasks like (a) just -# generating API docs or (b) testing existing pre-built versions of Nix +# Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true # Run the unit tests as part of the build. See `installUnitTests` for an @@ -93,10 +91,6 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to build the external API docs, can be done separately from -# everything else. -, enableExternalAPIDocs ? forDevShell - # Whether to install unit tests. This is useful when cross compiling # since we cannot run them natively during the build, but can do so # later. @@ -184,13 +178,6 @@ in { ./scripts/local.mk ] ++ lib.optionals buildUnitTests [ ./doc/manual - ] ++ lib.optionals enableExternalAPIDocs [ - ./doc/external-api - ] ++ lib.optionals enableExternalAPIDocs [ - # Source might not be compiled, but still must be available - # for Doxygen to gather comments. - (fileset.difference ./src ./src/perl) - ./tests/unit ] ++ lib.optionals buildUnitTests [ ./tests/unit ] ++ lib.optionals doInstallCheck [ @@ -204,7 +191,7 @@ in { ++ lib.optional doBuild "dev" # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. - ++ lib.optional (doBuild && (enableManual || enableExternalAPIDocs)) "doc" + ++ lib.optional (doBuild && enableManual) "doc" ++ lib.optional installUnitTests "check" ++ lib.optional doCheck "testresults" ; @@ -228,7 +215,6 @@ in { ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux - ++ lib.optional enableExternalAPIDocs doxygen ; buildInputs = lib.optionals doBuild [ @@ -291,7 +277,6 @@ in { (lib.enableFeature doBuild "build") (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") - (lib.enableFeature enableExternalAPIDocs "external-api-docs") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") @@ -319,8 +304,7 @@ in { mkdir $testresults ''; - installTargets = lib.optional doBuild "install" - ++ lib.optional enableExternalAPIDocs "external-api-html"; + installTargets = lib.optional doBuild "install"; installFlags = "sysconfdir=$(out)/etc"; @@ -344,9 +328,6 @@ in { ) + lib.optionalString enableManual '' mkdir -p ''${!outputDoc}/nix-support echo "doc manual ''${!outputDoc}/share/doc/nix/manual" >> ''${!outputDoc}/nix-support/hydra-build-products - '' + lib.optionalString enableExternalAPIDocs '' - mkdir -p ''${!outputDoc}/nix-support - echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; # So the check output gets links for DLLs in the out output. diff --git a/doc/external-api/.gitignore b/src/external-api-docs/.gitignore similarity index 100% rename from doc/external-api/.gitignore rename to src/external-api-docs/.gitignore diff --git a/src/external-api-docs/.version b/src/external-api-docs/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/external-api-docs/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/doc/external-api/README.md b/src/external-api-docs/README.md similarity index 100% rename from doc/external-api/README.md rename to src/external-api-docs/README.md diff --git a/doc/external-api/doxygen.cfg.in b/src/external-api-docs/doxygen.cfg.in similarity index 91% rename from doc/external-api/doxygen.cfg.in rename to src/external-api-docs/doxygen.cfg.in index cd8b4989b..1be71d895 100644 --- a/doc/external-api/doxygen.cfg.in +++ b/src/external-api-docs/doxygen.cfg.in @@ -12,7 +12,9 @@ PROJECT_NAME = "Nix" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = @PACKAGE_VERSION@ +PROJECT_NUMBER = @PROJECT_NUMBER@ + +OUTPUT_DIRECTORY = @OUTPUT_DIRECTORY@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -36,10 +38,10 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - src/libutil-c \ - src/libexpr-c \ - src/libstore-c \ - doc/external-api/README.md + @src@/src/libutil-c \ + @src@/src/libexpr-c \ + @src@/src/libstore-c \ + @src@/doc/external-api/README.md FILE_PATTERNS = nix_api_*.h *.md @@ -49,7 +51,6 @@ FILE_PATTERNS = nix_api_*.h *.md # RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = @RAPIDCHECK_HEADERS@ EXCLUDE_PATTERNS = *_internal.h GENERATE_TREEVIEW = YES OPTIMIZE_OUTPUT_FOR_C = YES diff --git a/src/external-api-docs/meson.build b/src/external-api-docs/meson.build new file mode 100644 index 000000000..62474ffe4 --- /dev/null +++ b/src/external-api-docs/meson.build @@ -0,0 +1,31 @@ +project('nix-external-api-docs', + version : files('.version'), + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +fs = import('fs') + +doxygen_cfg = configure_file( + input : 'doxygen.cfg.in', + output : 'doxygen.cfg', + configuration : { + 'PROJECT_NUMBER': meson.project_version(), + 'OUTPUT_DIRECTORY' : meson.current_build_dir(), + 'src' : fs.parent(fs.parent(meson.project_source_root())), + }, +) + +doxygen = find_program('doxygen', native : true, required : true) + +custom_target( + 'external-api-docs', + command : [ doxygen , doxygen_cfg ], + input : [ + doxygen_cfg, + ], + output : 'html', + install : true, + install_dir : get_option('datadir') / 'doc/nix/external-api', + build_always_stale : true, +) diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix new file mode 100644 index 000000000..aa5cd49eb --- /dev/null +++ b/src/external-api-docs/package.nix @@ -0,0 +1,65 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, doxygen + +# Configuration Options + +, versionSuffix ? "" +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "nix-external-api-docs"; + version = lib.fileContents ./.version + versionSuffix; + + src = fileset.toSource { + root = ../..; + fileset = + let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "h"); + in + fileset.unions [ + ./meson.build + ./doxygen.cfg.in + ./README.md + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../libexpr-c) + (cpp ../libstore-c) + (cpp ../libutil-c) + ]; + }; + + nativeBuildInputs = [ + meson + ninja + doxygen + ]; + + postUnpack = '' + sourceRoot=$sourceRoot/src/external-api-docs + ''; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${finalAttrs.version} > .version + ''; + + postInstall = '' + mkdir -p ''${!outputDoc}/nix-support + echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products + ''; + + enableParallelBuilding = true; + + strictDeps = true; + + meta = { + platforms = lib.platforms.all; + }; +}) diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index bb20a68d3..b5f1b0da1 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -46,6 +46,11 @@ stdenv.mkDerivation (finalAttrs: { echo ${finalAttrs.version} > .version ''; + postInstall = '' + mkdir -p ''${!outputDoc}/nix-support + echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products + ''; + enableParallelBuilding = true; strictDeps = true; From dc720f89f2689ddfb4c1e0d0ea0d442b6be6c006 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 12:22:42 +0200 Subject: [PATCH 343/910] flake.nix: Factor pkgs.nix_noTests out of buildNoTests This is useful when iterating on the functional tests when trying to run them in a VM test, for example. --- flake.nix | 6 ++++++ maintainers/hydra.nix | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 4e7363cd6..7e7148afe 100644 --- a/flake.nix +++ b/flake.nix @@ -227,6 +227,12 @@ ; }; + nix_noTests = final.nix.override { + doCheck = false; + doInstallCheck = false; + installUnitTests = false; + }; + # See https://github.com/NixOS/nixpkgs/pull/214409 # Remove when fixed in this flake's nixpkgs pre-commit = diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index abd44efef..e21145f37 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -58,13 +58,7 @@ in self.packages.${system}.nix.override { enableGC = false; } ); - buildNoTests = forAllSystems (system: - self.packages.${system}.nix.override { - doCheck = false; - doInstallCheck = false; - installUnitTests = false; - } - ); + buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nix_noTests); # Toggles some settings for better coverage. Windows needs these # library combinations, and Debian build Nix with GNU readline too. From 439022c5acfce4284c902bd6ac59575902c2c15c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 12:13:07 +0200 Subject: [PATCH 344/910] tests: Add hydraJobs.tests.functional_* --- .../build-remote-input-addressed.sh | 2 +- tests/functional/common/init.sh | 27 +++++++ tests/functional/common/subst-vars.sh.in | 7 +- tests/functional/common/vars-and-functions.sh | 81 ++++++++++++++----- tests/functional/impure-env.sh | 4 +- .../functional/local-overlay-store/common.sh | 2 +- tests/functional/post-hook.sh | 2 +- tests/nixos/default.nix | 7 ++ tests/nixos/functional/as-root.nix | 12 +++ tests/nixos/functional/as-trusted-user.nix | 18 +++++ tests/nixos/functional/as-user.nix | 16 ++++ tests/nixos/functional/common.nix | 76 +++++++++++++++++ tests/nixos/functional/quick-build.nix | 47 +++++++++++ 13 files changed, 273 insertions(+), 28 deletions(-) create mode 100644 tests/nixos/functional/as-root.nix create mode 100644 tests/nixos/functional/as-trusted-user.nix create mode 100644 tests/nixos/functional/as-user.nix create mode 100644 tests/nixos/functional/common.nix create mode 100644 tests/nixos/functional/quick-build.nix diff --git a/tests/functional/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh index 986692dbc..11199a408 100755 --- a/tests/functional/build-remote-input-addressed.sh +++ b/tests/functional/build-remote-input-addressed.sh @@ -23,7 +23,7 @@ EOF chmod +x "$TEST_ROOT/post-build-hook.sh" rm -f "$TEST_ROOT/post-hook-counter" - echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> "$NIX_CONF_DIR/nix.conf" + echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> "$test_nix_conf" } registerBuildHook diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 4f2a393af..19cef5af9 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -1,5 +1,30 @@ # shellcheck shell=bash +# for shellcheck +: "${test_nix_conf_dir?}" "${test_nix_conf?}" + +if isTestOnNixOS; then + + mkdir -p "$test_nix_conf_dir" "$TEST_HOME" + + export NIX_USER_CONF_FILES="$test_nix_conf_dir/nix.conf" + mkdir -p "$test_nix_conf_dir" "$TEST_HOME" + ! test -e "$test_nix_conf" + cat > "$test_nix_conf_dir/nix.conf" <&2 + exit 1 +} + set +x commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" @@ -15,27 +24,35 @@ source "$commonDir/subst-vars.sh" : "${PATH?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} -export NIX_STORE_DIR -if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then - # Maybe the build directory is symlinked. - export NIX_IGNORE_SYMLINK_STORE=1 - NIX_STORE_DIR=$TEST_ROOT/store -fi -export NIX_LOCALSTATE_DIR=$TEST_ROOT/var -export NIX_LOG_DIR=$TEST_ROOT/var/log/nix -export NIX_STATE_DIR=$TEST_ROOT/var/nix -export NIX_CONF_DIR=$TEST_ROOT/etc -export NIX_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket -unset NIX_USER_CONF_FILES -export _NIX_TEST_SHARED=$TEST_ROOT/shared -if [[ -n $NIX_STORE ]]; then - export _NIX_TEST_NO_SANDBOX=1 -fi -export _NIX_IN_TEST=$TEST_ROOT/shared -export _NIX_TEST_NO_LSOF=1 -export NIX_REMOTE=${NIX_REMOTE_-} -unset NIX_PATH +test_nix_conf_dir=$TEST_ROOT/etc +test_nix_conf=$test_nix_conf_dir/nix.conf + export TEST_HOME=$TEST_ROOT/test-home + +if ! isTestOnNixOS; then + export NIX_STORE_DIR + if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then + # Maybe the build directory is symlinked. + export NIX_IGNORE_SYMLINK_STORE=1 + NIX_STORE_DIR=$TEST_ROOT/store + fi + export NIX_LOCALSTATE_DIR=$TEST_ROOT/var + export NIX_LOG_DIR=$TEST_ROOT/var/log/nix + export NIX_STATE_DIR=$TEST_ROOT/var/nix + export NIX_CONF_DIR=$test_nix_conf_dir + export NIX_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket + unset NIX_USER_CONF_FILES + export _NIX_TEST_SHARED=$TEST_ROOT/shared + if [[ -n $NIX_STORE ]]; then + export _NIX_TEST_NO_SANDBOX=1 + fi + export _NIX_IN_TEST=$TEST_ROOT/shared + export _NIX_TEST_NO_LSOF=1 + export NIX_REMOTE=${NIX_REMOTE_-} + +fi # ! isTestOnNixOS + +unset NIX_PATH export HOME=$TEST_HOME unset XDG_STATE_HOME unset XDG_DATA_HOME @@ -66,6 +83,10 @@ clearProfiles() { } clearStore() { + if isTestOnNixOS; then + die "clearStore: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + echo "clearing store..." chmod -R +w "$NIX_STORE_DIR" rm -rf "$NIX_STORE_DIR" @@ -84,6 +105,10 @@ clearCacheCache() { } startDaemon() { + if isTestOnNixOS; then + die "startDaemon: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + # Don’t start the daemon twice, as this would just make it loop indefinitely if [[ "${_NIX_TEST_DAEMON_PID-}" != '' ]]; then return @@ -110,6 +135,10 @@ startDaemon() { } killDaemon() { + if isTestOnNixOS; then + die "killDaemon: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + # Don’t fail trying to stop a non-existant daemon twice if [[ "${_NIX_TEST_DAEMON_PID-}" == '' ]]; then return @@ -130,6 +159,10 @@ killDaemon() { } restartDaemon() { + if isTestOnNixOS; then + die "restartDaemon: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + [[ -z "${_NIX_TEST_DAEMON_PID:-}" ]] && return 0 killDaemon @@ -152,6 +185,12 @@ skipTest () { exit 99 } +TODO_NixOS() { + if isTestOnNixOS; then + skipTest "This test has not been adapted for NixOS yet" + fi +} + requireDaemonNewerThan () { isDaemonNewer "$1" || skipTest "Daemon is too old" } @@ -234,7 +273,7 @@ buggyNeedLocalStore() { enableFeatures() { local features="$1" - sed -i 's/experimental-features .*/& '"$features"'/' "$NIX_CONF_DIR"/nix.conf + sed -i 's/experimental-features .*/& '"$features"'/' "$test_nix_conf_dir"/nix.conf } set -x diff --git a/tests/functional/impure-env.sh b/tests/functional/impure-env.sh index 3c7df169e..e65c78b00 100755 --- a/tests/functional/impure-env.sh +++ b/tests/functional/impure-env.sh @@ -20,13 +20,13 @@ startDaemon varTest env_name value --impure-env env_name=value -echo 'impure-env = set_in_config=config_value' >> "$NIX_CONF_DIR/nix.conf" +echo 'impure-env = set_in_config=config_value' >> "$test_nix_conf" set_in_config=daemon_value restartDaemon varTest set_in_config config_value varTest set_in_config client_value --impure-env set_in_config=client_value -sed -i -e '/^trusted-users =/d' "$NIX_CONF_DIR/nix.conf" +sed -i -e '/^trusted-users =/d' "$test_nix_conf" env_name=daemon_value restartDaemon diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 0e6097861..bbe84847a 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -31,7 +31,7 @@ requireEnvironment () { } addConfig () { - echo "$1" >> "$NIX_CONF_DIR/nix.conf" + echo "$1" >> "$test_nix_conf" } setupConfig () { diff --git a/tests/functional/post-hook.sh b/tests/functional/post-hook.sh index c0b1ab3aa..7540d1045 100755 --- a/tests/functional/post-hook.sh +++ b/tests/functional/post-hook.sh @@ -7,7 +7,7 @@ clearStore rm -f $TEST_ROOT/result export REMOTE_STORE=file:$TEST_ROOT/remote_store -echo 'require-sigs = false' >> $NIX_CONF_DIR/nix.conf +echo 'require-sigs = false' >> $test_nix_conf restartDaemon diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 710f8a273..2ab00b336 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -132,4 +132,11 @@ in ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak; gzip-content-encoding = runNixOSTestFor "x86_64-linux" ./gzip-content-encoding.nix; + + functional_user = runNixOSTestFor "x86_64-linux" ./functional/as-user.nix; + + functional_trusted = runNixOSTestFor "x86_64-linux" ./functional/as-trusted-user.nix; + + functional_root = runNixOSTestFor "x86_64-linux" ./functional/as-root.nix; + } diff --git a/tests/nixos/functional/as-root.nix b/tests/nixos/functional/as-root.nix new file mode 100644 index 000000000..96be3d593 --- /dev/null +++ b/tests/nixos/functional/as-root.nix @@ -0,0 +1,12 @@ +{ + name = "functional-tests-on-nixos_root"; + + imports = [ ./common.nix ]; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed(""" + run-test-suite >&2 + """) + ''; +} diff --git a/tests/nixos/functional/as-trusted-user.nix b/tests/nixos/functional/as-trusted-user.nix new file mode 100644 index 000000000..d6f825697 --- /dev/null +++ b/tests/nixos/functional/as-trusted-user.nix @@ -0,0 +1,18 @@ +{ + name = "functional-tests-on-nixos_trusted-user"; + + imports = [ ./common.nix ]; + + nodes.machine = { + users.users.alice = { isNormalUser = true; }; + nix.settings.trusted-users = [ "alice" ]; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed(""" + export TEST_TRUSTED_USER=1 + su --login --command "run-test-suite" alice >&2 + """) + ''; +} \ No newline at end of file diff --git a/tests/nixos/functional/as-user.nix b/tests/nixos/functional/as-user.nix new file mode 100644 index 000000000..1443f6e6c --- /dev/null +++ b/tests/nixos/functional/as-user.nix @@ -0,0 +1,16 @@ +{ + name = "functional-tests-on-nixos_user"; + + imports = [ ./common.nix ]; + + nodes.machine = { + users.users.alice = { isNormalUser = true; }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed(""" + su --login --command "run-test-suite" alice >&2 + """) + ''; +} diff --git a/tests/nixos/functional/common.nix b/tests/nixos/functional/common.nix new file mode 100644 index 000000000..493791a7b --- /dev/null +++ b/tests/nixos/functional/common.nix @@ -0,0 +1,76 @@ +{ lib, ... }: + +let + # FIXME (roberth) reference issue + inputDerivation = pkg: (pkg.overrideAttrs (o: { + disallowedReferences = [ ]; + })).inputDerivation; + +in +{ + imports = [ + # Add the quickBuild attribute to the check package + ./quick-build.nix + ]; + + # We rarely change the script in a way that benefits from type checking, so + # we skip it to save time. + skipTypeCheck = true; + + nodes.machine = { config, pkgs, ... }: { + + virtualisation.writableStore = true; + system.extraDependencies = [ + (inputDerivation config.nix.package) + ]; + + nix.settings.substituters = lib.mkForce []; + + environment.systemPackages = let + run-test-suite = pkgs.writeShellApplication { + name = "run-test-suite"; + runtimeInputs = [ pkgs.gnumake pkgs.jq pkgs.git ]; + text = '' + set -x + cat /proc/sys/fs/file-max + ulimit -Hn + ulimit -Sn + cd ~ + cp -r ${pkgs.nix.overrideAttrs (o: { + name = "nix-configured-source"; + outputs = [ "out" ]; + separateDebugInfo = false; + disallowedReferences = [ ]; + buildPhase = ":"; + checkPhase = ":"; + installPhase = '' + cp -r . $out + ''; + installCheckPhase = ":"; + fixupPhase = ":"; + doInstallCheck = true; + })} nix + chmod -R +w nix + cd nix + + # Tests we don't need + echo >tests/functional/plugins/local.mk + sed -i tests/functional/local.mk \ + -e 's!nix_tests += plugins\.sh!!' \ + -e 's!nix_tests += test-libstoreconsumer\.sh!!' \ + ; + + export isTestOnNixOS=1 + export version=${config.nix.package.version} + export NIX_REMOTE_=daemon + export NIX_REMOTE=daemon + export NIX_STORE=${builtins.storeDir} + make -j1 installcheck --keep-going + ''; + }; + in [ + run-test-suite + pkgs.git + ]; + }; +} diff --git a/tests/nixos/functional/quick-build.nix b/tests/nixos/functional/quick-build.nix new file mode 100644 index 000000000..10f4215e5 --- /dev/null +++ b/tests/nixos/functional/quick-build.nix @@ -0,0 +1,47 @@ +test@{ lib, extendModules, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + quickBuild = mkOption { + description = '' + Whether to perform a "quick" build of the Nix package to test. + + When iterating on the functional tests, it's recommended to "set" this + to `true`, so that changes to the functional tests don't require any + recompilation of the package. + You can do so by buildin the `.quickBuild` attribute on the check package, + e.g: + ```console + nix build .#hydraJobs.functional_user.quickBuild + ``` + + We don't enable this by default to avoid the mostly unnecessary work of + performing an additional build of the package in cases where we build + the package normally anyway, such as in our pre-merge CI. + ''; + type = types.bool; + default = false; + }; + }; + + config = { + passthru.quickBuild = + let withQuickBuild = extendModules { modules = [{ quickBuild = true; }]; }; + in withQuickBuild.config.test; + + defaults = { pkgs, ... }: { + config = lib.mkIf test.config.quickBuild { + nix.package = pkgs.nix_noTests; + + system.forbiddenDependenciesRegexes = [ + # This would indicate that the quickBuild feature is broken. + # It could happen if NixOS has a dependency on pkgs.nix instead of + # config.nix.package somewhere. + (builtins.unsafeDiscardStringContext pkgs.nix.outPath) + ]; + }; + }; + }; +} \ No newline at end of file From 211aec473e8dfe9782c34b40a72db8ae6e88df99 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 13:02:04 +0200 Subject: [PATCH 345/910] tests/functional/timeout.sh: Find missing test case This reproduces an instance of https://github.com/NixOS/nix/issues/4813 --- tests/functional/timeout.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/timeout.sh b/tests/functional/timeout.sh index 441c83b0e..f42354538 100755 --- a/tests/functional/timeout.sh +++ b/tests/functional/timeout.sh @@ -11,6 +11,10 @@ messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) && status= if [ $status -ne 101 ]; then echo "error: 'nix-store' exited with '$status'; should have exited 101" + + # FIXME: https://github.com/NixOS/nix/issues/4813 + skipTest "Do not block CI until fixed" + exit 1 fi From 8557d79650e8097a73a809583405a94ebaf0a0db Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 12:51:46 +0200 Subject: [PATCH 346/910] tests/functional: Skip tests that don't work in NixOS environment yet --- tests/functional/add.sh | 2 ++ tests/functional/binary-cache-build-remote.sh | 2 ++ tests/functional/binary-cache.sh | 2 ++ tests/functional/brotli.sh | 2 ++ tests/functional/build-delete.sh | 2 ++ tests/functional/build-dry.sh | 2 ++ tests/functional/build-remote-trustless-should-fail-0.sh | 1 + tests/functional/build-remote-trustless-should-pass-2.sh | 2 ++ tests/functional/build-remote-trustless-should-pass-3.sh | 1 + tests/functional/build.sh | 2 ++ tests/functional/ca/common.sh | 2 ++ tests/functional/case-hack.sh | 2 ++ tests/functional/check-refs.sh | 2 ++ tests/functional/check-reqs.sh | 2 ++ tests/functional/check.sh | 4 ++++ tests/functional/chroot-store.sh | 2 ++ tests/functional/compression-levels.sh | 2 ++ tests/functional/config.sh | 2 ++ tests/functional/db-migration.sh | 2 ++ tests/functional/debugger.sh | 2 ++ tests/functional/dependencies.sh | 2 ++ tests/functional/dump-db.sh | 2 ++ tests/functional/dyn-drv/common.sh | 2 ++ tests/functional/eval-store.sh | 2 ++ tests/functional/eval.sh | 2 ++ tests/functional/export-graph.sh | 2 ++ tests/functional/export.sh | 2 ++ tests/functional/fetchClosure.sh | 2 ++ tests/functional/fetchGit.sh | 2 ++ tests/functional/fetchGitRefs.sh | 2 ++ tests/functional/fetchGitSubmodules.sh | 2 ++ tests/functional/fetchGitVerification.sh | 2 ++ tests/functional/fetchMercurial.sh | 2 ++ tests/functional/fetchTree-file.sh | 2 ++ tests/functional/fetchurl.sh | 2 ++ tests/functional/fixed.sh | 2 ++ tests/functional/flakes/config.sh | 1 + tests/functional/flakes/develop.sh | 2 ++ tests/functional/flakes/flake-in-submodule.sh | 2 ++ tests/functional/flakes/flakes.sh | 2 ++ tests/functional/flakes/run.sh | 2 ++ tests/functional/flakes/search-root.sh | 2 ++ tests/functional/fmt.sh | 2 ++ tests/functional/gc-auto.sh | 2 ++ tests/functional/gc-concurrent.sh | 2 ++ tests/functional/gc-non-blocking.sh | 2 ++ tests/functional/gc-runtime.sh | 2 ++ tests/functional/gc.sh | 2 ++ tests/functional/git-hashing/common.sh | 2 ++ tests/functional/help.sh | 2 ++ tests/functional/import-derivation.sh | 2 ++ tests/functional/impure-derivations.sh | 2 ++ tests/functional/impure-env.sh | 2 ++ tests/functional/linux-sandbox.sh | 2 ++ tests/functional/local-overlay-store/bad-uris.sh | 2 ++ tests/functional/local-overlay-store/common.sh | 2 ++ tests/functional/logging.sh | 2 ++ tests/functional/multiple-outputs.sh | 2 ++ tests/functional/nar-access.sh | 2 ++ tests/functional/nested-sandboxing.sh | 2 ++ tests/functional/nix-build.sh | 2 ++ tests/functional/nix-collect-garbage-d.sh | 2 ++ tests/functional/nix-copy-ssh-common.sh | 2 ++ tests/functional/nix-copy-ssh-ng.sh | 2 ++ tests/functional/nix-profile.sh | 2 ++ tests/functional/nix-shell.sh | 2 ++ tests/functional/optimise-store.sh | 2 ++ tests/functional/output-normalization.sh | 1 + tests/functional/parallel.sh | 2 ++ tests/functional/pass-as-file.sh | 2 ++ tests/functional/placeholders.sh | 2 ++ tests/functional/post-hook.sh | 2 ++ tests/functional/pure-eval.sh | 2 ++ tests/functional/read-only-store.sh | 2 ++ tests/functional/readfile-context.sh | 2 ++ tests/functional/recursive.sh | 2 ++ tests/functional/referrers.sh | 2 ++ tests/functional/remote-store.sh | 2 ++ tests/functional/repair.sh | 2 ++ tests/functional/repl.sh | 2 ++ tests/functional/restricted.sh | 2 ++ tests/functional/search.sh | 2 ++ tests/functional/secure-drv-outputs.sh | 2 ++ tests/functional/selfref-gc.sh | 2 ++ tests/functional/shell.sh | 2 ++ tests/functional/signing.sh | 2 ++ tests/functional/simple.sh | 2 ++ tests/functional/store-info.sh | 2 ++ tests/functional/structured-attrs.sh | 2 ++ tests/functional/suggestions.sh | 2 ++ tests/functional/supplementary-groups.sh | 2 ++ tests/functional/tarball.sh | 2 ++ tests/functional/user-envs-migration.sh | 2 ++ tests/functional/user-envs-test-case.sh | 2 ++ tests/functional/why-depends.sh | 2 ++ tests/functional/zstd.sh | 2 ++ 96 files changed, 190 insertions(+) diff --git a/tests/functional/add.sh b/tests/functional/add.sh index a6cf88e1a..328b14831 100755 --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -31,6 +31,8 @@ test "$hash1" = "sha256:$hash2" #### New style commands +TODO_NixOS + clearStore ( diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh index 4edda85b6..17e8bdcc1 100755 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCacheCache diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh index 5ef6d89d4..6a177b657 100755 --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + needLocalStore "'--no-require-sigs' can’t be used with the daemon" # We can produce drvs directly into the binary cache diff --git a/tests/functional/brotli.sh b/tests/functional/brotli.sh index 672e771c2..327eab4a5 100755 --- a/tests/functional/brotli.sh +++ b/tests/functional/brotli.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh index 59cf95bd2..2ebf1fd3d 100755 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # https://github.com/NixOS/nix/issues/6572 diff --git a/tests/functional/build-dry.sh b/tests/functional/build-dry.sh index dca5888a6..cff0f9a49 100755 --- a/tests/functional/build-dry.sh +++ b/tests/functional/build-dry.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + ################################################### # Check that --dry-run isn't confused with read-only mode # https://github.com/NixOS/nix/issues/1795 diff --git a/tests/functional/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh index 269f7f112..4eccb73e0 100755 --- a/tests/functional/build-remote-trustless-should-fail-0.sh +++ b/tests/functional/build-remote-trustless-should-fail-0.sh @@ -4,6 +4,7 @@ source common.sh enableFeatures "daemon-trust-override" +TODO_NixOS restartDaemon requireSandboxSupport diff --git a/tests/functional/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh index ba5d1ff7a..34ce7fbe4 100755 --- a/tests/functional/build-remote-trustless-should-pass-2.sh +++ b/tests/functional/build-remote-trustless-should-pass-2.sh @@ -4,6 +4,8 @@ source common.sh enableFeatures "daemon-trust-override" +TODO_NixOS + restartDaemon # Remote doesn't trust us diff --git a/tests/functional/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh index 187b89948..d01d79191 100755 --- a/tests/functional/build-remote-trustless-should-pass-3.sh +++ b/tests/functional/build-remote-trustless-should-pass-3.sh @@ -4,6 +4,7 @@ source common.sh enableFeatures "daemon-trust-override" +TODO_NixOS restartDaemon # Remote doesn't trusts us, but this is fine because we are only diff --git a/tests/functional/build.sh b/tests/functional/build.sh index a14e6d672..db759b6e9 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # Make sure that 'nix build' returns all outputs by default. diff --git a/tests/functional/ca/common.sh b/tests/functional/ca/common.sh index b104b5a78..48f1ac46b 100644 --- a/tests/functional/ca/common.sh +++ b/tests/functional/ca/common.sh @@ -2,4 +2,6 @@ source ../common.sh enableFeatures "ca-derivations" +TODO_NixOS + restartDaemon diff --git a/tests/functional/case-hack.sh b/tests/functional/case-hack.sh index 48a2ab13f..feddc6583 100755 --- a/tests/functional/case-hack.sh +++ b/tests/functional/case-hack.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore rm -rf "$TEST_ROOT/case" diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh index 6534e55c6..26790c11b 100755 --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore RESULT=$TEST_ROOT/result diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh index 4d795391e..bb91bacde 100755 --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore RESULT=$TEST_ROOT/result diff --git a/tests/functional/check.sh b/tests/functional/check.sh index efb93eeb0..8f602b487 100755 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -15,6 +15,8 @@ checkBuildTempDirRemoved () # written to build temp directories to verify created by this instance checkBuildId=$(date +%s%N) +TODO_NixOS + clearStore nix-build dependencies.nix --no-out-link @@ -76,6 +78,8 @@ grep 'may not be deterministic' $TEST_ROOT/log [ "$status" = "104" ] if checkBuildTempDirRemoved $TEST_ROOT/log; then false; fi +TODO_NixOS + clearStore path=$(nix-build check.nix -A fetchurl --no-out-link) diff --git a/tests/functional/chroot-store.sh b/tests/functional/chroot-store.sh index 741907fca..03803a2b9 100755 --- a/tests/functional/chroot-store.sh +++ b/tests/functional/chroot-store.sh @@ -39,6 +39,8 @@ EOF cp simple.nix shell.nix simple.builder.sh config.nix "$flakeDir/" + TODO_NixOS + outPath=$(nix build --print-out-paths --no-link --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' --store "$TEST_ROOT/x" path:"$flakeDir") [[ $outPath =~ ^/nix2/store/.*-simple$ ]] diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh index 6a2111f10..f51c3f509 100755 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/config.sh b/tests/functional/config.sh index 1811b755c..50858eaa4 100755 --- a/tests/functional/config.sh +++ b/tests/functional/config.sh @@ -28,6 +28,8 @@ nix registry remove userhome-with-xdg # Assert the .config folder hasn't been created. [ ! -e "$HOME/.config" ] +TODO_NixOS # Very specific test setup not compatible with the NixOS test environment? + # Test that files are loaded from XDG by default export XDG_CONFIG_HOME=$TEST_ROOT/confighome export XDG_CONFIG_DIRS=$TEST_ROOT/dir1:$TEST_ROOT/dir2 diff --git a/tests/functional/db-migration.sh b/tests/functional/db-migration.sh index a6a5c7744..6feabb90d 100755 --- a/tests/functional/db-migration.sh +++ b/tests/functional/db-migration.sh @@ -10,6 +10,8 @@ if [[ -z "${NIX_DAEMON_PACKAGE-}" ]]; then skipTest "not using the Nix daemon" fi +TODO_NixOS + killDaemon # Fill the db using the older Nix diff --git a/tests/functional/debugger.sh b/tests/functional/debugger.sh index 47e644bb7..178822d79 100755 --- a/tests/functional/debugger.sh +++ b/tests/functional/debugger.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # regression #9932 diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh index 1b266935d..bb01b3988 100755 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore drvPath=$(nix-instantiate dependencies.nix) diff --git a/tests/functional/dump-db.sh b/tests/functional/dump-db.sh index 2d0460275..14181b4b6 100755 --- a/tests/functional/dump-db.sh +++ b/tests/functional/dump-db.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + needLocalStore "--dump-db requires a local store" clearStore diff --git a/tests/functional/dyn-drv/common.sh b/tests/functional/dyn-drv/common.sh index c786f6925..0d95881b6 100644 --- a/tests/functional/dyn-drv/common.sh +++ b/tests/functional/dyn-drv/common.sh @@ -5,4 +5,6 @@ requireDaemonNewerThan "2.16.0pre20230419" enableFeatures "ca-derivations dynamic-derivations" +TODO_NixOS + restartDaemon diff --git a/tests/functional/eval-store.sh b/tests/functional/eval-store.sh index 0ab608acc..202e7b004 100755 --- a/tests/functional/eval-store.sh +++ b/tests/functional/eval-store.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + # Using `--eval-store` with the daemon will eventually copy everything # to the build store, invalidating most of the tests here needLocalStore "“--eval-store” doesn't achieve much with the daemon" diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index acd6e2915..ba335d73a 100755 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore testStdinHeredoc=$(nix eval -f - < baz.cat-nar diff -u baz.cat-nar $storePath/foo/baz +TODO_NixOS + # Check that 'nix store cat' fails on invalid store paths. invalidPath="$(dirname $storePath)/99999999999999999999999999999999-foo" cp -r $storePath $invalidPath diff --git a/tests/functional/nested-sandboxing.sh b/tests/functional/nested-sandboxing.sh index 44c3bb2bc..ae0256de2 100755 --- a/tests/functional/nested-sandboxing.sh +++ b/tests/functional/nested-sandboxing.sh @@ -4,6 +4,8 @@ source common.sh # This test is run by `tests/functional/nested-sandboxing/runner.nix` in an extra layer of sandboxing. [[ -d /nix/store ]] || skipTest "running this test without Nix's deps being drawn from /nix/store is not yet supported" +TODO_NixOS + requireSandboxSupport source ./nested-sandboxing/command.sh diff --git a/tests/functional/nix-build.sh b/tests/functional/nix-build.sh index 45ff314c7..cfba7f020 100755 --- a/tests/functional/nix-build.sh +++ b/tests/functional/nix-build.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath=$(nix-build dependencies.nix -o $TEST_ROOT/result) diff --git a/tests/functional/nix-collect-garbage-d.sh b/tests/functional/nix-collect-garbage-d.sh index 07aaf61e9..119efe629 100755 --- a/tests/functional/nix-collect-garbage-d.sh +++ b/tests/functional/nix-collect-garbage-d.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore ## Test `nix-collect-garbage -d` diff --git a/tests/functional/nix-copy-ssh-common.sh b/tests/functional/nix-copy-ssh-common.sh index cc8314ff7..5eea9612d 100644 --- a/tests/functional/nix-copy-ssh-common.sh +++ b/tests/functional/nix-copy-ssh-common.sh @@ -2,6 +2,8 @@ proto=$1 shift (( $# == 0 )) +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/nix-copy-ssh-ng.sh b/tests/functional/nix-copy-ssh-ng.sh index 1fd735b9d..41958c2c3 100755 --- a/tests/functional/nix-copy-ssh-ng.sh +++ b/tests/functional/nix-copy-ssh-ng.sh @@ -4,6 +4,8 @@ source common.sh source nix-copy-ssh-common.sh "ssh-ng" +TODO_NixOS + clearStore clearRemoteStore diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 3e5846cf2..e2f19b99e 100755 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearProfiles diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index c38107e64..66aece388 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore if [[ -n ${CONTENT_ADDRESSED:-} ]]; then diff --git a/tests/functional/optimise-store.sh b/tests/functional/optimise-store.sh index 70ce954f9..f010c5549 100755 --- a/tests/functional/optimise-store.sh +++ b/tests/functional/optimise-store.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) diff --git a/tests/functional/output-normalization.sh b/tests/functional/output-normalization.sh index 2b319201a..c55f1b1d1 100755 --- a/tests/functional/output-normalization.sh +++ b/tests/functional/output-normalization.sh @@ -3,6 +3,7 @@ source common.sh testNormalization () { + TODO_NixOS clearStore outPath=$(nix-build ./simple.nix --no-out-link) test "$(stat -c %Y $outPath)" -eq 1 diff --git a/tests/functional/parallel.sh b/tests/functional/parallel.sh index 3b7bbe5a2..7e420688d 100644 --- a/tests/functional/parallel.sh +++ b/tests/functional/parallel.sh @@ -4,6 +4,8 @@ source common.sh # First, test that -jN performs builds in parallel. echo "testing nix-build -j..." +TODO_NixOS + clearStore rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max diff --git a/tests/functional/pass-as-file.sh b/tests/functional/pass-as-file.sh index 21d9ffc6d..0b5605d05 100755 --- a/tests/functional/pass-as-file.sh +++ b/tests/functional/pass-as-file.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath=$(nix-build --no-out-link -E " diff --git a/tests/functional/placeholders.sh b/tests/functional/placeholders.sh index f2b8bf6bf..d4e09a91b 100755 --- a/tests/functional/placeholders.sh +++ b/tests/functional/placeholders.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore nix-build --no-out-link -E ' diff --git a/tests/functional/post-hook.sh b/tests/functional/post-hook.sh index 7540d1045..94a6d0d69 100755 --- a/tests/functional/post-hook.sh +++ b/tests/functional/post-hook.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore rm -f $TEST_ROOT/result diff --git a/tests/functional/pure-eval.sh b/tests/functional/pure-eval.sh index 6d8aa35ec..8a18dec6e 100755 --- a/tests/functional/pure-eval.sh +++ b/tests/functional/pure-eval.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore nix eval --expr 'assert 1 + 2 == 3; true' diff --git a/tests/functional/read-only-store.sh b/tests/functional/read-only-store.sh index ecc57642d..f6b6eaf32 100755 --- a/tests/functional/read-only-store.sh +++ b/tests/functional/read-only-store.sh @@ -6,6 +6,8 @@ enableFeatures "read-only-local-store" needLocalStore "cannot open store read-only when daemon has already opened it writeable" +TODO_NixOS + clearStore happy () { diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh index d0644471d..0ef549c8d 100755 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath=$(nix-build --no-out-link readfile-context.nix) diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh index a9966aabd..6b255e81a 100755 --- a/tests/functional/recursive.sh +++ b/tests/functional/recursive.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + enableFeatures 'recursive-nix' restartDaemon diff --git a/tests/functional/referrers.sh b/tests/functional/referrers.sh index 0fda97378..411cdb7c1 100755 --- a/tests/functional/referrers.sh +++ b/tests/functional/referrers.sh @@ -4,6 +4,8 @@ source common.sh needLocalStore "uses some low-level store manipulations that aren’t available through the daemon" +TODO_NixOS + clearStore max=500 diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh index 171a5d391..841b6b27a 100755 --- a/tests/functional/remote-store.sh +++ b/tests/functional/remote-store.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # Ensure "fake ssh" remote store works just as legacy fake ssh would. diff --git a/tests/functional/repair.sh b/tests/functional/repair.sh index 552e04280..1f6004b2c 100755 --- a/tests/functional/repair.sh +++ b/tests/functional/repair.sh @@ -4,6 +4,8 @@ source common.sh needLocalStore "--repair needs a local store" +TODO_NixOS + clearStore path=$(nix-build dependencies.nix -o $TEST_ROOT/result) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index fca982807..86cd6f458 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -22,6 +22,8 @@ replUndefinedVariable=" import $testDir/undefined-variable.nix " +TODO_NixOS + testRepl () { local nixArgs nixArgs=("$@") diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh index ab4cad5cf..917a554c5 100755 --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore nix-instantiate --restrict-eval --eval -E '1 + 2' diff --git a/tests/functional/search.sh b/tests/functional/search.sh index ce17411d2..1195c2a06 100755 --- a/tests/functional/search.sh +++ b/tests/functional/search.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/secure-drv-outputs.sh b/tests/functional/secure-drv-outputs.sh index 7d81db58b..5cc4af435 100755 --- a/tests/functional/secure-drv-outputs.sh +++ b/tests/functional/secure-drv-outputs.sh @@ -6,6 +6,8 @@ source common.sh +TODO_NixOS + clearStore startDaemon diff --git a/tests/functional/selfref-gc.sh b/tests/functional/selfref-gc.sh index 37ce33089..a5e368b51 100755 --- a/tests/functional/selfref-gc.sh +++ b/tests/functional/selfref-gc.sh @@ -4,6 +4,8 @@ source common.sh requireDaemonNewerThan "2.6.0pre20211215" +TODO_NixOS + clearStore nix-build --no-out-link -E ' diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index 1760eefff..b4c03f547 100755 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh index cf84ab377..d268fd116 100755 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh index 4e7d37f59..86acca0c2 100755 --- a/tests/functional/simple.sh +++ b/tests/functional/simple.sh @@ -17,6 +17,8 @@ echo "output path is $outPath" text=$(cat "$outPath/hello") if test "$text" != "Hello World!"; then exit 1; fi +TODO_NixOS + # Directed delete: $outPath is not reachable from a root, so it should # be deleteable. nix-store --delete $outPath diff --git a/tests/functional/store-info.sh b/tests/functional/store-info.sh index 2398f5beb..f37889fbb 100755 --- a/tests/functional/store-info.sh +++ b/tests/functional/store-info.sh @@ -16,4 +16,6 @@ fi expect 127 NIX_REMOTE=unix:$PWD/store nix store info || \ fail "nix store info on a non-existent store should fail" +TODO_NixOS + [[ "$(echo "$STORE_INFO_JSON" | jq -r ".url")" == "${NIX_REMOTE:-local}" ]] diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh index ba7f5967e..32d4c9957 100755 --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -6,6 +6,8 @@ source common.sh # tests for the older versions requireDaemonNewerThan "2.4pre20210712" +TODO_NixOS + clearStore rm -f $TEST_ROOT/result diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh index 6ec1cd322..1140e4f78 100755 --- a/tests/functional/suggestions.sh +++ b/tests/functional/suggestions.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore cd "$TEST_HOME" diff --git a/tests/functional/supplementary-groups.sh b/tests/functional/supplementary-groups.sh index 9d474219f..5d329efc9 100755 --- a/tests/functional/supplementary-groups.sh +++ b/tests/functional/supplementary-groups.sh @@ -7,6 +7,8 @@ requireSandboxSupport if ! command -p -v unshare; then skipTest "Need unshare"; fi needLocalStore "The test uses --store always so we would just be bypassing the daemon" +TODO_NixOS + unshare --mount --map-root-user bash < Date: Sun, 16 Jun 2024 16:40:20 +0200 Subject: [PATCH 347/910] tests: Add quickBuild to all VM tests --- tests/nixos/default.nix | 8 +++++++- tests/nixos/functional/common.nix | 5 ----- tests/nixos/{functional => }/quick-build.nix | 0 3 files changed, 7 insertions(+), 6 deletions(-) rename tests/nixos/{functional => }/quick-build.nix (100%) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 2ab00b336..512195a67 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -7,7 +7,13 @@ let # https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests runNixOSTestFor = system: test: (nixos-lib.runTest { - imports = [ test ]; + imports = [ + test + + # Add the quickBuild attribute to the check packages + ./quick-build.nix + ]; + hostPkgs = nixpkgsFor.${system}.native; defaults = { nixpkgs.pkgs = nixpkgsFor.${system}.native; diff --git a/tests/nixos/functional/common.nix b/tests/nixos/functional/common.nix index 493791a7b..51fd76884 100644 --- a/tests/nixos/functional/common.nix +++ b/tests/nixos/functional/common.nix @@ -8,11 +8,6 @@ let in { - imports = [ - # Add the quickBuild attribute to the check package - ./quick-build.nix - ]; - # We rarely change the script in a way that benefits from type checking, so # we skip it to save time. skipTypeCheck = true; diff --git a/tests/nixos/functional/quick-build.nix b/tests/nixos/quick-build.nix similarity index 100% rename from tests/nixos/functional/quick-build.nix rename to tests/nixos/quick-build.nix From fca160fbcd9f8852b67c99eacf201ea0b693142a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 16:50:56 +0200 Subject: [PATCH 348/910] doc/contributing/testing: Describe functional VM tests and quickBuild --- doc/manual/src/contributing/testing.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 88b3c5cd9..717deabd7 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -114,6 +114,8 @@ On other platforms they wouldn't be run at all. The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/local.mk`. Each test is a bash script. +Functional tests are run during `installCheck` in the `nix` package build, as well as separately from the build, in VM tests. + ### Running the whole test suite The whole test suite can be run with: @@ -252,13 +254,30 @@ Regressions are caught, and improvements always show up in code review. To ensure that characterisation testing doesn't make it harder to intentionally change these interfaces, there always must be an easy way to regenerate the expected output, as we do with `_NIX_TEST_ACCEPT=1`. +### Running functional tests on NixOS + +We run the functional tests not just in the build, but also in VM tests. +This helps us ensure that Nix works correctly on NixOS, and environments that have similar characteristics that are hard to reproduce in a build environment. + +The recommended way to run these tests during development is: + +```shell +nix build .#hydraJobs.tests.functional_user.quickBuild +``` + +The `quickBuild` attribute configures the test to use a `nix` package that's built without integration tests, so that you can iterate on the tests without performing recompilations due to the changed sources for `installCheck`. + +Generally, this build is sufficient, but in nightly or CI we also test the attributes `functional_root` and `functional_trusted`, in which the test suite is run with different levels of authorization. + ## Integration tests The integration tests are defined in the Nix flake under the `hydraJobs.tests` attribute. These tests include everything that needs to interact with external services or run Nix in a non-trivial distributed setup. Because these tests are expensive and require more than what the standard github-actions setup provides, they only run on the master branch (on ). -You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}` +You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}`. + +If you are testing a build of `nix` that you haven't compiled yet, you may iterate faster by appending the `quickBuild` attribute: `nix build .#hydraJobs.tests.{testName}.quickBuild`. ## Installer tests From f0abe4d8f0f38e22e61cc79517494437e365375c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 16:57:15 +0200 Subject: [PATCH 349/910] ci: Build tests.functional_user for PRs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1aa3b776e..6362620b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -175,4 +175,4 @@ jobs: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes + - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user From 648302b833e6eae4a1c81fe897532e9fa8d7fd6b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 17:56:50 +0200 Subject: [PATCH 350/910] tests/functional: Enable more tests in NixOS VM --- tests/functional/add.sh | 4 +--- tests/functional/binary-cache-build-remote.sh | 2 +- tests/functional/build-delete.sh | 4 +--- tests/functional/build.sh | 4 +--- tests/functional/check-refs.sh | 19 ++++++++++++------- tests/functional/check-reqs.sh | 4 +--- tests/functional/common/vars-and-functions.sh | 16 +++++++++++++++- tests/functional/compression-levels.sh | 4 +--- tests/functional/debugger.sh | 4 +--- tests/functional/dependencies.sh | 6 +++--- tests/functional/eval.sh | 4 +--- tests/functional/fetchGit.sh | 4 +--- tests/functional/fetchGitRefs.sh | 4 +--- tests/functional/fetchGitSubmodules.sh | 4 +--- tests/functional/fetchGitVerification.sh | 4 +--- tests/functional/flakes/search-root.sh | 4 +--- tests/functional/fmt.sh | 6 ++---- tests/functional/git-hashing/common.sh | 2 +- tests/functional/help.sh | 4 ---- tests/functional/import-derivation.sh | 4 +--- tests/functional/impure-derivations.sh | 2 +- tests/functional/multiple-outputs.sh | 2 +- tests/functional/nix-build.sh | 2 +- tests/functional/nix-shell.sh | 4 +--- tests/functional/optimise-store.sh | 7 ++++--- tests/functional/pass-as-file.sh | 4 +--- tests/functional/placeholders.sh | 4 +--- tests/functional/pure-eval.sh | 4 +--- tests/functional/readfile-context.sh | 2 +- tests/functional/recursive.sh | 2 +- tests/functional/restricted.sh | 4 +--- tests/functional/search.sh | 4 +--- tests/functional/selfref-gc.sh | 4 +--- tests/functional/signing.sh | 5 ++--- tests/functional/structured-attrs.sh | 6 +++--- tests/functional/suggestions.sh | 4 +--- tests/functional/tarball.sh | 4 +--- tests/functional/why-depends.sh | 4 +--- 38 files changed, 71 insertions(+), 104 deletions(-) diff --git a/tests/functional/add.sh b/tests/functional/add.sh index 328b14831..3b37ee7d4 100755 --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -31,9 +31,7 @@ test "$hash1" = "sha256:$hash2" #### New style commands -TODO_NixOS - -clearStore +clearStoreIfPossible ( path1=$(nix store add ./dummy) diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh index 17e8bdcc1..5046d0064 100755 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -4,7 +4,7 @@ source common.sh TODO_NixOS -clearStore +clearStoreIfPossible clearCacheCache # Fails without remote builders diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh index 2ebf1fd3d..18841509d 100755 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible # https://github.com/NixOS/nix/issues/6572 issue_6572_independent_outputs() { diff --git a/tests/functional/build.sh b/tests/functional/build.sh index db759b6e9..9de953d8c 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible # Make sure that 'nix build' returns all outputs by default. nix build -f multiple-outputs.nix --json a b --no-link | jq --exit-status ' diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh index 26790c11b..5c3ac915e 100755 --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -45,13 +45,18 @@ nix-build -o "$RESULT" check-refs.nix -A test7 # test10 should succeed (no disallowed references). nix-build -o "$RESULT" check-refs.nix -A test10 -if isDaemonNewer 2.12pre20230103; then - if ! isDaemonNewer 2.16.0; then - enableFeatures discard-references - restartDaemon +if ! isTestOnNixOS; then + # If we have full control over our store, we can test some more things. + + if isDaemonNewer 2.12pre20230103; then + if ! isDaemonNewer 2.16.0; then + enableFeatures discard-references + restartDaemon + fi + + # test11 should succeed. + test11=$(nix-build -o "$RESULT" check-refs.nix -A test11) + [[ -z $(nix-store -q --references "$test11") ]] fi - # test11 should succeed. - test11=$(nix-build -o "$RESULT" check-refs.nix -A test11) - [[ -z $(nix-store -q --references "$test11") ]] fi diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh index bb91bacde..34eb133db 100755 --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible RESULT=$TEST_ROOT/result diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 4b0fee5b2..8237d853f 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -82,11 +82,25 @@ clearProfiles() { rm -rf "$profiles" } +# Clear the store, but do not fail if we're in an environment where we can't. +# This allows the test to run in a NixOS test environment, where we use the system store. +# See doc/manual/src/contributing/testing.md / Running functional tests on NixOS. +clearStoreIfPossible() { + if isTestOnNixOS; then + echo "clearStoreIfPossible: Not clearing store, because we're on NixOS. Moving on." + else + doClearStore + fi +} + clearStore() { if isTestOnNixOS; then - die "clearStore: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + die "clearStore: not supported when testing on NixOS. If not essential, call clearStoreIfPossible. If really needed, add conditionals; e.g. if ! isTestOnNixOS; then ..." fi + doClearStore +} +doClearStore() { echo "clearing store..." chmod -R +w "$NIX_STORE_DIR" rm -rf "$NIX_STORE_DIR" diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh index f51c3f509..399265f9c 100755 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible clearCache outPath=$(nix-build dependencies.nix --no-out-link) diff --git a/tests/functional/debugger.sh b/tests/functional/debugger.sh index 178822d79..b96b7e5d3 100755 --- a/tests/functional/debugger.sh +++ b/tests/functional/debugger.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible # regression #9932 echo ":env" | expect 1 nix eval --debugger --expr '(_: throw "oh snap") 42' diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh index bb01b3988..972bc5a9b 100755 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible drvPath=$(nix-instantiate dependencies.nix) @@ -67,6 +65,8 @@ drvPath2=$(nix-instantiate dependencies.nix --argstr hashInvalidator yay) # now --valid-derivers returns both test "$(nix-store -q --valid-derivers "$outPath" | sort)" = "$(sort <<< "$drvPath"$'\n'"$drvPath2")" +TODO_NixOS # The following --delete fails, because it seems to be still alive. This might be caused by a different test using the same path. We should try make the derivations unique, e.g. naming after tests, and adding a timestamp that's constant for that test script run. + # check that nix-store --valid-derivers only returns existing drv nix-store --delete "$drvPath" test "$(nix-store -q --valid-derivers "$outPath")" = "$drvPath2" diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index ba335d73a..27cdce478 100755 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible testStdinHeredoc=$(nix eval -f - < $TEST_ROOT/counter diff --git a/tests/functional/multiple-outputs.sh b/tests/functional/multiple-outputs.sh index c4af56f0b..35a78d152 100755 --- a/tests/functional/multiple-outputs.sh +++ b/tests/functional/multiple-outputs.sh @@ -4,7 +4,7 @@ source common.sh TODO_NixOS -clearStore +clearStoreIfPossible rm -f $TEST_ROOT/result* diff --git a/tests/functional/nix-build.sh b/tests/functional/nix-build.sh index cfba7f020..091e429e0 100755 --- a/tests/functional/nix-build.sh +++ b/tests/functional/nix-build.sh @@ -4,7 +4,7 @@ source common.sh TODO_NixOS -clearStore +clearStoreIfPossible outPath=$(nix-build dependencies.nix -o $TEST_ROOT/result) test "$(cat $TEST_ROOT/result/foobar)" = FOOBAR diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 66aece388..2c94705de 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible if [[ -n ${CONTENT_ADDRESSED:-} ]]; then shellDotNix="$PWD/ca-shell.nix" diff --git a/tests/functional/optimise-store.sh b/tests/functional/optimise-store.sh index f010c5549..0bedafc43 100755 --- a/tests/functional/optimise-store.sh +++ b/tests/functional/optimise-store.sh @@ -2,13 +2,14 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) outPath2=$(echo 'with import ./config.nix; mkDerivation { name = "foo2"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) +TODO_NixOS # ignoring the client-specified setting 'auto-optimise-store', because it is a restricted setting and you are not a trusted user + # TODO: only continue when trusted user or root + inode1="$(stat --format=%i $outPath1/foo)" inode2="$(stat --format=%i $outPath2/foo)" if [ "$inode1" != "$inode2" ]; then diff --git a/tests/functional/pass-as-file.sh b/tests/functional/pass-as-file.sh index 0b5605d05..6487bfffd 100755 --- a/tests/functional/pass-as-file.sh +++ b/tests/functional/pass-as-file.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible outPath=$(nix-build --no-out-link -E " with import ./config.nix; diff --git a/tests/functional/placeholders.sh b/tests/functional/placeholders.sh index d4e09a91b..33ec0c2b7 100755 --- a/tests/functional/placeholders.sh +++ b/tests/functional/placeholders.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible nix-build --no-out-link -E ' with import ./config.nix; diff --git a/tests/functional/pure-eval.sh b/tests/functional/pure-eval.sh index 8a18dec6e..250381099 100755 --- a/tests/functional/pure-eval.sh +++ b/tests/functional/pure-eval.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible nix eval --expr 'assert 1 + 2 == 3; true' diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh index 0ef549c8d..cb9ef6234 100755 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -2,7 +2,7 @@ source common.sh -TODO_NixOS +TODO_NixOS # NixOS doesn't provide $NIX_STATE_DIR (and shouldn't) clearStore diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh index 6b255e81a..640fb92d2 100755 --- a/tests/functional/recursive.sh +++ b/tests/functional/recursive.sh @@ -2,7 +2,7 @@ source common.sh -TODO_NixOS +TODO_NixOS # can't enable a sandbox feature easily enableFeatures 'recursive-nix' restartDaemon diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh index 917a554c5..915d973b0 100755 --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible nix-instantiate --restrict-eval --eval -E '1 + 2' (! nix-instantiate --eval --restrict-eval ./restricted.nix) diff --git a/tests/functional/search.sh b/tests/functional/search.sh index 1195c2a06..3fadecd02 100755 --- a/tests/functional/search.sh +++ b/tests/functional/search.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible clearCache (( $(nix search -f search.nix '' hello | wc -l) > 0 )) diff --git a/tests/functional/selfref-gc.sh b/tests/functional/selfref-gc.sh index a5e368b51..518aea66b 100755 --- a/tests/functional/selfref-gc.sh +++ b/tests/functional/selfref-gc.sh @@ -4,9 +4,7 @@ source common.sh requireDaemonNewerThan "2.6.0pre20211215" -TODO_NixOS - -clearStore +clearStoreIfPossible nix-build --no-out-link -E ' with import ./config.nix; diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh index d268fd116..890d1446f 100755 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible clearCache nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 @@ -18,6 +16,7 @@ outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROO # Verify that the path got signed. info=$(nix path-info --json $outPath) echo $info | jq -e '.[] | .ultimate == true' +TODO_NixOS # looks like an actual bug? Following line fails on NixOS: echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh index 32d4c9957..ec1282668 100755 --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -6,9 +6,7 @@ source common.sh # tests for the older versions requireDaemonNewerThan "2.4pre20210712" -TODO_NixOS - -clearStore +clearStoreIfPossible rm -f $TEST_ROOT/result @@ -23,6 +21,8 @@ env NIX_PATH=nixpkgs=shell.nix nix-shell structured-attrs-shell.nix \ nix develop -f structured-attrs-shell.nix -c bash -c 'test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' +TODO_NixOS # following line fails. + # `nix develop` is a slightly special way of dealing with environment vars, it parses # these from a shell-file exported from a derivation. This is to test especially `outputs` # (which is an associative array in thsi case) being fine. diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh index 1140e4f78..8db6f7b97 100755 --- a/tests/functional/suggestions.sh +++ b/tests/functional/suggestions.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible cd "$TEST_HOME" diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index eb541f3bb..a2824cd12 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible rm -rf $TEST_HOME diff --git a/tests/functional/why-depends.sh b/tests/functional/why-depends.sh index 1ce66f154..ce53546d8 100755 --- a/tests/functional/why-depends.sh +++ b/tests/functional/why-depends.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible cp ./dependencies.nix ./dependencies.builder0.sh ./config.nix $TEST_HOME From 7c9f3eeef8f3946a833b5336f8b0339766647058 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 19 Jun 2024 21:46:01 +0200 Subject: [PATCH 351/910] tests/functional/common/init.sh: Use parentheses around negation roberth: Not strictly necessary, but probably a good habit Co-authored-by: Eelco Dolstra --- tests/functional/common/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 19cef5af9..405ba3090 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -9,7 +9,7 @@ if isTestOnNixOS; then export NIX_USER_CONF_FILES="$test_nix_conf_dir/nix.conf" mkdir -p "$test_nix_conf_dir" "$TEST_HOME" - ! test -e "$test_nix_conf" + (! test -e "$test_nix_conf") cat > "$test_nix_conf_dir/nix.conf" < Date: Thu, 20 Jun 2024 14:49:53 +0200 Subject: [PATCH 352/910] Apply suggestions from code review Co-authored-by: Valentin Gagarin --- tests/functional/common/init.sh | 1 + tests/nixos/quick-build.nix | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 405ba3090..eb6be7eb0 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -11,6 +11,7 @@ if isTestOnNixOS; then mkdir -p "$test_nix_conf_dir" "$TEST_HOME" (! test -e "$test_nix_conf") cat > "$test_nix_conf_dir/nix.conf" < Date: Thu, 20 Jun 2024 19:53:25 +0530 Subject: [PATCH 353/910] fix: handle errors in `nix::createDirs` the `std::filesystem::create_directories` can fail due to insufficient permissions. We convert this error into a `SysError` and catch it wherever required. --- src/libcmd/repl-interacter.cc | 4 ++-- src/libstore/store-api.cc | 2 +- src/libutil/file-system.cc | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 254a86d7b..eb4361e25 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -107,8 +107,8 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter rl_readline_name = "nix-repl"; try { createDirs(dirOf(historyFile)); - } catch (std::filesystem::filesystem_error & e) { - warn(e.what()); + } catch (SystemError & e) { + logWarning(e.info()); } #ifndef USE_READLINE el_hist_size = 1000; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index ed5275377..2edb56510 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1304,7 +1304,7 @@ ref openStore(StoreReference && storeURI) if (!pathExists(chrootStore)) { try { createDirs(chrootStore); - } catch (std::filesystem::filesystem_error & e) { + } catch (SystemError & e) { return std::make_shared(params); } warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 5f269b7c0..443ccf829 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -415,7 +415,11 @@ void deletePath(const fs::path & path) void createDirs(const Path & path) { - fs::create_directories(path); + try { + fs::create_directories(path); + } catch (fs::filesystem_error & e) { + throw SysError("creating directory '%1%'", path); + } } From d9684664c8693b3f1d1ad69b6ed3dbce363748b0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 20 Jun 2024 20:26:07 +0200 Subject: [PATCH 354/910] Revert "tests/functional/common/init.sh: Use parentheses around negation" ShellCheck doesn't want us to add extra parentheses for show. This reverts commit 7c9f3eeef8f3946a833b5336f8b0339766647058. --- tests/functional/common/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index eb6be7eb0..d33ad5d57 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -9,7 +9,7 @@ if isTestOnNixOS; then export NIX_USER_CONF_FILES="$test_nix_conf_dir/nix.conf" mkdir -p "$test_nix_conf_dir" "$TEST_HOME" - (! test -e "$test_nix_conf") + ! test -e "$test_nix_conf" cat > "$test_nix_conf_dir/nix.conf" < Date: Wed, 24 Apr 2024 15:26:18 +0200 Subject: [PATCH 355/910] Run the flake-regressions test suite --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ scripts/flake-regressions.sh | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100755 scripts/flake-regressions.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1aa3b776e..a0235eb0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,3 +176,23 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes + + flake_regressions: + needs: vm_tests + runs-on: ubuntu-22.04 + steps: + - name: Checkout nix + uses: actions/checkout@v4 + - name: Checkout flake-regressions + uses: actions/checkout@v4 + with: + repository: DeterminateSystems/flake-regressions + path: flake-regressions + - name: Checkout flake-regressions-data + uses: actions/checkout@v4 + with: + repository: DeterminateSystems/flake-regressions-data + path: flake-regressions/tests + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix build --out-link ./new-nix && PATH=$(pwd)/new-nix/bin:$PATH scripts/flake-regressions.sh diff --git a/scripts/flake-regressions.sh b/scripts/flake-regressions.sh new file mode 100755 index 000000000..e6cfbfa24 --- /dev/null +++ b/scripts/flake-regressions.sh @@ -0,0 +1,27 @@ +#! /usr/bin/env bash + +set -e + +echo "Nix version:" +nix --version + +cd flake-regressions + +status=0 + +flakes=$(ls -d tests/*/*/* | head -n25) + +echo "Running flake tests..." + +for flake in $flakes; do + + if ! REGENERATE=0 ./eval-flake.sh $flake; then + status=1 + echo "❌ $flake" + else + echo "✅ $flake" + fi + +done + +exit "$status" From 0eec60968ad393281ae9e31f84f2cbc15fe0bc2f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 15:58:37 +0200 Subject: [PATCH 356/910] flake-regressions.sh: Make the sort order deterministic --- scripts/flake-regressions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/flake-regressions.sh b/scripts/flake-regressions.sh index e6cfbfa24..5cc55bf4f 100755 --- a/scripts/flake-regressions.sh +++ b/scripts/flake-regressions.sh @@ -9,7 +9,7 @@ cd flake-regressions status=0 -flakes=$(ls -d tests/*/*/* | head -n25) +flakes=$(ls -d tests/*/*/* | sort | head -n25) echo "Running flake tests..." From 6f3d2daee66e8de80faa45e1a30abbabcc08764a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 10 Jun 2024 15:16:41 +0200 Subject: [PATCH 357/910] Fix spellcheck --- scripts/flake-regressions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/flake-regressions.sh b/scripts/flake-regressions.sh index 5cc55bf4f..d76531134 100755 --- a/scripts/flake-regressions.sh +++ b/scripts/flake-regressions.sh @@ -9,13 +9,13 @@ cd flake-regressions status=0 -flakes=$(ls -d tests/*/*/* | sort | head -n25) +flakes=$(find tests -mindepth 3 -maxdepth 3 -type d -not -path '*/.*' | sort | head -n25) echo "Running flake tests..." for flake in $flakes; do - if ! REGENERATE=0 ./eval-flake.sh $flake; then + if ! REGENERATE=0 ./eval-flake.sh "$flake"; then status=1 echo "❌ $flake" else From d4a70b67a0d76328644ab975ef0f714b1f28c2cd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 21 Jun 2024 15:38:03 +0200 Subject: [PATCH 358/910] Move flake-regressions repos to the NixOS org --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0235eb0f..6f0b63eaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -186,12 +186,12 @@ jobs: - name: Checkout flake-regressions uses: actions/checkout@v4 with: - repository: DeterminateSystems/flake-regressions + repository: NixOS/flake-regressions path: flake-regressions - name: Checkout flake-regressions-data uses: actions/checkout@v4 with: - repository: DeterminateSystems/flake-regressions-data + repository: NixOS/flake-regressions-data path: flake-regressions/tests - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main From 717f3eea3981786a1092d975bb811fe93cf7a66e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 8 Apr 2024 14:51:54 +0200 Subject: [PATCH 359/910] Add a test for the user sandboxing --- tests/nixos/default.nix | 2 + tests/nixos/user-sandboxing/attacker.c | 82 +++++++++++++++ tests/nixos/user-sandboxing/default.nix | 127 ++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 tests/nixos/user-sandboxing/attacker.c create mode 100644 tests/nixos/user-sandboxing/default.nix diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 710f8a273..2cf7a64c6 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -132,4 +132,6 @@ in ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak; gzip-content-encoding = runNixOSTestFor "x86_64-linux" ./gzip-content-encoding.nix; + + user-sandboxing = runNixOSTestFor "x86_64-linux" ./user-sandboxing; } diff --git a/tests/nixos/user-sandboxing/attacker.c b/tests/nixos/user-sandboxing/attacker.c new file mode 100644 index 000000000..3bd729c04 --- /dev/null +++ b/tests/nixos/user-sandboxing/attacker.c @@ -0,0 +1,82 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#define SYS_fchmodat2 452 + +int fchmodat2(int dirfd, const char *pathname, mode_t mode, int flags) { + return syscall(SYS_fchmodat2, dirfd, pathname, mode, flags); +} + +int main(int argc, char **argv) { + if (argc <= 1) { + // stage 1: place the setuid-builder executable + + // make the build directory world-accessible first + chmod(".", 0755); + + if (fchmodat2(AT_FDCWD, "attacker", 06755, AT_SYMLINK_NOFOLLOW) < 0) { + perror("Setting the suid bit on attacker"); + exit(-1); + } + + } else { + // stage 2: corrupt the victim derivation while it's building + + // prevent the kill + if (setresuid(-1, -1, getuid())) { + perror("setresuid"); + exit(-1); + } + + if (fork() == 0) { + + // wait for the victim to build + int fd = inotify_init(); + inotify_add_watch(fd, argv[1], IN_CREATE); + int dirfd = open(argv[1], O_DIRECTORY); + if (dirfd < 0) { + perror("opening the global build directory"); + exit(-1); + } + char buf[4096]; + fprintf(stderr, "Entering the inotify loop\n"); + for (;;) { + ssize_t len = read(fd, buf, sizeof(buf)); + struct inotify_event *ev; + for (char *pe = buf; pe < buf + len; + pe += sizeof(struct inotify_event) + ev->len) { + ev = (struct inotify_event *)pe; + fprintf(stderr, "folder %s created\n", ev->name); + // wait a bit to prevent racing against the creation + sleep(1); + int builddir = openat(dirfd, ev->name, O_DIRECTORY); + if (builddir < 0) { + perror("opening the build directory"); + continue; + } + int resultfile = openat(builddir, "build/result", O_WRONLY | O_TRUNC); + if (resultfile < 0) { + perror("opening the hijacked file"); + continue; + } + int writeres = write(resultfile, "bad\n", 4); + if (writeres < 0) { + perror("writing to the hijacked file"); + continue; + } + fprintf(stderr, "Hijacked the build for %s\n", ev->name); + return 0; + } + } + } + + exit(0); + } +} + diff --git a/tests/nixos/user-sandboxing/default.nix b/tests/nixos/user-sandboxing/default.nix new file mode 100644 index 000000000..cdb0c7eb6 --- /dev/null +++ b/tests/nixos/user-sandboxing/default.nix @@ -0,0 +1,127 @@ +{ config, ... }: + +let + pkgs = config.nodes.machine.nixpkgs.pkgs; + + attacker = pkgs.runCommandWith { + name = "attacker"; + stdenv = pkgs.pkgsStatic.stdenv; + } '' + $CC -static -o $out ${./attacker.c} + ''; + + try-open-build-dir = pkgs.writeScript "try-open-build-dir" '' + export PATH=${pkgs.coreutils}/bin:$PATH + + set -x + + chmod 700 . + + touch foo + + # Synchronisation point: create a world-writable fifo and wait for someone + # to write into it + mkfifo syncPoint + chmod 777 syncPoint + cat syncPoint + + touch $out + + set +x + ''; + + create-hello-world = pkgs.writeScript "create-hello-world" '' + export PATH=${pkgs.coreutils}/bin:$PATH + + set -x + + echo "hello, world" > result + + # Synchronisation point: create a world-writable fifo and wait for someone + # to write into it + mkfifo syncPoint + chmod 777 syncPoint + cat syncPoint + + cp result $out + + set +x + ''; + +in +{ + name = "sandbox-setuid-leak"; + + nodes.machine = + { config, lib, pkgs, ... }: + { virtualisation.writableStore = true; + nix.settings.substituters = lib.mkForce [ ]; + nix.nrBuildUsers = 1; + virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell attacker try-open-build-dir create-hello-world pkgs.socat ]; + boot.kernelPackages = pkgs.linuxPackages_latest; + users.users.alice = { + isNormalUser = true; + }; + }; + + testScript = { nodes }: '' + start_all() + + with subtest("A builder can't give access to its build directory"): + # Make sure that a builder can't change the permissions on its build + # directory to the point of opening it up to external users + + # A derivation whose builder tries to make its build directory as open + # as possible and wait for someone to hijack it + machine.succeed(r""" + nix-build -v -E ' + builtins.derivation { + name = "open-build-dir"; + system = builtins.currentSystem; + builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; + args = [ (builtins.storePath "${try-open-build-dir}") ]; + }' >&2 & + """.strip()) + + # Wait for the build to be ready + # This is OK because it runs as root, so we can access everything + machine.wait_for_file("/tmp/nix-build-open-build-dir.drv-0/syncPoint") + + # But Alice shouldn't be able to access the build directory + machine.fail("su alice -c 'ls /tmp/nix-build-open-build-dir.drv-0'") + machine.fail("su alice -c 'touch /tmp/nix-build-open-build-dir.drv-0/bar'") + machine.fail("su alice -c 'cat /tmp/nix-build-open-build-dir.drv-0/foo'") + + # Tell the user to finish the build + machine.succeed("echo foo > /tmp/nix-build-open-build-dir.drv-0/syncPoint") + + with subtest("Being able to execute stuff as the build user doesn't give access to the build dir"): + machine.succeed(r""" + nix-build -E ' + builtins.derivation { + name = "innocent"; + system = builtins.currentSystem; + builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; + args = [ (builtins.storePath "${create-hello-world}") ]; + }' >&2 & + """.strip()) + machine.wait_for_file("/tmp/nix-build-innocent.drv-0/syncPoint") + + # The build ran as `nixbld1` (which is the only build user on the + # machine), but a process running as `nixbld1` outside the sandbox + # shouldn't be able to touch the build directory regardless + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'ls /tmp/nix-build-innocent.drv-0'") + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'echo pwned > /tmp/nix-build-innocent.drv-0/result'") + + # Finish the build + machine.succeed("echo foo > /tmp/nix-build-innocent.drv-0/syncPoint") + + # Check that the build was not affected + machine.succeed(r""" + cat ./result + test "$(cat ./result)" = "hello, world" + """.strip()) + ''; + +} + From 1d3696f0fb88d610abc234a60e0d6d424feafdf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Tue, 2 Apr 2024 17:06:48 +0200 Subject: [PATCH 360/910] Run the builds in a daemon-controled directory Instead of running the builds under `$TMPDIR/{unique-build-directory-owned-by-the-build-user}`, run them under `$TMPDIR/{unique-build-directory-owned-by-the-daemon}/{subdir-owned-by-the-build-user}` where the build directory is only readable and traversable by the daemon user. This achieves two things: 1. It prevents builders from making their build directory world-readable (or even writeable), which would allow the outside world to interact with them. 2. It prevents external processes running as the build user (either because that somehow leaked, maybe as a consequence of 1., or because `build-users` isn't in use) from gaining access to the build directory. --- .../unix/build/local-derivation-goal.cc | 5 +++-- src/libutil/file-system.cc | 5 +++++ src/libutil/file-system.hh | 5 +++++ tests/functional/check.sh | 2 +- tests/nixos/user-sandboxing/default.nix | 20 ++++++++++--------- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index a99439738..2e75aa031 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -503,8 +503,9 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ - tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); - + auto parentTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); + tmpDir = parentTmpDir + "/build"; + createDir(tmpDir, 0700); chownToBuilder(tmpDir); for (auto & [outputName, status] : initialOutputs) { diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9d6142556..cadcab96d 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -412,6 +412,11 @@ void deletePath(const fs::path & path) deletePath(path, dummy); } +void createDir(const Path &path, mode_t mode) +{ + if (mkdir(path.c_str(), mode) == -1) + throw SysError("creating directory '%1%'", path); +} Paths createDirs(const Path & path) { diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index c6b6ecedb..e7bf087eb 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -157,6 +157,11 @@ inline Paths createDirs(PathView path) return createDirs(Path(path)); } +/** + * Create a single directory. + */ +void createDir(const Path & path, mode_t mode = 0755); + /** * Create a symlink. */ diff --git a/tests/functional/check.sh b/tests/functional/check.sh index efb93eeb0..a5e61b035 100755 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -46,7 +46,7 @@ test_custom_build_dir() { --no-out-link --keep-failed --option build-dir "$TEST_ROOT/custom-build-dir" 2> $TEST_ROOT/log || status=$? [ "$status" = "100" ] [[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]] - local buildDir="$customBuildDir/nix-build-"* + local buildDir="$customBuildDir/nix-build-"*"/build" grep $checkBuildId $buildDir/checkBuildId } test_custom_build_dir diff --git a/tests/nixos/user-sandboxing/default.nix b/tests/nixos/user-sandboxing/default.nix index cdb0c7eb6..8a16f44e8 100644 --- a/tests/nixos/user-sandboxing/default.nix +++ b/tests/nixos/user-sandboxing/default.nix @@ -16,6 +16,8 @@ let set -x chmod 700 . + # Shouldn't be able to open the root build directory + (! chmod 700 ..) touch foo @@ -85,15 +87,15 @@ in # Wait for the build to be ready # This is OK because it runs as root, so we can access everything - machine.wait_for_file("/tmp/nix-build-open-build-dir.drv-0/syncPoint") + machine.wait_for_file("/tmp/nix-build-open-build-dir.drv-0/build/syncPoint") # But Alice shouldn't be able to access the build directory - machine.fail("su alice -c 'ls /tmp/nix-build-open-build-dir.drv-0'") - machine.fail("su alice -c 'touch /tmp/nix-build-open-build-dir.drv-0/bar'") - machine.fail("su alice -c 'cat /tmp/nix-build-open-build-dir.drv-0/foo'") + machine.fail("su alice -c 'ls /tmp/nix-build-open-build-dir.drv-0/build'") + machine.fail("su alice -c 'touch /tmp/nix-build-open-build-dir.drv-0/build/bar'") + machine.fail("su alice -c 'cat /tmp/nix-build-open-build-dir.drv-0/build/foo'") # Tell the user to finish the build - machine.succeed("echo foo > /tmp/nix-build-open-build-dir.drv-0/syncPoint") + machine.succeed("echo foo > /tmp/nix-build-open-build-dir.drv-0/build/syncPoint") with subtest("Being able to execute stuff as the build user doesn't give access to the build dir"): machine.succeed(r""" @@ -105,16 +107,16 @@ in args = [ (builtins.storePath "${create-hello-world}") ]; }' >&2 & """.strip()) - machine.wait_for_file("/tmp/nix-build-innocent.drv-0/syncPoint") + machine.wait_for_file("/tmp/nix-build-innocent.drv-0/build/syncPoint") # The build ran as `nixbld1` (which is the only build user on the # machine), but a process running as `nixbld1` outside the sandbox # shouldn't be able to touch the build directory regardless - machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'ls /tmp/nix-build-innocent.drv-0'") - machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'echo pwned > /tmp/nix-build-innocent.drv-0/result'") + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'ls /tmp/nix-build-innocent.drv-0/build'") + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'echo pwned > /tmp/nix-build-innocent.drv-0/build/result'") # Finish the build - machine.succeed("echo foo > /tmp/nix-build-innocent.drv-0/syncPoint") + machine.succeed("echo foo > /tmp/nix-build-innocent.drv-0/build/syncPoint") # Check that the build was not affected machine.succeed(r""" From d99c868b0410d44faf547eb5ac923ea62abb649f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Tue, 9 Apr 2024 10:48:05 +0200 Subject: [PATCH 361/910] Add a release note for the build-dir hardening --- doc/manual/rl-next/harden-user-sandboxing.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 doc/manual/rl-next/harden-user-sandboxing.md diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md new file mode 100644 index 000000000..fa3c49fc0 --- /dev/null +++ b/doc/manual/rl-next/harden-user-sandboxing.md @@ -0,0 +1,8 @@ +--- +synopsis: Harden the user sandboxing +significance: significant +issues: +prs: +--- + +The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. From ede95b1fc133bd1d8eabc862f2e3e03c024cb755 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 13:00:00 +0200 Subject: [PATCH 362/910] Put the chroot inside a directory that isn't group/world-accessible Previously, the .chroot directory had permission 750 or 755 (depending on the uid-range system feature) and was owned by root/nixbld. This makes it possible for any nixbld user (if uid-range is disabled) or any user (if uid-range is enabled) to inspect the contents of the chroot of an active build and maybe interfere with it (e.g. via /tmp in the chroot, which has 1777 permission). To prevent this, the root is now a subdirectory of .chroot, which has permission 700 and is owned by root/root. --- src/libstore/unix/build/local-derivation-goal.cc | 14 +++++++++----- src/libstore/unix/build/local-derivation-goal.hh | 10 ++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 2e75aa031..d51a4817c 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -673,15 +673,19 @@ void LocalDerivationGoal::startBuilder() environment using bind-mounts. We put it in the Nix store so that the build outputs can be moved efficiently from the chroot to their final location. */ - chrootRootDir = worker.store.Store::toRealPath(drvPath) + ".chroot"; - deletePath(chrootRootDir); + chrootParentDir = worker.store.Store::toRealPath(drvPath) + ".chroot"; + deletePath(chrootParentDir); /* Clean up the chroot directory automatically. */ - autoDelChroot = std::make_shared(chrootRootDir); + autoDelChroot = std::make_shared(chrootParentDir); - printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootRootDir); + printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootParentDir); + + if (mkdir(chrootParentDir.c_str(), 0700) == -1) + throw SysError("cannot create '%s'", chrootRootDir); + + chrootRootDir = chrootParentDir + "/root"; - // FIXME: make this 0700 if (mkdir(chrootRootDir.c_str(), buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1) throw SysError("cannot create '%1%'", chrootRootDir); diff --git a/src/libstore/unix/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh index f25cb9424..77d07de98 100644 --- a/src/libstore/unix/build/local-derivation-goal.hh +++ b/src/libstore/unix/build/local-derivation-goal.hh @@ -65,6 +65,16 @@ struct LocalDerivationGoal : public DerivationGoal */ bool useChroot = false; + /** + * The parent directory of `chrootRootDir`. It has permission 700 + * and is owned by root to ensure other users cannot mess with + * `chrootRootDir`. + */ + Path chrootParentDir; + + /** + * The root of the chroot environment. + */ Path chrootRootDir; /** From 58b7b3fd15d09da983d34c5ac1acf6cba10887d8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 14:10:18 +0200 Subject: [PATCH 363/910] Formatting --- src/libutil/file-system.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index cadcab96d..7b20e078b 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -412,7 +412,7 @@ void deletePath(const fs::path & path) deletePath(path, dummy); } -void createDir(const Path &path, mode_t mode) +void createDir(const Path & path, mode_t mode) { if (mkdir(path.c_str(), mode) == -1) throw SysError("creating directory '%1%'", path); From d54590fdf328ea2764cf79fcba72cbf091b38acf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 14:12:08 +0200 Subject: [PATCH 364/910] Fix --no-sandbox When sandboxing is disabled, we cannot put $TMPDIR underneath an inaccessible directory. --- src/libstore/unix/build/local-derivation-goal.cc | 11 ++++++++--- tests/functional/check.sh | 5 ++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index d51a4817c..34b20925a 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -503,9 +503,14 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ - auto parentTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); - tmpDir = parentTmpDir + "/build"; - createDir(tmpDir, 0700); + tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); + if (useChroot) { + /* If sandboxing is enabled, put the actual TMPDIR underneath + an inaccessible root-owned directory, to prevent outside + access. */ + tmpDir = tmpDir + "/build"; + createDir(tmpDir, 0700); + } chownToBuilder(tmpDir); for (auto & [outputName, status] : initialOutputs) { diff --git a/tests/functional/check.sh b/tests/functional/check.sh index a5e61b035..7b70d6aef 100755 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -46,7 +46,10 @@ test_custom_build_dir() { --no-out-link --keep-failed --option build-dir "$TEST_ROOT/custom-build-dir" 2> $TEST_ROOT/log || status=$? [ "$status" = "100" ] [[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]] - local buildDir="$customBuildDir/nix-build-"*"/build" + local buildDir="$customBuildDir/nix-build-"*"" + if [[ -e $buildDir/build ]]; then + buildDir=$buildDir/build + fi grep $checkBuildId $buildDir/checkBuildId } test_custom_build_dir From 0468061dd2f9e70042b0bd0b4bf503c54637fd1a Mon Sep 17 00:00:00 2001 From: Shogo Takata Date: Sun, 23 Jun 2024 00:52:19 +0900 Subject: [PATCH 365/910] accept response from gitlab with more than one entry --- src/libfetchers/github.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 267e8607f..ddb41e63f 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -433,7 +433,7 @@ struct GitLabInputScheme : GitArchiveInputScheme store->toRealPath( downloadFile(store, url, "source", headers).storePath))); - if (json.is_array() && json.size() == 1 && json[0]["id"] != nullptr) { + if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) { return RefInfo { .rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1) }; From 490ca93cf886f046939ba4511dd88e5eb5dcddbc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 23 Jun 2024 15:33:45 -0400 Subject: [PATCH 366/910] Factor out a bit more language testings infra Will be used in a second test after `lang.sh`. --- maintainers/flake-module.nix | 3 +- ...nfra.sh => characterisation-test-infra.sh} | 2 +- .../empty.exp => characterisation/empty} | 0 .../functional/characterisation/framework.sh | 77 +++++++++++++++++++ tests/functional/lang.sh | 32 +------- tests/functional/lang/framework.sh | 33 -------- tests/functional/local.mk | 2 +- 7 files changed, 82 insertions(+), 67 deletions(-) rename tests/functional/{lang-test-infra.sh => characterisation-test-infra.sh} (98%) rename tests/functional/{lang/empty.exp => characterisation/empty} (100%) create mode 100644 tests/functional/characterisation/framework.sh delete mode 100644 tests/functional/lang/framework.sh diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index b1d21e8fe..0bd353f1a 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -522,6 +522,7 @@ ''^tests/functional/ca/repl\.sh$'' ''^tests/functional/ca/selfref-gc\.sh$'' ''^tests/functional/ca/why-depends\.sh$'' + ''^tests/functional/characterisation-test-infra\.sh$'' ''^tests/functional/check\.sh$'' ''^tests/functional/common/vars-and-functions\.sh$'' ''^tests/functional/completions\.sh$'' @@ -579,9 +580,7 @@ ''^tests/functional/impure-env\.sh$'' ''^tests/functional/impure-eval\.sh$'' ''^tests/functional/install-darwin\.sh$'' - ''^tests/functional/lang-test-infra\.sh$'' ''^tests/functional/lang\.sh$'' - ''^tests/functional/lang/framework\.sh$'' ''^tests/functional/legacy-ssh-store\.sh$'' ''^tests/functional/linux-sandbox\.sh$'' ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' diff --git a/tests/functional/lang-test-infra.sh b/tests/functional/characterisation-test-infra.sh similarity index 98% rename from tests/functional/lang-test-infra.sh rename to tests/functional/characterisation-test-infra.sh index f32ccef05..279454550 100755 --- a/tests/functional/lang-test-infra.sh +++ b/tests/functional/characterisation-test-infra.sh @@ -3,7 +3,7 @@ # Test the function for lang.sh source common.sh -source lang/framework.sh +source characterisation/framework.sh # We are testing this, so don't want outside world to affect us. unset _NIX_TEST_ACCEPT diff --git a/tests/functional/lang/empty.exp b/tests/functional/characterisation/empty similarity index 100% rename from tests/functional/lang/empty.exp rename to tests/functional/characterisation/empty diff --git a/tests/functional/characterisation/framework.sh b/tests/functional/characterisation/framework.sh new file mode 100644 index 000000000..913fdd967 --- /dev/null +++ b/tests/functional/characterisation/framework.sh @@ -0,0 +1,77 @@ +# shellcheck shell=bash + +# Golden test support +# +# Test that the output of the given test matches what is expected. If +# `_NIX_TEST_ACCEPT` is non-empty also update the expected output so +# that next time the test succeeds. +function diffAndAcceptInner() { + local -r testName=$1 + local -r got="$2" + local -r expected="$3" + + # Absence of expected file indicates empty output expected. + if test -e "$expected"; then + local -r expectedOrEmpty="$expected" + else + local -r expectedOrEmpty=characterisation/empty + fi + + # Diff so we get a nice message + if ! diff --color=always --unified "$expectedOrEmpty" "$got"; then + echo "FAIL: evaluation result of $testName not as expected" + # shellcheck disable=SC2034 + badDiff=1 + fi + + # Update expected if `_NIX_TEST_ACCEPT` is non-empty. + if test -n "${_NIX_TEST_ACCEPT-}"; then + cp "$got" "$expected" + # Delete empty expected files to avoid bloating the repo with + # empty files. + if ! test -s "$expected"; then + rm "$expected" + fi + fi +} + +function characterisationTestExit() { + # Make sure shellcheck knows all these will be defined by the caller + : "${badDiff?} ${badExitCode?}" + + if test -n "${_NIX_TEST_ACCEPT-}"; then + if (( "$badDiff" )); then + set +x + echo 'Output did mot match, but accepted output as the persisted expected output.' + echo 'That means the next time the tests are run, they should pass.' + set -x + else + set +x + echo 'NOTE: Environment variable _NIX_TEST_ACCEPT is defined,' + echo 'indicating the unexpected output should be accepted as the expected output going forward,' + echo 'but no tests had unexpected output so there was no expected output to update.' + set -x + fi + if (( "$badExitCode" )); then + exit "$badExitCode" + else + skipTest "regenerating golden masters" + fi + else + if (( "$badDiff" )); then + set +x + echo '' + echo 'You can rerun this test with:' + echo '' + echo " _NIX_TEST_ACCEPT=1 make tests/functional/${TEST_NAME}.test" + echo '' + echo 'to regenerate the files containing the expected output,' + echo 'and then view the git diff to decide whether a change is' + echo 'good/intentional or bad/unintentional.' + echo 'If the diff contains arbitrary or impure information,' + echo 'please improve the normalization that the test applies to the output.' + set -x + fi + exit $(( "$badExitCode" + "$badDiff" )) + fi +} diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 569e7082e..8cb8e98fb 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -4,7 +4,7 @@ source common.sh set -o pipefail -source lang/framework.sh +source characterisation/framework.sh # specialize function a bit function diffAndAccept() { @@ -138,32 +138,4 @@ for i in lang/eval-okay-*.nix; do fi done -if test -n "${_NIX_TEST_ACCEPT-}"; then - if (( "$badDiff" )); then - echo 'Output did mot match, but accepted output as the persisted expected output.' - echo 'That means the next time the tests are run, they should pass.' - else - echo 'NOTE: Environment variable _NIX_TEST_ACCEPT is defined,' - echo 'indicating the unexpected output should be accepted as the expected output going forward,' - echo 'but no tests had unexpected output so there was no expected output to update.' - fi - if (( "$badExitCode" )); then - exit "$badExitCode" - else - skipTest "regenerating golden masters" - fi -else - if (( "$badDiff" )); then - echo '' - echo 'You can rerun this test with:' - echo '' - echo ' _NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test' - echo '' - echo 'to regenerate the files containing the expected output,' - echo 'and then view the git diff to decide whether a change is' - echo 'good/intentional or bad/unintentional.' - echo 'If the diff contains arbitrary or impure information,' - echo 'please improve the normalization that the test applies to the output.' - fi - exit $(( "$badExitCode" + "$badDiff" )) -fi +characterisationTestExit diff --git a/tests/functional/lang/framework.sh b/tests/functional/lang/framework.sh deleted file mode 100644 index 9b886e983..000000000 --- a/tests/functional/lang/framework.sh +++ /dev/null @@ -1,33 +0,0 @@ -# Golden test support -# -# Test that the output of the given test matches what is expected. If -# `_NIX_TEST_ACCEPT` is non-empty also update the expected output so -# that next time the test succeeds. -function diffAndAcceptInner() { - local -r testName=$1 - local -r got="$2" - local -r expected="$3" - - # Absence of expected file indicates empty output expected. - if test -e "$expected"; then - local -r expectedOrEmpty="$expected" - else - local -r expectedOrEmpty=lang/empty.exp - fi - - # Diff so we get a nice message - if ! diff --color=always --unified "$expectedOrEmpty" "$got"; then - echo "FAIL: evaluation result of $testName not as expected" - badDiff=1 - fi - - # Update expected if `_NIX_TEST_ACCEPT` is non-empty. - if test -n "${_NIX_TEST_ACCEPT-}"; then - cp "$got" "$expected" - # Delete empty expected files to avoid bloating the repo with - # empty files. - if ! test -s "$expected"; then - rm "$expected" - fi - fi -} diff --git a/tests/functional/local.mk b/tests/functional/local.mk index b379eeefe..7e4536323 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -23,7 +23,7 @@ nix_tests = \ remote-store.sh \ legacy-ssh-store.sh \ lang.sh \ - lang-test-infra.sh \ + characterisation-test-infra.sh \ experimental-features.sh \ fetchMercurial.sh \ gc-auto.sh \ From 9f9984e4d07c3176a1781b2149ba7488383ddcde Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Sat, 8 Jun 2024 13:10:51 +0200 Subject: [PATCH 367/910] Functional test for derivation "advanced attrs" This tests the Nix language side of things. We are purposely skipping most of `common.sh` because it is overkill for this test: we don't want to have an "overfit" test environment. Co-Authored-By: John Ericson --- tests/functional/common/paths.sh | 20 +++++++++ tests/functional/common/subst-vars.sh.in | 8 +++- tests/functional/common/test-root.sh | 4 ++ tests/functional/common/vars-and-functions.sh | 14 ++---- .../derivation-advanced-attributes.sh | 23 ++++++++++ .../advanced-attributes-defaults.drv | 1 + .../advanced-attributes-defaults.nix | 6 +++ ...d-attributes-structured-attrs-defaults.drv | 1 + ...d-attributes-structured-attrs-defaults.nix | 8 ++++ .../advanced-attributes-structured-attrs.drv | 1 + .../advanced-attributes-structured-attrs.nix | 45 +++++++++++++++++++ .../derivation/advanced-attributes.drv | 1 + .../derivation/advanced-attributes.nix | 33 ++++++++++++++ tests/functional/local.mk | 1 + 14 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 tests/functional/common/paths.sh create mode 100644 tests/functional/common/test-root.sh create mode 100755 tests/functional/derivation-advanced-attributes.sh create mode 100644 tests/functional/derivation/advanced-attributes-defaults.drv create mode 100644 tests/functional/derivation/advanced-attributes-defaults.nix create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs.drv create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs.nix create mode 100644 tests/functional/derivation/advanced-attributes.drv create mode 100644 tests/functional/derivation/advanced-attributes.nix diff --git a/tests/functional/common/paths.sh b/tests/functional/common/paths.sh new file mode 100644 index 000000000..938b90899 --- /dev/null +++ b/tests/functional/common/paths.sh @@ -0,0 +1,20 @@ +# shellcheck shell=bash + +commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" + +# Since this is a generated file +# shellcheck disable=SC1091 +source "$commonDir/subst-vars.sh" +# Make sure shellcheck knows this will be defined by the above generated snippet +: "${bindir?}" + +export PATH="$bindir:$PATH" + +if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then + export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH +fi + +DAEMON_PATH="$PATH" +if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then + DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" +fi diff --git a/tests/functional/common/subst-vars.sh.in b/tests/functional/common/subst-vars.sh.in index 4105e9f35..cd89a0033 100644 --- a/tests/functional/common/subst-vars.sh.in +++ b/tests/functional/common/subst-vars.sh.in @@ -1,6 +1,10 @@ # NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk -export PATH=@bindir@:$PATH +if [[ -z "${COMMON_SUBST_VARS_SH_SOURCED-}" ]]; then + +COMMON_SUBST_VARS_SH_SOURCED=1 + +bindir=@bindir@ export coreutils=@coreutils@ #lsof=@lsof@ @@ -13,3 +17,5 @@ export version=@PACKAGE_VERSION@ export system=@system@ export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@ + +fi diff --git a/tests/functional/common/test-root.sh b/tests/functional/common/test-root.sh new file mode 100644 index 000000000..b50a06267 --- /dev/null +++ b/tests/functional/common/test-root.sh @@ -0,0 +1,4 @@ +# shellcheck shell=bash + +TEST_ROOT=$(realpath "${TMPDIR:-/tmp}/nix-test")/${TEST_NAME:-default/tests\/functional//} +export TEST_ROOT diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index ad5b29a94..f6ccf9941 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -12,9 +12,11 @@ commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" source "$commonDir/subst-vars.sh" # Make sure shellcheck knows all these will be defined by the above generated snippet -: "${PATH?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" +: "${bindir?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" + +source "$commonDir/paths.sh" +source "$commonDir/test-root.sh" -export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} export NIX_STORE_DIR if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then # Maybe the build directory is symlinked. @@ -43,14 +45,6 @@ unset XDG_CONFIG_HOME unset XDG_CONFIG_DIRS unset XDG_CACHE_HOME -if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then - export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH -fi -DAEMON_PATH="$PATH" -if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then - DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" -fi - export IMPURE_VAR1=foo export IMPURE_VAR2=bar diff --git a/tests/functional/derivation-advanced-attributes.sh b/tests/functional/derivation-advanced-attributes.sh new file mode 100755 index 000000000..6c0c76b4c --- /dev/null +++ b/tests/functional/derivation-advanced-attributes.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +source common/test-root.sh +source common/paths.sh + +set -o pipefail + +source characterisation/framework.sh + +badDiff=0 +badExitCode=0 + +store="$TEST_ROOT/store" + +for nixFile in derivation/*.nix; do + drvPath=$(nix-instantiate --store "$store" --pure-eval --expr "$(< "$nixFile")") + testName=$(basename "$nixFile" .nix) + got="${store}${drvPath}" + expected="derivation/$testName.drv" + diffAndAcceptInner "$testName" "$got" "$expected" +done + +characterisationTestExit diff --git a/tests/functional/derivation/advanced-attributes-defaults.drv b/tests/functional/derivation/advanced-attributes-defaults.drv new file mode 100644 index 000000000..391c6ab80 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-defaults.drv @@ -0,0 +1 @@ +Derive([("out","/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults","","")],[],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("builder","/bin/bash"),("name","advanced-attributes-defaults"),("out","/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults"),("system","my-system")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes-defaults.nix b/tests/functional/derivation/advanced-attributes-defaults.nix new file mode 100644 index 000000000..51a8d0e7e --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-defaults.nix @@ -0,0 +1,6 @@ +derivation { + name = "advanced-attributes-defaults"; + system = "my-system"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; +} diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv new file mode 100644 index 000000000..9dd402057 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -0,0 +1 @@ +Derive([("dev","/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev","",""),("out","/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults","","")],[],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"builder\":\"/bin/bash\",\"name\":\"advanced-attributes-structured-attrs-defaults\",\"outputs\":[\"out\",\"dev\"],\"system\":\"my-system\"}"),("dev","/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev"),("out","/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix new file mode 100644 index 000000000..0c13a7691 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix @@ -0,0 +1,8 @@ +derivation { + name = "advanced-attributes-structured-attrs-defaults"; + system = "my-system"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; + outputs = [ "out" "dev" ]; + __structuredAttrs = true; +} diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs.drv b/tests/functional/derivation/advanced-attributes-structured-attrs.drv new file mode 100644 index 000000000..e47a41ad5 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs.drv @@ -0,0 +1 @@ +Derive([("bin","/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin","",""),("dev","/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev","",""),("out","/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs","","")],[("/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv",["out"]),("/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv",["out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"],\"disallowedRequisites\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"],\"allowedRequisites\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}"),("bin","/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin"),("dev","/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev"),("out","/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs.nix b/tests/functional/derivation/advanced-attributes-structured-attrs.nix new file mode 100644 index 000000000..0044b65fd --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs.nix @@ -0,0 +1,45 @@ +let + system = "my-system"; + foo = derivation { + inherit system; + name = "foo"; + builder = "/bin/bash"; + args = ["-c" "echo foo > $out"]; + }; + bar = derivation { + inherit system; + name = "bar"; + builder = "/bin/bash"; + args = ["-c" "echo bar > $out"]; + }; +in +derivation { + inherit system; + name = "advanced-attributes-structured-attrs"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; + __sandboxProfile = "sandcastle"; + __noChroot = true; + __impureHostDeps = ["/usr/bin/ditto"]; + impureEnvVars = ["UNICORN"]; + __darwinAllowLocalNetworking = true; + outputs = [ "out" "bin" "dev" ]; + __structuredAttrs = true; + outputChecks = { + out = { + allowedReferences = [foo]; + allowedRequisites = [foo]; + }; + bin = { + disallowedReferences = [bar]; + disallowedRequisites = [bar]; + }; + dev = { + maxSize = 789; + maxClosureSize = 5909; + }; + }; + requiredSystemFeatures = ["rainbow" "uid-range"]; + preferLocalBuild = true; + allowSubstitutes = false; +} diff --git a/tests/functional/derivation/advanced-attributes.drv b/tests/functional/derivation/advanced-attributes.drv new file mode 100644 index 000000000..ec3112ab2 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes.drv @@ -0,0 +1 @@ +Derive([("out","/nix/store/33a6fdmn8q9ih9d7npbnrxn2q56a4l8q-advanced-attributes","","")],[("/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv",["out"]),("/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv",["out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__darwinAllowLocalNetworking","1"),("__impureHostDeps","/usr/bin/ditto"),("__noChroot","1"),("__sandboxProfile","sandcastle"),("allowSubstitutes",""),("allowedReferences","/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"),("allowedRequisites","/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"),("builder","/bin/bash"),("disallowedReferences","/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"),("disallowedRequisites","/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"),("impureEnvVars","UNICORN"),("name","advanced-attributes"),("out","/nix/store/33a6fdmn8q9ih9d7npbnrxn2q56a4l8q-advanced-attributes"),("preferLocalBuild","1"),("requiredSystemFeatures","rainbow uid-range"),("system","my-system")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes.nix b/tests/functional/derivation/advanced-attributes.nix new file mode 100644 index 000000000..ff680c567 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes.nix @@ -0,0 +1,33 @@ +let + system = "my-system"; + foo = derivation { + inherit system; + name = "foo"; + builder = "/bin/bash"; + args = ["-c" "echo foo > $out"]; + }; + bar = derivation { + inherit system; + name = "bar"; + builder = "/bin/bash"; + args = ["-c" "echo bar > $out"]; + }; +in +derivation { + inherit system; + name = "advanced-attributes"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; + __sandboxProfile = "sandcastle"; + __noChroot = true; + __impureHostDeps = ["/usr/bin/ditto"]; + impureEnvVars = ["UNICORN"]; + __darwinAllowLocalNetworking = true; + allowedReferences = [foo]; + allowedRequisites = [foo]; + disallowedReferences = [bar]; + disallowedRequisites = [bar]; + requiredSystemFeatures = ["rainbow" "uid-range"]; + preferLocalBuild = true; + allowSubstitutes = false; +} diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 7e4536323..49ee31284 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -106,6 +106,7 @@ nix_tests = \ eval-store.sh \ why-depends.sh \ derivation-json.sh \ + derivation-advanced-attributes.sh \ import-derivation.sh \ nix_path.sh \ case-hack.sh \ From 7fb14201afdeebc1327ff3ca75dcb4657c51cdc8 Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Sat, 8 Jun 2024 13:10:51 +0200 Subject: [PATCH 368/910] Unit test for derivation "advanced attrs" This tests the parser and JSON format using the DRV files from the tests added in the previous commit. Co-Authored-By: John Ericson --- maintainers/flake-module.nix | 1 - tests/unit/libstore-support/tests/libstore.hh | 30 ++- .../advanced-attributes-defaults.drv | 1 + .../advanced-attributes-defaults.json | 22 ++ ...d-attributes-structured-attrs-defaults.drv | 1 + ...-attributes-structured-attrs-defaults.json | 24 ++ .../advanced-attributes-structured-attrs.drv | 1 + .../advanced-attributes-structured-attrs.json | 41 +++ .../data/derivation/advanced-attributes.drv | 1 + .../libstore/derivation-advanced-attrs.cc | 234 ++++++++++++++++++ .../libutil-support/tests/characterization.hh | 1 + 11 files changed, 345 insertions(+), 12 deletions(-) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv create mode 100644 tests/unit/libstore/data/derivation/advanced-attributes-defaults.json create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv create mode 100644 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv create mode 100644 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes.drv create mode 100644 tests/unit/libstore/derivation-advanced-attrs.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 0bd353f1a..5e4291fcb 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -447,7 +447,6 @@ ''^tests/unit/libfetchers/public-key\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.hh'' - ''^tests/unit/libstore-support/tests/libstore\.hh'' ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' diff --git a/tests/unit/libstore-support/tests/libstore.hh b/tests/unit/libstore-support/tests/libstore.hh index 267188224..84be52c23 100644 --- a/tests/unit/libstore-support/tests/libstore.hh +++ b/tests/unit/libstore-support/tests/libstore.hh @@ -8,19 +8,27 @@ namespace nix { -class LibStoreTest : public virtual ::testing::Test { - public: - static void SetUpTestSuite() { - initLibStore(false); - } +class LibStoreTest : public virtual ::testing::Test +{ +public: + static void SetUpTestSuite() + { + initLibStore(false); + } - protected: - LibStoreTest() - : store(openStore("dummy://")) - { } +protected: + LibStoreTest() + : store(openStore({ + .variant = + StoreReference::Specified{ + .scheme = "dummy", + }, + .params = {}, + })) + { + } - ref store; + ref store; }; - } /* namespace nix */ diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv new file mode 120000 index 000000000..353090ad8 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json new file mode 100644 index 000000000..d58e7d5b5 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json @@ -0,0 +1,22 @@ +{ + "args": [ + "-c", + "echo hello > $out" + ], + "builder": "/bin/bash", + "env": { + "builder": "/bin/bash", + "name": "advanced-attributes-defaults", + "out": "/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults", + "system": "my-system" + }, + "inputDrvs": {}, + "inputSrcs": [], + "name": "advanced-attributes-defaults", + "outputs": { + "out": { + "path": "/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults" + } + }, + "system": "my-system" +} diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv new file mode 120000 index 000000000..11713da12 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json new file mode 100644 index 000000000..473d006ac --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json @@ -0,0 +1,24 @@ +{ + "args": [ + "-c", + "echo hello > $out" + ], + "builder": "/bin/bash", + "env": { + "__json": "{\"builder\":\"/bin/bash\",\"name\":\"advanced-attributes-structured-attrs-defaults\",\"outputs\":[\"out\",\"dev\"],\"system\":\"my-system\"}", + "dev": "/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev", + "out": "/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults" + }, + "inputDrvs": {}, + "inputSrcs": [], + "name": "advanced-attributes-structured-attrs-defaults", + "outputs": { + "dev": { + "path": "/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev" + }, + "out": { + "path": "/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults" + } + }, + "system": "my-system" +} diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv new file mode 120000 index 000000000..962f8ea3f --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json new file mode 100644 index 000000000..324428124 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json @@ -0,0 +1,41 @@ +{ + "args": [ + "-c", + "echo hello > $out" + ], + "builder": "/bin/bash", + "env": { + "__json": "{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"],\"disallowedRequisites\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"],\"allowedRequisites\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}", + "bin": "/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin", + "dev": "/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev", + "out": "/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs" + }, + "inputDrvs": { + "/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv": { + "dynamicOutputs": {}, + "outputs": [ + "out" + ] + }, + "/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv": { + "dynamicOutputs": {}, + "outputs": [ + "out" + ] + } + }, + "inputSrcs": [], + "name": "advanced-attributes-structured-attrs", + "outputs": { + "bin": { + "path": "/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin" + }, + "dev": { + "path": "/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev" + }, + "out": { + "path": "/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs" + } + }, + "system": "my-system" +} diff --git a/tests/unit/libstore/data/derivation/advanced-attributes.drv b/tests/unit/libstore/data/derivation/advanced-attributes.drv new file mode 120000 index 000000000..2a53a05ca --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes.drv \ No newline at end of file diff --git a/tests/unit/libstore/derivation-advanced-attrs.cc b/tests/unit/libstore/derivation-advanced-attrs.cc new file mode 100644 index 000000000..26cf947a8 --- /dev/null +++ b/tests/unit/libstore/derivation-advanced-attrs.cc @@ -0,0 +1,234 @@ +#include +#include +#include + +#include "experimental-features.hh" +#include "derivations.hh" + +#include "tests/libstore.hh" +#include "tests/characterization.hh" +#include "parsed-derivations.hh" +#include "types.hh" + +namespace nix { + +using nlohmann::json; + +class DerivationAdvancedAttrsTest : public CharacterizationTest, public LibStoreTest +{ + Path unitTestData = getUnitTestData() + "/derivation"; + +public: + Path goldenMaster(std::string_view testStem) const override + { + return unitTestData + "/" + testStem; + } +}; + +#define TEST_ATERM_JSON(STEM, NAME) \ + TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_from_json) \ + { \ + readTest(NAME ".json", [&](const auto & encoded_) { \ + auto encoded = json::parse(encoded_); \ + /* Use DRV file instead of C++ literal as source of truth. */ \ + auto aterm = readFile(goldenMaster(NAME ".drv")); \ + auto expected = parseDerivation(*store, std::move(aterm), NAME); \ + Derivation got = Derivation::fromJSON(*store, encoded); \ + EXPECT_EQ(got, expected); \ + }); \ + } \ + \ + TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_to_json) \ + { \ + writeTest( \ + NAME ".json", \ + [&]() -> json { \ + /* Use DRV file instead of C++ literal as source of truth. */ \ + auto aterm = readFile(goldenMaster(NAME ".drv")); \ + return parseDerivation(*store, std::move(aterm), NAME).toJSON(*store); \ + }, \ + [](const auto & file) { return json::parse(readFile(file)); }, \ + [](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \ + } \ + \ + TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_from_aterm) \ + { \ + readTest(NAME ".drv", [&](auto encoded) { \ + /* Use JSON file instead of C++ literal as source of truth. */ \ + auto json = json::parse(readFile(goldenMaster(NAME ".json"))); \ + auto expected = Derivation::fromJSON(*store, json); \ + auto got = parseDerivation(*store, std::move(encoded), NAME); \ + EXPECT_EQ(got.toJSON(*store), expected.toJSON(*store)); \ + EXPECT_EQ(got, expected); \ + }); \ + } \ + \ + /* No corresponding write test, because we need to read the drv to write the json file */ + +TEST_ATERM_JSON(advancedAttributes_defaults, "advanced-attributes-defaults"); +TEST_ATERM_JSON(advancedAttributes, "advanced-attributes-defaults"); +TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attributes-structured-attrs"); +TEST_ATERM_JSON(advancedAttributes_structuredAttrs, "advanced-attributes-structured-attrs-defaults"); + +#undef TEST_ATERM_JSON + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_defaults) +{ + readTest("advanced-attributes-defaults.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), ""); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), false); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), false); + EXPECT_EQ(parsedDrv.getStringsAttr("allowedReferences"), std::nullopt); + EXPECT_EQ(parsedDrv.getStringsAttr("allowedRequisites"), std::nullopt); + EXPECT_EQ(parsedDrv.getStringsAttr("disallowedReferences"), std::nullopt); + EXPECT_EQ(parsedDrv.getStringsAttr("disallowedRequisites"), std::nullopt); + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), StringSet()); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), true); + EXPECT_EQ(parsedDrv.useUidRange(), false); + }); +}; + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes) +{ + readTest("advanced-attributes.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + StringSet systemFeatures{"rainbow", "uid-range"}; + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), "sandcastle"); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), true); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings{"/usr/bin/ditto"}); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings{"UNICORN"}); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), true); + EXPECT_EQ( + parsedDrv.getStringsAttr("allowedReferences"), Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + EXPECT_EQ( + parsedDrv.getStringsAttr("allowedRequisites"), Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + EXPECT_EQ( + parsedDrv.getStringsAttr("disallowedReferences"), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + EXPECT_EQ( + parsedDrv.getStringsAttr("disallowedRequisites"), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), systemFeatures); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), false); + EXPECT_EQ(parsedDrv.useUidRange(), true); + }); +}; + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttrs_defaults) +{ + readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), ""); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), false); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), false); + + { + auto structuredAttrs_ = parsedDrv.getStructuredAttrs(); + ASSERT_TRUE(structuredAttrs_); + auto & structuredAttrs = *structuredAttrs_; + + auto outputChecks_ = get(structuredAttrs, "outputChecks"); + ASSERT_FALSE(outputChecks_); + } + + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), StringSet()); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), true); + EXPECT_EQ(parsedDrv.useUidRange(), false); + }); +}; + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttrs) +{ + readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + StringSet systemFeatures{"rainbow", "uid-range"}; + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), "sandcastle"); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), true); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings{"/usr/bin/ditto"}); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings{"UNICORN"}); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), true); + + { + auto structuredAttrs_ = parsedDrv.getStructuredAttrs(); + ASSERT_TRUE(structuredAttrs_); + auto & structuredAttrs = *structuredAttrs_; + + auto outputChecks_ = get(structuredAttrs, "outputChecks"); + ASSERT_TRUE(outputChecks_); + auto & outputChecks = *outputChecks_; + + { + auto output_ = get(outputChecks, "out"); + ASSERT_TRUE(output_); + auto & output = *output_; + EXPECT_EQ( + get(output, "allowedReferences")->get(), + Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + EXPECT_EQ( + get(output, "allowedRequisites")->get(), + Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + } + + { + auto output_ = get(outputChecks, "bin"); + ASSERT_TRUE(output_); + auto & output = *output_; + EXPECT_EQ( + get(output, "disallowedReferences")->get(), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + EXPECT_EQ( + get(output, "disallowedRequisites")->get(), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + } + + { + auto output_ = get(outputChecks, "dev"); + ASSERT_TRUE(output_); + auto & output = *output_; + EXPECT_EQ(get(output, "maxSize")->get(), 789); + EXPECT_EQ(get(output, "maxClosureSize")->get(), 5909); + } + } + + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), systemFeatures); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), false); + EXPECT_EQ(parsedDrv.useUidRange(), true); + }); +}; + +} diff --git a/tests/unit/libutil-support/tests/characterization.hh b/tests/unit/libutil-support/tests/characterization.hh index c2f686dbf..19ba824ac 100644 --- a/tests/unit/libutil-support/tests/characterization.hh +++ b/tests/unit/libutil-support/tests/characterization.hh @@ -5,6 +5,7 @@ #include "types.hh" #include "environment-variables.hh" +#include "file-system.hh" namespace nix { From 64e599ebe162758a7e077a6c4e319649374307e2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 17:46:43 -0400 Subject: [PATCH 369/910] Rename `Recursive` -> `NixArchive` For enums: - `FileIngestionMethod` - `FileSerialisationMethod` --- src/libexpr/eval.cc | 2 +- src/libexpr/primops.cc | 10 +++++----- src/libexpr/primops/fetchTree.cc | 2 +- src/libfetchers/fetch-to-store.hh | 2 +- src/libfetchers/fetchers.cc | 2 +- src/libfetchers/mercurial.cc | 2 +- src/libstore/binary-cache-store.cc | 4 ++-- src/libstore/build/worker.cc | 2 +- src/libstore/content-address.cc | 6 +++--- src/libstore/daemon.cc | 12 ++++++------ src/libstore/dummy-store.cc | 4 ++-- src/libstore/legacy-ssh-store.hh | 4 ++-- src/libstore/local-store.cc | 8 ++++---- src/libstore/make-content-addressed.cc | 2 +- src/libstore/optimise-store.cc | 4 ++-- src/libstore/remote-store.cc | 12 ++++++------ src/libstore/remote-store.hh | 4 ++-- src/libstore/store-api.cc | 12 ++++++------ src/libstore/store-api.hh | 10 +++++----- src/libstore/store-dir-config.hh | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 6 +++--- src/libutil/file-content-address.cc | 12 ++++++------ src/libutil/file-content-address.hh | 10 +++++----- src/nix-store/nix-store.cc | 6 +++--- src/nix/add-to-store.cc | 2 +- src/nix/hash.cc | 8 ++++---- src/nix/prefetch.cc | 2 +- src/nix/profile.cc | 2 +- src/perl/lib/Nix/Store.xs | 6 +++--- tests/unit/libstore/common-protocol.cc | 2 +- tests/unit/libstore/content-address.cc | 2 +- tests/unit/libstore/derivation.cc | 6 +++--- tests/unit/libstore/nar-info.cc | 2 +- tests/unit/libstore/path-info.cc | 2 +- tests/unit/libstore/serve-protocol.cc | 4 ++-- tests/unit/libstore/worker-protocol.cc | 4 ++-- tests/unit/libutil/file-content-address.cc | 4 ++-- 37 files changed, 93 insertions(+), 93 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7aaec2e73..fc211e694 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2303,7 +2303,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat path.resolveSymlinks(), settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy, path.baseName(), - FileIngestionMethod::Recursive, + FileIngestionMethod::NixArchive, nullptr, repair); allowPath(dstPath); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 98649f081..02b970112 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1209,7 +1209,7 @@ static void derivationStrictInternal( auto handleHashMode = [&](const std::string_view s) { if (s == "recursive") { // back compat, new name is "nar" - ingestionMethod = FileIngestionMethod::Recursive; + ingestionMethod = FileIngestionMethod::NixArchive; } else try { ingestionMethod = ContentAddressMethod::parse(s); } catch (UsageError &) { @@ -1432,7 +1432,7 @@ static void derivationStrictInternal( .atPos(v).debugThrow(); auto ha = outputHashAlgo.value_or(HashAlgorithm::SHA256); - auto method = ingestionMethod.value_or(FileIngestionMethod::Recursive); + auto method = ingestionMethod.value_or(FileIngestionMethod::NixArchive); for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); @@ -2391,7 +2391,7 @@ static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * arg "while evaluating the second argument (the path to filter) passed to 'builtins.filterSource'"); state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource"); - addPath(state, pos, path.baseName(), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); + addPath(state, pos, path.baseName(), path, args[0], FileIngestionMethod::NixArchive, std::nullopt, v, context); } static RegisterPrimOp primop_filterSource({ @@ -2454,7 +2454,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value std::optional path; std::string name; Value * filterFun = nullptr; - ContentAddressMethod method = FileIngestionMethod::Recursive; + ContentAddressMethod method = FileIngestionMethod::NixArchive; std::optional expectedHash; NixStringContext context; @@ -2470,7 +2470,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value state.forceFunction(*(filterFun = attr.value), attr.pos, "while evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") method = state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") - ? FileIngestionMethod::Recursive + ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a99a71577..af152f4af 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -468,7 +468,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v auto expectedPath = state.store->makeFixedOutputPath( name, FixedOutputInfo { - .method = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat, + .method = unpack ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat, .hash = *expectedHash, .references = {} }); diff --git a/src/libfetchers/fetch-to-store.hh b/src/libfetchers/fetch-to-store.hh index 81af1e240..95d1e6b01 100644 --- a/src/libfetchers/fetch-to-store.hh +++ b/src/libfetchers/fetch-to-store.hh @@ -18,7 +18,7 @@ StorePath fetchToStore( const SourcePath & path, FetchMode mode, std::string_view name = "source", - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, PathFilter * filter = nullptr, RepairFlag repair = NoRepair); diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 73923907c..170a8910c 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -305,7 +305,7 @@ StorePath Input::computeStorePath(Store & store) const if (!narHash) throw Error("cannot compute store path for unlocked input '%s'", to_string()); return store.makeFixedOutputPath(getName(), FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = *narHash, .references = {}, }); diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 7bdf1e937..4b80cbe9b 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -213,7 +213,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), {getFSSourceAccessor(), CanonPath(actualPath)}, - FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, + FileIngestionMethod::NixArchive, HashAlgorithm::SHA256, {}, filter); return storePath; diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 95a8d5a7a..e8c8892b3 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -322,7 +322,7 @@ StorePath BinaryCacheStore::addToStoreFromDump( if (static_cast(dumpMethod) == hashMethod.getFileIngestionMethod()) caHash = hashString(HashAlgorithm::SHA256, dump2.s); switch (dumpMethod) { - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: // The dump is already NAR in this case, just use it. nar = dump2.s; break; @@ -339,7 +339,7 @@ StorePath BinaryCacheStore::addToStoreFromDump( } else { // Otherwise, we have to do th same hashing as NAR so our single // hash will suffice for both purposes. - if (dumpMethod != FileSerialisationMethod::Recursive || hashAlgo != HashAlgorithm::SHA256) + if (dumpMethod != FileSerialisationMethod::NixArchive || hashAlgo != HashAlgorithm::SHA256) unsupported("addToStoreFromDump"); } StringSource narDump { nar }; diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index b53dc771a..31cfa8adc 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -530,7 +530,7 @@ bool Worker::pathContentsGood(const StorePath & path) else { auto current = hashPath( {store.getFSAccessor(), CanonPath(store.printStorePath(path))}, - FileIngestionMethod::Recursive, info->narHash.algo).first; + FileIngestionMethod::NixArchive, info->narHash.algo).first; Hash nullHash(HashAlgorithm::SHA256); res = info->narHash == nullHash || info->narHash == current; } diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc index 4ed4f2de5..fa06c4aa3 100644 --- a/src/libstore/content-address.cc +++ b/src/libstore/content-address.cc @@ -9,7 +9,7 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m) switch (m) { case FileIngestionMethod::Flat: return ""; - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: return "r:"; case FileIngestionMethod::Git: experimentalFeatureSettings.require(Xp::GitHashing); @@ -52,7 +52,7 @@ std::string_view ContentAddressMethod::renderPrefix() const ContentAddressMethod ContentAddressMethod::parsePrefix(std::string_view & m) { if (splitPrefix(m, "r:")) { - return FileIngestionMethod::Recursive; + return FileIngestionMethod::NixArchive; } else if (splitPrefix(m, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); @@ -137,7 +137,7 @@ static std::pair parseContentAddressMethodP // Parse method auto method = FileIngestionMethod::Flat; if (splitPrefix(rest, "r:")) - method = FileIngestionMethod::Recursive; + method = FileIngestionMethod::NixArchive; else if (splitPrefix(rest, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); method = FileIngestionMethod::Git; diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index fe60cb918..788c5e2ea 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -415,12 +415,12 @@ static void performOp(TunnelLogger * logger, ref store, case FileIngestionMethod::Flat: dumpMethod = FileSerialisationMethod::Flat; break; - case FileIngestionMethod::Recursive: - dumpMethod = FileSerialisationMethod::Recursive; + case FileIngestionMethod::NixArchive: + dumpMethod = FileSerialisationMethod::NixArchive; break; case FileIngestionMethod::Git: // Use NAR; Git is not a serialization method - dumpMethod = FileSerialisationMethod::Recursive; + dumpMethod = FileSerialisationMethod::NixArchive; break; default: assert(false); @@ -441,13 +441,13 @@ static void performOp(TunnelLogger * logger, ref store, uint8_t recursive; std::string hashAlgoRaw; from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; - if (recursive > (uint8_t) FileIngestionMethod::Recursive) + if (recursive > (uint8_t) FileIngestionMethod::NixArchive) throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive); method = FileIngestionMethod { recursive }; /* Compatibility hack. */ if (!fixed) { hashAlgoRaw = "sha256"; - method = FileIngestionMethod::Recursive; + method = FileIngestionMethod::NixArchive; } hashAlgo = parseHashAlgo(hashAlgoRaw); } @@ -468,7 +468,7 @@ static void performOp(TunnelLogger * logger, ref store, }); logger->startWork(); auto path = store->addToStoreFromDump( - *dumpSource, baseName, FileSerialisationMethod::Recursive, method, hashAlgo); + *dumpSource, baseName, FileSerialisationMethod::NixArchive, method, hashAlgo); logger->stopWork(); to << store->printStorePath(path); diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index 0d5d03091..17ebaace6 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -64,8 +64,8 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store virtual StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) override diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index b683ed580..db49188ec 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -76,8 +76,8 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor virtual StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) override diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 676a035fa..07ace70d0 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1155,7 +1155,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, auto fim = specified.method.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: { HashModuloSink caSink { specified.hash.algo, @@ -1314,7 +1314,7 @@ StorePath LocalStore::addToStoreFromDump( auto fim = hashMethod.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: restorePath(realPath, dumpSource, (FileSerialisationMethod) fim); break; case FileIngestionMethod::Git: @@ -1330,7 +1330,7 @@ StorePath LocalStore::addToStoreFromDump( /* For computing the nar hash. In recursive SHA-256 mode, this is the same as the store hash, so no need to do it again. */ auto narHash = std::pair { dumpHash, size }; - if (dumpMethod != FileSerialisationMethod::Recursive || hashAlgo != HashAlgorithm::SHA256) { + if (dumpMethod != FileSerialisationMethod::NixArchive || hashAlgo != HashAlgorithm::SHA256) { HashSink narSink { HashAlgorithm::SHA256 }; dumpPath(realPath, narSink); narHash = narSink.finish(); @@ -1423,7 +1423,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) PosixSourceAccessor accessor; std::string hash = hashPath( PosixSourceAccessor::createAtRoot(link.path()), - FileIngestionMethod::Recursive, HashAlgorithm::SHA256).first.to_string(HashFormat::Nix32, false); + FileIngestionMethod::NixArchive, HashAlgorithm::SHA256).first.to_string(HashFormat::Nix32, false); if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", link.path(), name, hash); diff --git a/src/libstore/make-content-addressed.cc b/src/libstore/make-content-addressed.cc index 170fe67b9..a3130d7cc 100644 --- a/src/libstore/make-content-addressed.cc +++ b/src/libstore/make-content-addressed.cc @@ -52,7 +52,7 @@ std::map makeContentAddressed( dstStore, path.name(), FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = narModuloHash, .references = std::move(refs), }, diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index e9b6d2d50..9d903f218 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -151,7 +151,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, Hash hash = ({ hashPath( {make_ref(), CanonPath(path)}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256).first; }); debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true)); @@ -165,7 +165,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, || (repair && hash != ({ hashPath( PosixSourceAccessor::createAtRoot(linkPath), - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256).first; }))) { // XXX: Consider overwriting linkPath with our valid version. diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index d6efc14f9..9adad9c2a 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -406,8 +406,8 @@ ref RemoteStore::addCAToStore( conn->to << WorkerProto::Op::AddToStore << name - << ((hashAlgo == HashAlgorithm::SHA256 && fim == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */ - << (fim == FileIngestionMethod::Recursive ? 1 : 0) + << ((hashAlgo == HashAlgorithm::SHA256 && fim == FileIngestionMethod::NixArchive) ? 0 : 1) /* backwards compatibility hack */ + << (fim == FileIngestionMethod::NixArchive ? 1 : 0) << printHashAlgo(hashAlgo); try { @@ -415,7 +415,7 @@ ref RemoteStore::addCAToStore( connections->incCapacity(); { Finally cleanup([&]() { connections->decCapacity(); }); - if (fim == FileIngestionMethod::Recursive) { + if (fim == FileIngestionMethod::NixArchive) { dump.drainInto(conn->to); } else { std::string contents = dump.drain(); @@ -457,12 +457,12 @@ StorePath RemoteStore::addToStoreFromDump( case FileIngestionMethod::Flat: fsm = FileSerialisationMethod::Flat; break; - case FileIngestionMethod::Recursive: - fsm = FileSerialisationMethod::Recursive; + case FileIngestionMethod::NixArchive: + fsm = FileSerialisationMethod::NixArchive; break; case FileIngestionMethod::Git: // Use NAR; Git is not a serialization method - fsm = FileSerialisationMethod::Recursive; + fsm = FileSerialisationMethod::NixArchive; break; default: assert(false); diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index d630adc08..4e1896268 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -87,8 +87,8 @@ public: StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) override; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 2edb56510..8c957ff1a 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -122,7 +122,7 @@ StorePath StoreDirConfig::makeFixedOutputPath(std::string_view name, const Fixed if (info.method == FileIngestionMethod::Git && info.hash.algo != HashAlgorithm::SHA1) throw Error("Git file ingestion must use SHA-1 hash"); - if (info.hash.algo == HashAlgorithm::SHA256 && info.method == FileIngestionMethod::Recursive) { + if (info.hash.algo == HashAlgorithm::SHA256 && info.method == FileIngestionMethod::NixArchive) { return makeStorePath(makeType(*this, "source", info.references), info.hash, name); } else { if (!info.references.empty()) { @@ -200,12 +200,12 @@ StorePath Store::addToStore( case FileIngestionMethod::Flat: fsm = FileSerialisationMethod::Flat; break; - case FileIngestionMethod::Recursive: - fsm = FileSerialisationMethod::Recursive; + case FileIngestionMethod::NixArchive: + fsm = FileSerialisationMethod::NixArchive; break; case FileIngestionMethod::Git: // Use NAR; Git is not a serialization method - fsm = FileSerialisationMethod::Recursive; + fsm = FileSerialisationMethod::NixArchive; break; } auto source = sinkToSource([&](Sink & sink) { @@ -356,7 +356,7 @@ ValidPathInfo Store::addToStoreSlow( RegularFileSink fileSink { caHashSink }; TeeSink unusualHashTee { narHashSink, caHashSink }; - auto & narSink = method == FileIngestionMethod::Recursive && hashAlgo != HashAlgorithm::SHA256 + auto & narSink = method == FileIngestionMethod::NixArchive && hashAlgo != HashAlgorithm::SHA256 ? static_cast(unusualHashTee) : narHashSink; @@ -384,7 +384,7 @@ ValidPathInfo Store::addToStoreSlow( finish. */ auto [narHash, narSize] = narHashSink.finish(); - auto hash = method == FileIngestionMethod::Recursive && hashAlgo == HashAlgorithm::SHA256 + auto hash = method == FileIngestionMethod::NixArchive && hashAlgo == HashAlgorithm::SHA256 ? narHash : method == FileIngestionMethod::Git ? git::dumpHash(hashAlgo, srcPath).hash diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 15712458c..e719f9bf9 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -441,7 +441,7 @@ public: virtual StorePath addToStore( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), PathFilter & filter = defaultPathFilter, @@ -455,7 +455,7 @@ public: ValidPathInfo addToStoreSlow( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), std::optional expectedCAHash = {}); @@ -470,7 +470,7 @@ public: * * @param dumpMethod What serialisation format is `dump`, i.e. how * to deserialize it. Must either match hashMethod or be - * `FileSerialisationMethod::Recursive`. + * `FileSerialisationMethod::NixArchive`. * * @param hashMethod How content addressing? Need not match be the * same as `dumpMethod`. @@ -480,8 +480,8 @@ public: virtual StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) = 0; diff --git a/src/libstore/store-dir-config.hh b/src/libstore/store-dir-config.hh index 643f8854d..fe86ce40d 100644 --- a/src/libstore/store-dir-config.hh +++ b/src/libstore/store-dir-config.hh @@ -97,7 +97,7 @@ struct StoreDirConfig : public Config std::pair computeStorePath( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = {}, PathFilter & filter = defaultPathFilter) const; diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 95df0fdee..ab461d428 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -2489,7 +2489,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() auto fim = outputHash.method.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: { HashModuloSink caSink { outputHash.hashAlgo, oldHashPart }; auto fim = outputHash.method.getFileIngestionMethod(); @@ -2531,7 +2531,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() { HashResult narHashAndSize = hashPath( {getFSSourceAccessor(), CanonPath(actualPath)}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256); newInfo0.narHash = narHashAndSize.first; newInfo0.narSize = narHashAndSize.second; } @@ -2554,7 +2554,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() rewriteOutput(outputRewrites); HashResult narHashAndSize = hashPath( {getFSSourceAccessor(), CanonPath(actualPath)}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256); ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first }; newInfo0.narSize = narHashAndSize.second; auto refs = rewriteRefs(); diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 8b1e3117a..438dac7da 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -10,7 +10,7 @@ static std::optional parseFileSerialisationMethodOpt(st if (input == "flat") { return FileSerialisationMethod::Flat; } else if (input == "nar") { - return FileSerialisationMethod::Recursive; + return FileSerialisationMethod::NixArchive; } else { return std::nullopt; } @@ -45,7 +45,7 @@ std::string_view renderFileSerialisationMethod(FileSerialisationMethod method) switch (method) { case FileSerialisationMethod::Flat: return "flat"; - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: return "nar"; default: assert(false); @@ -57,7 +57,7 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method) { switch (method) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: return renderFileSerialisationMethod( static_cast(method)); case FileIngestionMethod::Git: @@ -78,7 +78,7 @@ void dumpPath( case FileSerialisationMethod::Flat: path.readFile(sink); break; - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: path.dumpPath(sink, filter); break; } @@ -94,7 +94,7 @@ void restorePath( case FileSerialisationMethod::Flat: writeFile(path, source); break; - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: restorePath(path, source); break; } @@ -119,7 +119,7 @@ std::pair> hashPath( { switch (method) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: { + case FileIngestionMethod::NixArchive: { auto res = hashPath(path, (FileSerialisationMethod) method, ht, filter); return {res.first, {res.second}}; } diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index e216ee4a7..f3f5589de 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -35,14 +35,14 @@ enum struct FileSerialisationMethod : uint8_t { * See `file-system-object/content-address.md#serial-nix-archive` in * the manual. */ - Recursive, + NixArchive, }; /** * Parse a `FileSerialisationMethod` by name. Choice of: * * - `flat`: `FileSerialisationMethod::Flat` - * - `nar`: `FileSerialisationMethod::Recursive` + * - `nar`: `FileSerialisationMethod::NixArchive` * * Opposite of `renderFileSerialisationMethod`. */ @@ -107,12 +107,12 @@ enum struct FileIngestionMethod : uint8_t { Flat, /** - * Hash `FileSerialisationMethod::Recursive` serialisation. + * Hash `FileSerialisationMethod::NixArchive` serialisation. * * See `file-system-object/content-address.md#serial-flat` in the * manual. */ - Recursive, + NixArchive, /** * Git hashing. @@ -127,7 +127,7 @@ enum struct FileIngestionMethod : uint8_t { * Parse a `FileIngestionMethod` by name. Choice of: * * - `flat`: `FileIngestionMethod::Flat` - * - `nar`: `FileIngestionMethod::Recursive` + * - `nar`: `FileIngestionMethod::NixArchive` * - `git`: `FileIngestionMethod::Git` * * Opposite of `renderFileIngestionMethod`. diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 6d028e0a7..178702f91 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -197,7 +197,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs) auto method = FileIngestionMethod::Flat; for (auto & i : opFlags) - if (i == "--recursive") method = FileIngestionMethod::Recursive; + if (i == "--recursive") method = FileIngestionMethod::NixArchive; else throw UsageError("unknown flag '%1%'", i); if (opArgs.empty()) @@ -223,7 +223,7 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs) auto method = FileIngestionMethod::Flat; for (auto i : opFlags) - if (i == "--recursive") method = FileIngestionMethod::Recursive; + if (i == "--recursive") method = FileIngestionMethod::NixArchive; else throw UsageError("unknown flag '%1%'", i); if (opArgs.size() != 3) @@ -563,7 +563,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) if (!hashGiven) { HashResult hash = hashPath( {store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256); info->narHash = hash.first; info->narSize = hash.second; } diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index af6743375..ed46254f3 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -12,7 +12,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand { Path path; std::optional namePart; - ContentAddressMethod caMethod = FileIngestionMethod::Recursive; + ContentAddressMethod caMethod = FileIngestionMethod::NixArchive; HashAlgorithm hashAlgo = HashAlgorithm::SHA256; CmdAddToStore() diff --git a/src/nix/hash.cc b/src/nix/hash.cc index f57b224d2..62266fda1 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -68,7 +68,7 @@ struct CmdHashBase : Command switch (mode) { case FileIngestionMethod::Flat: return "print cryptographic hash of a regular file"; - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: return "print cryptographic hash of the NAR serialisation of a path"; case FileIngestionMethod::Git: return "print cryptographic hash of the Git serialisation of a path"; @@ -91,7 +91,7 @@ struct CmdHashBase : Command Hash h { HashAlgorithm::SHA256 }; // throwaway def to appease C++ switch (mode) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: { auto hashSink = makeSink(); dumpPath(path2, *hashSink, (FileSerialisationMethod) mode); @@ -126,7 +126,7 @@ struct CmdHashBase : Command struct CmdHashPath : CmdHashBase { CmdHashPath() - : CmdHashBase(FileIngestionMethod::Recursive) + : CmdHashBase(FileIngestionMethod::NixArchive) { addFlag(flag::hashAlgo("algo", &hashAlgo)); addFlag(flag::fileIngestionMethod(&mode)); @@ -311,7 +311,7 @@ static int compatNixHash(int argc, char * * argv) }); if (op == opHash) { - CmdHashBase cmd(flat ? FileIngestionMethod::Flat : FileIngestionMethod::Recursive); + CmdHashBase cmd(flat ? FileIngestionMethod::Flat : FileIngestionMethod::NixArchive); if (!hashAlgo.has_value()) hashAlgo = HashAlgorithm::MD5; cmd.hashAlgo = hashAlgo.value(); cmd.hashFormat = hashFormat; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 3ce52acc5..143935572 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -57,7 +57,7 @@ std::tuple prefetchFile( bool unpack, bool executable) { - auto ingestionMethod = unpack || executable ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; + auto ingestionMethod = unpack || executable ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; /* Figure out a name in the Nix store. */ if (!name) { diff --git a/src/nix/profile.cc b/src/nix/profile.cc index a5a40e4f6..c89b8c9bd 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -258,7 +258,7 @@ struct ProfileManifest *store, "profile", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = narHash, .references = { .others = std::move(references), diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index 15eb5c4f8..e8e209d97 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -259,7 +259,7 @@ hashPath(char * algo, int base32, char * path) try { Hash h = hashPath( PosixSourceAccessor::createAtRoot(path), - FileIngestionMethod::Recursive, parseHashAlgo(algo)).first; + FileIngestionMethod::NixArchive, parseHashAlgo(algo)).first; auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { @@ -335,7 +335,7 @@ SV * StoreWrapper::addToStore(char * srcPath, int recursive, char * algo) PPCODE: try { - auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; + auto method = recursive ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; auto path = THIS->store->addToStore( std::string(baseNameOf(srcPath)), PosixSourceAccessor::createAtRoot(srcPath), @@ -351,7 +351,7 @@ StoreWrapper::makeFixedOutputPath(int recursive, char * algo, char * hash, char PPCODE: try { auto h = Hash::parseAny(hash, parseHashAlgo(algo)); - auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; + auto method = recursive ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; auto path = THIS->store->makeFixedOutputPath(name, FixedOutputInfo { .method = method, .hash = h, diff --git a/tests/unit/libstore/common-protocol.cc b/tests/unit/libstore/common-protocol.cc index d23805fc3..2c629b601 100644 --- a/tests/unit/libstore/common-protocol.cc +++ b/tests/unit/libstore/common-protocol.cc @@ -91,7 +91,7 @@ CHARACTERIZATION_TEST( .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) diff --git a/tests/unit/libstore/content-address.cc b/tests/unit/libstore/content-address.cc index cc1c7fcc6..f0806bd9a 100644 --- a/tests/unit/libstore/content-address.cc +++ b/tests/unit/libstore/content-address.cc @@ -12,7 +12,7 @@ TEST(ContentAddressMethod, testRoundTripPrintParse_1) { for (const ContentAddressMethod & cam : { ContentAddressMethod { TextIngestionMethod {} }, ContentAddressMethod { FileIngestionMethod::Flat }, - ContentAddressMethod { FileIngestionMethod::Recursive }, + ContentAddressMethod { FileIngestionMethod::NixArchive }, ContentAddressMethod { FileIngestionMethod::Git }, }) { EXPECT_EQ(ContentAddressMethod::parse(cam.render()), cam); diff --git a/tests/unit/libstore/derivation.cc b/tests/unit/libstore/derivation.cc index 7a4b1403a..210813137 100644 --- a/tests/unit/libstore/derivation.cc +++ b/tests/unit/libstore/derivation.cc @@ -117,7 +117,7 @@ TEST_JSON(DerivationTest, caFixedFlat, TEST_JSON(DerivationTest, caFixedNAR, (DerivationOutput::CAFixed { .ca = { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -133,7 +133,7 @@ TEST_JSON(DynDerivationTest, caFixedText, TEST_JSON(CaDerivationTest, caFloating, (DerivationOutput::CAFloating { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") @@ -144,7 +144,7 @@ TEST_JSON(DerivationTest, deferred, TEST_JSON(ImpureDerivationTest, impure, (DerivationOutput::Impure { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") diff --git a/tests/unit/libstore/nar-info.cc b/tests/unit/libstore/nar-info.cc index bd10602e7..a6cb62de4 100644 --- a/tests/unit/libstore/nar-info.cc +++ b/tests/unit/libstore/nar-info.cc @@ -25,7 +25,7 @@ static NarInfo makeNarInfo(const Store & store, bool includeImpureInfo) { store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { diff --git a/tests/unit/libstore/path-info.cc b/tests/unit/libstore/path-info.cc index 06c662b74..7637cb366 100644 --- a/tests/unit/libstore/path-info.cc +++ b/tests/unit/libstore/path-info.cc @@ -32,7 +32,7 @@ static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index ebf0c52b0..17d7153bb 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -63,7 +63,7 @@ VERSIONED_CHARACTERIZATION_TEST( .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -280,7 +280,7 @@ VERSIONED_CHARACTERIZATION_TEST( *LibStoreTest::store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { .others = { diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 70e03a8ab..8d81717c9 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -64,7 +64,7 @@ VERSIONED_CHARACTERIZATION_TEST( .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -512,7 +512,7 @@ VERSIONED_CHARACTERIZATION_TEST( *LibStoreTest::store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { .others = { diff --git a/tests/unit/libutil/file-content-address.cc b/tests/unit/libutil/file-content-address.cc index 294e39806..27d926a87 100644 --- a/tests/unit/libutil/file-content-address.cc +++ b/tests/unit/libutil/file-content-address.cc @@ -11,7 +11,7 @@ namespace nix { TEST(FileSerialisationMethod, testRoundTripPrintParse_1) { for (const FileSerialisationMethod fim : { FileSerialisationMethod::Flat, - FileSerialisationMethod::Recursive, + FileSerialisationMethod::NixArchive, }) { EXPECT_EQ(parseFileSerialisationMethod(renderFileSerialisationMethod(fim)), fim); } @@ -37,7 +37,7 @@ TEST(FileSerialisationMethod, testParseFileSerialisationMethodOptException) { TEST(FileIngestionMethod, testRoundTripPrintParse_1) { for (const FileIngestionMethod fim : { FileIngestionMethod::Flat, - FileIngestionMethod::Recursive, + FileIngestionMethod::NixArchive, FileIngestionMethod::Git, }) { EXPECT_EQ(parseFileIngestionMethod(renderFileIngestionMethod(fim)), fim); From b51e161af57e92691b9d74413e24050ddf9ed2c5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 19:08:28 -0400 Subject: [PATCH 370/910] Cleanup `ContentAddressMethod` to match docs The old `std::variant` is bad because we aren't adding a new case to `FileIngestionMethod` so much as we are defining a separate concept --- store object content addressing rather than file system object content addressing. As such, it is more correct to just create a fresh enumeration. Co-authored-by: Robert Hensing --- src/libexpr/eval.cc | 2 +- src/libexpr/primops.cc | 22 +-- src/libfetchers/fetch-to-store.hh | 2 +- src/libfetchers/mercurial.cc | 2 +- src/libstore/content-address.cc | 217 +++++++++++++++---------- src/libstore/content-address.hh | 76 +++++---- src/libstore/daemon.cc | 12 +- src/libstore/derivations.cc | 6 +- src/libstore/local-store.cc | 2 +- src/libstore/path-info.cc | 32 ++-- src/libstore/remote-store.cc | 19 ++- src/libstore/store-api.cc | 6 +- src/libstore/store-api.hh | 6 +- src/libutil/file-content-address.hh | 2 + src/nix-env/user-env.cc | 2 +- src/nix-store/nix-store.cc | 4 +- src/nix/add-to-store.cc | 4 +- src/nix/develop.cc | 2 +- src/nix/prefetch.cc | 15 +- src/perl/lib/Nix/Store.xs | 2 +- tests/unit/libstore/common-protocol.cc | 8 +- tests/unit/libstore/content-address.cc | 10 +- tests/unit/libstore/derivation.cc | 9 +- tests/unit/libstore/serve-protocol.cc | 8 +- tests/unit/libstore/worker-protocol.cc | 8 +- 25 files changed, 275 insertions(+), 203 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fc211e694..0c45a7436 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2303,7 +2303,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat path.resolveSymlinks(), settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy, path.baseName(), - FileIngestionMethod::NixArchive, + ContentAddressMethod::Raw::NixArchive, nullptr, repair); allowPath(dstPath); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 02b970112..8a71359de 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1209,7 +1209,7 @@ static void derivationStrictInternal( auto handleHashMode = [&](const std::string_view s) { if (s == "recursive") { // back compat, new name is "nar" - ingestionMethod = FileIngestionMethod::NixArchive; + ingestionMethod = ContentAddressMethod::Raw::NixArchive; } else try { ingestionMethod = ContentAddressMethod::parse(s); } catch (UsageError &) { @@ -1217,9 +1217,9 @@ static void derivationStrictInternal( "invalid value '%s' for 'outputHashMode' attribute", s ).atPos(v).debugThrow(); } - if (ingestionMethod == TextIngestionMethod {}) + if (ingestionMethod == ContentAddressMethod::Raw::Text) experimentalFeatureSettings.require(Xp::DynamicDerivations); - if (ingestionMethod == FileIngestionMethod::Git) + if (ingestionMethod == ContentAddressMethod::Raw::Git) experimentalFeatureSettings.require(Xp::GitHashing); }; @@ -1391,7 +1391,7 @@ static void derivationStrictInternal( /* Check whether the derivation name is valid. */ if (isDerivation(drvName) && - !(ingestionMethod == ContentAddressMethod { TextIngestionMethod { } } && + !(ingestionMethod == ContentAddressMethod::Raw::Text && outputs.size() == 1 && *(outputs.begin()) == "out")) { @@ -1413,7 +1413,7 @@ static void derivationStrictInternal( auto h = newHashAllowEmpty(*outputHash, outputHashAlgo); - auto method = ingestionMethod.value_or(FileIngestionMethod::Flat); + auto method = ingestionMethod.value_or(ContentAddressMethod::Raw::Flat); DerivationOutput::CAFixed dof { .ca = ContentAddress { @@ -1432,7 +1432,7 @@ static void derivationStrictInternal( .atPos(v).debugThrow(); auto ha = outputHashAlgo.value_or(HashAlgorithm::SHA256); - auto method = ingestionMethod.value_or(FileIngestionMethod::NixArchive); + auto method = ingestionMethod.value_or(ContentAddressMethod::Raw::NixArchive); for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); @@ -2208,7 +2208,7 @@ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Val }) : ({ StringSource s { contents }; - state.store->addToStoreFromDump(s, name, FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, refs, state.repair); + state.store->addToStoreFromDump(s, name, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, refs, state.repair); }); /* Note: we don't need to add `context' to the context of the @@ -2391,7 +2391,7 @@ static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * arg "while evaluating the second argument (the path to filter) passed to 'builtins.filterSource'"); state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource"); - addPath(state, pos, path.baseName(), path, args[0], FileIngestionMethod::NixArchive, std::nullopt, v, context); + addPath(state, pos, path.baseName(), path, args[0], ContentAddressMethod::Raw::NixArchive, std::nullopt, v, context); } static RegisterPrimOp primop_filterSource({ @@ -2454,7 +2454,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value std::optional path; std::string name; Value * filterFun = nullptr; - ContentAddressMethod method = FileIngestionMethod::NixArchive; + auto method = ContentAddressMethod::Raw::NixArchive; std::optional expectedHash; NixStringContext context; @@ -2470,8 +2470,8 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value state.forceFunction(*(filterFun = attr.value), attr.pos, "while evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") method = state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") - ? FileIngestionMethod::NixArchive - : FileIngestionMethod::Flat; + ? ContentAddressMethod::Raw::NixArchive + : ContentAddressMethod::Raw::Flat; else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256); else diff --git a/src/libfetchers/fetch-to-store.hh b/src/libfetchers/fetch-to-store.hh index 95d1e6b01..c762629f3 100644 --- a/src/libfetchers/fetch-to-store.hh +++ b/src/libfetchers/fetch-to-store.hh @@ -18,7 +18,7 @@ StorePath fetchToStore( const SourcePath & path, FetchMode mode, std::string_view name = "source", - ContentAddressMethod method = FileIngestionMethod::NixArchive, + ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive, PathFilter * filter = nullptr, RepairFlag repair = NoRepair); diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 4b80cbe9b..198795caa 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -213,7 +213,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), {getFSSourceAccessor(), CanonPath(actualPath)}, - FileIngestionMethod::NixArchive, HashAlgorithm::SHA256, {}, + ContentAddressMethod::Raw::NixArchive, HashAlgorithm::SHA256, {}, filter); return storePath; diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc index fa06c4aa3..e1cdfece6 100644 --- a/src/libstore/content-address.cc +++ b/src/libstore/content-address.cc @@ -8,6 +8,7 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m) { switch (m) { case FileIngestionMethod::Flat: + // Not prefixed for back compat return ""; case FileIngestionMethod::NixArchive: return "r:"; @@ -15,91 +16,128 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m) experimentalFeatureSettings.require(Xp::GitHashing); return "git:"; default: - throw Error("impossible, caught both cases"); + assert(false); } } std::string_view ContentAddressMethod::render() const { - return std::visit(overloaded { - [](TextIngestionMethod) -> std::string_view { return "text"; }, - [](FileIngestionMethod m2) { - /* Not prefixed for back compat with things that couldn't produce text before. */ - return renderFileIngestionMethod(m2); - }, - }, raw); + switch (raw) { + case ContentAddressMethod::Raw::Text: + return "text"; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return renderFileIngestionMethod(getFileIngestionMethod()); + default: + assert(false); + } +} + +/** + * **Not surjective** + * + * This is not exposed because `FileIngestionMethod::Flat` maps to + * `ContentAddressMethod::Raw::Flat` and + * `ContentAddressMethod::Raw::Text` alike. We can thus only safely use + * this when the latter is ruled out (e.g. because it is already + * handled). + */ +static ContentAddressMethod fileIngestionMethodToContentAddressMethod(FileIngestionMethod m) +{ + switch (m) { + case FileIngestionMethod::Flat: + return ContentAddressMethod::Raw::Flat; + case FileIngestionMethod::NixArchive: + return ContentAddressMethod::Raw::NixArchive; + case FileIngestionMethod::Git: + return ContentAddressMethod::Raw::Git; + default: + assert(false); + } } ContentAddressMethod ContentAddressMethod::parse(std::string_view m) { if (m == "text") - return TextIngestionMethod {}; + return ContentAddressMethod::Raw::Text; else - return parseFileIngestionMethod(m); + return fileIngestionMethodToContentAddressMethod( + parseFileIngestionMethod(m)); } std::string_view ContentAddressMethod::renderPrefix() const { - return std::visit(overloaded { - [](TextIngestionMethod) -> std::string_view { return "text:"; }, - [](FileIngestionMethod m2) { - /* Not prefixed for back compat with things that couldn't produce text before. */ - return makeFileIngestionPrefix(m2); - }, - }, raw); + switch (raw) { + case ContentAddressMethod::Raw::Text: + return "text:"; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return makeFileIngestionPrefix(getFileIngestionMethod()); + default: + assert(false); + } } ContentAddressMethod ContentAddressMethod::parsePrefix(std::string_view & m) { if (splitPrefix(m, "r:")) { - return FileIngestionMethod::NixArchive; + return ContentAddressMethod::Raw::NixArchive; } else if (splitPrefix(m, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); - return FileIngestionMethod::Git; + return ContentAddressMethod::Raw::Git; } else if (splitPrefix(m, "text:")) { - return TextIngestionMethod {}; + return ContentAddressMethod::Raw::Text; + } + return ContentAddressMethod::Raw::Flat; +} + +/** + * This is slightly more mindful of forward compat in that it uses `fixed:` + * rather than just doing a raw empty prefix or `r:`, which doesn't "save room" + * for future changes very well. + */ +static std::string renderPrefixModern(const ContentAddressMethod & ca) +{ + switch (ca.raw) { + case ContentAddressMethod::Raw::Text: + return "text:"; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return "fixed:" + makeFileIngestionPrefix(ca.getFileIngestionMethod()); + default: + assert(false); } - return FileIngestionMethod::Flat; } std::string ContentAddressMethod::renderWithAlgo(HashAlgorithm ha) const { - return std::visit(overloaded { - [&](const TextIngestionMethod & th) { - return std::string{"text:"} + printHashAlgo(ha); - }, - [&](const FileIngestionMethod & fim) { - return "fixed:" + makeFileIngestionPrefix(fim) + printHashAlgo(ha); - } - }, raw); + return renderPrefixModern(*this) + printHashAlgo(ha); } FileIngestionMethod ContentAddressMethod::getFileIngestionMethod() const { - return std::visit(overloaded { - [&](const TextIngestionMethod & th) { - return FileIngestionMethod::Flat; - }, - [&](const FileIngestionMethod & fim) { - return fim; - } - }, raw); + switch (raw) { + case ContentAddressMethod::Raw::Flat: + return FileIngestionMethod::Flat; + case ContentAddressMethod::Raw::NixArchive: + return FileIngestionMethod::NixArchive; + case ContentAddressMethod::Raw::Git: + return FileIngestionMethod::Git; + case ContentAddressMethod::Raw::Text: + return FileIngestionMethod::Flat; + default: + assert(false); + } } std::string ContentAddress::render() const { - return std::visit(overloaded { - [](const TextIngestionMethod &) -> std::string { - return "text:"; - }, - [](const FileIngestionMethod & method) { - return "fixed:" - + makeFileIngestionPrefix(method); - }, - }, method.raw) - + this->hash.to_string(HashFormat::Nix32, true); + return renderPrefixModern(method) + this->hash.to_string(HashFormat::Nix32, true); } /** @@ -130,17 +168,17 @@ static std::pair parseContentAddressMethodP // No parsing of the ingestion method, "text" only support flat. HashAlgorithm hashAlgo = parseHashAlgorithm_(); return { - TextIngestionMethod {}, + ContentAddressMethod::Raw::Text, std::move(hashAlgo), }; } else if (prefix == "fixed") { // Parse method - auto method = FileIngestionMethod::Flat; + auto method = ContentAddressMethod::Raw::Flat; if (splitPrefix(rest, "r:")) - method = FileIngestionMethod::NixArchive; + method = ContentAddressMethod::Raw::NixArchive; else if (splitPrefix(rest, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); - method = FileIngestionMethod::Git; + method = ContentAddressMethod::Raw::Git; } HashAlgorithm hashAlgo = parseHashAlgorithm_(); return { @@ -201,57 +239,58 @@ size_t StoreReferences::size() const ContentAddressWithReferences ContentAddressWithReferences::withoutRefs(const ContentAddress & ca) noexcept { - return std::visit(overloaded { - [&](const TextIngestionMethod &) -> ContentAddressWithReferences { - return TextInfo { - .hash = ca.hash, - .references = {}, - }; - }, - [&](const FileIngestionMethod & method) -> ContentAddressWithReferences { - return FixedOutputInfo { - .method = method, - .hash = ca.hash, - .references = {}, - }; - }, - }, ca.method.raw); + switch (ca.method.raw) { + case ContentAddressMethod::Raw::Text: + return TextInfo { + .hash = ca.hash, + .references = {}, + }; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return FixedOutputInfo { + .method = ca.method.getFileIngestionMethod(), + .hash = ca.hash, + .references = {}, + }; + default: + assert(false); + } } ContentAddressWithReferences ContentAddressWithReferences::fromParts( ContentAddressMethod method, Hash hash, StoreReferences refs) { - return std::visit(overloaded { - [&](TextIngestionMethod _) -> ContentAddressWithReferences { - if (refs.self) - throw Error("self-reference not allowed with text hashing"); - return ContentAddressWithReferences { - TextInfo { - .hash = std::move(hash), - .references = std::move(refs.others), - } - }; - }, - [&](FileIngestionMethod m2) -> ContentAddressWithReferences { - return ContentAddressWithReferences { - FixedOutputInfo { - .method = m2, - .hash = std::move(hash), - .references = std::move(refs), - } - }; - }, - }, method.raw); + switch (method.raw) { + case ContentAddressMethod::Raw::Text: + if (refs.self) + throw Error("self-reference not allowed with text hashing"); + return TextInfo { + .hash = std::move(hash), + .references = std::move(refs.others), + }; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return FixedOutputInfo { + .method = method.getFileIngestionMethod(), + .hash = std::move(hash), + .references = std::move(refs), + }; + default: + assert(false); + } } ContentAddressMethod ContentAddressWithReferences::getMethod() const { return std::visit(overloaded { [](const TextInfo & th) -> ContentAddressMethod { - return TextIngestionMethod {}; + return ContentAddressMethod::Raw::Text; }, [](const FixedOutputInfo & fsh) -> ContentAddressMethod { - return fsh.method; + return fileIngestionMethodToContentAddressMethod( + fsh.method); }, }, raw); } diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index 5925f8e01..6cc3b7cd9 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -5,7 +5,6 @@ #include "hash.hh" #include "path.hh" #include "file-content-address.hh" -#include "comparator.hh" #include "variant-wrapper.hh" namespace nix { @@ -14,24 +13,6 @@ namespace nix { * Content addressing method */ -/* We only have one way to hash text with references, so this is a single-value - type, mainly useful with std::variant. -*/ - -/** - * The single way we can serialize "text" file system objects. - * - * Somewhat obscure, used by \ref Derivation derivations and - * `builtins.toFile` currently. - * - * TextIngestionMethod is identical to FileIngestionMethod::Fixed except that - * the former may not have self-references and is tagged `text:${algo}:${hash}` - * rather than `fixed:${algo}:${hash}`. The contents of the store path are - * ingested and hashed identically, aside from the slightly different tag and - * restriction on self-references. - */ -struct TextIngestionMethod : std::monostate { }; - /** * Compute the prefix to the hash algorithm which indicates how the * files were ingested. @@ -48,14 +29,51 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m); */ struct ContentAddressMethod { - typedef std::variant< - TextIngestionMethod, - FileIngestionMethod - > Raw; + enum struct Raw { + /** + * Calculate a store path using the `FileIngestionMethod::Flat` + * hash of the file system objects, and references. + * + * See `store-object/content-address.md#method-flat` in the + * manual. + */ + Flat, + + /** + * Calculate a store path using the + * `FileIngestionMethod::NixArchive` hash of the file system + * objects, and references. + * + * See `store-object/content-address.md#method-flat` in the + * manual. + */ + NixArchive, + + /** + * Calculate a store path using the `FileIngestionMethod::Git` + * hash of the file system objects, and references. + * + * Part of `ExperimentalFeature::GitHashing`. + * + * See `store-object/content-address.md#method-git` in the + * manual. + */ + Git, + + /** + * Calculate a store path using the `FileIngestionMethod::Flat` + * hash of the file system objects, and references, but in a + * different way than `ContentAddressMethod::Raw::Flat`. + * + * See `store-object/content-address.md#method-text` in the + * manual. + */ + Text, + }; Raw raw; - GENERATE_CMP(ContentAddressMethod, me->raw); + auto operator <=>(const ContentAddressMethod &) const = default; MAKE_WRAPPER_CONSTRUCTOR(ContentAddressMethod); @@ -141,7 +159,7 @@ struct ContentAddress */ Hash hash; - GENERATE_CMP(ContentAddress, me->method, me->hash); + auto operator <=>(const ContentAddress &) const = default; /** * Compute the content-addressability assertion @@ -200,7 +218,7 @@ struct StoreReferences */ size_t size() const; - GENERATE_CMP(StoreReferences, me->self, me->others); + auto operator <=>(const StoreReferences &) const = default; }; // This matches the additional info that we need for makeTextPath @@ -217,7 +235,7 @@ struct TextInfo */ StorePathSet references; - GENERATE_CMP(TextInfo, me->hash, me->references); + auto operator <=>(const TextInfo &) const = default; }; struct FixedOutputInfo @@ -237,7 +255,7 @@ struct FixedOutputInfo */ StoreReferences references; - GENERATE_CMP(FixedOutputInfo, me->hash, me->references); + auto operator <=>(const FixedOutputInfo &) const = default; }; /** @@ -254,7 +272,7 @@ struct ContentAddressWithReferences Raw raw; - GENERATE_CMP(ContentAddressWithReferences, me->raw); + auto operator <=>(const ContentAddressWithReferences &) const = default; MAKE_WRAPPER_CONSTRUCTOR(ContentAddressWithReferences); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 788c5e2ea..40163a621 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -435,19 +435,21 @@ static void performOp(TunnelLogger * logger, ref store, } else { HashAlgorithm hashAlgo; std::string baseName; - FileIngestionMethod method; + ContentAddressMethod method; { bool fixed; uint8_t recursive; std::string hashAlgoRaw; from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; - if (recursive > (uint8_t) FileIngestionMethod::NixArchive) + if (recursive > true) throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive); - method = FileIngestionMethod { recursive }; + method = recursive + ? ContentAddressMethod::Raw::NixArchive + : ContentAddressMethod::Raw::Flat; /* Compatibility hack. */ if (!fixed) { hashAlgoRaw = "sha256"; - method = FileIngestionMethod::NixArchive; + method = ContentAddressMethod::Raw::NixArchive; } hashAlgo = parseHashAlgo(hashAlgoRaw); } @@ -500,7 +502,7 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); auto path = ({ StringSource source { s }; - store->addToStoreFromDump(source, suffix, FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, refs, NoRepair); + store->addToStoreFromDump(source, suffix, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, refs, NoRepair); }); logger->stopWork(); to << store->printStorePath(path); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 869880112..6dfcc408c 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -150,7 +150,7 @@ StorePath writeDerivation(Store & store, }) : ({ StringSource s { contents }; - store.addToStoreFromDump(s, suffix, FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, references, repair); + store.addToStoreFromDump(s, suffix, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, references, repair); }); } @@ -274,7 +274,7 @@ static DerivationOutput parseDerivationOutput( { if (hashAlgoStr != "") { ContentAddressMethod method = ContentAddressMethod::parsePrefix(hashAlgoStr); - if (method == TextIngestionMethod {}) + if (method == ContentAddressMethod::Raw::Text) xpSettings.require(Xp::DynamicDerivations); const auto hashAlgo = parseHashAlgo(hashAlgoStr); if (hashS == "impure") { @@ -1249,7 +1249,7 @@ DerivationOutput DerivationOutput::fromJSON( auto methodAlgo = [&]() -> std::pair { auto & method_ = getString(valueAt(json, "method")); ContentAddressMethod method = ContentAddressMethod::parse(method_); - if (method == TextIngestionMethod {}) + if (method == ContentAddressMethod::Raw::Text) xpSettings.require(Xp::DynamicDerivations); auto & hashAlgo_ = getString(valueAt(json, "hashAlgo")); diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 07ace70d0..2b4e01eb3 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1253,7 +1253,7 @@ StorePath LocalStore::addToStoreFromDump( std::filesystem::path tempDir; AutoCloseFD tempDirFd; - bool methodsMatch = ContentAddressMethod(FileIngestionMethod(dumpMethod)) == hashMethod; + bool methodsMatch = static_cast(dumpMethod) == hashMethod.getFileIngestionMethod(); /* If the methods don't match, our streaming hash of the dump is the wrong sort, and we need to rehash. */ diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index ddd7f50d9..5c27182b7 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -48,15 +48,21 @@ std::optional ValidPathInfo::contentAddressWithRef if (! ca) return std::nullopt; - return std::visit(overloaded { - [&](const TextIngestionMethod &) -> ContentAddressWithReferences { + switch (ca->method.raw) { + case ContentAddressMethod::Raw::Text: + { assert(references.count(path) == 0); return TextInfo { .hash = ca->hash, .references = references, }; - }, - [&](const FileIngestionMethod & m2) -> ContentAddressWithReferences { + } + + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + default: + { auto refs = references; bool hasSelfReference = false; if (refs.count(path)) { @@ -64,15 +70,15 @@ std::optional ValidPathInfo::contentAddressWithRef refs.erase(path); } return FixedOutputInfo { - .method = m2, + .method = ca->method.getFileIngestionMethod(), .hash = ca->hash, .references = { .others = std::move(refs), .self = hasSelfReference, }, }; - }, - }, ca->method.raw); + } + } } bool ValidPathInfo::isContentAddressed(const Store & store) const @@ -127,22 +133,18 @@ ValidPathInfo::ValidPathInfo( : UnkeyedValidPathInfo(narHash) , path(store.makeFixedOutputPathFromCA(name, ca)) { + this->ca = ContentAddress { + .method = ca.getMethod(), + .hash = ca.getHash(), + }; std::visit(overloaded { [this](TextInfo && ti) { this->references = std::move(ti.references); - this->ca = ContentAddress { - .method = TextIngestionMethod {}, - .hash = std::move(ti.hash), - }; }, [this](FixedOutputInfo && foi) { this->references = std::move(foi.references.others); if (foi.references.self) this->references.insert(path); - this->ca = ContentAddress { - .method = std::move(foi.method), - .hash = std::move(foi.hash), - }; }, }, std::move(ca).raw); } diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 9adad9c2a..d749ccd0a 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -392,8 +392,9 @@ ref RemoteStore::addCAToStore( else { if (repair) throw Error("repairing is not supported when building through the Nix daemon protocol < 1.25"); - std::visit(overloaded { - [&](const TextIngestionMethod & thm) -> void { + switch (caMethod.raw) { + case ContentAddressMethod::Raw::Text: + { if (hashAlgo != HashAlgorithm::SHA256) throw UnimplementedError("When adding text-hashed data called '%s', only SHA-256 is supported but '%s' was given", name, printHashAlgo(hashAlgo)); @@ -401,8 +402,14 @@ ref RemoteStore::addCAToStore( conn->to << WorkerProto::Op::AddTextToStore << name << s; WorkerProto::write(*this, *conn, references); conn.processStderr(); - }, - [&](const FileIngestionMethod & fim) -> void { + break; + } + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + default: + { + auto fim = caMethod.getFileIngestionMethod(); conn->to << WorkerProto::Op::AddToStore << name @@ -432,9 +439,9 @@ ref RemoteStore::addCAToStore( } catch (EndOfFile & e) { } throw; } - + break; } - }, caMethod.raw); + } auto path = parseStorePath(readString(conn->from)); // Release our connection to prevent a deadlock in queryPathInfo(). conn_.reset(); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 8c957ff1a..05c4e1c5e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -356,7 +356,7 @@ ValidPathInfo Store::addToStoreSlow( RegularFileSink fileSink { caHashSink }; TeeSink unusualHashTee { narHashSink, caHashSink }; - auto & narSink = method == FileIngestionMethod::NixArchive && hashAlgo != HashAlgorithm::SHA256 + auto & narSink = method == ContentAddressMethod::Raw::NixArchive && hashAlgo != HashAlgorithm::SHA256 ? static_cast(unusualHashTee) : narHashSink; @@ -384,9 +384,9 @@ ValidPathInfo Store::addToStoreSlow( finish. */ auto [narHash, narSize] = narHashSink.finish(); - auto hash = method == FileIngestionMethod::NixArchive && hashAlgo == HashAlgorithm::SHA256 + auto hash = method == ContentAddressMethod::Raw::NixArchive && hashAlgo == HashAlgorithm::SHA256 ? narHash - : method == FileIngestionMethod::Git + : method == ContentAddressMethod::Raw::Git ? git::dumpHash(hashAlgo, srcPath).hash : caHashSink.finish().first; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index e719f9bf9..a5effb4c1 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -441,7 +441,7 @@ public: virtual StorePath addToStore( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::NixArchive, + ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), PathFilter & filter = defaultPathFilter, @@ -455,7 +455,7 @@ public: ValidPathInfo addToStoreSlow( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::NixArchive, + ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), std::optional expectedCAHash = {}); @@ -481,7 +481,7 @@ public: Source & dump, std::string_view name, FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, - ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, + ContentAddressMethod hashMethod = ContentAddressMethod::Raw::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) = 0; diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index f3f5589de..4c7218f19 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -117,6 +117,8 @@ enum struct FileIngestionMethod : uint8_t { /** * Git hashing. * + * Part of `ExperimentalFeature::GitHashing`. + * * See `file-system-object/content-address.md#serial-git` in the * manual. */ diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 5246b03e4..a24dd11d6 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -115,7 +115,7 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, std::string str2 = str.str(); StringSource source { str2 }; state.store->addToStoreFromDump( - source, "env-manifest.nix", FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, references); + source, "env-manifest.nix", FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, references); }); /* Get the environment builder expression. */ diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 178702f91..d0840a02e 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -194,10 +194,10 @@ static void opAdd(Strings opFlags, Strings opArgs) store. */ static void opAddFixed(Strings opFlags, Strings opArgs) { - auto method = FileIngestionMethod::Flat; + ContentAddressMethod method = ContentAddressMethod::Raw::Flat; for (auto & i : opFlags) - if (i == "--recursive") method = FileIngestionMethod::NixArchive; + if (i == "--recursive") method = ContentAddressMethod::Raw::NixArchive; else throw UsageError("unknown flag '%1%'", i); if (opArgs.empty()) diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index ed46254f3..5c08f7616 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -12,7 +12,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand { Path path; std::optional namePart; - ContentAddressMethod caMethod = FileIngestionMethod::NixArchive; + ContentAddressMethod caMethod = ContentAddressMethod::Raw::NixArchive; HashAlgorithm hashAlgo = HashAlgorithm::SHA256; CmdAddToStore() @@ -68,7 +68,7 @@ struct CmdAddFile : CmdAddToStore { CmdAddFile() { - caMethod = FileIngestionMethod::Flat; + caMethod = ContentAddressMethod::Raw::Flat; } std::string description() override diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 27287a1a8..80510dc78 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -238,7 +238,7 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore auto getEnvShPath = ({ StringSource source { getEnvSh }; evalStore->addToStoreFromDump( - source, "get-env.sh", FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, {}); + source, "get-env.sh", FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, {}); }); drv.args = {store->printStorePath(getEnvShPath)}; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 143935572..6bbf2578e 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -57,7 +57,9 @@ std::tuple prefetchFile( bool unpack, bool executable) { - auto ingestionMethod = unpack || executable ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; + ContentAddressMethod method = unpack || executable + ? ContentAddressMethod::Raw::NixArchive + : ContentAddressMethod::Raw::Flat; /* Figure out a name in the Nix store. */ if (!name) { @@ -73,11 +75,10 @@ std::tuple prefetchFile( the store. */ if (expectedHash) { hashAlgo = expectedHash->algo; - storePath = store->makeFixedOutputPath(*name, FixedOutputInfo { - .method = ingestionMethod, - .hash = *expectedHash, - .references = {}, - }); + storePath = store->makeFixedOutputPathFromCA(*name, ContentAddressWithReferences::fromParts( + method, + *expectedHash, + {})); if (store->isValidPath(*storePath)) hash = expectedHash; else @@ -128,7 +129,7 @@ std::tuple prefetchFile( auto info = store->addToStoreSlow( *name, PosixSourceAccessor::createAtRoot(tmpFile), - ingestionMethod, hashAlgo, {}, expectedHash); + method, hashAlgo, {}, expectedHash); storePath = info.path; assert(info.ca); hash = info.ca->hash; diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index e8e209d97..acce25f3a 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -335,7 +335,7 @@ SV * StoreWrapper::addToStore(char * srcPath, int recursive, char * algo) PPCODE: try { - auto method = recursive ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; + auto method = recursive ? ContentAddressMethod::Raw::NixArchive : ContentAddressMethod::Raw::Flat; auto path = THIS->store->addToStore( std::string(baseNameOf(srcPath)), PosixSourceAccessor::createAtRoot(srcPath), diff --git a/tests/unit/libstore/common-protocol.cc b/tests/unit/libstore/common-protocol.cc index 2c629b601..c8f6dd002 100644 --- a/tests/unit/libstore/common-protocol.cc +++ b/tests/unit/libstore/common-protocol.cc @@ -83,15 +83,15 @@ CHARACTERIZATION_TEST( "content-address", (std::tuple { ContentAddress { - .method = TextIngestionMethod {}, + .method = ContentAddressMethod::Raw::Text, .hash = hashString(HashAlgorithm::SHA256, "Derive(...)"), }, ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -178,7 +178,7 @@ CHARACTERIZATION_TEST( std::nullopt, std::optional { ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, }, diff --git a/tests/unit/libstore/content-address.cc b/tests/unit/libstore/content-address.cc index f0806bd9a..72eb84fec 100644 --- a/tests/unit/libstore/content-address.cc +++ b/tests/unit/libstore/content-address.cc @@ -9,11 +9,11 @@ namespace nix { * --------------------------------------------------------------------------*/ TEST(ContentAddressMethod, testRoundTripPrintParse_1) { - for (const ContentAddressMethod & cam : { - ContentAddressMethod { TextIngestionMethod {} }, - ContentAddressMethod { FileIngestionMethod::Flat }, - ContentAddressMethod { FileIngestionMethod::NixArchive }, - ContentAddressMethod { FileIngestionMethod::Git }, + for (ContentAddressMethod cam : { + ContentAddressMethod::Raw::Text, + ContentAddressMethod::Raw::Flat, + ContentAddressMethod::Raw::NixArchive, + ContentAddressMethod::Raw::Git, }) { EXPECT_EQ(ContentAddressMethod::parse(cam.render()), cam); } diff --git a/tests/unit/libstore/derivation.cc b/tests/unit/libstore/derivation.cc index 210813137..71979f885 100644 --- a/tests/unit/libstore/derivation.cc +++ b/tests/unit/libstore/derivation.cc @@ -108,7 +108,7 @@ TEST_JSON(DerivationTest, inputAddressed, TEST_JSON(DerivationTest, caFixedFlat, (DerivationOutput::CAFixed { .ca = { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -117,7 +117,7 @@ TEST_JSON(DerivationTest, caFixedFlat, TEST_JSON(DerivationTest, caFixedNAR, (DerivationOutput::CAFixed { .ca = { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -126,6 +126,7 @@ TEST_JSON(DerivationTest, caFixedNAR, TEST_JSON(DynDerivationTest, caFixedText, (DerivationOutput::CAFixed { .ca = { + .method = ContentAddressMethod::Raw::Text, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -133,7 +134,7 @@ TEST_JSON(DynDerivationTest, caFixedText, TEST_JSON(CaDerivationTest, caFloating, (DerivationOutput::CAFloating { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") @@ -144,7 +145,7 @@ TEST_JSON(DerivationTest, deferred, TEST_JSON(ImpureDerivationTest, impure, (DerivationOutput::Impure { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index 17d7153bb..2505c5a9a 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -55,15 +55,15 @@ VERSIONED_CHARACTERIZATION_TEST( defaultVersion, (std::tuple { ContentAddress { - .method = TextIngestionMethod {}, + .method = ContentAddressMethod::Raw::Text, .hash = hashString(HashAlgorithm::SHA256, "Derive(...)"), }, ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -398,7 +398,7 @@ VERSIONED_CHARACTERIZATION_TEST( std::nullopt, std::optional { ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, }, diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 8d81717c9..c15120010 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -56,15 +56,15 @@ VERSIONED_CHARACTERIZATION_TEST( defaultVersion, (std::tuple { ContentAddress { - .method = TextIngestionMethod {}, + .method = ContentAddressMethod::Raw::Text, .hash = hashString(HashAlgorithm::SHA256, "Derive(...)"), }, ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -598,7 +598,7 @@ VERSIONED_CHARACTERIZATION_TEST( std::nullopt, std::optional { ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, }, From 1620ad4587f280187124891dff909402b20db014 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 11:34:58 -0400 Subject: [PATCH 371/910] Split out `GlobalConfig` into its own header This makes it easier to understand the reach of global variables / global state in the config system. --- src/libcmd/repl.cc | 1 + src/libexpr/eval-settings.cc | 1 + src/libexpr/flake/config.cc | 1 + src/libfetchers/fetch-settings.cc | 1 + src/libmain/common-args.cc | 1 + src/libstore/build/derivation-goal.cc | 1 + src/libstore/filetransfer.cc | 1 + src/libstore/globals.cc | 1 + src/libstore/unix/build/hook-instance.cc | 1 + src/libutil-c/nix_api_util.cc | 2 +- src/libutil/archive.cc | 2 +- src/libutil/config-global.cc | 66 ++++++++++++++++++++++++ src/libutil/config-global.hh | 33 ++++++++++++ src/libutil/config.cc | 61 ---------------------- src/libutil/config.hh | 25 --------- src/libutil/fs-sink.cc | 2 +- src/libutil/logging.cc | 2 +- src/nix/config.cc | 1 + src/nix/develop.cc | 1 + src/nix/unix/daemon.cc | 1 + tests/functional/plugins/plugintest.cc | 2 +- tests/unit/libutil/nix_api_util.cc | 2 +- 22 files changed, 117 insertions(+), 92 deletions(-) create mode 100644 src/libutil/config-global.cc create mode 100644 src/libutil/config-global.hh diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 1e80d14e1..53dd94c7a 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -8,6 +8,7 @@ #include "ansicolor.hh" #include "shared.hh" +#include "config-global.hh" #include "eval.hh" #include "eval-cache.hh" #include "eval-inline.hh" diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 85b1677ea..a642bb684 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -1,4 +1,5 @@ #include "users.hh" +#include "config-global.hh" #include "globals.hh" #include "profiles.hh" #include "eval.hh" diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index e0c5d4512..03430b0e3 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -1,4 +1,5 @@ #include "users.hh" +#include "config-global.hh" #include "globals.hh" #include "fetch-settings.hh" #include "flake.hh" diff --git a/src/libfetchers/fetch-settings.cc b/src/libfetchers/fetch-settings.cc index e7d5244dc..21c42567c 100644 --- a/src/libfetchers/fetch-settings.cc +++ b/src/libfetchers/fetch-settings.cc @@ -1,4 +1,5 @@ #include "fetch-settings.hh" +#include "config-global.hh" namespace nix { diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index 5b49aaabc..a94845ab8 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -1,5 +1,6 @@ #include "common-args.hh" #include "args/root.hh" +#include "config-global.hh" #include "globals.hh" #include "logging.hh" #include "loggers.hh" diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 146a060f3..64b8495e1 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -3,6 +3,7 @@ # include "hook-instance.hh" #endif #include "processes.hh" +#include "config-global.hh" #include "worker.hh" #include "builtins.hh" #include "builtins/buildenv.hh" diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index a54ebdcf3..cbbb0fe7a 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -1,5 +1,6 @@ #include "filetransfer.hh" #include "globals.hh" +#include "config-global.hh" #include "store-api.hh" #include "s3.hh" #include "compression.hh" diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 88f899dbf..8fec10d51 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -1,4 +1,5 @@ #include "globals.hh" +#include "config-global.hh" #include "current-process.hh" #include "archive.hh" #include "args.hh" diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index 5d045ec3d..dfc208798 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -1,4 +1,5 @@ #include "globals.hh" +#include "config-global.hh" #include "hook-instance.hh" #include "file-system.hh" #include "child.hh" diff --git a/src/libutil-c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc index 0a9b49345..4f65a4c12 100644 --- a/src/libutil-c/nix_api_util.cc +++ b/src/libutil-c/nix_api_util.cc @@ -1,5 +1,5 @@ #include "nix_api_util.h" -#include "config.hh" +#include "config-global.hh" #include "error.hh" #include "nix_api_util_internal.h" #include "util.hh" diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index d20936de4..22be392d4 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -6,7 +6,7 @@ #include // for strcasecmp #include "archive.hh" -#include "config.hh" +#include "config-global.hh" #include "posix-source-accessor.hh" #include "source-path.hh" #include "file-system.hh" diff --git a/src/libutil/config-global.cc b/src/libutil/config-global.cc new file mode 100644 index 000000000..e8025b303 --- /dev/null +++ b/src/libutil/config-global.cc @@ -0,0 +1,66 @@ +#include "config-global.hh" + +namespace nix { + +bool GlobalConfig::set(const std::string & name, const std::string & value) +{ + for (auto & config : *configRegistrations) + if (config->set(name, value)) return true; + + unknownSettings.emplace(name, value); + + return false; +} + +void GlobalConfig::getSettings(std::map & res, bool overriddenOnly) +{ + for (auto & config : *configRegistrations) + config->getSettings(res, overriddenOnly); +} + +void GlobalConfig::resetOverridden() +{ + for (auto & config : *configRegistrations) + config->resetOverridden(); +} + +nlohmann::json GlobalConfig::toJSON() +{ + auto res = nlohmann::json::object(); + for (const auto & config : *configRegistrations) + res.update(config->toJSON()); + return res; +} + +std::string GlobalConfig::toKeyValue() +{ + std::string res; + std::map settings; + globalConfig.getSettings(settings); + for (const auto & s : settings) + res += fmt("%s = %s\n", s.first, s.second.value); + return res; +} + +void GlobalConfig::convertToArgs(Args & args, const std::string & category) +{ + for (auto & config : *configRegistrations) + config->convertToArgs(args, category); +} + +GlobalConfig globalConfig; + +GlobalConfig::ConfigRegistrations * GlobalConfig::configRegistrations; + +GlobalConfig::Register::Register(Config * config) +{ + if (!configRegistrations) + configRegistrations = new ConfigRegistrations; + configRegistrations->emplace_back(config); +} + +ExperimentalFeatureSettings experimentalFeatureSettings; + +static GlobalConfig::Register rSettings(&experimentalFeatureSettings); + +} diff --git a/src/libutil/config-global.hh b/src/libutil/config-global.hh new file mode 100644 index 000000000..1b7b69a0d --- /dev/null +++ b/src/libutil/config-global.hh @@ -0,0 +1,33 @@ +#pragma once +///@file + +#include "config.hh" + +namespace nix { + +struct GlobalConfig : public AbstractConfig +{ + typedef std::vector ConfigRegistrations; + static ConfigRegistrations * configRegistrations; + + bool set(const std::string & name, const std::string & value) override; + + void getSettings(std::map & res, bool overriddenOnly = false) override; + + void resetOverridden() override; + + nlohmann::json toJSON() override; + + std::string toKeyValue() override; + + void convertToArgs(Args & args, const std::string & category) override; + + struct Register + { + Register(Config * config); + }; +}; + +extern GlobalConfig globalConfig; + +} diff --git a/src/libutil/config.cc b/src/libutil/config.cc index efde8591b..192a4ecb9 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -443,67 +443,6 @@ void OptionalPathSetting::operator =(const std::optional & v) this->assign(v); } -bool GlobalConfig::set(const std::string & name, const std::string & value) -{ - for (auto & config : *configRegistrations) - if (config->set(name, value)) return true; - - unknownSettings.emplace(name, value); - - return false; -} - -void GlobalConfig::getSettings(std::map & res, bool overriddenOnly) -{ - for (auto & config : *configRegistrations) - config->getSettings(res, overriddenOnly); -} - -void GlobalConfig::resetOverridden() -{ - for (auto & config : *configRegistrations) - config->resetOverridden(); -} - -nlohmann::json GlobalConfig::toJSON() -{ - auto res = nlohmann::json::object(); - for (const auto & config : *configRegistrations) - res.update(config->toJSON()); - return res; -} - -std::string GlobalConfig::toKeyValue() -{ - std::string res; - std::map settings; - globalConfig.getSettings(settings); - for (const auto & s : settings) - res += fmt("%s = %s\n", s.first, s.second.value); - return res; -} - -void GlobalConfig::convertToArgs(Args & args, const std::string & category) -{ - for (auto & config : *configRegistrations) - config->convertToArgs(args, category); -} - -GlobalConfig globalConfig; - -GlobalConfig::ConfigRegistrations * GlobalConfig::configRegistrations; - -GlobalConfig::Register::Register(Config * config) -{ - if (!configRegistrations) - configRegistrations = new ConfigRegistrations; - configRegistrations->emplace_back(config); -} - -ExperimentalFeatureSettings experimentalFeatureSettings; - -static GlobalConfig::Register rSettings(&experimentalFeatureSettings); - bool ExperimentalFeatureSettings::isEnabled(const ExperimentalFeature & feature) const { auto & f = experimentalFeatures.get(); diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 07322b60d..1952ba1b8 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -375,31 +375,6 @@ public: void operator =(const std::optional & v); }; -struct GlobalConfig : public AbstractConfig -{ - typedef std::vector ConfigRegistrations; - static ConfigRegistrations * configRegistrations; - - bool set(const std::string & name, const std::string & value) override; - - void getSettings(std::map & res, bool overriddenOnly = false) override; - - void resetOverridden() override; - - nlohmann::json toJSON() override; - - std::string toKeyValue() override; - - void convertToArgs(Args & args, const std::string & category) override; - - struct Register - { - Register(Config * config); - }; -}; - -extern GlobalConfig globalConfig; - struct ExperimentalFeatureSettings : Config { diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 91070ea89..a6a743737 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -1,7 +1,7 @@ #include #include "error.hh" -#include "config.hh" +#include "config-global.hh" #include "fs-sink.hh" #if _WIN32 diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 5fa01f0d9..55751b4cf 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -3,7 +3,7 @@ #include "environment-variables.hh" #include "terminal.hh" #include "util.hh" -#include "config.hh" +#include "config-global.hh" #include "source-path.hh" #include "position.hh" diff --git a/src/nix/config.cc b/src/nix/config.cc index 52706afcf..07f975a00 100644 --- a/src/nix/config.cc +++ b/src/nix/config.cc @@ -2,6 +2,7 @@ #include "common-args.hh" #include "shared.hh" #include "store-api.hh" +#include "config-global.hh" #include diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 27287a1a8..a7acbff0b 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -1,3 +1,4 @@ +#include "config-global.hh" #include "eval.hh" #include "installable-flake.hh" #include "command-installable-value.hh" diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index f1fc51682..41ea1f5a4 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -10,6 +10,7 @@ #include "serialise.hh" #include "archive.hh" #include "globals.hh" +#include "config-global.hh" #include "derivations.hh" #include "finally.hh" #include "legacy.hh" diff --git a/tests/functional/plugins/plugintest.cc b/tests/functional/plugins/plugintest.cc index e02fd68d5..7433ad190 100644 --- a/tests/functional/plugins/plugintest.cc +++ b/tests/functional/plugins/plugintest.cc @@ -1,4 +1,4 @@ -#include "config.hh" +#include "config-global.hh" #include "primops.hh" using namespace nix; diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index d2999f55b..2b7e38225 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -1,4 +1,4 @@ -#include "config.hh" +#include "config-global.hh" #include "args.hh" #include "nix_api_util.h" #include "nix_api_util_internal.h" From b46e13840b948a363e2aa082f6064dfaeab58c06 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 12:07:08 -0400 Subject: [PATCH 372/910] Format `config-global.{cc,hh}` Since the code is factored out, it is no longer avoding the formatter. --- src/libutil/config-global.cc | 3 ++- src/libutil/config-global.hh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libutil/config-global.cc b/src/libutil/config-global.cc index e8025b303..4c08898a4 100644 --- a/src/libutil/config-global.cc +++ b/src/libutil/config-global.cc @@ -5,7 +5,8 @@ namespace nix { bool GlobalConfig::set(const std::string & name, const std::string & value) { for (auto & config : *configRegistrations) - if (config->set(name, value)) return true; + if (config->set(name, value)) + return true; unknownSettings.emplace(name, value); diff --git a/src/libutil/config-global.hh b/src/libutil/config-global.hh index 1b7b69a0d..2caf51524 100644 --- a/src/libutil/config-global.hh +++ b/src/libutil/config-global.hh @@ -7,7 +7,7 @@ namespace nix { struct GlobalConfig : public AbstractConfig { - typedef std::vector ConfigRegistrations; + typedef std::vector ConfigRegistrations; static ConfigRegistrations * configRegistrations; bool set(const std::string & name, const std::string & value) override; From cb0c868da4ced09179182792b8be1d7259238f98 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 11:55:36 -0400 Subject: [PATCH 373/910] Allow loading config files into other config objects This gives us some hope of moving away from global variables. --- src/libstore/globals.cc | 10 +++++----- src/libstore/globals.hh | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 8fec10d51..7e1d7ea6d 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -124,12 +124,12 @@ Settings::Settings() }; } -void loadConfFile() +void loadConfFile(AbstractConfig & config) { auto applyConfigFile = [&](const Path & path) { try { std::string contents = readFile(path); - globalConfig.applyConfig(contents, path); + config.applyConfig(contents, path); } catch (SystemError &) { } }; @@ -137,7 +137,7 @@ void loadConfFile() /* We only want to send overrides to the daemon, i.e. stuff from ~/.nix/nix.conf or the command line. */ - globalConfig.resetOverridden(); + config.resetOverridden(); auto files = settings.nixUserConfFiles; for (auto file = files.rbegin(); file != files.rend(); file++) { @@ -146,7 +146,7 @@ void loadConfFile() auto nixConfEnv = getEnv("NIX_CONFIG"); if (nixConfEnv.has_value()) { - globalConfig.applyConfig(nixConfEnv.value(), "NIX_CONFIG"); + config.applyConfig(nixConfEnv.value(), "NIX_CONFIG"); } } @@ -438,7 +438,7 @@ void initLibStore(bool loadConfig) { initLibUtil(); if (loadConfig) - loadConfFile(); + loadConfFile(globalConfig); preloadNSS(); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 843e77bcf..439e9f4fc 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1284,7 +1284,13 @@ extern Settings settings; */ void initPlugins(); -void loadConfFile(); +/** + * Load the configuration (from `nix.conf`, `NIX_CONFIG`, etc.) into the + * given configuration object. + * + * Usually called with `globalConfig`. + */ +void loadConfFile(AbstractConfig & config); // Used by the Settings constructor std::vector getUserConfigFiles(); From d4ca634508236ff0343f200c9aef4c21e752e961 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 24 Jun 2024 18:11:10 +0200 Subject: [PATCH 374/910] tests/functional: Differentiate die and fail --- tests/functional/common/vars-and-functions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 8d99800ef..37c109015 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -11,7 +11,7 @@ isTestOnNixOS() { } die() { - echo "fatal error: $*" >&2 + echo "unexpected fatal error: $*" >&2 exit 1 } @@ -216,7 +216,7 @@ requireGit() { } fail() { - echo "$1" >&2 + echo "test failed: $1" >&2 exit 1 } From 5a7ccd658093c7c0598d4e5cce823aee9a3265f1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 24 Jun 2024 18:11:58 +0200 Subject: [PATCH 375/910] tests/functional: Print all args of fail() --- tests/functional/common/vars-and-functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 37c109015..4316a30d5 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -216,7 +216,7 @@ requireGit() { } fail() { - echo "test failed: $1" >&2 + echo "test failed: $*" >&2 exit 1 } From 52bfccf8d8112ba738e6fc9e4891f85b6b864566 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 14 Jun 2024 12:41:09 -0400 Subject: [PATCH 376/910] No global eval settings in `libnixexpr` Progress on #5638 There is still a global eval settings, but it pushed down into `libnixcmd`, which is a lot less bad a place for this sort of thing. --- .editorconfig | 8 +++--- src/libcmd/command.cc | 8 +++--- src/libcmd/common-eval-args.cc | 7 +++++ src/libcmd/common-eval-args.hh | 6 +++++ src/libexpr-c/nix_api_expr.cc | 17 +++++++++++- src/libexpr-c/nix_api_expr_internal.h | 2 ++ src/libexpr/eval-settings.cc | 13 ++++----- src/libexpr/eval-settings.hh | 10 +++---- src/libexpr/eval.cc | 29 +++++++++++---------- src/libexpr/eval.hh | 4 ++- src/libexpr/flake/config.cc | 1 - src/libexpr/flake/flake.cc | 6 ++--- src/libexpr/parser-state.hh | 1 + src/libexpr/parser.y | 6 +++-- src/libexpr/primops.cc | 25 +++++++++--------- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 8 +++--- src/nix-build/nix-build.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix/main.cc | 4 +-- src/nix/prefetch.cc | 2 +- src/nix/upgrade-nix.cc | 2 +- tests/unit/libexpr-support/tests/libexpr.hh | 6 +++-- 24 files changed, 102 insertions(+), 71 deletions(-) diff --git a/.editorconfig b/.editorconfig index 86360e658..e1c8bae39 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,20 +4,20 @@ # Top-most EditorConfig file root = true -# Unix-style newlines with a newline ending every file, utf-8 charset +# Unix-style newlines with a newline ending every file, UTF-8 charset [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 -# Match nix files, set indent to spaces with width of two +# Match Nix files, set indent to spaces with width of two [*.nix] indent_style = space indent_size = 2 -# Match c++/shell/perl, set indent to spaces with width of four -[*.{hpp,cc,hh,sh,pl,xs}] +# Match C++/C/shell/Perl, set indent to spaces with width of four +[*.{hpp,cc,hh,c,h,sh,pl,xs}] indent_style = space indent_size = 4 diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 543250da3..23aaaf2ad 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -126,13 +126,11 @@ ref EvalCommand::getEvalState() { if (!evalState) { evalState = + std::allocate_shared( #if HAVE_BOEHMGC - std::allocate_shared(traceable_allocator(), - lookupPath, getEvalStore(), getStore()) - #else - std::make_shared( - lookupPath, getEvalStore(), getStore()) + traceable_allocator(), #endif + lookupPath, getEvalStore(), evalSettings, getStore()) ; evalState->repair = repair; diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index cd0f19257..77dba546d 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -1,6 +1,7 @@ #include "eval-settings.hh" #include "common-eval-args.hh" #include "shared.hh" +#include "config-global.hh" #include "filetransfer.hh" #include "eval.hh" #include "fetchers.hh" @@ -13,6 +14,12 @@ namespace nix { +EvalSettings evalSettings { + settings.readOnlyMode +}; + +static GlobalConfig::Register rEvalSettings(&evalSettings); + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 75cb19334..189abf0ed 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -12,9 +12,15 @@ namespace nix { class Store; class EvalState; +struct EvalSettings; class Bindings; struct SourcePath; +/** + * @todo Get rid of global setttings variables + */ +extern EvalSettings evalSettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index bdf7a1e63..13e0f5f3a 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -7,6 +7,7 @@ #include "eval.hh" #include "globals.hh" #include "util.hh" +#include "eval-settings.hh" #include "nix_api_expr.h" #include "nix_api_expr_internal.h" @@ -106,7 +107,21 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c for (size_t i = 0; lookupPath_c[i] != nullptr; i++) lookupPath.push_back(lookupPath_c[i]); - return new EvalState{nix::EvalState(nix::LookupPath::parse(lookupPath), store->ptr)}; + void * p = ::operator new( + sizeof(EvalState), + static_cast(alignof(EvalState))); + auto * p2 = static_cast(p); + new (p) EvalState { + .settings = nix::EvalSettings{ + nix::settings.readOnlyMode, + }, + .state = nix::EvalState( + nix::LookupPath::parse(lookupPath), + store->ptr, + p2->settings), + }; + loadConfFile(p2->settings); + return p2; } NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index 5a875ef39..d4ccffd29 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -2,11 +2,13 @@ #define NIX_API_EXPR_INTERNAL_H #include "eval.hh" +#include "eval-settings.hh" #include "attr-set.hh" #include "nix_api_value.h" struct EvalState { + nix::EvalSettings settings; nix::EvalState state; }; diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index a642bb684..11a62b0fd 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -45,7 +45,8 @@ static Strings parseNixPath(const std::string & s) return res; } -EvalSettings::EvalSettings() +EvalSettings::EvalSettings(bool & readOnlyMode) + : readOnlyMode{readOnlyMode} { auto var = getEnv("NIX_PATH"); if (var) nixPath = parseNixPath(*var); @@ -55,7 +56,7 @@ EvalSettings::EvalSettings() builtinsAbortOnWarn = true; } -Strings EvalSettings::getDefaultNixPath() +Strings EvalSettings::getDefaultNixPath() const { Strings res; auto add = [&](const Path & p, const std::string & s = std::string()) { @@ -68,7 +69,7 @@ Strings EvalSettings::getDefaultNixPath() } }; - if (!evalSettings.restrictEval && !evalSettings.pureEval) { + if (!restrictEval && !pureEval) { add(getNixDefExpr() + "/channels"); add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); add(rootChannelsDir()); @@ -94,16 +95,12 @@ std::string EvalSettings::resolvePseudoUrl(std::string_view url) return std::string(url); } -const std::string & EvalSettings::getCurrentSystem() +const std::string & EvalSettings::getCurrentSystem() const { const auto & evalSystem = currentSystem.get(); return evalSystem != "" ? evalSystem : settings.thisSystem.get(); } -EvalSettings evalSettings; - -static GlobalConfig::Register rEvalSettings(&evalSettings); - Path getNixDefExpr() { return settings.useXDGBaseDirectories diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index d01de980f..2689a8465 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -7,9 +7,11 @@ namespace nix { struct EvalSettings : Config { - EvalSettings(); + EvalSettings(bool & readOnlyMode); - static Strings getDefaultNixPath(); + bool & readOnlyMode; + + Strings getDefaultNixPath() const; static bool isPseudoUrl(std::string_view s); @@ -74,7 +76,7 @@ struct EvalSettings : Config * Implements the `eval-system` vs `system` defaulting logic * described for `eval-system`. */ - const std::string & getCurrentSystem(); + const std::string & getCurrentSystem() const; Setting restrictEval{ this, false, "restrict-eval", @@ -193,8 +195,6 @@ struct EvalSettings : Config )"}; }; -extern EvalSettings evalSettings; - /** * Conventionally part of the default nix path in impure mode. */ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7aaec2e73..551bcdcaf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -9,7 +9,6 @@ #include "store-api.hh" #include "derivations.hh" #include "downstream-placeholder.hh" -#include "globals.hh" #include "eval-inline.hh" #include "filetransfer.hh" #include "function-trace.hh" @@ -219,8 +218,10 @@ static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( const LookupPath & _lookupPath, ref store, + const EvalSettings & settings, std::shared_ptr buildStore) - : sWith(symbols.create("")) + : settings{settings} + , sWith(symbols.create("")) , sOutPath(symbols.create("outPath")) , sDrvPath(symbols.create("drvPath")) , sType(symbols.create("type")) @@ -276,10 +277,10 @@ EvalState::EvalState( , repair(NoRepair) , emptyBindings(0) , rootFS( - evalSettings.restrictEval || evalSettings.pureEval + settings.restrictEval || settings.pureEval ? ref(AllowListSourceAccessor::create(getFSSourceAccessor(), {}, - [](const CanonPath & path) -> RestrictedPathError { - auto modeInformation = evalSettings.pureEval + [&settings](const CanonPath & path) -> RestrictedPathError { + auto modeInformation = settings.pureEval ? "in pure evaluation mode (use '--impure' to override)" : "in restricted mode"; throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); @@ -330,10 +331,10 @@ EvalState::EvalState( vStringUnknown.mkString("unknown"); /* Initialise the Nix expression search path. */ - if (!evalSettings.pureEval) { + if (!settings.pureEval) { for (auto & i : _lookupPath.elements) lookupPath.elements.emplace_back(LookupPath::Elem {i}); - for (auto & i : evalSettings.nixPath.get()) + for (auto & i : settings.nixPath.get()) lookupPath.elements.emplace_back(LookupPath::Elem::parse(i)); } @@ -411,9 +412,9 @@ bool isAllowedURI(std::string_view uri, const Strings & allowedUris) void EvalState::checkURI(const std::string & uri) { - if (!evalSettings.restrictEval) return; + if (!settings.restrictEval) return; - if (isAllowedURI(uri, evalSettings.allowedUris.get())) return; + if (isAllowedURI(uri, settings.allowedUris.get())) return; /* If the URI is a path, then check it against allowedPaths as well. */ @@ -458,7 +459,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info) constantInfos.push_back({name2, info}); - if (!(evalSettings.pureEval && info.impureOnly)) { + if (!(settings.pureEval && info.impureOnly)) { /* Check the type, if possible. We might know the type of a thunk in advance, so be allowed @@ -1413,11 +1414,11 @@ public: void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos) { - if (callDepth > evalSettings.maxCallDepth) + if (callDepth > settings.maxCallDepth) error("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow(); CallDepth _level(callDepth); - auto trace = evalSettings.traceFunctionCalls + auto trace = settings.traceFunctionCalls ? std::make_unique(positions[pos]) : nullptr; @@ -2745,7 +2746,7 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_ return {corepkgsFS, CanonPath(path.substr(3))}; error( - evalSettings.pureEval + settings.pureEval ? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)" : "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)", path @@ -2825,7 +2826,7 @@ Expr * EvalState::parse( const SourcePath & basePath, std::shared_ptr & staticEnv) { - auto result = parseExprFromBuf(text, length, origin, basePath, symbols, positions, rootFS, exprSymbols); + auto result = parseExprFromBuf(text, length, origin, basePath, symbols, settings, positions, rootFS, exprSymbols); result->bindVars(*this, staticEnv); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 774ded3e7..b84bc9907 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -30,6 +30,7 @@ namespace nix { constexpr size_t maxPrimOpArity = 8; class Store; +struct EvalSettings; class EvalState; class StorePath; struct SingleDerivedPath; @@ -39,7 +40,6 @@ namespace eval_cache { class EvalCache; } - /** * Function that implements a primop. */ @@ -162,6 +162,7 @@ struct DebugTrace { class EvalState : public std::enable_shared_from_this { public: + const EvalSettings & settings; SymbolTable symbols; PosTable positions; @@ -352,6 +353,7 @@ public: EvalState( const LookupPath & _lookupPath, ref store, + const EvalSettings & settings, std::shared_ptr buildStore = nullptr); ~EvalState(); diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index 03430b0e3..b348f6d44 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -1,6 +1,5 @@ #include "users.hh" #include "config-global.hh" -#include "globals.hh" #include "fetch-settings.hh" #include "flake.hh" diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 3af9ef14e..67b19bd57 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -803,7 +803,7 @@ static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, V { std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); - if (evalSettings.pureEval && !flakeRef.input.isLocked()) + if (state.settings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); callFlake(state, @@ -811,8 +811,8 @@ static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, V LockFlags { .updateLockFile = false, .writeLockFile = false, - .useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries, - .allowUnlocked = !evalSettings.pureEval, + .useRegistries = !state.settings.pureEval && fetchSettings.useRegistries, + .allowUnlocked = !state.settings.pureEval, }), v); } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 5a928e9aa..cff6282fa 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -46,6 +46,7 @@ struct ParserState PosTable::Origin origin; const ref rootFS; const Expr::AstSymbols & s; + const EvalSettings & settings; void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 00300449f..709a4532a 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -25,7 +25,6 @@ #include "nixexpr.hh" #include "eval.hh" #include "eval-settings.hh" -#include "globals.hh" #include "parser-state.hh" #define YYLTYPE ::nix::ParserLocation @@ -40,6 +39,7 @@ Expr * parseExprFromBuf( Pos::Origin origin, const SourcePath & basePath, SymbolTable & symbols, + const EvalSettings & settings, PosTable & positions, const ref rootFS, const Expr::AstSymbols & astSymbols); @@ -294,7 +294,7 @@ path_start $$ = new ExprPath(ref(state->rootFS), std::move(path)); } | HPATH { - if (evalSettings.pureEval) { + if (state->settings.pureEval) { throw Error( "the path '%s' can not be resolved in pure mode", std::string_view($1.p, $1.l) @@ -429,6 +429,7 @@ Expr * parseExprFromBuf( Pos::Origin origin, const SourcePath & basePath, SymbolTable & symbols, + const EvalSettings & settings, PosTable & positions, const ref rootFS, const Expr::AstSymbols & astSymbols) @@ -441,6 +442,7 @@ Expr * parseExprFromBuf( .origin = positions.addOrigin(origin, length), .rootFS = rootFS, .s = astSymbols, + .settings = settings, }; yylex_init(&scanner); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 98649f081..4e2aaf5b2 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -5,7 +5,6 @@ #include "eval.hh" #include "eval-settings.hh" #include "gc-small-vector.hh" -#include "globals.hh" #include "json-to-value.hh" #include "names.hh" #include "path-references.hh" @@ -78,7 +77,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS if (drvs.empty()) return {}; - if (isIFD && !evalSettings.enableImportFromDerivation) + if (isIFD && !settings.enableImportFromDerivation) error( "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", drvs.begin()->to_string(*store) @@ -901,7 +900,7 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va MaintainCount trylevel(state.trylevel); ReplExitStatus (* savedDebugRepl)(ref es, const ValMap & extraEnv) = nullptr; - if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry) + if (state.debugRepl && state.settings.ignoreExceptionsDuringTry) { /* to prevent starting the repl from exceptions withing a tryEval, null it. */ savedDebugRepl = state.debugRepl; @@ -950,7 +949,7 @@ static RegisterPrimOp primop_tryEval({ static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v) { std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getEnv")); - v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); + v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or("")); } static RegisterPrimOp primop_getEnv({ @@ -1017,7 +1016,7 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu printError("trace: %1%", args[0]->string_view()); else printError("trace: %1%", ValuePrinter(state, *args[0])); - if (evalSettings.builtinsTraceDebugger) { + if (state.settings.builtinsTraceDebugger) { state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); @@ -1056,11 +1055,11 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value logWarning(info); } - if (evalSettings.builtinsAbortOnWarn) { + if (state.settings.builtinsAbortOnWarn) { // Not an EvalError or subclass, which would cause the error to be stored in the eval cache. state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").setIsFromExpr().debugThrow(); } - if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { + if (state.settings.builtinsTraceDebugger || state.settings.builtinsDebuggerOnWarn) { state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); @@ -1578,7 +1577,7 @@ static RegisterPrimOp primop_toPath({ corner cases. */ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - if (evalSettings.pureEval) + if (state.settings.pureEval) state.error( "'%s' is not allowed in pure evaluation mode", "builtins.storePath" @@ -4562,7 +4561,7 @@ void EvalState::createBaseEnv() )", }); - if (!evalSettings.pureEval) { + if (!settings.pureEval) { v.mkInt(time(0)); } addConstant("__currentTime", v, { @@ -4589,8 +4588,8 @@ void EvalState::createBaseEnv() .impureOnly = true, }); - if (!evalSettings.pureEval) - v.mkString(evalSettings.getCurrentSystem()); + if (!settings.pureEval) + v.mkString(settings.getCurrentSystem()); addConstant("__currentSystem", v, { .type = nString, .doc = R"( @@ -4670,7 +4669,7 @@ void EvalState::createBaseEnv() #ifndef _WIN32 // TODO implement on Windows // Miscellaneous - if (evalSettings.enableNativeCode) { + if (settings.enableNativeCode) { addPrimOp({ .name = "__importNative", .arity = 2, @@ -4693,7 +4692,7 @@ void EvalState::createBaseEnv() error if `--trace-verbose` is enabled. Then return *e2*. This function is useful for debugging. )", - .fun = evalSettings.traceVerbose ? prim_trace : prim_second, + .fun = settings.traceVerbose ? prim_trace : prim_second, }); /* Add a value containing the current Nix expression search path. */ diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index d9ba6aa97..7b5f4193a 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -53,7 +53,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a // whitelist. Ah well. state.checkURI(url); - if (evalSettings.pureEval && !rev) + if (state.settings.pureEval && !rev) throw Error("in pure evaluation mode, 'fetchMercurial' requires a Mercurial revision"); fetchers::Attrs attrs; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a99a71577..7b05c5b48 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -171,10 +171,10 @@ static void fetchTree( } } - if (!evalSettings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes)) + if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes)) input = lookupInRegistries(state.store, input).first; - if (evalSettings.pureEval && !input.isLocked()) { + if (state.settings.pureEval && !input.isLocked()) { auto fetcher = "fetchTree"; if (params.isFetchGit) fetcher = "fetchGit"; @@ -453,14 +453,14 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch"); if (who == "fetchTarball") - url = evalSettings.resolvePseudoUrl(*url); + url = state.settings.resolvePseudoUrl(*url); state.checkURI(*url); if (name == "") name = baseNameOf(*url); - if (evalSettings.pureEval && !expectedHash) + if (state.settings.pureEval && !expectedHash) state.error("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow(); // early exit if pinned and already in the store diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 77df08edd..57630c8c3 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -259,7 +259,7 @@ static void main_nix_build(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index b5e13cc23..c72378cd5 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1525,7 +1525,7 @@ static int main_nix_env(int argc, char * * argv) auto store = openStore(); - globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store)); + globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store, evalSettings)); globals.state->repair = myArgs.repair; globals.instSource.nixExprPath = std::make_shared( diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 35664374c..a4bfeebbb 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -157,7 +157,7 @@ static int main_nix_instantiate(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); state->repair = myArgs.repair; Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/main.cc b/src/nix/main.cc index 81d368d6a..e95558781 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -242,7 +242,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) evalSettings.restrictEval = false; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://")); + EvalState state({}, openStore("dummy://"), evalSettings); auto vGenerateManpage = state.allocValue(); state.eval(state.parseExprFromString( @@ -418,7 +418,7 @@ void mainWrapped(int argc, char * * argv) Xp::FetchTree, }; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://")); + EvalState state({}, openStore("dummy://"), evalSettings); auto res = nlohmann::json::object(); res["builtins"] = ({ auto builtinsJson = nlohmann::json::object(); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 3ce52acc5..ce79e576f 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -193,7 +193,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) startProgressBar(); auto store = openStore(); - auto state = std::make_unique(myArgs.lookupPath, store); + auto state = std::make_unique(myArgs.lookupPath, store, evalSettings); Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 17d1edb97..29297253b 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -147,7 +147,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto req = FileTransferRequest((std::string&) settings.upgradeNixStorePathUrl); auto res = getFileTransfer()->download(req); - auto state = std::make_unique(LookupPath{}, store); + auto state = std::make_unique(LookupPath{}, store, evalSettings); auto v = state->allocValue(); state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v); Bindings & bindings(*state->allocBindings(0)); diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index 1a4313990..eacbf0d5c 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -19,14 +19,14 @@ namespace nix { static void SetUpTestSuite() { LibStoreTest::SetUpTestSuite(); initGC(); - evalSettings.nixPath = {}; } protected: LibExprTest() : LibStoreTest() - , state({}, store) + , state({}, store, evalSettings, nullptr) { + evalSettings.nixPath = {}; } Value eval(std::string input, bool forceValue = true) { Value v; @@ -42,6 +42,8 @@ namespace nix { return state.symbols.create(value); } + bool readOnlyMode = true; + EvalSettings evalSettings{readOnlyMode}; EvalState state; }; From 445a4a02987ae24b69597dfb7debaeb9a474d26d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 24 Jun 2024 19:02:22 +0200 Subject: [PATCH 377/910] ci.yml: Add swap and monitor it --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6362620b0..2cd908f9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,23 @@ jobs: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - if: matrix.os == 'ubuntu-latest' + run: | + free -h + swapon --show + swap=$(swapon --show --noheadings | head -n 1 | awk '{print $1}') + echo "Found swap: $swap" + sudo swapoff $swap + # resize it (fallocate) + sudo fallocate -l 10G $swap + sudo mkswap $swap + sudo swapon $swap + free -h + ( + while sleep 60; do + free -h + done + ) & - run: nix --experimental-features 'nix-command flakes' flake check -L # Steps to test CI automation in your own fork. From 05580a373f5f85b97927dafff5a50486a7c1dbd9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 17:17:54 -0400 Subject: [PATCH 378/910] Fix error in the no-GC build --- src/libcmd/command.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 23aaaf2ad..74d146c66 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -126,9 +126,11 @@ ref EvalCommand::getEvalState() { if (!evalState) { evalState = - std::allocate_shared( #if HAVE_BOEHMGC + std::allocate_shared( traceable_allocator(), + #else + std::make_shared( #endif lookupPath, getEvalStore(), evalSettings, getStore()) ; From 5be44d235a2aaaeb9da58bcb8502fa5cae0f1d30 Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Thu, 15 Sep 2022 05:56:57 +0000 Subject: [PATCH 379/910] Guard uses of lutimes, for portability --- src/libfetchers/git.cc | 22 ++------ src/libstore/posix-fs-canonicalise.cc | 16 ++---- src/libutil/file-system.cc | 78 ++++++++++++++++++++------- src/libutil/file-system.hh | 24 +++++++++ 4 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index ce80932f6..184c1383e 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -41,21 +41,6 @@ bool isCacheFileWithinTtl(time_t now, const struct stat & st) return st.st_mtime + settings.tarballTtl > now; } -bool touchCacheFile(const Path & path, time_t touch_time) -{ -#ifndef _WIN32 // TODO implement - struct timeval times[2]; - times[0].tv_sec = touch_time; - times[0].tv_usec = 0; - times[1].tv_sec = touch_time; - times[1].tv_usec = 0; - - return lutimes(path.c_str(), times) == 0; -#else - return false; -#endif -} - Path getCachePath(std::string_view key, bool shallow) { return getCacheDir() @@ -594,8 +579,11 @@ struct GitInputScheme : InputScheme warn("could not update local clone of Git repository '%s'; continuing with the most recent version", repoInfo.url); } - if (!touchCacheFile(localRefFile, now)) - warn("could not update mtime for file '%s': %s", localRefFile, strerror(errno)); + try { + setWriteTime(localRefFile, now, now); + } catch (Error & e) { + warn("could not update mtime for file '%s': %s", localRefFile, e.msg()); + } if (!originalRef && !storeCachedHead(repoInfo.url, ref)) warn("could not update cached head '%s' for '%s'", ref, repoInfo.url); } diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc index 8cb13d810..46a78cc86 100644 --- a/src/libstore/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -33,19 +33,9 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct #ifndef _WIN32 // TODO implement if (st.st_mtime != mtimeStore) { - struct timeval times[2]; - times[0].tv_sec = st.st_atime; - times[0].tv_usec = 0; - times[1].tv_sec = mtimeStore; - times[1].tv_usec = 0; -#if HAVE_LUTIMES - if (lutimes(path.c_str(), times) == -1) - if (errno != ENOSYS || - (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)) -#else - if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1) -#endif - throw SysError("changing modification time of '%1%'", path); + struct stat st2 = st; + st2.st_mtime = mtimeStore, + setWriteTime(path, st2); } #endif } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 443ccf829..2536361b2 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -557,29 +557,69 @@ void replaceSymlink(const Path & target, const Path & link) } } -#ifndef _WIN32 -static void setWriteTime(const fs::path & p, const struct stat & st) +void setWriteTime( + const std::filesystem::path & path, + time_t accessedTime, + time_t modificationTime, + std::optional optIsSymlink) { - struct timeval times[2]; - times[0] = { - .tv_sec = st.st_atime, - .tv_usec = 0, +#ifndef _WIN32 + struct timeval times[2] = { + { + .tv_sec = accessedTime, + .tv_usec = 0, + }, + { + .tv_sec = modificationTime, + .tv_usec = 0, + }, }; - times[1] = { - .tv_sec = st.st_mtime, - .tv_usec = 0, - }; - if (lutimes(p.c_str(), times) != 0) - throw SysError("changing modification time of '%s'", p); -} #endif + auto nonSymlink = [&]{ + bool isSymlink = optIsSymlink + ? *optIsSymlink + : fs::is_symlink(path); + + if (!isSymlink) { +#ifdef _WIN32 + // FIXME use `fs::last_write_time`. + // + // Would be nice to use std::filesystem unconditionally, but + // doesn't support access time just modification time. + // + // System clock vs File clock issues also make that annoying. + warn("Changing file times is not yet implemented on Windows, path is '%s'", path); +#else + if (utimes(path.c_str(), times) == -1) { + + throw SysError("changing modification time of '%s' (not a symlink)", path); + } +#endif + } else { + throw Error("Cannot modification time of symlink '%s'", path); + } + }; + +#if HAVE_LUTIMES + if (lutimes(path.c_str(), times) == -1) { + if (errno == ENOSYS) + nonSymlink(); + else + throw SysError("changing modification time of '%s'", path); + } +#else + nonSymlink(); +#endif +} + +void setWriteTime(const fs::path & path, const struct stat & st) +{ + setWriteTime(path, st.st_atime, st.st_mtime, S_ISLNK(st.st_mode)); +} + void copyFile(const fs::path & from, const fs::path & to, bool andDelete) { -#ifndef _WIN32 - // TODO: Rewrite the `is_*` to use `symlink_status()` - auto statOfFrom = lstat(from.c_str()); -#endif auto fromStatus = fs::symlink_status(from); // Mark the directory as writable so that we can delete its children @@ -599,9 +639,7 @@ void copyFile(const fs::path & from, const fs::path & to, bool andDelete) throw Error("file '%s' has an unsupported type", from); } -#ifndef _WIN32 - setWriteTime(to, statOfFrom); -#endif + setWriteTime(to, lstat(from.string().c_str())); if (andDelete) { if (!fs::is_symlink(fromStatus)) fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 9405cda0c..aeea80e00 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -156,6 +156,30 @@ inline void createDirs(PathView path) return createDirs(Path(path)); } +/** + * Set the access and modification times of the given path, not + * following symlinks. + * + * @param accessTime Specified in seconds. + * + * @param modificationTime Specified in seconds. + * + * @param isSymlink Whether the file in question is a symlink. Used for + * fallback code where we don't have `lutimes` or similar. if + * `std::optional` is passed, the information will be recomputed if it + * is needed. Race conditions are possible so be careful! + */ +void setWriteTime( + const std::filesystem::path & path, + time_t accessedTime, + time_t modificationTime, + std::optional isSymlink = std::nullopt); + +/** + * Convenience wrapper that takes all arguments from the `struct stat`. + */ +void setWriteTime(const std::filesystem::path & path, const struct stat & st); + /** * Create a symlink. */ From 1801119e2919b2a746964fd6e9a12f44f267b711 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 10:14:06 +0200 Subject: [PATCH 380/910] ci.yml: Add meson_build Restore meson CI after https://github.com/NixOS/nix/pull/10929 --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cd908f9e..c9e4c25ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -193,3 +193,11 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user + + meson_build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix build -L .#hydraJobs.build.{nix-fetchers,nix-store,nix-util}.$(nix-instantiate --eval --expr builtins.currentSystem | sed -e 's/"//g') From 0674be8d498d6ad1ca8bb383b26107ccdb64148d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 10:21:34 +0200 Subject: [PATCH 381/910] nix-util: Fix build --- src/libutil/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 2259d4e22..9d0b28539 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -161,6 +161,7 @@ sources = files( 'compression.cc', 'compute-levels.cc', 'config.cc', + 'config-global.cc', 'current-process.cc', 'english.cc', 'environment-variables.cc', @@ -211,6 +212,7 @@ headers = [config_h] + files( 'comparator.hh', 'compression.hh', 'compute-levels.hh', + 'config-global.hh', 'config-impl.hh', 'config.hh', 'current-process.hh', From 7df9d6da6542c06ba017e8dec1b63751a4bae847 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 20 Jun 2024 22:20:17 +0200 Subject: [PATCH 382/910] Improve error messages for invalid derivation names --- src/libexpr/primops.cc | 22 ++++++++++++++ src/libexpr/primops/fetchTree.cc | 22 ++++++++++++-- src/libstore/path.cc | 30 ++++++++++++------- src/libstore/path.hh | 9 ++++++ src/libstore/store-dir-config.hh | 1 + src/libutil/error.hh | 1 + src/libutil/fmt.hh | 2 ++ .../lang/eval-fail-derivation-name.err.exp | 26 ++++++++++++++++ .../lang/eval-fail-derivation-name.nix | 5 ++++ ...-fail-fetchurl-baseName-attrs-name.err.exp | 8 +++++ ...eval-fail-fetchurl-baseName-attrs-name.nix | 1 + .../eval-fail-fetchurl-baseName-attrs.err.exp | 8 +++++ .../eval-fail-fetchurl-baseName-attrs.nix | 1 + .../lang/eval-fail-fetchurl-baseName.err.exp | 8 +++++ .../lang/eval-fail-fetchurl-baseName.nix | 1 + tests/unit/libstore/path.cc | 16 ++++++++-- 16 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 tests/functional/lang/eval-fail-derivation-name.err.exp create mode 100644 tests/functional/lang/eval-fail-derivation-name.nix create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName.err.exp create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName.nix diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 000fce455..212441019 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1162,12 +1162,34 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * } } +/** + * Early validation for the derivation name, for better error message. + * It is checked again when constructing store paths. + * + * @todo Check that the `.drv` suffix also fits. + */ +static void checkDerivationName(EvalState & state, std::string_view drvName) +{ + try { + checkName(drvName); + } catch (BadStorePathName & e) { + // "Please pass a different name": Users may not be aware that they can + // pass a different one, in functions like `fetchurl` where the name + // is optional. + // Note that Nixpkgs generally won't trigger this, because `mkDerivation` + // sanitizes the name. + state.error("invalid derivation name: %s. Please pass a different '%s'.", Uncolored(e.message()), "name").debugThrow(); + } +} + static void derivationStrictInternal( EvalState & state, const std::string & drvName, const Bindings * attrs, Value & v) { + checkDerivationName(state, drvName); + /* Check whether attributes should be passed as a JSON file. */ using nlohmann::json; std::optional jsonObject; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index db2cc0d2b..fa6b1c4b6 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -431,7 +431,10 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v state.forceValue(*args[0], pos); - if (args[0]->type() == nAttrs) { + bool isArgAttrs = args[0]->type() == nAttrs; + bool nameAttrPassed = false; + + if (isArgAttrs) { for (auto & attr : *args[0]->attrs()) { std::string_view n(state.symbols[attr.name]); @@ -439,8 +442,10 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch"); else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), HashAlgorithm::SHA256); - else if (n == "name") + else if (n == "name") { + nameAttrPassed = true; name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch"); + } else state.error("unsupported argument '%s' to '%s'", n, who) .atPos(pos).debugThrow(); @@ -460,6 +465,19 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v if (name == "") name = baseNameOf(*url); + try { + checkName(name); + } catch (BadStorePathName & e) { + auto resolution = + nameAttrPassed ? HintFmt("Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.", who) : + isArgAttrs ? HintFmt("Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.", who) : + HintFmt("Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.", who); + + state.error( + std::string("invalid store path name when fetching URL '%s': %s. %s"), *url, Uncolored(e.message()), Uncolored(resolution.str())) + .atPos(pos).debugThrow(); + } + if (state.settings.pureEval && !expectedHash) state.error("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow(); diff --git a/src/libstore/path.cc b/src/libstore/path.cc index 8d9726722..3e9d05477 100644 --- a/src/libstore/path.cc +++ b/src/libstore/path.cc @@ -2,25 +2,24 @@ namespace nix { -static void checkName(std::string_view path, std::string_view name) +void checkName(std::string_view name) { if (name.empty()) - throw BadStorePath("store path '%s' has an empty name", path); + throw BadStorePathName("name must not be empty"); if (name.size() > StorePath::MaxPathLen) - throw BadStorePath("store path '%s' has a name longer than %d characters", - path, StorePath::MaxPathLen); + throw BadStorePathName("name '%s' must be no longer than %d characters", name, StorePath::MaxPathLen); // See nameRegexStr for the definition if (name[0] == '.') { // check against "." and "..", followed by end or dash if (name.size() == 1) - throw BadStorePath("store path '%s' has invalid name '%s'", path, name); + throw BadStorePathName("name '%s' is not valid", name); if (name[1] == '-') - throw BadStorePath("store path '%s' has invalid name '%s': first dash-separated component must not be '%s'", path, name, "."); + throw BadStorePathName("name '%s' is not valid: first dash-separated component must not be '%s'", name, "."); if (name[1] == '.') { if (name.size() == 2) - throw BadStorePath("store path '%s' has invalid name '%s'", path, name); + throw BadStorePathName("name '%s' is not valid", name); if (name[2] == '-') - throw BadStorePath("store path '%s' has invalid name '%s': first dash-separated component must not be '%s'", path, name, ".."); + throw BadStorePathName("name '%s' is not valid: first dash-separated component must not be '%s'", name, ".."); } } for (auto c : name) @@ -28,7 +27,16 @@ static void checkName(std::string_view path, std::string_view name) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '+' || c == '-' || c == '.' || c == '_' || c == '?' || c == '=')) - throw BadStorePath("store path '%s' contains illegal character '%s'", path, c); + throw BadStorePathName("name '%s' contains illegal character '%s'", name, c); +} + +static void checkPathName(std::string_view path, std::string_view name) +{ + try { + checkName(name); + } catch (BadStorePathName & e) { + throw BadStorePath("path '%s' is not a valid store path: %s", path, Uncolored(e.message())); + } } StorePath::StorePath(std::string_view _baseName) @@ -40,13 +48,13 @@ StorePath::StorePath(std::string_view _baseName) if (c == 'e' || c == 'o' || c == 'u' || c == 't' || !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))) throw BadStorePath("store path '%s' contains illegal base-32 character '%s'", baseName, c); - checkName(baseName, name()); + checkPathName(baseName, name()); } StorePath::StorePath(const Hash & hash, std::string_view _name) : baseName((hash.to_string(HashFormat::Nix32, false) + "-").append(std::string(_name))) { - checkName(baseName, name()); + checkPathName(baseName, name()); } bool StorePath::isDerivation() const noexcept diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 4abbfcd7c..2380dc6a2 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -9,6 +9,13 @@ namespace nix { struct Hash; +/** + * Check whether a name is a valid store path name. + * + * @throws BadStorePathName if the name is invalid. The message is of the format "name %s is not valid, for this specific reason". + */ +void checkName(std::string_view name); + /** * \ref StorePath "Store path" is the fundamental reference type of Nix. * A store paths refers to a Store object. @@ -31,8 +38,10 @@ public: StorePath() = delete; + /** @throws BadStorePath */ StorePath(std::string_view baseName); + /** @throws BadStorePath */ StorePath(const Hash & hash, std::string_view name); std::string_view to_string() const noexcept diff --git a/src/libstore/store-dir-config.hh b/src/libstore/store-dir-config.hh index fe86ce40d..64c0dd8b7 100644 --- a/src/libstore/store-dir-config.hh +++ b/src/libstore/store-dir-config.hh @@ -16,6 +16,7 @@ namespace nix { struct SourcePath; MakeError(BadStorePath, Error); +MakeError(BadStorePathName, BadStorePath); struct StoreDirConfig : public Config { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 269000016..4b08a045e 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -155,6 +155,7 @@ public: : err(e) { } + /** The error message without "error: " prefixed to it. */ std::string message() { return err.msg.str(); } diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index ef44a8409..850b7162d 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -111,6 +111,8 @@ std::ostream & operator<<(std::ostream & out, const Magenta & y) /** * Values wrapped in this class are printed without coloring. * + * Specifically, the color is reset to normal before printing the value. + * * By default, arguments to `HintFmt` are printed in magenta (see `Magenta`). */ template diff --git a/tests/functional/lang/eval-fail-derivation-name.err.exp b/tests/functional/lang/eval-fail-derivation-name.err.exp new file mode 100644 index 000000000..eb2206df1 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivation-name.err.exp @@ -0,0 +1,26 @@ +error: + … while evaluating the attribute 'outPath' + at :19:9: + 18| value = commonAttrs // { + 19| outPath = builtins.getAttr outputName strict; + | ^ + 20| drvPath = strict.drvPath; + + … while calling the 'getAttr' builtin + at :19:19: + 18| value = commonAttrs // { + 19| outPath = builtins.getAttr outputName strict; + | ^ + 20| drvPath = strict.drvPath; + + … while calling the 'derivationStrict' builtin + at :9:12: + 8| + 9| strict = derivationStrict drvAttrs; + | ^ + 10| + + … while evaluating derivation '~jiggle~' + whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:2:3 + + error: invalid derivation name: name '~jiggle~' contains illegal character '~'. Please pass a different 'name'. diff --git a/tests/functional/lang/eval-fail-derivation-name.nix b/tests/functional/lang/eval-fail-derivation-name.nix new file mode 100644 index 000000000..e779ad6ff --- /dev/null +++ b/tests/functional/lang/eval-fail-derivation-name.nix @@ -0,0 +1,5 @@ +derivation { + name = "~jiggle~"; + system = "some-system"; + builder = "/dontcare"; +} diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp new file mode 100644 index 000000000..30f8b6a35 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fetchurl' builtin + at /pwd/lang/eval-fail-fetchurl-baseName-attrs-name.nix:1:1: + 1| builtins.fetchurl { url = "https://example.com/foo.tar.gz"; name = "~wobble~"; } + | ^ + 2| + + error: invalid store path name when fetching URL 'https://example.com/foo.tar.gz': name '~wobble~' contains illegal character '~'. Please change the value for the 'name' attribute passed to 'fetchurl', so that it can create a valid store path. diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix new file mode 100644 index 000000000..583805539 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix @@ -0,0 +1 @@ +builtins.fetchurl { url = "https://example.com/foo.tar.gz"; name = "~wobble~"; } diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp new file mode 100644 index 000000000..cef532e94 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fetchurl' builtin + at /pwd/lang/eval-fail-fetchurl-baseName-attrs.nix:1:1: + 1| builtins.fetchurl { url = "https://example.com/~wiggle~"; } + | ^ + 2| + + error: invalid store path name when fetching URL 'https://example.com/~wiggle~': name '~wiggle~' contains illegal character '~'. Please add a valid 'name' attribute to the argument for 'fetchurl', so that it can create a valid store path. diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix new file mode 100644 index 000000000..068120edb --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix @@ -0,0 +1 @@ +builtins.fetchurl { url = "https://example.com/~wiggle~"; } diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName.err.exp b/tests/functional/lang/eval-fail-fetchurl-baseName.err.exp new file mode 100644 index 000000000..0950e8e70 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fetchurl' builtin + at /pwd/lang/eval-fail-fetchurl-baseName.nix:1:1: + 1| builtins.fetchurl "https://example.com/~wiggle~" + | ^ + 2| + + error: invalid store path name when fetching URL 'https://example.com/~wiggle~': name '~wiggle~' contains illegal character '~'. Please pass an attribute set with 'url' and 'name' attributes to 'fetchurl', so that it can create a valid store path. diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName.nix b/tests/functional/lang/eval-fail-fetchurl-baseName.nix new file mode 100644 index 000000000..965093843 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName.nix @@ -0,0 +1 @@ +builtins.fetchurl "https://example.com/~wiggle~" diff --git a/tests/unit/libstore/path.cc b/tests/unit/libstore/path.cc index 213b6e95f..c4c055abf 100644 --- a/tests/unit/libstore/path.cc +++ b/tests/unit/libstore/path.cc @@ -26,10 +26,19 @@ static std::regex nameRegex { std::string { nameRegexStr } }; TEST_F(StorePathTest, bad_ ## NAME) { \ std::string_view str = \ STORE_DIR HASH_PART "-" STR; \ - ASSERT_THROW( \ - store->parseStorePath(str), \ - BadStorePath); \ + /* ASSERT_THROW generates a duplicate goto label */ \ + /* A lambda isolates those labels. */ \ + [&](){ \ + ASSERT_THROW( \ + store->parseStorePath(str), \ + BadStorePath); \ + }(); \ std::string name { STR }; \ + [&](){ \ + ASSERT_THROW( \ + nix::checkName(name), \ + BadStorePathName); \ + }(); \ EXPECT_FALSE(std::regex_match(name, nameRegex)); \ } @@ -54,6 +63,7 @@ TEST_DONT_PARSE(dot_dash_a, ".-a") STORE_DIR HASH_PART "-" STR; \ auto p = store->parseStorePath(str); \ std::string name { p.name() }; \ + EXPECT_EQ(p.name(), STR); \ EXPECT_TRUE(std::regex_match(name, nameRegex)); \ } From ac89828b5a78045fc4943d47041a5599302a566c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 15:35:47 +0200 Subject: [PATCH 383/910] Build nix-util-c with meson and unit test --- flake.nix | 51 ++++++++- maintainers/hydra.nix | 3 + meson.build | 7 ++ src/libutil-c/.version | 1 + src/libutil-c/meson.build | 117 ++++++++++++++++++++ src/libutil-c/meson.options | 1 + src/libutil-c/nix-util-c.pc.in | 9 ++ src/libutil-c/package.nix | 96 +++++++++++++++++ src/libutil-test | 1 + src/libutil-test-support | 1 + tests/unit/libutil-support/.version | 1 + tests/unit/libutil-support/meson.build | 110 +++++++++++++++++++ tests/unit/libutil-support/package.nix | 98 +++++++++++++++++ tests/unit/libutil/.version | 1 + tests/unit/libutil/meson.build | 142 +++++++++++++++++++++++++ tests/unit/libutil/package.nix | 112 +++++++++++++++++++ 16 files changed, 750 insertions(+), 1 deletion(-) create mode 120000 src/libutil-c/.version create mode 100644 src/libutil-c/meson.build create mode 100644 src/libutil-c/meson.options create mode 100644 src/libutil-c/nix-util-c.pc.in create mode 100644 src/libutil-c/package.nix create mode 120000 src/libutil-test create mode 120000 src/libutil-test-support create mode 120000 tests/unit/libutil-support/.version create mode 100644 tests/unit/libutil-support/meson.build create mode 100644 tests/unit/libutil-support/package.nix create mode 100644 tests/unit/libutil/.version create mode 100644 tests/unit/libutil/meson.build create mode 100644 tests/unit/libutil/package.nix diff --git a/flake.nix b/flake.nix index 7e7148afe..aac45af34 100644 --- a/flake.nix +++ b/flake.nix @@ -160,6 +160,19 @@ }; }); + # TODO: define everything here instead of top level? + nix-components = { + inherit (final) + nix-util + nix-util-test-support + nix-util-test + nix-util-c + nix-store + nix-fetchers + nix-perl-bindings + ; + }; + nix-util = final.callPackage ./src/libutil/package.nix { inherit fileset @@ -169,6 +182,30 @@ ; }; + nix-util-test-support = final.callPackage ./tests/unit/libutil-support/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-test = final.callPackage ./tests/unit/libutil/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-c = final.callPackage ./src/libutil-c/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + nix-store = final.callPackage ./src/libstore/package.nix { inherit fileset @@ -281,7 +318,19 @@ # the old build system is gone and we are back to one build # system, we should reenable this. #perlBindings = self.hydraJobs.perlBindings.${system}; - } // devFlake.checks.${system} or {} + } + // lib.concatMapAttrs (nixpkgsPrefix: nixpkgs: + lib.concatMapAttrs (pkgName: pkg: + lib.concatMapAttrs (testName: test: { + # Add "passthru" tests + "${nixpkgsPrefix}${pkgName}-${testName}" = test; + }) pkg.tests or {} + ) nixpkgs.nix-components + ) { + "" = nixpkgsFor.${system}.native; + "static-" = nixpkgsFor.${system}.static; + } + // devFlake.checks.${system} or {} ); packages = forAllSystems (system: { diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index e21145f37..9e6f2c468 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -36,6 +36,9 @@ let forAllPackages = lib.genAttrs [ "nix" "nix-util" + "nix-util-c" + "nix-util-test-support" + "nix-util-test" "nix-store" "nix-fetchers" ]; diff --git a/meson.build b/meson.build index e67fd4a7a..085ac0865 100644 --- a/meson.build +++ b/meson.build @@ -12,3 +12,10 @@ subproject('libfetchers') subproject('perl') subproject('internal-api-docs') subproject('external-api-docs') + +# C wrappers +subproject('libutil-c') + +# Testing +subproject('libutil-test-support') +subproject('libutil-test') diff --git a/src/libutil-c/.version b/src/libutil-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build new file mode 100644 index 000000000..a2b020818 --- /dev/null +++ b/src/libutil-c/meson.build @@ -0,0 +1,117 @@ +project('nix-util-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + # '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'nix_api_util.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'nix_api_util.h', + 'nix_api_util_internal.h', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-util.h', +) + +this_library = library( + 'nixutilc', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : deps_public, + requires_private : deps_private, + libraries_private : libraries_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/src/libutil-c/meson.options b/src/libutil-c/meson.options new file mode 100644 index 000000000..04422feaf --- /dev/null +++ b/src/libutil-c/meson.options @@ -0,0 +1 @@ +# vim: filetype=meson diff --git a/src/libutil-c/nix-util-c.pc.in b/src/libutil-c/nix-util-c.pc.in new file mode 100644 index 000000000..0ccae3f8a --- /dev/null +++ b/src/libutil-c/nix-util-c.pc.in @@ -0,0 +1,9 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix libutil C API +Description: Common functions for the Nix C API, such as error handling +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnixutil +Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix new file mode 100644 index 000000000..a45b36d4c --- /dev/null +++ b/src/libutil-c/package.nix @@ -0,0 +1,96 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-c"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-util + ] + ; + + propagatedBuildInputs = [ + nix-util + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libutil-test b/src/libutil-test new file mode 120000 index 000000000..62c86f54b --- /dev/null +++ b/src/libutil-test @@ -0,0 +1 @@ +../tests/unit/libutil/ \ No newline at end of file diff --git a/src/libutil-test-support b/src/libutil-test-support new file mode 120000 index 000000000..f7da46d4c --- /dev/null +++ b/src/libutil-test-support @@ -0,0 +1 @@ +../tests/unit/libutil-support/ \ No newline at end of file diff --git a/tests/unit/libutil-support/.version b/tests/unit/libutil-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libutil-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build new file mode 100644 index 000000000..5912c00e0 --- /dev/null +++ b/tests/unit/libutil-support/meson.build @@ -0,0 +1,110 @@ +project('nix-util-test-support', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +add_project_arguments( + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'tests/hash.cc', + 'tests/string_callback.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'tests/characterization.hh', + 'tests/hash.hh', + 'tests/nix_api_util.hh', + 'tests/string_callback.hh', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +this_library = library( + 'nix-util-test-support', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 + # is available. See also ../libutil/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : deps_public, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix new file mode 100644 index 000000000..caa37b748 --- /dev/null +++ b/tests/unit/libutil-support/package.nix @@ -0,0 +1,98 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util + +, rapidcheck + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-test-support"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-util + rapidcheck + ] + ; + + propagatedBuildInputs = [ + nix-util + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/tests/unit/libutil/.version b/tests/unit/libutil/.version new file mode 100644 index 000000000..ad2261920 --- /dev/null +++ b/tests/unit/libutil/.version @@ -0,0 +1 @@ +2.24.0 diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build new file mode 100644 index 000000000..56147686b --- /dev/null +++ b/tests/unit/libutil/meson.build @@ -0,0 +1,142 @@ +project('nix-util-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util-test.h', + # '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-util-test.h', +) + +sources = files( + 'args.cc', + 'canon-path.cc', + 'chunked-vector.cc', + 'closure.cc', + 'compression.cc', + 'config.cc', + 'file-content-address.cc', + 'git.cc', + 'hash.cc', + 'hilite.cc', + 'json-utils.cc', + 'logging.cc', + 'lru-cache.cc', + 'nix_api_util.cc', + 'pool.cc', + 'references.cc', + 'spawn.cc', + 'suggestions.cc', + 'tests.cc', + 'url.cc', + 'xml-writer.cc', +) + +include_dirs = [include_directories('.')] + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +nix_util_c = dependency('nix-util-c') +if nix_util_c.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util_c +else + deps_public += nix_util_c +endif + +nix_util_test_support = dependency('nix-util-test-support') +if nix_util_test_support.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util_test_support +else + deps_public += nix_util_test_support +endif + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +gtest = dependency('gtest', main : true) +deps_public += gtest + +this_exe = executable( + 'nix-util-test', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test('nix-util-test', this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix new file mode 100644 index 000000000..7a09e4aa5 --- /dev/null +++ b/tests/unit/libutil/package.nix @@ -0,0 +1,112 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util +, nix-util-c +, nix-util-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-util + nix-util-c + nix-util-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-util-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) From 6a28566db663fe8b185142e0d9cfdb988f6b04a9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 15:49:39 +0200 Subject: [PATCH 384/910] refact: concatMapAttrs -> flatMapAttrs This should be slightly easier to read. We could apply this to all concatMapAttrs calls. --- flake.nix | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index aac45af34..1be9d2fc0 100644 --- a/flake.nix +++ b/flake.nix @@ -58,6 +58,18 @@ "stdenv" ]; + /** + `flatMapAttrs attrs f` applies `f` to each attribute in `attrs` and + merges the results into a single attribute set. + + This can be nested to form a build matrix where all the attributes + generated by the innermost `f` are returned as is. + (Provided that the names are unique.) + + See https://nixos.org/manual/nixpkgs/stable/index.html#function-library-lib.attrsets.concatMapAttrs + */ + flatMapAttrs = attrs: f: lib.concatMapAttrs f attrs; + forAllSystems = lib.genAttrs systems; forAllCrossSystems = lib.genAttrs crossSystems; @@ -319,17 +331,20 @@ # system, we should reenable this. #perlBindings = self.hydraJobs.perlBindings.${system}; } - // lib.concatMapAttrs (nixpkgsPrefix: nixpkgs: - lib.concatMapAttrs (pkgName: pkg: - lib.concatMapAttrs (testName: test: { - # Add "passthru" tests - "${nixpkgsPrefix}${pkgName}-${testName}" = test; - }) pkg.tests or {} - ) nixpkgs.nix-components - ) { + # Add "passthru" tests + // flatMapAttrs { "" = nixpkgsFor.${system}.native; "static-" = nixpkgsFor.${system}.static; } + (nixpkgsPrefix: nixpkgs: + flatMapAttrs nixpkgs.nix-components + (pkgName: pkg: + flatMapAttrs pkg.tests or {} + (testName: test: { + "${nixpkgsPrefix}${pkgName}-${testName}" = test; + }) + ) + ) // devFlake.checks.${system} or {} ); From 1eaddb209d240f424b8a40930b963c19a0883a57 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 16:17:13 +0200 Subject: [PATCH 385/910] TMP: disable static meson build on darwin --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 1be9d2fc0..c499fea5b 100644 --- a/flake.nix +++ b/flake.nix @@ -334,7 +334,8 @@ # Add "passthru" tests // flatMapAttrs { "" = nixpkgsFor.${system}.native; - "static-" = nixpkgsFor.${system}.static; + # FIXME: Static build on darwin currently broken + # "static-" = nixpkgsFor.${system}.static; } (nixpkgsPrefix: nixpkgs: flatMapAttrs nixpkgs.nix-components From ae3304bde9730e7cc2f43b71540fabd20fd12c6f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 16:56:45 +0200 Subject: [PATCH 386/910] Test static build of nix-util on non-darwin --- flake.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index c499fea5b..35280f038 100644 --- a/flake.nix +++ b/flake.nix @@ -332,11 +332,13 @@ #perlBindings = self.hydraJobs.perlBindings.${system}; } # Add "passthru" tests - // flatMapAttrs { + // flatMapAttrs ({ "" = nixpkgsFor.${system}.native; - # FIXME: Static build on darwin currently broken - # "static-" = nixpkgsFor.${system}.static; - } + } // lib.optionalAttrs (! nixpkgsFor.${system}.native.stdenv.hostPlatform.isDarwin) { + # TODO: enable static builds for darwin, blocked on: + # https://github.com/NixOS/nixpkgs/issues/320448 + "static-" = nixpkgsFor.${system}.static; + }) (nixpkgsPrefix: nixpkgs: flatMapAttrs nixpkgs.nix-components (pkgName: pkg: From fba81cf74b2081b5242126fac01061e1fedaa99d Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Tue, 25 Jun 2024 18:44:56 -0400 Subject: [PATCH 387/910] docs: internal documentation touchup Make two comments more accurate for the next reader. --- doc/manual/redirects.js | 2 +- doc/manual/src/_redirects | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 633932e55..f7b479106 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -1,7 +1,7 @@ // redirect rules for URL fragments (client-side) to prevent link rot. // this must be done on the client side, as web servers do not see the fragment part of the URL. // it will only work with JavaScript enabled in the browser, but this is the best we can do here. -// see ./_redirects for path redirects (client-side) +// see src/_redirects for path redirects (server-side) // redirects are declared as follows: // each entry has as its key a path matching the requested URL path, relative to the mdBook document root. diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index a04a36f1e..7f8edca8e 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -1,5 +1,5 @@ # redirect rules for paths (server-side) to prevent link rot. -# see ./redirects.js for redirects based on URL fragments (client-side) +# see ../redirects.js for redirects based on URL fragments (client-side) # # concrete user story this supports: # - user finds URL to the manual for Nix x.y From fd0b376c795f770a82c4f7b9ff5ef3df0c03e125 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 1 Feb 2024 12:27:29 +0100 Subject: [PATCH 388/910] libstore/worker.cc: Remove outdated comment It was added above this conditional Worker::Worker(LocalStore & store) : store(store) { /* Debugging: prevent recursive workers. */ if (working) abort(); working = true; However, `working` has since been removed. Source: https://github.com/NixOS/nix/blame/7f8e805c8ef2d7728648553de6b762964730a09a/src/libstore/build.cc#L2617 --- src/libstore/build/worker.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 31cfa8adc..8a5d6de72 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -19,7 +19,6 @@ Worker::Worker(Store & store, Store & evalStore) , store(store) , evalStore(evalStore) { - /* Debugging: prevent recursive workers. */ nrLocalBuilds = 0; nrSubstitutions = 0; lastWokenUp = steady_time_point::min(); From 6fe8fb967a71e95f04e286ea5301d33f592788bf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 1 Feb 2024 12:39:45 +0100 Subject: [PATCH 389/910] libstore/worker.hh: Document Worker --- src/libstore/build/worker.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index 7d67030d7..33a7bf015 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -59,7 +59,7 @@ struct HookInstance; #endif /** - * The worker class. + * Coordinates one or more realisations and their interdependencies. */ class Worker { From e28e6b96bb671309218e5b9de56aeac22b2bf7be Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 02:03:21 +0200 Subject: [PATCH 390/910] flake.nix: Tidy `packages` build matrix code flatMapAttrs is easier to read because it introduces the values before using them, kind of like a `let` bindings with multiple values. The repeated comments remind the reader of the purpose of the innermost attrsets, which is actually very simple. Knowing that they go right into the result should help a lot with building a mental model for this pattern. --- flake.nix | 62 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/flake.nix b/flake.nix index 35280f038..dfae6ce2d 100644 --- a/flake.nix +++ b/flake.nix @@ -351,36 +351,40 @@ // devFlake.checks.${system} or {} ); - packages = forAllSystems (system: { - inherit (nixpkgsFor.${system}.native) - changelog-d; - default = self.packages.${system}.nix; - nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; - nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; - } // lib.concatMapAttrs - # We need to flatten recursive attribute sets of derivations to pass `flake check`. - (pkgName: {}: { - "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; - "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; - } // lib.concatMapAttrs - (crossSystem: {}: { - "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; - }) - (lib.genAttrs crossSystems (_: { })) - // lib.concatMapAttrs - (stdenvName: {}: { - "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; - }) - (lib.genAttrs stdenvs (_: { }))) - { - "nix" = { }; - # Temporarily disabled because GitHub Actions OOM issues. Once - # the old build system is gone and we are back to one build - # system, we should reenable these. - #"nix-util" = { }; - #"nix-store" = { }; - #"nix-fetchers" = { }; + packages = forAllSystems (system: + { # Here we put attributes that map 1:1 into packages., ie + # for which we don't apply the full build matrix such as cross or static. + inherit (nixpkgsFor.${system}.native) + changelog-d; + default = self.packages.${system}.nix; + nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; + nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; } + # We need to flatten recursive attribute sets of derivations to pass `flake check`. + // flatMapAttrs + { # Components we'll iterate over in the upcoming lambda + "nix" = { }; + # Temporarily disabled because GitHub Actions OOM issues. Once + # the old build system is gone and we are back to one build + # system, we should reenable these. + #"nix-util" = { }; + #"nix-store" = { }; + #"nix-fetchers" = { }; + } + (pkgName: {}: { + # These attributes go right into `packages.`. + "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; + "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; + } + // flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: { + # These attributes go right into `packages.`. + "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; + }) + // flatMapAttrs (lib.genAttrs stdenvs (_: { })) (stdenvName: {}: { + # These attributes go right into `packages.`. + "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; + }) + ) // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = let From a14faa869d12d6280ac5ebf94c585ef200bb4c9c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 02:38:07 +0200 Subject: [PATCH 391/910] flake.nix: Use a Nixpkgs scope for components --- flake.nix | 97 +++++----------------------------------- packaging/components.nix | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 87 deletions(-) create mode 100644 packaging/components.nix diff --git a/flake.nix b/flake.nix index dfae6ce2d..30a8e4354 100644 --- a/flake.nix +++ b/flake.nix @@ -172,93 +172,16 @@ }; }); - # TODO: define everything here instead of top level? - nix-components = { - inherit (final) - nix-util - nix-util-test-support - nix-util-test - nix-util-c - nix-store - nix-fetchers - nix-perl-bindings - ; - }; + # A new scope, so that we can use `callPackage` to inject our own interdependencies + # without "polluting" the top level "`pkgs`" attrset. + # This also has the benefit of providing us with a distinct set of packages + # we can iterate over. + nixComponents = lib.makeScope final.newScope (import ./packaging/components.nix { + pkgs = final; + inherit stdenv versionSuffix officialRelease; + }); - nix-util = final.callPackage ./src/libutil/package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - }; - - nix-util-test-support = final.callPackage ./tests/unit/libutil-support/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; - - nix-util-test = final.callPackage ./tests/unit/libutil/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; - - nix-util-c = final.callPackage ./src/libutil-c/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; - - nix-store = final.callPackage ./src/libstore/package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - libseccomp = final.libseccomp-nix; - busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; - }; - - nix-fetchers = final.callPackage ./src/libfetchers/package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - }; - - nix = - final.callPackage ./package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - boehmgc = final.boehmgc-nix; - libgit2 = final.libgit2-nix; - libseccomp = final.libseccomp-nix; - busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; - }; - - nix-perl-bindings = final.callPackage ./src/perl/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; + nix = final.nixComponents.nix; nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { inherit @@ -340,7 +263,7 @@ "static-" = nixpkgsFor.${system}.static; }) (nixpkgsPrefix: nixpkgs: - flatMapAttrs nixpkgs.nix-components + flatMapAttrs nixpkgs.nixComponents (pkgName: pkg: flatMapAttrs pkg.tests or {} (testName: test: { diff --git a/packaging/components.nix b/packaging/components.nix new file mode 100644 index 000000000..af26e05ce --- /dev/null +++ b/packaging/components.nix @@ -0,0 +1,86 @@ +{pkgs, stdenv, officialRelease, versionSuffix}: scope: +let + inherit (scope) callPackage; + + # TODO: push fileset parameter into package.nix files as `lib` parameter + inherit (callPackage (args@{ lib }: args) {}) lib; + inherit (lib) fileset; +in + +# This becomes the pkgs.nixComponents attribute set +{ + # TODO: build the nix CLI with meson + nix = pkgs.callPackage ../package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + boehmgc = pkgs.boehmgc-nix; + libgit2 = pkgs.libgit2-nix; + libseccomp = pkgs.libseccomp-nix; + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; + }; + + nix-util = callPackage ../src/libutil/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; + + nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-test = callPackage ../tests/unit/libutil/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-c = callPackage ../src/libutil-c/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-store = callPackage ../src/libstore/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + libseccomp = pkgs.libseccomp-nix; + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; + }; + + nix-fetchers = callPackage ../src/libfetchers/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; + + nix-perl-bindings = callPackage ../src/perl/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; +} From d40c59ed194b715bffdcd40f4b654ebc60cf5be3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 02:44:21 +0200 Subject: [PATCH 392/910] flake.nix: Use the nixComponents scope instead of bare pkgs packages ... which aren't around anymore. --- flake.nix | 24 ++++++++++++------------ maintainers/hydra.nix | 11 +++++++---- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/flake.nix b/flake.nix index 30a8e4354..a66603cc8 100644 --- a/flake.nix +++ b/flake.nix @@ -296,16 +296,16 @@ } (pkgName: {}: { # These attributes go right into `packages.`. - "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; - "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; + "${pkgName}" = nixpkgsFor.${system}.native.nixComponents.${pkgName}; + "${pkgName}-static" = nixpkgsFor.${system}.static.nixComponents.${pkgName}; } // flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: { # These attributes go right into `packages.`. - "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; + "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents.${pkgName}; }) // flatMapAttrs (lib.genAttrs stdenvs (_: { })) (stdenvName: {}: { # These attributes go right into `packages.`. - "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; + "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nixComponents.${pkgName}; }) ) // lib.optionalAttrs (builtins.elem system linux64BitSystems) { @@ -367,17 +367,17 @@ }; mesonFlags = - map (transformFlag "libutil") pkgs.nix-util.mesonFlags - ++ map (transformFlag "libstore") pkgs.nix-store.mesonFlags - ++ map (transformFlag "libfetchers") pkgs.nix-fetchers.mesonFlags - ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nix-perl-bindings.mesonFlags) + map (transformFlag "libutil") pkgs.nixComponents.nix-util.mesonFlags + ++ map (transformFlag "libstore") pkgs.nixComponents.nix-store.mesonFlags + ++ map (transformFlag "libfetchers") pkgs.nixComponents.nix-fetchers.mesonFlags + ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nixComponents.nix-perl-bindings.mesonFlags) ; nativeBuildInputs = attrs.nativeBuildInputs or [] - ++ pkgs.nix-util.nativeBuildInputs - ++ pkgs.nix-store.nativeBuildInputs - ++ pkgs.nix-fetchers.nativeBuildInputs - ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs + ++ pkgs.nixComponents.nix-util.nativeBuildInputs + ++ pkgs.nixComponents.nix-store.nativeBuildInputs + ++ pkgs.nixComponents.nix-fetchers.nativeBuildInputs + ++ lib.optionals havePerl pkgs.nixComponents.nix-perl-bindings.nativeBuildInputs ++ pkgs.nix-internal-api-docs.nativeBuildInputs ++ pkgs.nix-external-api-docs.nativeBuildInputs ++ [ diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 9e6f2c468..b03cbdfa0 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -33,6 +33,9 @@ let doBuild = false; }; + # Technically we could just return `pkgs.nixComponents`, but for Hydra it's + # convention to transpose it, and to transpose it efficiently, we need to + # enumerate them manually, so that we don't evaluate unnecessary package sets. forAllPackages = lib.genAttrs [ "nix" "nix-util" @@ -46,16 +49,16 @@ in { # Binary package for various platforms. build = forAllPackages (pkgName: - forAllSystems (system: nixpkgsFor.${system}.native.${pkgName})); + forAllSystems (system: nixpkgsFor.${system}.native.nixComponents.${pkgName})); shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); buildStatic = forAllPackages (pkgName: - lib.genAttrs linux64BitSystems (system: nixpkgsFor.${system}.static.${pkgName})); + lib.genAttrs linux64BitSystems (system: nixpkgsFor.${system}.static.nixComponents.${pkgName})); buildCross = forAllPackages (pkgName: forAllCrossSystems (crossSystem: - lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}))); + lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.nixComponents.${pkgName}))); buildNoGc = forAllSystems (system: self.packages.${system}.nix.override { enableGC = false; } @@ -73,7 +76,7 @@ in ); # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix-perl-bindings); + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents.nix-perl-bindings); # Binary tarball for various platforms, containing a Nix store # with the closure of 'nix' package, and the second half of From 85de5a60c77c256d5e57943becb683f50f9d7537 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:18:01 +0200 Subject: [PATCH 393/910] Use lib instead of explicit fileset passing --- flake.nix | 2 -- maintainers/hydra.nix | 3 --- package.nix | 6 ++---- packaging/components.nix | 16 ---------------- src/external-api-docs/package.nix | 6 ++++-- src/internal-api-docs/package.nix | 6 ++++-- src/libfetchers/package.nix | 6 ++---- src/libstore/package.nix | 5 ++--- src/libutil-c/package.nix | 3 ++- src/libutil/package.nix | 4 ++-- src/perl/package.nix | 5 ++++- tests/unit/libutil-support/package.nix | 3 ++- tests/unit/libutil/package.nix | 3 ++- 13 files changed, 26 insertions(+), 42 deletions(-) diff --git a/flake.nix b/flake.nix index a66603cc8..7fc491035 100644 --- a/flake.nix +++ b/flake.nix @@ -185,7 +185,6 @@ nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { inherit - fileset stdenv versionSuffix ; @@ -193,7 +192,6 @@ nix-external-api-docs = final.callPackage ./src/external-api-docs/package.nix { inherit - fileset stdenv versionSuffix ; diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index b03cbdfa0..98d8b3961 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -9,7 +9,6 @@ }: let inherit (inputs) nixpkgs nixpkgs-regression; - inherit (lib) fileset; installScriptFor = tarballs: nixpkgsFor.x86_64-linux.native.callPackage ../scripts/installer.nix { @@ -25,8 +24,6 @@ let lib.versionAtLeast client.version "2.4pre20211005") "-${client.version}-against-${daemon.version}"; - inherit fileset; - test-client = client; test-daemon = daemon; diff --git a/package.nix b/package.nix index f414b9a73..9a6fc272a 100644 --- a/package.nix +++ b/package.nix @@ -1,12 +1,10 @@ { lib -, fetchurl , stdenv , releaseTools , autoconf-archive , autoreconfHook , aws-sdk-cpp , boehmgc -, buildPackages , nlohmann_json , bison , boost @@ -15,7 +13,6 @@ , curl , editline , readline -, fileset , flex , git , gtest @@ -50,7 +47,6 @@ , pname ? "nix" , versionSuffix ? "" -, officialRelease ? false # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true @@ -113,6 +109,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; # selected attributes with defaults, will be used to define some diff --git a/packaging/components.nix b/packaging/components.nix index af26e05ce..d3809ff38 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -1,10 +1,6 @@ {pkgs, stdenv, officialRelease, versionSuffix}: scope: let inherit (scope) callPackage; - - # TODO: push fileset parameter into package.nix files as `lib` parameter - inherit (callPackage (args@{ lib }: args) {}) lib; - inherit (lib) fileset; in # This becomes the pkgs.nixComponents attribute set @@ -12,9 +8,7 @@ in # TODO: build the nix CLI with meson nix = pkgs.callPackage ../package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; boehmgc = pkgs.boehmgc-nix; @@ -25,16 +19,13 @@ in nix-util = callPackage ../src/libutil/package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; }; nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { inherit - fileset stdenv versionSuffix ; @@ -42,7 +33,6 @@ in nix-util-test = callPackage ../tests/unit/libutil/package.nix { inherit - fileset stdenv versionSuffix ; @@ -50,7 +40,6 @@ in nix-util-c = callPackage ../src/libutil-c/package.nix { inherit - fileset stdenv versionSuffix ; @@ -58,9 +47,7 @@ in nix-store = callPackage ../src/libstore/package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; libseccomp = pkgs.libseccomp-nix; @@ -69,16 +56,13 @@ in nix-fetchers = callPackage ../src/libfetchers/package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; }; nix-perl-bindings = callPackage ../src/perl/package.nix { inherit - fileset stdenv versionSuffix ; diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index aa5cd49eb..352698360 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -1,7 +1,5 @@ { lib , stdenv -, releaseTools -, fileset , meson , ninja @@ -12,6 +10,10 @@ , versionSuffix ? "" }: +let + inherit (lib) fileset; +in + stdenv.mkDerivation (finalAttrs: { pname = "nix-external-api-docs"; version = lib.fileContents ./.version + versionSuffix; diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index b5f1b0da1..fa54d55f3 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -1,7 +1,5 @@ { lib , stdenv -, releaseTools -, fileset , meson , ninja @@ -12,6 +10,10 @@ , versionSuffix ? "" }: +let + inherit (lib) fileset; +in + stdenv.mkDerivation (finalAttrs: { pname = "nix-internal-api-docs"; version = lib.fileContents ./.version + versionSuffix; diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 800256030..a5583d14c 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -16,17 +15,16 @@ # Configuration Options , versionSuffix ? "" -, officialRelease ? false # Check test coverage of Nix. Probably want to use with with at least # one of `doCheck` or `doInstallCheck` enabled. , withCoverageChecks ? false -# Avoid setting things that would interfere with a functioning devShell -, forDevShell ? false }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/libstore/package.nix b/src/libstore/package.nix index e54dfe597..e118f3cd2 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -13,7 +12,6 @@ , aws-sdk-cpp , libseccomp , nlohmann_json -, man , sqlite , busybox-sandbox-shell ? null @@ -21,7 +19,6 @@ # Configuration Options , versionSuffix ? "" -, officialRelease ? false # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. @@ -32,6 +29,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index a45b36d4c..05a26c17e 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -19,6 +18,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/libutil/package.nix b/src/libutil/package.nix index b36e3879c..892951cdf 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -18,7 +17,6 @@ # Configuration Options , versionSuffix ? "" -, officialRelease ? false # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. @@ -26,6 +24,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/perl/package.nix b/src/perl/package.nix index a31b1b66c..85f1547b7 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -1,5 +1,4 @@ { lib -, fileset , stdenv , perl , perlPackages @@ -16,6 +15,10 @@ , versionSuffix ? "" }: +let + inherit (lib) fileset; +in + perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { pname = "nix-perl"; version = lib.fileContents ./.version + versionSuffix; diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix index caa37b748..0be0a9945 100644 --- a/tests/unit/libutil-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -21,6 +20,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index 7a09e4aa5..391f8d853 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -25,6 +24,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = From 74b9b77c9f05099c2bb207890d0728455a85efa7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:33:30 +0200 Subject: [PATCH 394/910] components.nix: Simplify with scope --- flake.nix | 2 +- packaging/components.nix | 71 +++++++++------------------------------- 2 files changed, 16 insertions(+), 57 deletions(-) diff --git a/flake.nix b/flake.nix index 7fc491035..ed330b669 100644 --- a/flake.nix +++ b/flake.nix @@ -178,7 +178,7 @@ # we can iterate over. nixComponents = lib.makeScope final.newScope (import ./packaging/components.nix { pkgs = final; - inherit stdenv versionSuffix officialRelease; + inherit stdenv versionSuffix; }); nix = final.nixComponents.nix; diff --git a/packaging/components.nix b/packaging/components.nix index d3809ff38..7cef0e356 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -1,70 +1,29 @@ -{pkgs, stdenv, officialRelease, versionSuffix}: scope: +{pkgs, stdenv, versionSuffix}: scope: let inherit (scope) callPackage; in # This becomes the pkgs.nixComponents attribute set { - # TODO: build the nix CLI with meson - nix = pkgs.callPackage ../package.nix { - inherit - stdenv - versionSuffix - ; - boehmgc = pkgs.boehmgc-nix; - libgit2 = pkgs.libgit2-nix; - libseccomp = pkgs.libseccomp-nix; - busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - }; + inherit stdenv versionSuffix; + libseccomp = pkgs.libseccomp-nix; + boehmgc = pkgs.boehmgc-nix; + libgit2 = pkgs.libgit2-nix; + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - nix-util = callPackage ../src/libutil/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix = callPackage ../package.nix { }; - nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-util = callPackage ../src/libutil/package.nix { }; - nix-util-test = callPackage ../tests/unit/libutil/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { }; - nix-util-c = callPackage ../src/libutil-c/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-util-test = callPackage ../tests/unit/libutil/package.nix { }; - nix-store = callPackage ../src/libstore/package.nix { - inherit - stdenv - versionSuffix - ; - libseccomp = pkgs.libseccomp-nix; - busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - }; + nix-util-c = callPackage ../src/libutil-c/package.nix { }; - nix-fetchers = callPackage ../src/libfetchers/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-store = callPackage ../src/libstore/package.nix { }; - nix-perl-bindings = callPackage ../src/perl/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-fetchers = callPackage ../src/libfetchers/package.nix { }; + + nix-perl-bindings = callPackage ../src/perl/package.nix { }; } From ebf77c79ae1cd1516117311c6315bd8c56b8b837 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:36:03 +0200 Subject: [PATCH 395/910] flake.nix: Use Nixpkgs convention for package variants --- flake.nix | 6 +++--- packaging/components.nix | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index ed330b669..d49bccda8 100644 --- a/flake.nix +++ b/flake.nix @@ -153,18 +153,18 @@ ''; }; - libgit2-nix = final.libgit2.overrideAttrs (attrs: { + libgit2_nix = final.libgit2.overrideAttrs (attrs: { src = libgit2; version = libgit2.lastModifiedDate; cmakeFlags = attrs.cmakeFlags or [] ++ [ "-DUSE_SSH=exec" ]; }); - boehmgc-nix = final.boehmgc.override { + boehmgc_nix = final.boehmgc.override { enableLargeConfig = true; }; - libseccomp-nix = final.libseccomp.overrideAttrs (_: rec { + libseccomp_nix = final.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; src = final.fetchurl { url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; diff --git a/packaging/components.nix b/packaging/components.nix index 7cef0e356..2d06cfec4 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -6,9 +6,9 @@ in # This becomes the pkgs.nixComponents attribute set { inherit stdenv versionSuffix; - libseccomp = pkgs.libseccomp-nix; - boehmgc = pkgs.boehmgc-nix; - libgit2 = pkgs.libgit2-nix; + libseccomp = pkgs.libseccomp_nix; + boehmgc = pkgs.boehmgc_nix; + libgit2 = pkgs.libgit2_nix; busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; nix = callPackage ../package.nix { }; From 25dc12aab139b4bd3438c764a1d479ae8517a81a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:58:47 +0200 Subject: [PATCH 396/910] components.nix: Extract dependency scope This avoids polluting nixComponents with things that aren't our components. Fixes the extraction of passthru tests, which failed for boehmgc which had many irrelevant ones anyway. --- flake.nix | 12 +++++++++++- packaging/components.nix | 6 ------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index d49bccda8..870b82950 100644 --- a/flake.nix +++ b/flake.nix @@ -176,11 +176,21 @@ # without "polluting" the top level "`pkgs`" attrset. # This also has the benefit of providing us with a distinct set of packages # we can iterate over. - nixComponents = lib.makeScope final.newScope (import ./packaging/components.nix { + nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix { pkgs = final; inherit stdenv versionSuffix; }); + # The dependencies are in their own scope, so that they don't have to be + # in Nixpkgs top level `pkgs` or `nixComponents`. + nixDependencies = lib.makeScope final.newScope (scope: { + inherit stdenv versionSuffix; + libseccomp = final.libseccomp_nix; + boehmgc = final.boehmgc_nix; + libgit2 = final.libgit2_nix; + busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; + }); + nix = final.nixComponents.nix; nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { diff --git a/packaging/components.nix b/packaging/components.nix index 2d06cfec4..e1e73d4c0 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -5,12 +5,6 @@ in # This becomes the pkgs.nixComponents attribute set { - inherit stdenv versionSuffix; - libseccomp = pkgs.libseccomp_nix; - boehmgc = pkgs.boehmgc_nix; - libgit2 = pkgs.libgit2_nix; - busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - nix = callPackage ../package.nix { }; nix-util = callPackage ../src/libutil/package.nix { }; From c24dbf14571cd59ef4e7b2a3470c61760a6a6aa4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 04:17:30 +0200 Subject: [PATCH 397/910] components.nix: Simplify --- flake.nix | 5 +---- packaging/components.nix | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index 870b82950..90a48a382 100644 --- a/flake.nix +++ b/flake.nix @@ -176,10 +176,7 @@ # without "polluting" the top level "`pkgs`" attrset. # This also has the benefit of providing us with a distinct set of packages # we can iterate over. - nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix { - pkgs = final; - inherit stdenv versionSuffix; - }); + nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix); # The dependencies are in their own scope, so that they don't have to be # in Nixpkgs top level `pkgs` or `nixComponents`. diff --git a/packaging/components.nix b/packaging/components.nix index e1e73d4c0..f96ac6b51 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -1,4 +1,4 @@ -{pkgs, stdenv, versionSuffix}: scope: +scope: let inherit (scope) callPackage; in From 65802da98db906e61a810e686e140536d3ab4e54 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 04:24:50 +0200 Subject: [PATCH 398/910] Move maintainers/hydra.nix -> packaging/hydra.nix --- flake.nix | 2 +- {maintainers => packaging}/hydra.nix | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {maintainers => packaging}/hydra.nix (100%) diff --git a/flake.nix b/flake.nix index 90a48a382..510df73f1 100644 --- a/flake.nix +++ b/flake.nix @@ -224,7 +224,7 @@ # 'nix-perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - hydraJobs = import ./maintainers/hydra.nix { + hydraJobs = import ./packaging/hydra.nix { inherit inputs binaryTarball diff --git a/maintainers/hydra.nix b/packaging/hydra.nix similarity index 100% rename from maintainers/hydra.nix rename to packaging/hydra.nix From 409eded5415f00c2cc7ef6e124a51c1bb0290df1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 10:41:56 +0200 Subject: [PATCH 399/910] flake.nix: Move dependencies scope to packaging/dependencies.nix --- flake.nix | 52 ++-------------------------------- packaging/dependencies.nix | 58 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 packaging/dependencies.nix diff --git a/flake.nix b/flake.nix index 510df73f1..8b4deb78a 100644 --- a/flake.nix +++ b/flake.nix @@ -129,49 +129,6 @@ { nixStable = prev.nix; - default-busybox-sandbox-shell = final.busybox.override { - useMusl = true; - enableStatic = true; - enableMinimal = true; - extraConfig = '' - CONFIG_FEATURE_FANCY_ECHO y - CONFIG_FEATURE_SH_MATH y - CONFIG_FEATURE_SH_MATH_64 y - - CONFIG_ASH y - CONFIG_ASH_OPTIMIZE_FOR_SIZE y - - CONFIG_ASH_ALIAS y - CONFIG_ASH_BASH_COMPAT y - CONFIG_ASH_CMDCMD y - CONFIG_ASH_ECHO y - CONFIG_ASH_GETOPTS y - CONFIG_ASH_INTERNAL_GLOB y - CONFIG_ASH_JOB_CONTROL y - CONFIG_ASH_PRINTF y - CONFIG_ASH_TEST y - ''; - }; - - libgit2_nix = final.libgit2.overrideAttrs (attrs: { - src = libgit2; - version = libgit2.lastModifiedDate; - cmakeFlags = attrs.cmakeFlags or [] - ++ [ "-DUSE_SSH=exec" ]; - }); - - boehmgc_nix = final.boehmgc.override { - enableLargeConfig = true; - }; - - libseccomp_nix = final.libseccomp.overrideAttrs (_: rec { - version = "2.5.5"; - src = final.fetchurl { - url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; - hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U="; - }; - }); - # A new scope, so that we can use `callPackage` to inject our own interdependencies # without "polluting" the top level "`pkgs`" attrset. # This also has the benefit of providing us with a distinct set of packages @@ -180,12 +137,9 @@ # The dependencies are in their own scope, so that they don't have to be # in Nixpkgs top level `pkgs` or `nixComponents`. - nixDependencies = lib.makeScope final.newScope (scope: { - inherit stdenv versionSuffix; - libseccomp = final.libseccomp_nix; - boehmgc = final.boehmgc_nix; - libgit2 = final.libgit2_nix; - busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; + nixDependencies = lib.makeScope final.newScope (import ./packaging/dependencies.nix { + inherit inputs stdenv versionSuffix; + pkgs = final; }); nix = final.nixComponents.nix; diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix new file mode 100644 index 000000000..88273df22 --- /dev/null +++ b/packaging/dependencies.nix @@ -0,0 +1,58 @@ +# These overrides are applied to the dependencies of the Nix components. + +{ + # Flake inputs; used for sources + inputs, + + # The raw Nixpkgs, not affected by this scope + pkgs, + + stdenv, + versionSuffix, +}: +scope: { + inherit stdenv versionSuffix; + + libseccomp = pkgs.libseccomp.overrideAttrs (_: rec { + version = "2.5.5"; + src = pkgs.fetchurl { + url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; + hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U="; + }; + }); + + boehmgc = pkgs.boehmgc.override { + enableLargeConfig = true; + }; + + libgit2 = pkgs.libgit2.overrideAttrs (attrs: { + src = inputs.libgit2; + version = inputs.libgit2.lastModifiedDate; + cmakeFlags = attrs.cmakeFlags or [] + ++ [ "-DUSE_SSH=exec" ]; + }); + + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or (pkgs.busybox.override { + useMusl = true; + enableStatic = true; + enableMinimal = true; + extraConfig = '' + CONFIG_FEATURE_FANCY_ECHO y + CONFIG_FEATURE_SH_MATH y + CONFIG_FEATURE_SH_MATH_64 y + + CONFIG_ASH y + CONFIG_ASH_OPTIMIZE_FOR_SIZE y + + CONFIG_ASH_ALIAS y + CONFIG_ASH_BASH_COMPAT y + CONFIG_ASH_CMDCMD y + CONFIG_ASH_ECHO y + CONFIG_ASH_GETOPTS y + CONFIG_ASH_INTERNAL_GLOB y + CONFIG_ASH_JOB_CONTROL y + CONFIG_ASH_PRINTF y + CONFIG_ASH_TEST y + ''; + }); +} From 985c211061be71317eb2a9fc7a4d35278f5d8e41 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 10:47:13 +0200 Subject: [PATCH 400/910] flake.nix: Move {in,ex}ternal-api-docs into nixComponents scope --- flake.nix | 22 ++++------------------ packaging/components.nix | 5 +++++ packaging/hydra.nix | 4 ++-- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/flake.nix b/flake.nix index 8b4deb78a..582a946c4 100644 --- a/flake.nix +++ b/flake.nix @@ -144,20 +144,6 @@ nix = final.nixComponents.nix; - nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { - inherit - stdenv - versionSuffix - ; - }; - - nix-external-api-docs = final.callPackage ./src/external-api-docs/package.nix { - inherit - stdenv - versionSuffix - ; - }; - nix_noTests = final.nix.override { doCheck = false; doInstallCheck = false; @@ -239,8 +225,8 @@ inherit (nixpkgsFor.${system}.native) changelog-d; default = self.packages.${system}.nix; - nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; - nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; + nix-internal-api-docs = nixpkgsFor.${system}.native.nixComponents.nix-internal-api-docs; + nix-external-api-docs = nixpkgsFor.${system}.native.nixComponents.nix-external-api-docs; } # We need to flatten recursive attribute sets of derivations to pass `flake check`. // flatMapAttrs @@ -337,8 +323,8 @@ ++ pkgs.nixComponents.nix-store.nativeBuildInputs ++ pkgs.nixComponents.nix-fetchers.nativeBuildInputs ++ lib.optionals havePerl pkgs.nixComponents.nix-perl-bindings.nativeBuildInputs - ++ pkgs.nix-internal-api-docs.nativeBuildInputs - ++ pkgs.nix-external-api-docs.nativeBuildInputs + ++ pkgs.nixComponents.nix-internal-api-docs.nativeBuildInputs + ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/packaging/components.nix b/packaging/components.nix index f96ac6b51..b5e47969e 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -20,4 +20,9 @@ in nix-fetchers = callPackage ../src/libfetchers/package.nix { }; nix-perl-bindings = callPackage ../src/perl/package.nix { }; + + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; + + nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; + } diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 98d8b3961..5715abd8e 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -122,10 +122,10 @@ in }; # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.nix-internal-api-docs; + internal-api-docs = nixpkgsFor.x86_64-linux.native.nixComponents.nix-internal-api-docs; # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.nix-external-api-docs; + external-api-docs = nixpkgsFor.x86_64-linux.native.nixComponents.nix-external-api-docs; # System tests. tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor self; } // { From e6442891611b2f2ceee7ab1c573eec918a5fe5d7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 10:54:17 +0200 Subject: [PATCH 401/910] Remove unused boehmgc patch --- dep-patches/boehmgc-traceable_allocator-public.diff | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 dep-patches/boehmgc-traceable_allocator-public.diff diff --git a/dep-patches/boehmgc-traceable_allocator-public.diff b/dep-patches/boehmgc-traceable_allocator-public.diff deleted file mode 100644 index 903c707a6..000000000 --- a/dep-patches/boehmgc-traceable_allocator-public.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/include/gc_allocator.h b/include/gc_allocator.h -index 597c7f13..587286be 100644 ---- a/include/gc_allocator.h -+++ b/include/gc_allocator.h -@@ -312,6 +312,7 @@ public: - - template<> - class traceable_allocator { -+public: - typedef size_t size_type; - typedef ptrdiff_t difference_type; - typedef void* pointer; From 9f8e387c3fd78e46c5656a503eafea4757ae2616 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 11:02:45 +0200 Subject: [PATCH 402/910] ci.yml: Build meson on darwin We're building a bit of Darwin meson indirectly through `checks`, but it'd be annoying to encounter broken un-`check`-ed stuff during the porting process, so let's just do the right thing now. --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9e4c25ea..103ce4ff4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -195,7 +195,11 @@ jobs: - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user meson_build: - runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main From 32e67eba8ba297045627cd0259c75a2668eda8df Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 19:34:57 -0400 Subject: [PATCH 403/910] Remove invalid release notes YAML field There is no PR for this, since it was an embargoed fix before disclosure. --- doc/manual/rl-next/harden-user-sandboxing.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md index fa3c49fc0..a647acf25 100644 --- a/doc/manual/rl-next/harden-user-sandboxing.md +++ b/doc/manual/rl-next/harden-user-sandboxing.md @@ -2,7 +2,6 @@ synopsis: Harden the user sandboxing significance: significant issues: -prs: --- The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. From 88f9d8ccb1d8091c8a35d5916d8490d609f6ce48 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 19:53:36 -0400 Subject: [PATCH 404/910] Don't format the just-added test .c file On one hand, new things should be formatted. On the other, we just bacported this file to many prior branches, and if we need to make changes to it and backport them also, formatting the file on master but not the release branches would cause issues. --- maintainers/flake-module.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 5e4291fcb..5febb1011 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -429,6 +429,7 @@ ''^tests/functional/test-libstoreconsumer/main\.cc'' ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' + ''^tests/nixos/user-sandboxing/attacker\.c'' ''^tests/unit/libexpr-support/tests/libexpr\.hh'' ''^tests/unit/libexpr-support/tests/value/context\.cc'' ''^tests/unit/libexpr-support/tests/value/context\.hh'' From 52730d38e25d23d44d5ef48836d062a978ecdd72 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 17:33:15 -0400 Subject: [PATCH 405/910] Factor out `flake:...` lookup path from evaluator Co-authored-by: Robert Hensing --- src/libcmd/common-eval-args.cc | 15 ++++++++++++++- src/libexpr/eval-settings.cc | 3 ++- src/libexpr/eval-settings.hh | 35 +++++++++++++++++++++++++++++++++- src/libexpr/eval.cc | 35 +++++++++++++++++----------------- 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 77dba546d..393fed532 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -15,7 +15,20 @@ namespace nix { EvalSettings evalSettings { - settings.readOnlyMode + settings.readOnlyMode, + { + { + "flake", + [](ref store, std::string_view rest) { + experimentalFeatureSettings.require(Xp::Flakes); + // FIXME `parseFlakeRef` should take a `std::string_view`. + auto flakeRef = parseFlakeRef(std::string { rest }, {}, true, false); + debug("fetching flake search path element '%s''", rest); + auto storePath = flakeRef.resolve(store).fetchTree(store).first; + return store->toRealPath(storePath); + }, + }, + }, }; static GlobalConfig::Register rEvalSettings(&evalSettings); diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 11a62b0fd..6b7b52cef 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -45,8 +45,9 @@ static Strings parseNixPath(const std::string & s) return res; } -EvalSettings::EvalSettings(bool & readOnlyMode) +EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lookupPathHooks) : readOnlyMode{readOnlyMode} + , lookupPathHooks{lookupPathHooks} { auto var = getEnv("NIX_PATH"); if (var) nixPath = parseNixPath(*var); diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 2689a8465..5eae708a2 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -5,9 +5,40 @@ namespace nix { +class Store; + struct EvalSettings : Config { - EvalSettings(bool & readOnlyMode); + /** + * Function used to interpet look path entries of a given scheme. + * + * The argument is the non-scheme part of the lookup path entry (see + * `LookupPathHooks` below). + * + * The return value is (a) whether the entry was valid, and, if so, + * what does it map to. + * + * @todo Return (`std::optional` of) `SourceAccssor` or something + * more structured instead of mere `std::string`? + */ + using LookupPathHook = std::optional(ref store, std::string_view); + + /** + * Map from "scheme" to a `LookupPathHook`. + * + * Given a lookup path value (i.e. either the whole thing, or after + * the `=`) in the form of: + * + * ``` + * : + * ``` + * + * if `` is a key in this map, then `` is + * passed to the hook that is the value in this map. + */ + using LookupPathHooks = std::map>; + + EvalSettings(bool & readOnlyMode, LookupPathHooks lookupPathHooks = {}); bool & readOnlyMode; @@ -17,6 +48,8 @@ struct EvalSettings : Config static std::string resolvePseudoUrl(std::string_view url); + LookupPathHooks lookupPathHooks; + Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"( Enable built-in functions that allow executing native code. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 717ccc803..dd389ccea 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2760,14 +2760,18 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa auto i = lookupPathResolved.find(value); if (i != lookupPathResolved.end()) return i->second; - std::optional res; + auto finish = [&](std::string res) { + debug("resolved search path element '%s' to '%s'", value, res); + lookupPathResolved.emplace(value, res); + return res; + }; if (EvalSettings::isPseudoUrl(value)) { try { auto accessor = fetchers::downloadTarball( EvalSettings::resolvePseudoUrl(value)).accessor; auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); - res = { store->toRealPath(storePath) }; + return finish(store->toRealPath(storePath)); } catch (Error & e) { logWarning({ .msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value) @@ -2775,15 +2779,17 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa } } - else if (hasPrefix(value, "flake:")) { - experimentalFeatureSettings.require(Xp::Flakes); - auto flakeRef = parseFlakeRef(value.substr(6), {}, true, false); - debug("fetching flake search path element '%s''", value); - auto storePath = flakeRef.resolve(store).fetchTree(store).first; - res = { store->toRealPath(storePath) }; + if (auto colPos = value.find(':'); colPos != value.npos) { + auto scheme = value.substr(0, colPos); + auto rest = value.substr(colPos + 1); + if (auto * hook = get(settings.lookupPathHooks, scheme)) { + auto res = (*hook)(store, rest); + if (res) + return finish(std::move(*res)); + } } - else { + { auto path = absPath(value); /* Allow access to paths in the search path. */ @@ -2800,22 +2806,17 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa } if (pathExists(path)) - res = { path }; + return finish(std::move(path)); else { logWarning({ .msg = HintFmt("Nix search path entry '%1%' does not exist, ignoring", value) }); - res = std::nullopt; } } - if (res) - debug("resolved search path element '%s' to '%s'", value, *res); - else - debug("failed to resolve search path element '%s'", value); + debug("failed to resolve search path element '%s'", value); + return std::nullopt; - lookupPathResolved.emplace(value, res); - return res; } From 0084a486ccf7bfd12dab6b9a34b0f947c3d979a0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 28 Sep 2023 21:51:28 -0400 Subject: [PATCH 406/910] Split out a new `libnixflake` Co-authored-by: Robert Hensing --- Makefile | 4 +- maintainers/flake-module.nix | 20 ++++----- src/libcmd/local.mk | 4 +- src/libexpr/eval.cc | 1 - src/libexpr/local.mk | 3 -- src/libfetchers/fetch-settings.hh | 25 ----------- src/libfetchers/registry.cc | 20 +++++++-- src/libflake/flake-settings.cc | 14 ++++++ src/libflake/flake-settings.hh | 39 +++++++++++++++++ src/{libexpr => libflake}/flake/config.cc | 4 +- src/{libexpr => libflake}/flake/flake.cc | 7 +-- src/{libexpr => libflake}/flake/flake.hh | 0 src/{libexpr => libflake}/flake/flakeref.cc | 0 src/{libexpr => libflake}/flake/flakeref.hh | 0 src/{libexpr => libflake}/flake/lockfile.cc | 0 src/{libexpr => libflake}/flake/lockfile.hh | 0 src/{libexpr => libflake}/flake/url-name.cc | 0 src/{libexpr => libflake}/flake/url-name.hh | 0 src/libflake/local.mk | 17 ++++++++ src/nix/local.mk | 4 +- .../{libexpr/flake => libflake}/flakeref.cc | 0 tests/unit/libflake/local.mk | 43 +++++++++++++++++++ .../{libexpr/flake => libflake}/url-name.cc | 0 23 files changed, 153 insertions(+), 52 deletions(-) create mode 100644 src/libflake/flake-settings.cc create mode 100644 src/libflake/flake-settings.hh rename src/{libexpr => libflake}/flake/config.cc (96%) rename src/{libexpr => libflake}/flake/flake.cc (99%) rename src/{libexpr => libflake}/flake/flake.hh (100%) rename src/{libexpr => libflake}/flake/flakeref.cc (100%) rename src/{libexpr => libflake}/flake/flakeref.hh (100%) rename src/{libexpr => libflake}/flake/lockfile.cc (100%) rename src/{libexpr => libflake}/flake/lockfile.hh (100%) rename src/{libexpr => libflake}/flake/url-name.cc (100%) rename src/{libexpr => libflake}/flake/url-name.hh (100%) create mode 100644 src/libflake/local.mk rename tests/unit/{libexpr/flake => libflake}/flakeref.cc (100%) create mode 100644 tests/unit/libflake/local.mk rename tests/unit/{libexpr/flake => libflake}/url-name.cc (100%) diff --git a/Makefile b/Makefile index 227aaf6d9..132fe29cc 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ makefiles = \ src/libfetchers/local.mk \ src/libmain/local.mk \ src/libexpr/local.mk \ + src/libflake/local.mk \ src/libcmd/local.mk \ src/nix/local.mk \ src/libutil-c/local.mk \ @@ -45,7 +46,8 @@ makefiles += \ tests/unit/libstore-support/local.mk \ tests/unit/libfetchers/local.mk \ tests/unit/libexpr/local.mk \ - tests/unit/libexpr-support/local.mk + tests/unit/libexpr-support/local.mk \ + tests/unit/libflake/local.mk endif ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 5febb1011..cc7241d21 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -65,14 +65,6 @@ ''^src/libexpr/eval-settings\.hh$'' ''^src/libexpr/eval\.cc$'' ''^src/libexpr/eval\.hh$'' - ''^src/libexpr/flake/config\.cc$'' - ''^src/libexpr/flake/flake\.cc$'' - ''^src/libexpr/flake/flake\.hh$'' - ''^src/libexpr/flake/flakeref\.cc$'' - ''^src/libexpr/flake/flakeref\.hh$'' - ''^src/libexpr/flake/lockfile\.cc$'' - ''^src/libexpr/flake/lockfile\.hh$'' - ''^src/libexpr/flake/url-name\.cc$'' ''^src/libexpr/function-trace\.cc$'' ''^src/libexpr/gc-small-vector\.hh$'' ''^src/libexpr/get-drvs\.cc$'' @@ -127,6 +119,14 @@ ''^src/libfetchers/tarball\.hh$'' ''^src/libfetchers/git\.cc$'' ''^src/libfetchers/mercurial\.cc$'' + ''^src/libflake/flake/config\.cc$'' + ''^src/libflake/flake/flake\.cc$'' + ''^src/libflake/flake/flake\.hh$'' + ''^src/libflake/flake/flakeref\.cc$'' + ''^src/libflake/flake/flakeref\.hh$'' + ''^src/libflake/flake/lockfile\.cc$'' + ''^src/libflake/flake/lockfile\.hh$'' + ''^src/libflake/flake/url-name\.cc$'' ''^src/libmain/common-args\.cc$'' ''^src/libmain/common-args\.hh$'' ''^src/libmain/loggers\.cc$'' @@ -436,8 +436,6 @@ ''^tests/unit/libexpr/derived-path\.cc'' ''^tests/unit/libexpr/error_traces\.cc'' ''^tests/unit/libexpr/eval\.cc'' - ''^tests/unit/libexpr/flake/flakeref\.cc'' - ''^tests/unit/libexpr/flake/url-name\.cc'' ''^tests/unit/libexpr/json\.cc'' ''^tests/unit/libexpr/main\.cc'' ''^tests/unit/libexpr/primops\.cc'' @@ -446,6 +444,8 @@ ''^tests/unit/libexpr/value/context\.cc'' ''^tests/unit/libexpr/value/print\.cc'' ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libflake/flakeref\.cc'' + ''^tests/unit/libflake/url-name\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.hh'' ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk index 9aa33a9d3..a270333f4 100644 --- a/src/libcmd/local.mk +++ b/src/libcmd/local.mk @@ -6,10 +6,10 @@ libcmd_DIR := $(d) libcmd_SOURCES := $(wildcard $(d)/*.cc) -libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) +libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) $(INCLUDE_libmain) libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) $(THREAD_LDFLAGS) -libcmd_LIBS = libstore libutil libexpr libmain libfetchers +libcmd_LIBS = libutil libstore libfetchers libflake libexpr libmain $(eval $(call install-file-in, $(buildprefix)$(d)/nix-cmd.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index dd389ccea..e2ae493cf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -21,7 +21,6 @@ #include "url.hh" #include "fetch-to-store.hh" #include "tarball.hh" -#include "flake/flakeref.hh" #include "parser-tab.hh" #include diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index d128064a5..95ce4de63 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -8,7 +8,6 @@ libexpr_SOURCES := \ $(wildcard $(d)/*.cc) \ $(wildcard $(d)/value/*.cc) \ $(wildcard $(d)/primops/*.cc) \ - $(wildcard $(d)/flake/*.cc) \ $(d)/lexer-tab.cc \ $(d)/parser-tab.cc # Not just for this library itself, but also for downstream libraries using this library @@ -45,8 +44,6 @@ $(eval $(call install-file-in, $(buildprefix)$(d)/nix-expr.pc, $(libdir)/pkgconf $(foreach i, $(wildcard src/libexpr/value/*.hh), \ $(eval $(call install-file-in, $(i), $(includedir)/nix/value, 0644))) -$(foreach i, $(wildcard src/libexpr/flake/*.hh), \ - $(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644))) $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 50cd4d161..629967697 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -70,30 +70,6 @@ struct FetchSettings : public Config Setting warnDirty{this, true, "warn-dirty", "Whether to warn about dirty Git/Mercurial trees."}; - Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", - R"( - 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.", - {}, true, Xp::Flakes}; - - Setting acceptFlakeConfig{this, false, "accept-flake-config", - "Whether to accept nix configuration from a flake without prompting.", - {}, true, Xp::Flakes}; - - Setting commitLockFileSummary{ - this, "", "commit-lock-file-summary", - R"( - The commit summary to use when committing changed flake lock files. If - empty, the summary is generated based on the action performed. - )", - {"commit-lockfile-summary"}, true, Xp::Flakes}; - Setting trustTarballsFromGitForges{ this, true, "trust-tarballs-from-git-forges", R"( @@ -108,7 +84,6 @@ struct FetchSettings : public Config `narHash` attribute is specified, e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f?narHash=sha256-PPXqKY2hJng4DBVE0I4xshv/vGLUskL7jl53roB8UdU%3D`. )"}; - }; // FIXME: don't use a global variable. diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index e00b9de46..52cbac5e0 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -1,12 +1,11 @@ #include "registry.hh" #include "tarball.hh" #include "users.hh" +#include "config-global.hh" #include "globals.hh" #include "store-api.hh" #include "local-fs-store.hh" -#include "fetch-settings.hh" - #include namespace nix::fetchers { @@ -149,10 +148,25 @@ void overrideRegistry( flagRegistry->add(from, to, extraAttrs); } +struct RegistrySettings : Config +{ + Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", + R"( + Path or URI of the global flake registry. + + When empty, disables the global flake registry. + )", + {}, true, Xp::Flakes}; +}; + +RegistrySettings registrySettings; + +static GlobalConfig::Register rRegistrySettings(®istrySettings); + static std::shared_ptr getGlobalRegistry(ref store) { static auto reg = [&]() { - auto path = fetchSettings.flakeRegistry.get(); + auto path = registrySettings.flakeRegistry.get(); if (path == "") { return std::make_shared(Registry::Global); // empty registry } diff --git a/src/libflake/flake-settings.cc b/src/libflake/flake-settings.cc new file mode 100644 index 000000000..77e35bc7b --- /dev/null +++ b/src/libflake/flake-settings.cc @@ -0,0 +1,14 @@ +#include "flake-settings.hh" +#include "config-global.hh" + +namespace nix { + +FlakeSettings::FlakeSettings() +{ +} + +FlakeSettings flakeSettings; + +static GlobalConfig::Register rFlakeSettings(&flakeSettings); + +} diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake-settings.hh new file mode 100644 index 000000000..ae88dfd9c --- /dev/null +++ b/src/libflake/flake-settings.hh @@ -0,0 +1,39 @@ +#pragma once +///@file + +#include "types.hh" +#include "config.hh" +#include "util.hh" + +#include +#include + +#include + +namespace nix { + +struct FlakeSettings : public Config +{ + FlakeSettings(); + + Setting useRegistries{this, true, "use-registries", + "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.", + {}, 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}; +}; + +// TODO: don't use a global variable. +extern FlakeSettings flakeSettings; + +} diff --git a/src/libexpr/flake/config.cc b/src/libflake/flake/config.cc similarity index 96% rename from src/libexpr/flake/config.cc rename to src/libflake/flake/config.cc index b348f6d44..498595359 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libflake/flake/config.cc @@ -1,6 +1,6 @@ #include "users.hh" #include "config-global.hh" -#include "fetch-settings.hh" +#include "flake-settings.hh" #include "flake.hh" #include @@ -51,7 +51,7 @@ void ConfigFile::apply() else assert(false); - if (!whitelist.count(baseName) && !nix::fetchSettings.acceptFlakeConfig) { + if (!whitelist.count(baseName) && !nix::flakeSettings.acceptFlakeConfig) { bool trusted = false; auto trustedList = readTrustedList(); auto tlname = get(trustedList, name); diff --git a/src/libexpr/flake/flake.cc b/src/libflake/flake/flake.cc similarity index 99% rename from src/libexpr/flake/flake.cc rename to src/libflake/flake/flake.cc index 67b19bd57..93d528d61 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -9,6 +9,7 @@ #include "fetchers.hh" #include "finally.hh" #include "fetch-settings.hh" +#include "flake-settings.hh" #include "value-to-json.hh" #include "local-fs-store.hh" @@ -346,7 +347,7 @@ LockedFlake lockFlake( FlakeCache flakeCache; - auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries); + auto useRegistries = lockFlags.useRegistries.value_or(flakeSettings.useRegistries); auto flake = getFlake(state, topRef, useRegistries, flakeCache); @@ -691,7 +692,7 @@ LockedFlake lockFlake( if (lockFlags.commitLockFile) { std::string cm; - cm = fetchSettings.commitLockFileSummary.get(); + cm = flakeSettings.commitLockFileSummary.get(); if (cm == "") { cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); @@ -811,7 +812,7 @@ static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, V LockFlags { .updateLockFile = false, .writeLockFile = false, - .useRegistries = !state.settings.pureEval && fetchSettings.useRegistries, + .useRegistries = !state.settings.pureEval && flakeSettings.useRegistries, .allowUnlocked = !state.settings.pureEval, }), v); diff --git a/src/libexpr/flake/flake.hh b/src/libflake/flake/flake.hh similarity index 100% rename from src/libexpr/flake/flake.hh rename to src/libflake/flake/flake.hh diff --git a/src/libexpr/flake/flakeref.cc b/src/libflake/flake/flakeref.cc similarity index 100% rename from src/libexpr/flake/flakeref.cc rename to src/libflake/flake/flakeref.cc diff --git a/src/libexpr/flake/flakeref.hh b/src/libflake/flake/flakeref.hh similarity index 100% rename from src/libexpr/flake/flakeref.hh rename to src/libflake/flake/flakeref.hh diff --git a/src/libexpr/flake/lockfile.cc b/src/libflake/flake/lockfile.cc similarity index 100% rename from src/libexpr/flake/lockfile.cc rename to src/libflake/flake/lockfile.cc diff --git a/src/libexpr/flake/lockfile.hh b/src/libflake/flake/lockfile.hh similarity index 100% rename from src/libexpr/flake/lockfile.hh rename to src/libflake/flake/lockfile.hh diff --git a/src/libexpr/flake/url-name.cc b/src/libflake/flake/url-name.cc similarity index 100% rename from src/libexpr/flake/url-name.cc rename to src/libflake/flake/url-name.cc diff --git a/src/libexpr/flake/url-name.hh b/src/libflake/flake/url-name.hh similarity index 100% rename from src/libexpr/flake/url-name.hh rename to src/libflake/flake/url-name.hh diff --git a/src/libflake/local.mk b/src/libflake/local.mk new file mode 100644 index 000000000..2cceda2bf --- /dev/null +++ b/src/libflake/local.mk @@ -0,0 +1,17 @@ +libraries += libflake + +libflake_NAME = libnixflake + +libflake_DIR := $(d) + +libflake_SOURCES := $(wildcard $(d)/*.cc $(d)/flake/*.cc) + +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libflake := -I $(d) + +libflake_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) + +libflake_LDFLAGS += $(THREAD_LDFLAGS) + +libflake_LIBS = libutil libstore libfetchers libexpr diff --git a/src/nix/local.mk b/src/nix/local.mk index 9883509fb..4b6117330 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -24,9 +24,9 @@ ifdef HOST_UNIX INCLUDE_nix += -I $(d)/unix endif -nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix) +nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix) -nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd +nix_LIBS = libexpr libmain libfetchers libflake libstore libutil libcmd nix_LDFLAGS = $(THREAD_LDFLAGS) $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) $(LOWDOWN_LIBS) diff --git a/tests/unit/libexpr/flake/flakeref.cc b/tests/unit/libflake/flakeref.cc similarity index 100% rename from tests/unit/libexpr/flake/flakeref.cc rename to tests/unit/libflake/flakeref.cc diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk new file mode 100644 index 000000000..590bcf7c0 --- /dev/null +++ b/tests/unit/libflake/local.mk @@ -0,0 +1,43 @@ +check: libflake-tests_RUN + +programs += libflake-tests + +libflake-tests_NAME := libnixflake-tests + +libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml + +libflake-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libflake-tests_INSTALL_DIR := $(checkbindir) +else + libflake-tests_INSTALL_DIR := +endif + +libflake-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libflake-tests_EXTRA_INCLUDES = \ + -I tests/unit/libflake-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libflake) \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) \ + +libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) + +libflake-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libflake libexpr libfetchers libstore libutil + +libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libexpr/flake/url-name.cc b/tests/unit/libflake/url-name.cc similarity index 100% rename from tests/unit/libexpr/flake/url-name.cc rename to tests/unit/libflake/url-name.cc From 7181d1f4a1100d223b99c372f97a40b952428916 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 13:42:19 -0400 Subject: [PATCH 407/910] Reformat Factored out code is now elegible for formatting. --- src/libflake/flake-settings.cc | 4 +--- src/libflake/flake-settings.hh | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/libflake/flake-settings.cc b/src/libflake/flake-settings.cc index 77e35bc7b..ba97e0ce7 100644 --- a/src/libflake/flake-settings.cc +++ b/src/libflake/flake-settings.cc @@ -3,9 +3,7 @@ namespace nix { -FlakeSettings::FlakeSettings() -{ -} +FlakeSettings::FlakeSettings() {} FlakeSettings flakeSettings; diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake-settings.hh index ae88dfd9c..1087c0eba 100644 --- a/src/libflake/flake-settings.hh +++ b/src/libflake/flake-settings.hh @@ -16,21 +16,35 @@ struct FlakeSettings : public Config { FlakeSettings(); - Setting useRegistries{this, true, "use-registries", + Setting useRegistries{ + this, + true, + "use-registries", "Whether to use flake registries to resolve flake references.", - {}, true, Xp::Flakes}; + {}, + true, + Xp::Flakes}; - Setting acceptFlakeConfig{this, false, "accept-flake-config", + Setting acceptFlakeConfig{ + this, + false, + "accept-flake-config", "Whether to accept nix configuration from a flake without prompting.", - {}, true, Xp::Flakes}; + {}, + true, + Xp::Flakes}; Setting commitLockFileSummary{ - this, "", "commit-lockfile-summary", + 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}; + {}, + true, + Xp::Flakes}; }; // TODO: don't use a global variable. From f002f85861e9b3ed89554b03684be566858e7b12 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 22:26:45 -0400 Subject: [PATCH 408/910] Avoid libmain header in libexpr We just don't need it! --- src/libexpr/eval.cc | 2 +- src/libexpr/local.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index e2ae493cf..d2be00e55 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -3,7 +3,7 @@ #include "hash.hh" #include "primops.hh" #include "print-options.hh" -#include "shared.hh" +#include "exit.hh" #include "types.hh" #include "util.hh" #include "store-api.hh" diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 95ce4de63..d35101a7c 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -15,7 +15,7 @@ libexpr_SOURCES := \ INCLUDE_libexpr := -I $(d) libexpr_CXXFLAGS += \ - $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) \ + $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) \ -DGC_THREADS libexpr_LIBS = libutil libstore libfetchers From 149d8eb8aa3bfdc4abebabaa97323dd4e1dd8db5 Mon Sep 17 00:00:00 2001 From: Winter Date: Tue, 26 Mar 2024 22:36:17 -0400 Subject: [PATCH 409/910] Stop vendoring toml11 We don't apply any patches to it, and vendoring it locks users into bugs (it hasn't been updated since its introduction in late 2021). Closes https://git.lix.systems/lix-project/lix/issues/164 Change-Id: Ied071c841fc30b0dfb575151afd1e7f66970fdb9 (cherry picked from commit 80405d06264f0de1c16ee2646388ab501df20628) --- .gitignore | 3 + configure.ac | 5 + doc/manual/rl-next/drop-vendored-toml11.md | 6 + maintainers/flake-module.nix | 1 - package.nix | 2 + src/libexpr/local.mk | 2 - src/libexpr/primops/fromTOML.cc | 3 +- src/toml11/LICENSE | 21 - src/toml11/README.md | 1966 ---------------- src/toml11/toml.hpp | 46 - src/toml11/toml/color.hpp | 64 - src/toml11/toml/combinator.hpp | 306 --- src/toml11/toml/comments.hpp | 472 ---- src/toml11/toml/datetime.hpp | 631 ------ src/toml11/toml/exception.hpp | 65 - src/toml11/toml/from.hpp | 19 - src/toml11/toml/get.hpp | 1117 --------- src/toml11/toml/into.hpp | 19 - src/toml11/toml/lexer.hpp | 293 --- src/toml11/toml/literal.hpp | 113 - src/toml11/toml/macros.hpp | 121 - src/toml11/toml/parser.hpp | 2364 -------------------- src/toml11/toml/region.hpp | 417 ---- src/toml11/toml/result.hpp | 717 ------ src/toml11/toml/serializer.hpp | 922 -------- src/toml11/toml/source_location.hpp | 233 -- src/toml11/toml/storage.hpp | 43 - src/toml11/toml/string.hpp | 225 -- src/toml11/toml/traits.hpp | 327 --- src/toml11/toml/types.hpp | 173 -- src/toml11/toml/utility.hpp | 149 -- src/toml11/toml/value.hpp | 2035 ----------------- 32 files changed, 17 insertions(+), 12863 deletions(-) create mode 100644 doc/manual/rl-next/drop-vendored-toml11.md delete mode 100644 src/toml11/LICENSE delete mode 100644 src/toml11/README.md delete mode 100644 src/toml11/toml.hpp delete mode 100644 src/toml11/toml/color.hpp delete mode 100644 src/toml11/toml/combinator.hpp delete mode 100644 src/toml11/toml/comments.hpp delete mode 100644 src/toml11/toml/datetime.hpp delete mode 100644 src/toml11/toml/exception.hpp delete mode 100644 src/toml11/toml/from.hpp delete mode 100644 src/toml11/toml/get.hpp delete mode 100644 src/toml11/toml/into.hpp delete mode 100644 src/toml11/toml/lexer.hpp delete mode 100644 src/toml11/toml/literal.hpp delete mode 100644 src/toml11/toml/macros.hpp delete mode 100644 src/toml11/toml/parser.hpp delete mode 100644 src/toml11/toml/region.hpp delete mode 100644 src/toml11/toml/result.hpp delete mode 100644 src/toml11/toml/serializer.hpp delete mode 100644 src/toml11/toml/source_location.hpp delete mode 100644 src/toml11/toml/storage.hpp delete mode 100644 src/toml11/toml/string.hpp delete mode 100644 src/toml11/toml/traits.hpp delete mode 100644 src/toml11/toml/types.hpp delete mode 100644 src/toml11/toml/utility.hpp delete mode 100644 src/toml11/toml/value.hpp diff --git a/.gitignore b/.gitignore index 1bf540ba2..a17b627f4 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,9 @@ perl/Makefile.config # /src/libfetchers /tests/unit/libfetchers/libnixfetchers-tests +# /src/libflake +/tests/unit/libflake/libnixflake-tests + # /src/libstore/ *.gen.* /src/libstore/tests diff --git a/configure.ac b/configure.ac index 2b5cd115f..4f66a3efc 100644 --- a/configure.ac +++ b/configure.ac @@ -400,6 +400,11 @@ AS_CASE(["$enable_markdown"], PKG_CHECK_MODULES([LIBGIT2], [libgit2]) +# Look for toml11, a required dependency. +AC_LANG_PUSH(C++) +AC_CHECK_HEADER([toml.hpp], [], [AC_MSG_ERROR([toml11 is not found.])]) +AC_LANG_POP(C++) + # Setuid installations. AC_CHECK_FUNCS([setresuid setreuid lchown]) diff --git a/doc/manual/rl-next/drop-vendored-toml11.md b/doc/manual/rl-next/drop-vendored-toml11.md new file mode 100644 index 000000000..d1feeb703 --- /dev/null +++ b/doc/manual/rl-next/drop-vendored-toml11.md @@ -0,0 +1,6 @@ +--- +synopsis: Stop vendoring toml11 +--- + +We don't apply any patches to it, and vendoring it locks users into +bugs (it hasn't been updated since its introduction in late 2021). diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index cc7241d21..8f95e788b 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -18,7 +18,6 @@ ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code - ''^src/toml11/.*'' ''^doc/manual/redirects\.js$'' ''^doc/manual/theme/highlight\.js$'' diff --git a/package.nix b/package.nix index 9a6fc272a..126af6add 100644 --- a/package.nix +++ b/package.nix @@ -32,6 +32,7 @@ , pkg-config , rapidcheck , sqlite +, toml11 , util-linux , xz @@ -225,6 +226,7 @@ in { libsodium openssl sqlite + toml11 xz ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index d35101a7c..26958bf2c 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -48,5 +48,3 @@ $(foreach i, $(wildcard src/libexpr/value/*.hh), \ $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/flake/call-flake.nix.gen.hh - -$(buildprefix)src/libexpr/primops/fromTOML.o: ERROR_SWITCH_ENUM = diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 9bee8ca38..6c7d303e8 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -1,9 +1,8 @@ #include "primops.hh" #include "eval-inline.hh" -#include "../../toml11/toml.hpp" - #include +#include namespace nix { diff --git a/src/toml11/LICENSE b/src/toml11/LICENSE deleted file mode 100644 index f55c511d6..000000000 --- a/src/toml11/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Toru Niina - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/src/toml11/README.md b/src/toml11/README.md deleted file mode 100644 index 62b586305..000000000 --- a/src/toml11/README.md +++ /dev/null @@ -1,1966 +0,0 @@ -toml11 -====== - -[![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions) -[![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11) -[![Build status on Appveyor](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master) -[![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master) -[![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases) -[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) -[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) - -toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library. - -- It is compatible to the latest version of [TOML v1.0.0](https://toml.io/en/v1.0.0). -- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). -- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11). -- It has configurable container. You can use any random-access containers and key-value maps as backend containers. -- It optionally preserves comments without any overhead. -- It has configurable serializer that supports comments, inline tables, literal strings and multiline strings. -- It supports user-defined type conversion from/into toml values. -- It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows. - -## Example - -```cpp -#include -#include - -int main() -{ - // ```toml - // title = "an example toml file" - // nums = [3, 1, 4, 1, 5] - // ``` - auto data = toml::parse("example.toml"); - - // find a value with the specified type from a table - std::string title = toml::find(data, "title"); - - // convert the whole array into any container automatically - std::vector nums = toml::find>(data, "nums"); - - // access with STL-like manner - if(!data.contains("foo")) - { - data["foo"] = "bar"; - } - - // pass a fallback - std::string name = toml::find_or(data, "name", "not found"); - - // width-dependent formatting - std::cout << std::setw(80) << data << std::endl; - - return 0; -} -``` - -## Table of Contents - -- [Integration](#integration) -- [Decoding a toml file](#decoding-a-toml-file) - - [In the case of syntax error](#in-the-case-of-syntax-error) - - [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints) -- [Finding a toml value](#finding-a-toml-value) - - [Finding a value in a table](#finding-a-value-in-a-table) - - [In case of error](#in-case-of-error) - - [Dotted keys](#dotted-keys) -- [Casting a toml value](#casting-a-toml-value) -- [Checking value type](#checking-value-type) -- [More about conversion](#more-about-conversion) - - [Converting an array](#converting-an-array) - - [Converting a table](#converting-a-table) - - [Getting an array of tables](#getting-an-array-of-tables) - - [Cost of conversion](#cost-of-conversion) - - [Converting datetime and its variants](#converting-datetime-and-its-variants) -- [Getting with a fallback](#getting-with-a-fallback) -- [Expecting conversion](#expecting-conversion) -- [Visiting a toml::value](#visiting-a-tomlvalue) -- [Constructing a toml::value](#constructing-a-tomlvalue) -- [Preserving Comments](#preserving-comments) -- [Customizing containers](#customizing-containers) -- [TOML literal](#toml-literal) -- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) -- [Formatting user-defined error messages](#formatting-user-defined-error-messages) -- [Obtaining location information](#obtaining-location-information) -- [Exceptions](#exceptions) -- [Colorize Error Messages](#colorize-error-messages) -- [Serializing TOML data](#serializing-toml-data) -- [Underlying types](#underlying-types) -- [Unreleased TOML features](#unreleased-toml-features) -- [Breaking Changes from v2](#breaking-changes-from-v2) -- [Running Tests](#running-tests) -- [Contributors](#contributors) -- [Licensing Terms](#licensing-terms) - -## Integration - -Just include the file after adding it to the include path. - -```cpp -#include // that's all! now you can use it. -#include - -int main() -{ - const auto data = toml::parse("example.toml"); - const auto title = toml::find(data, "title"); - std::cout << "the title is " << title << std::endl; - return 0; -} -``` - -The convenient way is to add this repository as a git-submodule or to install -it in your system by CMake. - -Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly. - -## Decoding a toml file - -To parse a toml file, the only thing you have to do is -to pass a filename to the `toml::parse` function. - -```cpp -const std::string fname("sample.toml"); -const toml::value data = toml::parse(fname); -``` - -As required by the TOML specification, the top-level value is always a table. -You can find a value inside it, cast it into a table explicitly, and insert it as a value into other `toml::value`. - -If it encounters an error while opening a file, it will throw `std::runtime_error`. - -You can also pass a `std::istream` to the `toml::parse` function. -To show a filename in an error message, however, it is recommended to pass the -filename with the stream. - -```cpp -std::ifstream ifs("sample.toml", std::ios_base::binary); -assert(ifs.good()); -const auto data = toml::parse(ifs, /*optional -> */ "sample.toml"); -``` - -**Note**: When you are **on Windows, open a file in binary mode**. -If a file is opened in text-mode, CRLF ("\r\n") will automatically be -converted to LF ("\n") and this causes inconsistency between file size -and the contents that would be read. This causes weird error. - -### In the case of syntax error - -If there is a syntax error in a toml file, `toml::parse` will throw -`toml::syntax_error` that inherits `std::exception`. - -toml11 has clean and informative error messages inspired by Rust and -it looks like the following. - -```console -terminate called after throwing an instance of 'toml::syntax_error' - what(): [error] toml::parse_table: invalid line format # error description - --> example.toml # file name - 3 | a = 42 = true # line num and content - | ^------ expected newline, but got '='. # error reason -``` - -If you (mistakenly) duplicate tables and got an error, it is helpful to see -where they are. toml11 shows both at the same time like the following. - -```console -terminate called after throwing an instance of 'toml::syntax_error' - what(): [error] toml::insert_value: table ("table") already exists. - --> duplicate-table.toml - 1 | [table] - | ~~~~~~~ table already exists here - ... - 3 | [table] - | ~~~~~~~ table defined twice -``` - -When toml11 encounters a malformed value, it tries to detect what type it is. -Then it shows hints to fix the format. An error message while reading one of -the malformed files in [the language agnostic test suite](https://github.com/BurntSushi/toml-test). -is shown below. - -```console -what(): [error] bad time: should be HH:MM:SS.subsec - --> ./datetime-malformed-no-secs.toml - 1 | no-secs = 1987-07-05T17:45Z - | ^------- HH:MM:SS.subsec - | -Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999 -Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32 -``` - -You can find other examples in a job named `output_result` on -[CircleCI](https://circleci.com/gh/ToruNiina/toml11). - -Since the error message generation is generally a difficult task, the current -status is not ideal. If you encounter a weird error message, please let us know -and contribute to improve the quality! - -### Invalid UTF-8 codepoints - -It throws `syntax_error` if a value of an escape sequence -representing unicode character is not a valid UTF-8 codepoint. - -```console - what(): [error] toml::read_utf8_codepoint: input codepoint is too large. - --> utf8.toml - 1 | exceeds_unicode = "\U0011FFFF example" - | ^--------- should be in [0x00..0x10FFFF] -``` - -## Finding a toml value - -After parsing successfully, you can obtain the values from the result of -`toml::parse` using `toml::find` function. - -```toml -# sample.toml -answer = 42 -pi = 3.14 -numbers = [1,2,3] -time = 1979-05-27T07:32:00Z -``` - -``` cpp -const auto data = toml::parse("sample.toml"); -const auto answer = toml::find(data, "answer"); -const auto pi = toml::find(data, "pi"); -const auto numbers = toml::find>(data, "numbers"); -const auto timepoint = toml::find(data, "time"); -``` - -By default, `toml::find` returns a `toml::value`. - -```cpp -const toml::value& answer = toml::find(data, "answer"); -``` - -When you pass an exact TOML type that does not require type conversion, -`toml::find` returns a reference without copying the value. - -```cpp -const auto data = toml::parse("sample.toml"); -const auto& answer = toml::find(data, "answer"); -``` - -If the specified type requires conversion, you can't take a reference to the value. -See also [underlying types](#underlying-types). - -**NOTE**: For some technical reason, automatic conversion between `integer` and -`floating` is not supported. If you want to get a floating value even if a value -has integer value, you need to convert it manually after obtaining a value, -like the following. - -```cpp -const auto vx = toml::find(data, "x"); -double x = vx.is_floating() ? vx.as_floating(std::nothrow) : - static_cast(vx.as_integer()); // it throws if vx is neither - // floating nor integer. -``` - -### Finding a value in a table - -There are several way to get a value defined in a table. -First, you can get a table as a normal value and find a value from the table. - -```toml -[fruit] -name = "apple" -[fruit.physical] -color = "red" -shape = "round" -``` - -``` cpp -const auto data = toml::parse("fruit.toml"); -const auto& fruit = toml::find(data, "fruit"); -const auto name = toml::find(fruit, "name"); - -const auto& physical = toml::find(fruit, "physical"); -const auto color = toml::find(physical, "color"); -const auto shape = toml::find(physical, "shape"); -``` - -Here, variable `fruit` is a `toml::value` and can be used as the first argument -of `toml::find`. - -Second, you can pass as many arguments as the number of subtables to `toml::find`. - -```cpp -const auto data = toml::parse("fruit.toml"); -const auto color = toml::find(data, "fruit", "physical", "color"); -const auto shape = toml::find(data, "fruit", "physical", "shape"); -``` - -### Finding a value in an array - -You can find n-th value in an array by `toml::find`. - -```toml -values = ["foo", "bar", "baz"] -``` - -``` cpp -const auto data = toml::parse("sample.toml"); -const auto values = toml::find(data, "values"); -const auto bar = toml::find(values, 1); -``` - -`toml::find` can also search array recursively. - -```cpp -const auto data = toml::parse("fruit.toml"); -const auto bar = toml::find(data, "values", 1); -``` - -Before calling `toml::find`, you can check if a value corresponding to a key -exists. You can use both `bool toml::value::contains(const key&) const` and -`std::size_t toml::value::count(const key&) const`. Those behaves like the -`std::map::contains` and `std::map::count`. - -```cpp -const auto data = toml::parse("fruit.toml"); -if(data.contains("fruit") && data.at("fruit").count("physical") != 0) -{ - // ... -} -``` - -### In case of error - -If the value does not exist, `toml::find` throws `std::out_of_range` with the -location of the table. - -```console -terminate called after throwing an instance of 'std::out_of_range' - what(): [error] key "answer" not found - --> example.toml - 6 | [tab] - | ~~~~~ in this table -``` - ----- - -If the specified type differs from the actual value contained, it throws -`toml::type_error` that inherits `std::exception`. - -Similar to the case of syntax error, toml11 also displays clean error messages. -The error message when you choose `int` to get `string` value would be like this. - -```console -terminate called after throwing an instance of 'toml::type_error' - what(): [error] toml::value bad_cast to integer - --> example.toml - 3 | title = "TOML Example" - | ~~~~~~~~~~~~~~ the actual type is string -``` - -**NOTE**: In order to show this kind of error message, all the toml values have -a pointer to represent its range in a file. The entire contents of a file is -shared by `toml::value`s and remains on the heap memory. It is recommended to -destruct all the `toml::value` classes after configuring your application -if you have a large TOML file compared to the memory resource. - -### Dotted keys - -TOML v0.5.0 has a new feature named "dotted keys". -You can chain keys to represent the structure of the data. - -```toml -physical.color = "orange" -physical.shape = "round" -``` - -This is equivalent to the following. - -```toml -[physical] -color = "orange" -shape = "round" -``` - -You can get both of the above tables with the same c++ code. - -```cpp -const auto physical = toml::find(data, "physical"); -const auto color = toml::find(physical, "color"); -``` - -The following code does not work for the above toml file. - -```cpp -// XXX this does not work! -const auto color = toml::find(data, "physical.color"); -``` - -The above code works with the following toml file. - -```toml -"physical.color" = "orange" -# equivalent to {"physical.color": "orange"}, -# NOT {"physical": {"color": "orange"}}. -``` - - -## Casting a toml value - -### `toml::get` - -`toml::parse` returns `toml::value`. `toml::value` is a union type that can -contain one of the following types. - -- `toml::boolean` (`bool`) -- `toml::integer` (`std::int64_t`) -- `toml::floating` (`double`) -- `toml::string` (a type convertible to std::string) -- `toml::local_date` -- `toml::local_time` -- `toml::local_datetime` -- `toml::offset_datetime` -- `toml::array` (by default, `std::vector`) - - It depends. See [customizing containers](#customizing-containers) for detail. -- `toml::table` (by default, `std::unordered_map`) - - It depends. See [customizing containers](#customizing-containers) for detail. - -To get a value inside, you can use `toml::get()`. The usage is the same as -`toml::find` (actually, `toml::find` internally uses `toml::get` after casting -a value to `toml::table`). - -``` cpp -const toml::value data = toml::parse("sample.toml"); -const toml::value answer_ = toml::get(data).at("answer"); -const std::int64_t answer = toml::get(answer_); -``` - -When you pass an exact TOML type that does not require type conversion, -`toml::get` returns a reference through which you can modify the content -(if the `toml::value` is `const`, it returns `const` reference). - -```cpp -toml::value data = toml::parse("sample.toml"); -toml::value answer_ = toml::get(data).at("answer"); -toml::integer& answer = toml::get(answer_); -answer = 6 * 9; // write to data.answer. now `answer_` contains 54. -``` - -If the specified type requires conversion, you can't take a reference to the value. -See also [underlying types](#underlying-types). - -It also throws a `toml::type_error` if the type differs. - -### `as_xxx` - -You can also use a member function to cast a value. - -```cpp -const std::int64_t answer = data.as_table().at("answer").as_integer(); -``` - -It also throws a `toml::type_error` if the type differs. If you are sure that -the value `v` contains a value of the specified type, you can suppress checking -by passing `std::nothrow`. - -```cpp -const auto& answer = data.as_table().at("answer"); -if(answer.is_integer() && answer.as_integer(std::nothrow) == 42) -{ - std::cout << "value is 42" << std::endl; -} -``` - -If `std::nothrow` is passed, the functions are marked as noexcept. - -By casting a `toml::value` into an array or a table, you can iterate over the -elements. - -```cpp -const auto data = toml::parse("example.toml"); -std::cout << "keys in the top-level table are the following: \n"; -for(const auto& [k, v] : data.as_table()) -{ - std::cout << k << '\n'; -} - -const auto& fruits = toml::find(data, "fruits"); -for(const auto& v : fruits.as_array()) -{ - std::cout << toml::find(v, "name") << '\n'; -} -``` - -The full list of the functions is below. - -```cpp -namespace toml { -class value { - // ... - const boolean& as_boolean() const&; - const integer& as_integer() const&; - const floating& as_floating() const&; - const string& as_string() const&; - const offset_datetime& as_offset_datetime() const&; - const local_datetime& as_local_datetime() const&; - const local_date& as_local_date() const&; - const local_time& as_local_time() const&; - const array& as_array() const&; - const table& as_table() const&; - // -------------------------------------------------------- - // non-const version - boolean& as_boolean() &; - // ditto... - // -------------------------------------------------------- - // rvalue version - boolean&& as_boolean() &&; - // ditto... - - // -------------------------------------------------------- - // noexcept versions ... - const boolean& as_boolean(const std::nothrow_t&) const& noexcept; - boolean& as_boolean(const std::nothrow_t&) & noexcept; - boolean&& as_boolean(const std::nothrow_t&) && noexcept; - // ditto... -}; -} // toml -``` - -### `at()` - -You can access to the element of a table and an array by `toml::basic_value::at`. - -```cpp -const toml::value v{1,2,3,4,5}; -std::cout << v.at(2).as_integer() << std::endl; // 3 - -const toml::value v{{"foo", 42}, {"bar", 3.14}}; -std::cout << v.at("foo").as_integer() << std::endl; // 42 -``` - -If an invalid key (integer for a table, string for an array), it throws -`toml::type_error` for the conversion. If the provided key is out-of-range, -it throws `std::out_of_range`. - -Note that, although `std::string` has `at()` member function, `toml::value::at` -throws if the contained type is a string. Because `std::string` does not -contain `toml::value`. - -### `operator[]` - -You can also access to the element of a table and an array by -`toml::basic_value::operator[]`. - -```cpp -const toml::value v{1,2,3,4,5}; -std::cout << v[2].as_integer() << std::endl; // 3 - -const toml::value v{{"foo", 42}, {"bar", 3.14}}; -std::cout << v["foo"].as_integer() << std::endl; // 42 -``` - -When you access to a `toml::value` that is not initialized yet via -`operator[](const std::string&)`, the `toml::value` will be a table, -just like the `std::map`. - -```cpp -toml::value v; // not initialized as a table. -v["foo"] = 42; // OK. `v` will be a table. -``` - -Contrary, if you access to a `toml::value` that contains an array via `operator[]`, -it does not check anything. It converts `toml::value` without type check and then -access to the n-th element without boundary check, just like the `std::vector::operator[]`. - -```cpp -toml::value v; // not initialized as an array -v[2] = 42; // error! UB -``` - -Please make sure that the `toml::value` has an array inside when you access to -its element via `operator[]`. - -## Checking value type - -You can check the type of a value by `is_xxx` function. - -```cpp -const toml::value v = /* ... */; -if(v.is_integer()) -{ - std::cout << "value is an integer" << std::endl; -} -``` - -The complete list of the functions is below. - -```cpp -namespace toml { -class value { - // ... - bool is_boolean() const noexcept; - bool is_integer() const noexcept; - bool is_floating() const noexcept; - bool is_string() const noexcept; - bool is_offset_datetime() const noexcept; - bool is_local_datetime() const noexcept; - bool is_local_date() const noexcept; - bool is_local_time() const noexcept; - bool is_array() const noexcept; - bool is_table() const noexcept; - bool is_uninitialized() const noexcept; - // ... -}; -} // toml -``` - -Also, you can get `enum class value_t` from `toml::value::type()`. - -```cpp -switch(data.at("something").type()) -{ - case toml::value_t::integer: /*do some stuff*/ ; break; - case toml::value_t::floating: /*do some stuff*/ ; break; - case toml::value_t::string : /*do some stuff*/ ; break; - default : throw std::runtime_error( - "unexpected type : " + toml::stringize(data.at("something").type())); -} -``` - -The complete list of the `enum`s can be found in the section -[underlying types](#underlying-types). - -The `enum`s can be used as a parameter of `toml::value::is` function like the following. - -```cpp -toml::value v = /* ... */; -if(v.is(toml::value_t::boolean)) // ... -``` - -## More about conversion - -Since `toml::find` internally uses `toml::get`, all the following examples work -with both `toml::get` and `toml::find`. - -### Converting an array - -You can get any kind of `container` class from a `toml::array` -except for `map`-like classes. - -``` cpp -// # sample.toml -// numbers = [1,2,3] - -const auto numbers = toml::find(data, "numbers"); - -const auto vc = toml::get >(numbers); -const auto ls = toml::get >(numbers); -const auto dq = toml::get >(numbers); -const auto ar = toml::get>(numbers); -// if the size of data.at("numbers") is larger than that of std::array, -// it will throw toml::type_error because std::array is not resizable. -``` - -Surprisingly, you can convert `toml::array` into `std::pair` and `std::tuple`. - -```cpp -// numbers = [1,2,3] -const auto tp = toml::get>(numbers); -``` - -This functionality is helpful when you have a toml file like the following. - -```toml -array_of_arrays = [[1, 2, 3], ["foo", "bar", "baz"]] # toml allows this -``` - -What is the corresponding C++ type? -Obviously, it is a `std::pair` of `std::vector`s. - -```cpp -const auto array_of_arrays = toml::find(data, "array_of_arrays"); -const auto aofa = toml::get< - std::pair, std::vector> - >(array_of_arrays); -``` - -If you don't know the type of the elements, you can use `toml::array`, -which is a `std::vector` of `toml::value`, instead. - -```cpp -const auto a_of_a = toml::get(array_of_arrays); -const auto first = toml::get>(a_of_a.at(0)); -``` - -You can change the implementation of `toml::array` with `std::deque` or some -other array-like container. See [Customizing containers](#customizing-containers) -for detail. - -### Converting a table - -When all the values of the table have the same type, toml11 allows you to -convert a `toml::table` to a `map` that contains the convertible type. - -```toml -[tab] -key1 = "foo" # all the values are -key2 = "bar" # toml String -``` - -```cpp -const auto data = toml::parse("sample.toml"); -const auto tab = toml::find>(data, "tab"); -std::cout << tab["key1"] << std::endl; // foo -std::cout << tab["key2"] << std::endl; // bar -``` - -But since `toml::table` is just an alias of `std::unordered_map`, -normally you don't need to convert it because it has all the functionalities that -`std::unordered_map` has (e.g. `operator[]`, `count`, and `find`). In most cases -`toml::table` is sufficient. - -```cpp -toml::table tab = toml::get(data); -if(data.count("title") != 0) -{ - data["title"] = std::string("TOML example"); -} -``` - -You can change the implementation of `toml::table` with `std::map` or some -other map-like container. See [Customizing containers](#customizing-containers) -for detail. - -### Getting an array of tables - -An array of tables is just an array of tables. -You can get it in completely the same way as the other arrays and tables. - -```toml -# sample.toml -array_of_inline_tables = [{key = "value1"}, {key = "value2"}, {key = "value3"}] - -[[array_of_tables]] -key = "value4" -[[array_of_tables]] -key = "value5" -[[array_of_tables]] -key = "value6" -``` - -```cpp -const auto data = toml::parse("sample.toml"); -const auto aot1 = toml::find>(data, "array_of_inline_tables"); -const auto aot2 = toml::find>(data, "array_of_tables"); -``` - -### Cost of conversion - -Although conversion through `toml::(get|find)` is convenient, it has additional -copy-cost because it copies data contained in `toml::value` to the -user-specified type. Of course in some cases this overhead is not ignorable. - -```cpp -// the following code constructs a std::vector. -// it requires heap allocation for vector and element conversion. -const auto array = toml::find>(data, "foo"); -``` - -By passing the exact types, `toml::get` returns reference that has no overhead. - -``` cpp -const auto& tab = toml::find(data, "tab"); -const auto& numbers = toml::find(data, "numbers"); -``` - -Also, `as_xxx` are zero-overhead because they always return a reference. - -``` cpp -const auto& tab = toml::find(data, "tab" ).as_table(); -const auto& numbers = toml::find(data, "numbers").as_array(); -``` - -In this case you need to call `toml::get` each time you access to -the element of `toml::array` because `toml::array` is an array of `toml::value`. - -```cpp -const auto& num0 = toml::get(numbers.at(0)); -const auto& num1 = toml::get(numbers.at(1)); -const auto& num2 = toml::get(numbers.at(2)); -``` - -### Converting datetime and its variants - -TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, -`local_datetime`, and `offset_datetime`. - -Since `local_date`, `local_datetime`, and `offset_datetime` represent a time -point, you can convert them to `std::chrono::system_clock::time_point`. - -Contrary, `local_time` does not represents a time point because they lack a -date information, but it can be converted to `std::chrono::duration` that -represents a duration from the beginning of the day, `00:00:00.000`. - -```toml -# sample.toml -date = 2018-12-23 -time = 12:30:00 -l_dt = 2018-12-23T12:30:00 -o_dt = 2018-12-23T12:30:00+09:30 -``` - -```cpp -const auto data = toml::parse("sample.toml"); - -const auto date = toml::get(data.at("date")); -const auto l_dt = toml::get(data.at("l_dt")); -const auto o_dt = toml::get(data.at("o_dt")); - -const auto time = toml::get(data.at("time")); // 12 * 60 + 30 min -``` - -`local_date` and `local_datetime` are assumed to be in the local timezone when -they are converted into `time_point`. On the other hand, `offset_datetime` only -uses the offset part of the data and it does not take local timezone into account. - -To contain datetime data, toml11 defines its own datetime types. -For more detail, you can see the definitions in [toml/datetime.hpp](toml/datetime.hpp). - -## Getting with a fallback - -`toml::find_or` returns a default value if the value is not found or has a -different type. - -```cpp -const auto data = toml::parse("example.toml"); -const auto num = toml::find_or(data, "num", 42); -``` - -It works recursively if you pass several keys for subtables. -In that case, the last argument is considered to be the optional value. -All other arguments between `toml::value` and the optinoal value are considered as keys. - -```cpp -// [fruit.physical] -// color = "red" -auto data = toml::parse("fruit.toml"); -auto color = toml::find_or(data, "fruit", "physical", "color", "red"); -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ -// arguments optional value -``` - -Also, `toml::get_or` returns a default value if `toml::get` failed. - -```cpp -toml::value v("foo"); // v contains String -const int value = toml::get_or(v, 42); // conversion fails. it returns 42. -``` - -These functions automatically deduce what type you want to get -from the default value you passed. - -To get a reference through this function, take care about the default value. - -```cpp -toml::value v("foo"); // v contains String -toml::integer& i = toml::get_or(v, 42); // does not work because binding `42` - // to `integer&` is invalid -toml::integer opt = 42; -toml::integer& i = toml::get_or(v, opt); // this works. -``` - -## Expecting conversion - -By using `toml::expect`, you will get your expected value or an error message -without throwing `toml::type_error`. - -```cpp -const auto value = toml::expect(data.at("title")); -if(value.is_ok()) { - std::cout << value.unwrap() << std::endl; -} else { - std::cout << value.unwrap_err() << std::endl; -} -``` - -Also, you can pass a function object to modify the expected value. - -```cpp -const auto value = toml::expect(data.at("number")) - .map(// function that receives expected type (here, int) - [](const int number) -> double { - return number * 1.5 + 1.0; - }).unwrap_or(/*default value =*/ 3.14); -``` - -## Visiting a toml::value - -toml11 provides `toml::visit` to apply a function to `toml::value` in the -same way as `std::variant`. - -```cpp -const toml::value v(3.14); -toml::visit([](const auto& val) -> void { - std::cout << val << std::endl; - }, v); -``` - -The function object that would be passed to `toml::visit` must be able to -receive all the possible TOML types. Also, the result types should be the same -each other. - -## Constructing a toml::value - -`toml::value` can be constructed in various ways. - -```cpp -toml::value v(true); // boolean -toml::value v(42); // integer -toml::value v(3.14); // floating -toml::value v("foobar"); // string -toml::value v(toml::local_date(2019, toml::month_t::Apr, 1)); // date -toml::value v{1, 2, 3, 4, 5}; // array -toml::value v{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; // table -``` - -When constructing a string, you can choose to use either literal or basic string. -By default, it will be a basic string. - -```cpp -toml::value v("foobar", toml::string_t::basic ); -toml::value v("foobar", toml::string_t::literal); -``` - -Datetime objects can be constructed from `std::tm` and -`std::chrono::system_clock::time_point`. But you need to specify what type -you use to avoid ambiguity. - -```cpp -const auto now = std::chrono::system_clock::now(); -toml::value v(toml::local_date(now)); -toml::value v(toml::local_datetime(now)); -toml::value v(toml::offset_datetime(now)); -``` - -Since local time is not equivalent to a time point, because it lacks date -information, it will be constructed from `std::chrono::duration`. - -```cpp -toml::value v(toml::local_time(std::chrono::hours(10))); -``` - -You can construct an array object not only from `initializer_list`, but also -from STL containers. In that case, the element type must be convertible to -`toml::value`. - -```cpp -std::vector vec{1,2,3,4,5}; -toml::value v(vec); -``` - -When you construct an array value, all the elements of `initializer_list` -must be convertible into `toml::value`. - -If a `toml::value` has an array, you can `push_back` an element in it. - -```cpp -toml::value v{1,2,3,4,5}; -v.push_back(6); -``` - -`emplace_back` also works. - -## Preserving comments - -toml11 v3 or later allows you yo choose whether comments are preserved or not via template parameter - -```cpp -const auto data1 = toml::parse("example.toml"); -const auto data2 = toml::parse("example.toml"); -``` - -or macro definition. - -```cpp -#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT -#include -``` - -This feature is controlled by template parameter in `toml::basic_value<...>`. -`toml::value` is an alias of `toml::basic_value<...>`. - -If template parameter is explicitly specified, the return value of `toml::parse` -will be `toml::basic_value`. -If the macro is defined, the alias `toml::value` will be -`toml::basic_value`. - -Comments related to a value can be obtained by `toml::value::comments()`. -The return value has the same interface as `std::vector`. - -```cpp -const auto& com = v.comments(); -for(const auto& c : com) -{ - std::cout << c << std::endl; -} -``` - -Comments just before and just after (within the same line) a value are kept in a value. - -```toml -# this is a comment for v1. -v1 = "foo" - -v2 = "bar" # this is a comment for v2. -# Note that this comment is NOT a comment for v2. - -# this comment is not related to any value -# because there are empty lines between v3. -# this comment will be ignored even if you set `preserve_comments`. - -# this is a comment for v3 -# this is also a comment for v3. -v3 = "baz" # ditto. -``` - -Each comment line becomes one element of a `std::vector`. - -Hash signs will be removed, but spaces after hash sign will not be removed. - -```cpp -v1.comments().at(0) == " this is a comment for v1."s; - -v2.comments().at(1) == " this is a comment for v1."s; - -v3.comments().at(0) == " this is a comment for v3."s; -v3.comments().at(1) == " this is also a comment for v3."s; -v3.comments().at(2) == " ditto."s; -``` - -Note that a comment just after an opening brace of an array will not be a -comment for the array. - -```toml -# this is a comment for a. -a = [ # this is not a comment for a. this will be ignored. - 1, 2, 3, - # this is a comment for `42`. - 42, # this is also a comment for `42`. - 5 -] # this is a comment for a. -``` - -You can also append and modify comments. -The interfaces are the same as `std::vector`. - -```cpp -toml::basic_value v(42); -v.comments().push_back(" add this comment."); -// # add this comment. -// i = 42 -``` - -Also, you can pass a `std::vector` when constructing a -`toml::basic_value`. - -```cpp -std::vector comments{"comment 1", "comment 2"}; -const toml::basic_value v1(42, std::move(comments)); -const toml::basic_value v2(42, {"comment 1", "comment 2"}); -``` - -When `toml::discard_comments` is chosen, comments will not be contained in a value. -`value::comments()` will always be kept empty. -All the modification on comments would be ignored. -All the element access in a `discard_comments` causes the same error as accessing -an element of an empty `std::vector`. - -The comments will also be serialized. If comments exist, those comments will be -added just before the values. - -__NOTE__: Result types from `toml::parse(...)` and -`toml::parse(...)` are different. - -## Customizing containers - -Actually, `toml::basic_value` has 3 template arguments. - -```cpp -template class Table = std::unordered_map, - template class Array = std::vector> -class basic_value; -``` - -This enables you to change the containers used inside. E.g. you can use -`std::map` to contain a table object instead of `std::unordered_map`. -And also can use `std::deque` as a array object instead of `std::vector`. - -You can set these parameters while calling `toml::parse` function. - -```cpp -const auto data = toml::parse< - toml::preserve_comments, std::map, std::deque - >("example.toml"); -``` - -Needless to say, the result types from `toml::parse(...)` and -`toml::parse(...)` are different (unless you specify the same -types as default). - -Note that, since `toml::table` and `toml::array` is an alias for a table and an -array of a default `toml::value`, so it is different from the types actually -contained in a `toml::basic_value` when you customize containers. -To get the actual type in a generic way, use -`typename toml::basic_type::table_type` and -`typename toml::basic_type::array_type`. - -## TOML literal - -toml11 supports `"..."_toml` literal. -It accept both a bare value and a file content. - -```cpp -using namespace toml::literals::toml_literals; - -// `_toml` can convert a bare value without key -const toml::value v = u8"0xDEADBEEF"_toml; -// v is an Integer value containing 0xDEADBEEF. - -// raw string literal (`R"(...)"` is useful for this purpose) -const toml::value t = u8R"( - title = "this is TOML literal" - [table] - key = "value" -)"_toml; -// the literal will be parsed and the result will be contained in t -``` - -The literal function is defined in the same way as the standard library literals -such as `std::literals::string_literals::operator""s`. - -```cpp -namespace toml -{ -inline namespace literals -{ -inline namespace toml_literals -{ -toml::value operator"" _toml(const char* str, std::size_t len); -} // toml_literals -} // literals -} // toml -``` - -Access to the operator can be gained with `using namespace toml::literals;`, -`using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`. - -Note that a key that is composed only of digits is allowed in TOML. -And, unlike the file parser, toml-literal allows a bare value without a key. -Thus it is difficult to distinguish arrays having integers and definitions of -tables that are named as digits. -Currently, literal `[1]` becomes a table named "1". -To ensure a literal to be considered as an array with one element, you need to -add a comma after the first element (like `[1,]`). - -```cpp -"[1,2,3]"_toml; // This is an array -"[table]"_toml; // This is a table that has an empty table named "table" inside. -"[[1,2,3]]"_toml; // This is an array of arrays -"[[table]]"_toml; // This is a table that has an array of tables inside. - -"[[1]]"_toml; // This literal is ambiguous. - // Currently, it becomes a table that has array of table "1". -"1 = [{}]"_toml; // This is a table that has an array of table named 1. -"[[1,]]"_toml; // This is an array of arrays. -"[[1],]"_toml; // ditto. -``` - -NOTE: `_toml` literal returns a `toml::value` that does not have comments. - -## Conversion between toml value and arbitrary types - -You can also use `toml::get` and other related functions with the types -you defined after you implement a way to convert it. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; -}; -} // ext - -const auto data = toml::parse("example.toml"); - -// to do this -const foo f = toml::find(data, "foo"); -``` - -There are 3 ways to use `toml::get` with the types that you defined. - -The first one is to implement `from_toml(const toml::value&)` member function. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; - - void from_toml(const toml::value& v) - { - this->a = toml::find(v, "a"); - this->b = toml::find(v, "b"); - this->c = toml::find(v, "c"); - return; - } -}; -} // ext -``` - -In this way, because `toml::get` first constructs `foo` without arguments, -the type should be default-constructible. - -The second is to implement `constructor(const toml::value&)`. - -```cpp -namespace ext -{ -struct foo -{ - explicit foo(const toml::value& v) - : a(toml::find(v, "a")), b(toml::find(v, "b")), - c(toml::find(v, "c")) - {} - - int a; - double b; - std::string c; -}; -} // ext -``` - -Note that implicit default constructor declaration will be suppressed -when a constructor is defined. If you want to use the struct (here, `foo`) -in a container (e.g. `std::vector`), you may need to define default -constructor explicitly. - -The third is to implement specialization of `toml::from` for your type. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; -}; -} // ext - -namespace toml -{ -template<> -struct from -{ - static ext::foo from_toml(const value& v) - { - ext::foo f; - f.a = find(v, "a"); - f.b = find(v, "b"); - f.c = find(v, "c"); - return f; - } -}; -} // toml -``` - -In this way, since the conversion function is defined outside of the class, -you can add conversion between `toml::value` and classes defined in another library. - -In some cases, a class has a templatized constructor that takes a template, `T`. -It confuses `toml::get/find` because it makes the class "constructible" from -`toml::value`. To avoid this problem, `toml::from` and `from_toml` always -precede constructor. It makes easier to implement conversion between -`toml::value` and types defined in other libraries because it skips constructor. - -But, importantly, you cannot define `toml::from` and `T.from_toml` at the same -time because it causes ambiguity in the overload resolution of `toml::get` and `toml::find`. - -So the precedence is `toml::from` == `T.from_toml()` > `T(toml::value)`. - -If you want to convert any versions of `toml::basic_value`, -you need to templatize the conversion function as follows. - -```cpp -struct foo -{ - template class M, template class A> - void from_toml(const toml::basic_value& v) - { - this->a = toml::find(v, "a"); - this->b = toml::find(v, "b"); - this->c = toml::find(v, "c"); - return; - } -}; -// or -namespace toml -{ -template<> -struct from -{ - template class M, template class A> - static ext::foo from_toml(const basic_value& v) - { - ext::foo f; - f.a = find(v, "a"); - f.b = find(v, "b"); - f.c = find(v, "c"); - return f; - } -}; -} // toml -``` - ----- - -The opposite direction is also supported in a similar way. You can directly -pass your type to `toml::value`'s constructor by introducing `into_toml` or -`toml::into`. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; - - toml::value into_toml() const // you need to mark it const. - { - return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}}; - } -}; -} // ext - -ext::foo f{42, 3.14, "foobar"}; -toml::value v(f); -``` - -The definition of `toml::into` is similar to `toml::from`. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; -}; -} // ext - -namespace toml -{ -template<> -struct into -{ - static toml::value into_toml(const ext::foo& f) - { - return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}}; - } -}; -} // toml - -ext::foo f{42, 3.14, "foobar"}; -toml::value v(f); -``` - -Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and -`toml::array` are okay to return from `into_toml`. - -You can also return a custom `toml::basic_value` from `toml::into`. - -```cpp -namespace toml -{ -template<> -struct into -{ - static toml::basic_value into_toml(const ext::foo& f) - { - toml::basic_value v{{"a", f.a}, {"b", f.b}, {"c", f.c}}; - v.comments().push_back(" comment"); - return v; - } -}; -} // toml -``` - -But note that, if this `basic_value` would be assigned into other `toml::value` -that discards `comments`, the comments would be dropped. - -### Macro to automatically define conversion functions - -There is a helper macro that automatically generates conversion functions `from` and `into` for a simple struct. - -```cpp -namespace foo -{ -struct Foo -{ - std::string s; - double d; - int i; -}; -} // foo - -TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) - -int main() -{ - const auto file = toml::parse("example.toml"); - auto f = toml::find(file, "foo"); -} -``` - -And then you can use `toml::find(file, "foo");` - -**Note** that, because of a slight difference in implementation of preprocessor between gcc/clang and MSVC, [you need to define `/Zc:preprocessor`](https://github.com/ToruNiina/toml11/issues/139#issuecomment-803683682) to use it in MSVC (Thank you @glebm !). - -## Formatting user-defined error messages - -When you encounter an error after you read the toml value, you may want to -show the error with the value. - -toml11 provides you a function that formats user-defined error message with -related values. With a code like the following, - -```cpp -const auto value = toml::find(data, "num"); -if(value < 0) -{ - std::cerr << toml::format_error("[error] value should be positive", - data.at("num"), "positive number required") - << std::endl; -} -``` - -you will get an error message like this. - -```console -[error] value should be positive - --> example.toml - 3 | num = -42 - | ~~~ positive number required -``` - -When you pass two values to `toml::format_error`, - -```cpp -const auto min = toml::find(range, "min"); -const auto max = toml::find(range, "max"); -if(max < min) -{ - std::cerr << toml::format_error("[error] max should be larger than min", - data.at("min"), "minimum number here", - data.at("max"), "maximum number here"); - << std::endl; -} -``` - -you will get an error message like this. - -```console -[error] max should be larger than min - --> example.toml - 3 | min = 54 - | ~~ minimum number here - ... - 4 | max = 42 - | ~~ maximum number here -``` - -You can print hints at the end of the message. - -```cpp -std::vector hints; -hints.push_back("positive number means n >= 0."); -hints.push_back("negative number is not positive."); -std::cerr << toml::format_error("[error] value should be positive", - data.at("num"), "positive number required", hints) - << std::endl; -``` - -```console -[error] value should be positive - --> example.toml - 2 | num = 42 - | ~~ positive number required - | -Hint: positive number means n >= 0. -Hint: negative number is not positive. -``` - -## Obtaining location information - -You can also format error messages in your own way by using `source_location`. - -```cpp -struct source_location -{ - std::uint_least32_t line() const noexcept; - std::uint_least32_t column() const noexcept; - std::uint_least32_t region() const noexcept; - std::string const& file_name() const noexcept; - std::string const& line_str() const noexcept; -}; -// +-- line() +--- length of the region (here, region() == 9) -// v .---+---. -// 12 | value = "foo bar" <- line_str() returns the line itself. -// ^-------- column() points here -``` - -You can get this by -```cpp -const toml::value v = /*...*/; -const toml::source_location loc = v.location(); -``` - -## Exceptions - -The following `exception` classes inherits `toml::exception` that inherits -`std::exception`. - -```cpp -namespace toml { -struct exception : public std::exception {/**/}; -struct syntax_error : public toml::exception {/**/}; -struct type_error : public toml::exception {/**/}; -struct internal_error : public toml::exception {/**/}; -} // toml -``` - -`toml::exception` has `toml::exception::location()` member function that returns -`toml::source_location`, in addition to `what()`. - -```cpp -namespace toml { -struct exception : public std::exception -{ - // ... - source_location const& location() const noexcept; -}; -} // toml -``` - -It represents where the error occurs. - -`syntax_error` will be thrown from `toml::parse` and `_toml` literal. -`type_error` will be thrown from `toml::get/find`, `toml::value::as_xxx()`, and -other functions that takes a content inside of `toml::value`. - -Note that, currently, from `toml::value::at()` and `toml::find(value, key)` -may throw an `std::out_of_range` that does not inherits `toml::exception`. - -Also, in some cases, most likely in the file open error, it will throw an -`std::runtime_error`. - -## Colorize Error Messages - -By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from -`toml::parse` and `toml::find|get` will be colorized. By default, this feature -is turned off. - -With the following toml file taken from `toml-lang/toml/tests/hard_example.toml`, - -```toml -[error] -array = [ - "This might most likely happen in multiline arrays", - Like here, - "or here, - and here" - ] End of array comment, forgot the # -``` - -the error message would be like this. - -![error-message-1](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-1.png) - -With the following, - -```toml -[error] -# array = [ -# "This might most likely happen in multiline arrays", -# Like here, -# "or here, -# and here" -# ] End of array comment, forgot the # -number = 3.14 pi <--again forgot the # -``` - -the error message would be like this. - -![error-message-2](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-2.png) - -The message would be messy when it is written to a file, not a terminal because -it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). - -Without `TOML11_COLORIZE_ERROR_MESSAGE`, you can still colorize user-defined -error message by passing `true` to the `toml::format_error` function. -If you define `TOML11_COLORIZE_ERROR_MESSAGE`, the value is `true` by default. -If not, the default value would be `false`. - -```cpp -std::cerr << toml::format_error("[error] value should be positive", - data.at("num"), "positive number required", - hints, /*colorize = */ true) << std::endl; -``` - -Note: It colorize `[error]` in red. That means that it detects `[error]` prefix -at the front of the error message. If there is no `[error]` prefix, -`format_error` adds it to the error message. - -## Serializing TOML data - -toml11 enables you to serialize data into toml format. - -```cpp -const toml::value data{{"foo", 42}, {"bar", "baz"}}; -std::cout << data << std::endl; -// bar = "baz" -// foo = 42 -``` - -toml11 automatically makes a small table and small array inline. -You can specify the width to make them inline by `std::setw` for streams. - -```cpp -const toml::value data{ - {"qux", {{"foo", 42}, {"bar", "baz"}}}, - {"quux", {"small", "array", "of", "strings"}}, - {"foobar", {"this", "array", "of", "strings", "is", "too", "long", - "to", "print", "into", "single", "line", "isn't", "it?"}}, -}; - -// the threshold becomes 80. -std::cout << std::setw(80) << data << std::endl; -// foobar = [ -// "this","array","of","strings","is","too","long","to","print","into", -// "single","line","isn't","it?", -// ] -// quux = ["small","array","of","strings"] -// qux = {bar="baz",foo=42} - - -// the width is 0. nothing become inline. -std::cout << std::setw(0) << data << std::endl; -// foobar = [ -// "this", -// ... (snip) -// "it?", -// ] -// quux = [ -// "small", -// "array", -// "of", -// "strings", -// ] -// [qux] -// bar = "baz" -// foo = 42 -``` - -It is recommended to set width before printing data. Some I/O functions changes -width to 0, and it makes all the stuff (including `toml::array`) multiline. -The resulting files becomes too long. - -To control the precision of floating point numbers, you need to pass -`std::setprecision` to stream. - -```cpp -const toml::value data{ - {"pi", 3.141592653589793}, - {"e", 2.718281828459045} -}; -std::cout << std::setprecision(17) << data << std::endl; -// e = 2.7182818284590451 -// pi = 3.1415926535897931 -std::cout << std::setprecision( 7) << data << std::endl; -// e = 2.718282 -// pi = 3.141593 -``` - -There is another way to format toml values, `toml::format()`. -It returns `std::string` that represents a value. - -```cpp -const toml::value v{{"a", 42}}; -const std::string fmt = toml::format(v); -// a = 42 -``` - -Note that since `toml::format` formats a value, the resulting string may lack -the key value. - -```cpp -const toml::value v{3.14}; -const std::string fmt = toml::format(v); -// 3.14 -``` - -To control the width and precision, `toml::format` receives optional second and -third arguments to set them. By default, the width is 80 and the precision is -`std::numeric_limits::max_digit10`. - -```cpp -const auto serial = toml::format(data, /*width = */ 0, /*prec = */ 17); -``` - -When you pass a comment-preserving-value, the comment will also be serialized. -An array or a table containing a value that has a comment would not be inlined. - -## Underlying types - -The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below. - -| TOML type | underlying c++ type | enum class | -| -------------- | ---------------------------------- | -------------------------------- | -| Boolean | `bool` | `toml::value_t::boolean` | -| Integer | `std::int64_t` | `toml::value_t::integer` | -| Float | `double` | `toml::value_t::floating` | -| String | `toml::string` | `toml::value_t::string` | -| LocalDate | `toml::local_date` | `toml::value_t::local_date` | -| LocalTime | `toml::local_time` | `toml::value_t::local_time` | -| LocalDatetime | `toml::local_datetime` | `toml::value_t::local_datetime` | -| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offset_datetime` | -| Array | `array-like` | `toml::value_t::array` | -| Table | `map-like` | `toml::value_t::table` | - -`array-like` and `map-like` are the STL containers that works like a `std::vector` and -`std::unordered_map`, respectively. By default, `std::vector` and `std::unordered_map` -are used. See [Customizing containers](#customizing-containers) for detail. - -`toml::string` is effectively the same as `std::string` but has an additional -flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. -Although `std::string` is not an exact toml type, still you can get a reference -that points to internal `std::string` by using `toml::get()` for convenience. -The most important difference between `std::string` and `toml::string` is that -`toml::string` will be formatted as a TOML string when outputted with `ostream`. -This feature is introduced to make it easy to write a custom serializer. - -`Datetime` variants are `struct` that are defined in this library. -Because `std::chrono::system_clock::time_point` is a __time point__, -not capable of representing a Local Time independent from a specific day. - -## Unreleased TOML features - -Since TOML v1.0.0-rc.1 has been released, those features are now activated by -default. We no longer need to define `TOML11_USE_UNRELEASED_FEATURES`. - -- Leading zeroes in exponent parts of floats are permitted. - - e.g. `1.0e+01`, `5e+05` - - [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656) -- Allow raw tab characters in basic strings and multi-line basic strings. - - [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627) -- Allow heterogeneous arrays - - [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676) - -## Note about heterogeneous arrays - -Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value` -does not. Here the reason is explained. - -```cpp -// this won't be compiled -toml::value v{ - "foo", 3.14, 42, {1,2,3,4,5}, {{"key", "value"}} -} -``` - -There is a workaround for this. By explicitly converting values into -`toml::value`, you can initialize `toml::value` with a heterogeneous array. -Also, you can first initialize a `toml::value` with an array and then -`push_back` into it. - -```cpp -// OK! -toml::value v{ - toml::value("foo"), toml::value(3.14), toml::value(42), - toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} -} - -// OK! -toml::value v(toml::array{}); -v.push_back("foo"); -v.push_back(3.14); - -// OK! -toml::array a; -a.push_back("foo"); -a.push_back(3.14); -toml::value v(std::move(a)); -``` - -The reason why the first example is not allowed is the following. -Let's assume that you are initializing a `toml::value` with a table. - -```cpp - // # expecting TOML table. -toml::value v{ // [v] - {"answer", 42}, // answer = 42 - {"pi", 3.14}, // pi = 3.14 - {"foo", "bar"} // foo = "bar" -}; -``` - -This is indistinguishable from a (heterogeneous) TOML array definition. - -```toml -v = [ - ["answer", 42], - ["pi", 3.14], - ["foo", "bar"], -] -``` - -This means that the above C++ code makes constructor's overload resolution -ambiguous. So a constructor that allows both "table as an initializer-list" and -"heterogeneous array as an initializer-list" cannot be implemented. - -Thus, although it is painful, we need to explicitly cast values into -`toml::value` when you initialize heterogeneous array in a C++ code. - -```cpp -toml::value v{ - toml::value("foo"), toml::value(3.14), toml::value(42), - toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} -}; -``` - -## Breaking Changes from v2 - -Although toml11 is relatively new library (it's three years old now), it had -some confusing and inconvenient user-interfaces because of historical reasons. - -Between v2 and v3, those interfaces are rearranged. - -- `toml::parse` now returns a `toml::value`, not `toml::table`. -- `toml::value` is now an alias of `toml::basic_value`. - - See [Customizing containers](#customizing-containers) for detail. -- The elements of `toml::value_t` are renamed as `snake_case`. - - See [Underlying types](#underlying-types) for detail. -- Supports for the CamelCaseNames are dropped. - - See [Underlying types](#underlying-types) for detail. -- `(is|as)_float` has been removed to make the function names consistent with others. - - Since `float` is a keyword, toml11 named a float type as `toml::floating`. - - Also a `value_t` corresponds to `toml::floating` is named `value_t::floating`. - - So `(is|as)_floating` is introduced and `is_float` has been removed. - - See [Casting a toml::value](#casting-a-tomlvalue) and [Checking value type](#checking-value-type) for detail. -- An overload of `toml::find` for `toml::table` has been dropped. Use `toml::value` version instead. - - Because type conversion between a table and a value causes ambiguity while overload resolution - - Since `toml::parse` now returns a `toml::value`, this feature becomes less important. - - Also because `toml::table` is a normal STL container, implementing utility function is easy. - - See [Finding a toml::value](#finding-a-toml-value) for detail. -- An overload of `operator<<` and `toml::format` for `toml::table`s are dropped. - - Use `toml::value` instead. - - See [Serializing TOML data](#serializing-toml-data) for detail. -- Interface around comments. - - See [Preserving Comments](#preserving-comments) for detail. -- An ancient `from_toml/into_toml` has been removed. Use arbitrary type conversion support. - - See [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) for detail. - -Such a big change will not happen in the coming years. - -## Running Tests - -After cloning this repository, run the following command (thank you @jwillikers -for automating test set fetching!). - -```sh -$ mkdir build -$ cd build -$ cmake .. -Dtoml11_BUILD_TEST=ON -$ make -$ make test -``` - -To run the language agnostic test suite, you need to compile -`tests/check_toml_test.cpp` and pass it to the tester. - -## Contributors - -I appreciate the help of the contributors who introduced the great feature to this library. - -- Guillaume Fraux (@Luthaf) - - Windows support and CI on Appvayor - - Intel Compiler support -- Quentin Khan (@xaxousis) - - Found & Fixed a bug around ODR - - Improved error messages for invalid keys to show the location where the parser fails -- Petr Beneš (@wbenny) - - Fixed warnings on MSVC -- Ivan Shynkarenka (@chronoxor) - - Fixed Visual Studio 2019 warnings -- @khoitd1997 - - Fixed warnings while type conversion -- @KerstinKeller - - Added installation script to CMake -- J.C. Moyer (@jcmoyer) - - Fixed an example code in the documentation -- Jt Freeman (@blockparty-sh) - - Fixed feature test macro around `localtime_s` - - Suppress warnings in Debug mode -- OGAWA Kenichi (@kenichiice) - - Suppress warnings on intel compiler -- Jordan Williams (@jwillikers) - - Fixed clang range-loop-analysis warnings - - Fixed feature test macro to suppress -Wundef - - Use cache variables in CMakeLists.txt - - Automate test set fetching, update and refactor CMakeLists.txt -- Scott McCaskill - - Parse 9 digits (nanoseconds) of fractional seconds in a `local_time` -- Shu Wang (@halfelf) - - fix "Finding a value in an array" example in README -- @maass-tv and @SeverinLeonhardt - - Fix MSVC warning C4866 -- OGAWA KenIchi (@kenichiice) - - Fix include path in README -- Mohammed Alyousef (@MoAlyousef) - - Made testing optional in CMake -- Ivan Shynkarenka (@chronoxor) - - Fix compilation error in `` with MinGW -- Alex Merry (@amerry) - - Add missing include files -- sneakypete81 (@sneakypete81) - - Fix typo in error message -- Oliver Kahrmann (@founderio) - - Fix missing filename in error message if parsed file is empty -- Karl Nilsson (@karl-nilsson) - - Fix many spelling errors -- ohdarling88 (@ohdarling) - - Fix a bug in a constructor of serializer -- estshorter (@estshorter) - - Fix MSVC warning C26478 -- Philip Top (@phlptp) - - Improve checking standard library feature availability check -- Louis Marascio (@marascio) - - Fix free-nonheap-object warning - - -## Licensing terms - -This product is licensed under the terms of the [MIT License](LICENSE). - -- Copyright (c) 2017-2021 Toru Niina - -All rights reserved. diff --git a/src/toml11/toml.hpp b/src/toml11/toml.hpp deleted file mode 100644 index f34cfccca..000000000 --- a/src/toml11/toml.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Toru Niina - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef TOML_FOR_MODERN_CPP -#define TOML_FOR_MODERN_CPP - -#ifndef __cplusplus -# error "__cplusplus is not defined" -#endif - -#if __cplusplus < 201103L && _MSC_VER < 1900 -# error "toml11 requires C++11 or later." -#endif - -#define TOML11_VERSION_MAJOR 3 -#define TOML11_VERSION_MINOR 7 -#define TOML11_VERSION_PATCH 0 - -#include "toml/parser.hpp" -#include "toml/literal.hpp" -#include "toml/serializer.hpp" -#include "toml/get.hpp" -#include "toml/macros.hpp" - -#endif// TOML_FOR_MODERN_CPP diff --git a/src/toml11/toml/color.hpp b/src/toml11/toml/color.hpp deleted file mode 100644 index 4cb572cb0..000000000 --- a/src/toml11/toml/color.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TOML11_COLOR_HPP -#define TOML11_COLOR_HPP -#include -#include - -#ifdef TOML11_COLORIZE_ERROR_MESSAGE -#define TOML11_ERROR_MESSAGE_COLORIZED true -#else -#define TOML11_ERROR_MESSAGE_COLORIZED false -#endif - -namespace toml -{ - -// put ANSI escape sequence to ostream -namespace color_ansi -{ -namespace detail -{ -inline int colorize_index() -{ - static const int index = std::ios_base::xalloc(); - return index; -} -} // detail - -inline std::ostream& colorize(std::ostream& os) -{ - // by default, it is zero. - os.iword(detail::colorize_index()) = 1; - return os; -} -inline std::ostream& nocolorize(std::ostream& os) -{ - os.iword(detail::colorize_index()) = 0; - return os; -} -inline std::ostream& reset (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} -inline std::ostream& bold (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} -inline std::ostream& grey (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} -inline std::ostream& red (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} -inline std::ostream& green (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} -inline std::ostream& yellow (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} -inline std::ostream& blue (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} -inline std::ostream& magenta(std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} -inline std::ostream& cyan (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} -inline std::ostream& white (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} -} // color_ansi - -// ANSI escape sequence is the only and default colorization method currently -namespace color = color_ansi; - -} // toml -#endif// TOML11_COLOR_HPP diff --git a/src/toml11/toml/combinator.hpp b/src/toml11/toml/combinator.hpp deleted file mode 100644 index 33ecca1eb..000000000 --- a/src/toml11/toml/combinator.hpp +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_COMBINATOR_HPP -#define TOML11_COMBINATOR_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "region.hpp" -#include "result.hpp" -#include "traits.hpp" -#include "utility.hpp" - -// they scans characters and returns region if it matches to the condition. -// when they fail, it does not change the location. -// in lexer.hpp, these are used. - -namespace toml -{ -namespace detail -{ - -// to output character as an error message. -inline std::string show_char(const char c) -{ - // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows. - // I'm not completely sure but they check the value of char to be in the - // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes - // has negative value (if char has sign). So here it re-interprets c as - // unsigned char through pointer. In general, converting pointer to a - // pointer that has different type cause UB, but `(signed|unsigned)?char` - // are one of the exceptions. Converting pointer only to char and std::byte - // (c++17) are valid. - if(std::isgraph(*reinterpret_cast(std::addressof(c)))) - { - return std::string(1, c); - } - else - { - std::array buf; - buf.fill('\0'); - const auto r = std::snprintf( - buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF); - (void) r; // Unused variable warning - assert(r == static_cast(buf.size()) - 1); - return std::string(buf.data()); - } -} - -template -struct character -{ - static constexpr char target = C; - - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - const auto first = loc.iter(); - - const char c = *(loc.iter()); - if(c != target) - { - return none(); - } - loc.advance(); // update location - - return ok(region(loc, first, loc.iter())); - } -}; -template -constexpr char character::target; - -// closed interval [Low, Up]. both Low and Up are included. -template -struct in_range -{ - // assuming ascii part of UTF-8... - static_assert(Low <= Up, "lower bound should be less than upper bound."); - - static constexpr char upper = Up; - static constexpr char lower = Low; - - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - const auto first = loc.iter(); - - const char c = *(loc.iter()); - if(c < lower || upper < c) - { - return none(); - } - - loc.advance(); - return ok(region(loc, first, loc.iter())); - } -}; -template constexpr char in_range::upper; -template constexpr char in_range::lower; - -// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. -// for detecting invalid characters, like control sequences in toml string. -template -struct exclude -{ - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - auto first = loc.iter(); - - auto rslt = Combinator::invoke(loc); - if(rslt.is_ok()) - { - loc.reset(first); - return none(); - } - loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... - return ok(region(loc, first, loc.iter())); - } -}; - -// increment `iter`, if matches. otherwise, just return empty string. -template -struct maybe -{ - static result - invoke(location& loc) - { - const auto rslt = Combinator::invoke(loc); - if(rslt.is_ok()) - { - return rslt; - } - return ok(region(loc)); - } -}; - -template -struct sequence; - -template -struct sequence -{ - static result - invoke(location& loc) - { - const auto first = loc.iter(); - auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - return sequence::invoke(loc, std::move(rslt.unwrap()), first); - } - - // called from the above function only, recursively. - template - static result - invoke(location& loc, region reg, Iterator first) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - reg += rslt.unwrap(); // concat regions - return sequence::invoke(loc, std::move(reg), first); - } -}; - -template -struct sequence -{ - // would be called from sequence::invoke only. - template - static result - invoke(location& loc, region reg, Iterator first) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - reg += rslt.unwrap(); // concat regions - return ok(reg); - } -}; - -template -struct either; - -template -struct either -{ - static result - invoke(location& loc) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_ok()) {return rslt;} - return either::invoke(loc); - } -}; -template -struct either -{ - static result - invoke(location& loc) - { - return Head::invoke(loc); - } -}; - -template -struct repeat; - -template struct exactly{}; -template struct at_least{}; -struct unlimited{}; - -template -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat -{ - static result - invoke(location& loc) - { - region retval(loc); - while(true) - { - auto rslt = T::invoke(loc); - if(rslt.is_err()) - { - return ok(std::move(retval)); - } - retval += rslt.unwrap(); - } - } -}; - -} // detail -} // toml -#endif// TOML11_COMBINATOR_HPP diff --git a/src/toml11/toml/comments.hpp b/src/toml11/toml/comments.hpp deleted file mode 100644 index ec2504117..000000000 --- a/src/toml11/toml/comments.hpp +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_COMMENTS_HPP -#define TOML11_COMMENTS_HPP -#include -#include -#include -#include -#include -#include -#include - -#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments -#else -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments -#endif - -// This file provides mainly two classes, `preserve_comments` and `discard_comments`. -// Those two are a container that have the same interface as `std::vector` -// but bahaves in the opposite way. `preserve_comments` is just the same as -// `std::vector` and each `std::string` corresponds to a comment line. -// Conversely, `discard_comments` discards all the strings and ignores everything -// assigned in it. `discard_comments` is always empty and you will encounter an -// error whenever you access to the element. -namespace toml -{ -struct discard_comments; // forward decl - -// use it in the following way -// -// const toml::basic_value data = -// toml::parse("example.toml"); -// -// the interface is almost the same as std::vector. -struct preserve_comments -{ - // `container_type` is not provided in discard_comments. - // do not use this inner-type in a generic code. - using container_type = std::vector; - - using size_type = container_type::size_type; - using difference_type = container_type::difference_type; - using value_type = container_type::value_type; - using reference = container_type::reference; - using const_reference = container_type::const_reference; - using pointer = container_type::pointer; - using const_pointer = container_type::const_pointer; - using iterator = container_type::iterator; - using const_iterator = container_type::const_iterator; - using reverse_iterator = container_type::reverse_iterator; - using const_reverse_iterator = container_type::const_reverse_iterator; - - preserve_comments() = default; - ~preserve_comments() = default; - preserve_comments(preserve_comments const&) = default; - preserve_comments(preserve_comments &&) = default; - preserve_comments& operator=(preserve_comments const&) = default; - preserve_comments& operator=(preserve_comments &&) = default; - - explicit preserve_comments(const std::vector& c): comments(c){} - explicit preserve_comments(std::vector&& c) - : comments(std::move(c)) - {} - preserve_comments& operator=(const std::vector& c) - { - comments = c; - return *this; - } - preserve_comments& operator=(std::vector&& c) - { - comments = std::move(c); - return *this; - } - - explicit preserve_comments(const discard_comments&) {} - - explicit preserve_comments(size_type n): comments(n) {} - preserve_comments(size_type n, const std::string& x): comments(n, x) {} - preserve_comments(std::initializer_list x): comments(x) {} - template - preserve_comments(InputIterator first, InputIterator last) - : comments(first, last) - {} - - template - void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} - void assign(std::initializer_list ini) {comments.assign(ini);} - void assign(size_type n, const std::string& val) {comments.assign(n, val);} - - // Related to the issue #97. - // - // It is known that `std::vector::insert` and `std::vector::erase` in - // the standard library implementation included in GCC 4.8.5 takes - // `std::vector::iterator` instead of `std::vector::const_iterator`. - // Because of the const-correctness, we cannot convert a `const_iterator` to - // an `iterator`. It causes compilation error in GCC 4.8.5. -#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) -# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 -# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION -# endif -#endif - -#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION - iterator insert(iterator p, const std::string& x) - { - return comments.insert(p, x); - } - iterator insert(iterator p, std::string&& x) - { - return comments.insert(p, std::move(x)); - } - void insert(iterator p, size_type n, const std::string& x) - { - return comments.insert(p, n, x); - } - template - void insert(iterator p, InputIterator first, InputIterator last) - { - return comments.insert(p, first, last); - } - void insert(iterator p, std::initializer_list ini) - { - return comments.insert(p, ini); - } - - template - iterator emplace(iterator p, Ts&& ... args) - { - return comments.emplace(p, std::forward(args)...); - } - - iterator erase(iterator pos) {return comments.erase(pos);} - iterator erase(iterator first, iterator last) - { - return comments.erase(first, last); - } -#else - iterator insert(const_iterator p, const std::string& x) - { - return comments.insert(p, x); - } - iterator insert(const_iterator p, std::string&& x) - { - return comments.insert(p, std::move(x)); - } - iterator insert(const_iterator p, size_type n, const std::string& x) - { - return comments.insert(p, n, x); - } - template - iterator insert(const_iterator p, InputIterator first, InputIterator last) - { - return comments.insert(p, first, last); - } - iterator insert(const_iterator p, std::initializer_list ini) - { - return comments.insert(p, ini); - } - - template - iterator emplace(const_iterator p, Ts&& ... args) - { - return comments.emplace(p, std::forward(args)...); - } - - iterator erase(const_iterator pos) {return comments.erase(pos);} - iterator erase(const_iterator first, const_iterator last) - { - return comments.erase(first, last); - } -#endif - - void swap(preserve_comments& other) {comments.swap(other.comments);} - - void push_back(const std::string& v) {comments.push_back(v);} - void push_back(std::string&& v) {comments.push_back(std::move(v));} - void pop_back() {comments.pop_back();} - - template - void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} - - void clear() {comments.clear();} - - size_type size() const noexcept {return comments.size();} - size_type max_size() const noexcept {return comments.max_size();} - size_type capacity() const noexcept {return comments.capacity();} - bool empty() const noexcept {return comments.empty();} - - void reserve(size_type n) {comments.reserve(n);} - void resize(size_type n) {comments.resize(n);} - void resize(size_type n, const std::string& c) {comments.resize(n, c);} - void shrink_to_fit() {comments.shrink_to_fit();} - - reference operator[](const size_type n) noexcept {return comments[n];} - const_reference operator[](const size_type n) const noexcept {return comments[n];} - reference at(const size_type n) {return comments.at(n);} - const_reference at(const size_type n) const {return comments.at(n);} - reference front() noexcept {return comments.front();} - const_reference front() const noexcept {return comments.front();} - reference back() noexcept {return comments.back();} - const_reference back() const noexcept {return comments.back();} - - pointer data() noexcept {return comments.data();} - const_pointer data() const noexcept {return comments.data();} - - iterator begin() noexcept {return comments.begin();} - iterator end() noexcept {return comments.end();} - const_iterator begin() const noexcept {return comments.begin();} - const_iterator end() const noexcept {return comments.end();} - const_iterator cbegin() const noexcept {return comments.cbegin();} - const_iterator cend() const noexcept {return comments.cend();} - - reverse_iterator rbegin() noexcept {return comments.rbegin();} - reverse_iterator rend() noexcept {return comments.rend();} - const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} - const_reverse_iterator rend() const noexcept {return comments.rend();} - const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} - const_reverse_iterator crend() const noexcept {return comments.crend();} - - friend bool operator==(const preserve_comments&, const preserve_comments&); - friend bool operator!=(const preserve_comments&, const preserve_comments&); - friend bool operator< (const preserve_comments&, const preserve_comments&); - friend bool operator<=(const preserve_comments&, const preserve_comments&); - friend bool operator> (const preserve_comments&, const preserve_comments&); - friend bool operator>=(const preserve_comments&, const preserve_comments&); - - friend void swap(preserve_comments&, std::vector&); - friend void swap(std::vector&, preserve_comments&); - - private: - - container_type comments; -}; - -inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} -inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} -inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} -inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} -inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} -inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} - -inline void swap(preserve_comments& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs); - return; -} -inline void swap(preserve_comments& lhs, std::vector& rhs) -{ - lhs.comments.swap(rhs); - return; -} -inline void swap(std::vector& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs.comments); - return; -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const preserve_comments& com) -{ - for(const auto& c : com) - { - os << '#' << c << '\n'; - } - return os; -} - -namespace detail -{ - -// To provide the same interface with `preserve_comments`, `discard_comments` -// should have an iterator. But it does not contain anything, so we need to -// add an iterator that points nothing. -// -// It always points null, so DO NOT unwrap this iterator. It always crashes -// your program. -template -struct empty_iterator -{ - using value_type = T; - using reference_type = typename std::conditional::type; - using pointer_type = typename std::conditional::type; - using difference_type = std::ptrdiff_t; - using iterator_category = std::random_access_iterator_tag; - - empty_iterator() = default; - ~empty_iterator() = default; - empty_iterator(empty_iterator const&) = default; - empty_iterator(empty_iterator &&) = default; - empty_iterator& operator=(empty_iterator const&) = default; - empty_iterator& operator=(empty_iterator &&) = default; - - // DO NOT call these operators. - reference_type operator*() const noexcept {std::terminate();} - pointer_type operator->() const noexcept {return nullptr;} - reference_type operator[](difference_type) const noexcept {return this->operator*();} - - // These operators do nothing. - empty_iterator& operator++() noexcept {return *this;} - empty_iterator operator++(int) noexcept {return *this;} - empty_iterator& operator--() noexcept {return *this;} - empty_iterator operator--(int) noexcept {return *this;} - - empty_iterator& operator+=(difference_type) noexcept {return *this;} - empty_iterator& operator-=(difference_type) noexcept {return *this;} - - empty_iterator operator+(difference_type) const noexcept {return *this;} - empty_iterator operator-(difference_type) const noexcept {return *this;} -}; - -template -bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} -template -bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} -template -bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} - -template -typename empty_iterator::difference_type -operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} - -template -empty_iterator -operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} -template -empty_iterator -operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} - -} // detail - -// The default comment type. It discards all the comments. It requires only one -// byte to contain, so the memory footprint is smaller than preserve_comments. -// -// It just ignores `push_back`, `insert`, `erase`, and any other modifications. -// IT always returns size() == 0, the iterator taken by `begin()` is always the -// same as that of `end()`, and accessing through `operator[]` or iterators -// always causes a segmentation fault. DO NOT access to the element of this. -// -// Why this is chose as the default type is because the last version (2.x.y) -// does not contain any comments in a value. To minimize the impact on the -// efficiency, this is chosen as a default. -// -// To reduce the memory footprint, later we can try empty base optimization (EBO). -struct discard_comments -{ - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using value_type = std::string; - using reference = std::string&; - using const_reference = std::string const&; - using pointer = std::string*; - using const_pointer = std::string const*; - using iterator = detail::empty_iterator; - using const_iterator = detail::empty_iterator; - using reverse_iterator = detail::empty_iterator; - using const_reverse_iterator = detail::empty_iterator; - - discard_comments() = default; - ~discard_comments() = default; - discard_comments(discard_comments const&) = default; - discard_comments(discard_comments &&) = default; - discard_comments& operator=(discard_comments const&) = default; - discard_comments& operator=(discard_comments &&) = default; - - explicit discard_comments(const std::vector&) noexcept {} - explicit discard_comments(std::vector&&) noexcept {} - discard_comments& operator=(const std::vector&) noexcept {return *this;} - discard_comments& operator=(std::vector&&) noexcept {return *this;} - - explicit discard_comments(const preserve_comments&) noexcept {} - - explicit discard_comments(size_type) noexcept {} - discard_comments(size_type, const std::string&) noexcept {} - discard_comments(std::initializer_list) noexcept {} - template - discard_comments(InputIterator, InputIterator) noexcept {} - - template - void assign(InputIterator, InputIterator) noexcept {} - void assign(std::initializer_list) noexcept {} - void assign(size_type, const std::string&) noexcept {} - - iterator insert(const_iterator, const std::string&) {return iterator{};} - iterator insert(const_iterator, std::string&&) {return iterator{};} - iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} - template - iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} - iterator insert(const_iterator, std::initializer_list) {return iterator{};} - - template - iterator emplace(const_iterator, Ts&& ...) {return iterator{};} - iterator erase(const_iterator) {return iterator{};} - iterator erase(const_iterator, const_iterator) {return iterator{};} - - void swap(discard_comments&) {return;} - - void push_back(const std::string&) {return;} - void push_back(std::string&& ) {return;} - void pop_back() {return;} - - template - void emplace_back(Ts&& ...) {return;} - - void clear() {return;} - - size_type size() const noexcept {return 0;} - size_type max_size() const noexcept {return 0;} - size_type capacity() const noexcept {return 0;} - bool empty() const noexcept {return true;} - - void reserve(size_type) {return;} - void resize(size_type) {return;} - void resize(size_type, const std::string&) {return;} - void shrink_to_fit() {return;} - - // DO NOT access to the element of this container. This container is always - // empty, so accessing through operator[], front/back, data causes address - // error. - - reference operator[](const size_type) noexcept {return *data();} - const_reference operator[](const size_type) const noexcept {return *data();} - reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} - const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} - reference front() noexcept {return *data();} - const_reference front() const noexcept {return *data();} - reference back() noexcept {return *data();} - const_reference back() const noexcept {return *data();} - - pointer data() noexcept {return nullptr;} - const_pointer data() const noexcept {return nullptr;} - - iterator begin() noexcept {return iterator{};} - iterator end() noexcept {return iterator{};} - const_iterator begin() const noexcept {return const_iterator{};} - const_iterator end() const noexcept {return const_iterator{};} - const_iterator cbegin() const noexcept {return const_iterator{};} - const_iterator cend() const noexcept {return const_iterator{};} - - reverse_iterator rbegin() noexcept {return iterator{};} - reverse_iterator rend() noexcept {return iterator{};} - const_reverse_iterator rbegin() const noexcept {return const_iterator{};} - const_reverse_iterator rend() const noexcept {return const_iterator{};} - const_reverse_iterator crbegin() const noexcept {return const_iterator{};} - const_reverse_iterator crend() const noexcept {return const_iterator{};} -}; - -inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} -inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} -inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} - -inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const discard_comments&) -{ - return os; -} - -} // toml11 -#endif// TOML11_COMMENTS_HPP diff --git a/src/toml11/toml/datetime.hpp b/src/toml11/toml/datetime.hpp deleted file mode 100644 index d8127c150..000000000 --- a/src/toml11/toml/datetime.hpp +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_DATETIME_HPP -#define TOML11_DATETIME_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace toml -{ - -// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is -// provided in the absolutely same purpose, but C++11 is actually not compatible -// with C11. We need to dispatch the function depending on the OS. -namespace detail -{ -// TODO: find more sophisticated way to handle this -#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) -inline std::tm localtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::localtime_r(src, &dst); - if (!result) { throw std::runtime_error("localtime_r failed."); } - return dst; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::gmtime_r(src, &dst); - if (!result) { throw std::runtime_error("gmtime_r failed."); } - return dst; -} -#elif defined(_MSC_VER) -inline std::tm localtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::localtime_s(&dst, src); - if (result) { throw std::runtime_error("localtime_s failed."); } - return dst; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::gmtime_s(&dst, src); - if (result) { throw std::runtime_error("gmtime_s failed."); } - return dst; -} -#else // fallback. not threadsafe -inline std::tm localtime_s(const std::time_t* src) -{ - const auto result = std::localtime(src); - if (!result) { throw std::runtime_error("localtime failed."); } - return *result; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - const auto result = std::gmtime(src); - if (!result) { throw std::runtime_error("gmtime failed."); } - return *result; -} -#endif -} // detail - -enum class month_t : std::uint8_t -{ - Jan = 0, - Feb = 1, - Mar = 2, - Apr = 3, - May = 4, - Jun = 5, - Jul = 6, - Aug = 7, - Sep = 8, - Oct = 9, - Nov = 10, - Dec = 11 -}; - -struct local_date -{ - std::int16_t year; // A.D. (like, 2018) - std::uint8_t month; // [0, 11] - std::uint8_t day; // [1, 31] - - local_date(int y, month_t m, int d) - : year (static_cast(y)), - month(static_cast(m)), - day (static_cast(d)) - {} - - explicit local_date(const std::tm& t) - : year (static_cast(t.tm_year + 1900)), - month(static_cast(t.tm_mon)), - day (static_cast(t.tm_mday)) - {} - - explicit local_date(const std::chrono::system_clock::time_point& tp) - { - const auto t = std::chrono::system_clock::to_time_t(tp); - const auto time = detail::localtime_s(&t); - *this = local_date(time); - } - - explicit local_date(const std::time_t t) - : local_date(std::chrono::system_clock::from_time_t(t)) - {} - - operator std::chrono::system_clock::time_point() const - { - // std::mktime returns date as local time zone. no conversion needed - std::tm t; - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_mday = static_cast(this->day); - t.tm_mon = static_cast(this->month); - t.tm_year = static_cast(this->year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - return std::chrono::system_clock::from_time_t(std::mktime(&t)); - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - local_date() = default; - ~local_date() = default; - local_date(local_date const&) = default; - local_date(local_date&&) = default; - local_date& operator=(local_date const&) = default; - local_date& operator=(local_date&&) = default; -}; - -inline bool operator==(const local_date& lhs, const local_date& rhs) -{ - return std::make_tuple(lhs.year, lhs.month, lhs.day) == - std::make_tuple(rhs.year, rhs.month, rhs.day); -} -inline bool operator!=(const local_date& lhs, const local_date& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_date& lhs, const local_date& rhs) -{ - return std::make_tuple(lhs.year, lhs.month, lhs.day) < - std::make_tuple(rhs.year, rhs.month, rhs.day); -} -inline bool operator<=(const local_date& lhs, const local_date& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_date& lhs, const local_date& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_date& lhs, const local_date& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_date& date) -{ - os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; - return os; -} - -struct local_time -{ - std::uint8_t hour; // [0, 23] - std::uint8_t minute; // [0, 59] - std::uint8_t second; // [0, 60] - std::uint16_t millisecond; // [0, 999] - std::uint16_t microsecond; // [0, 999] - std::uint16_t nanosecond; // [0, 999] - - local_time(int h, int m, int s, - int ms = 0, int us = 0, int ns = 0) - : hour (static_cast(h)), - minute(static_cast(m)), - second(static_cast(s)), - millisecond(static_cast(ms)), - microsecond(static_cast(us)), - nanosecond (static_cast(ns)) - {} - - explicit local_time(const std::tm& t) - : hour (static_cast(t.tm_hour)), - minute(static_cast(t.tm_min)), - second(static_cast(t.tm_sec)), - millisecond(0), microsecond(0), nanosecond(0) - {} - - template - explicit local_time(const std::chrono::duration& t) - { - const auto h = std::chrono::duration_cast(t); - this->hour = static_cast(h.count()); - const auto t2 = t - h; - const auto m = std::chrono::duration_cast(t2); - this->minute = static_cast(m.count()); - const auto t3 = t2 - m; - const auto s = std::chrono::duration_cast(t3); - this->second = static_cast(s.count()); - const auto t4 = t3 - s; - const auto ms = std::chrono::duration_cast(t4); - this->millisecond = static_cast(ms.count()); - const auto t5 = t4 - ms; - const auto us = std::chrono::duration_cast(t5); - this->microsecond = static_cast(us.count()); - const auto t6 = t5 - us; - const auto ns = std::chrono::duration_cast(t6); - this->nanosecond = static_cast(ns.count()); - } - - operator std::chrono::nanoseconds() const - { - return std::chrono::nanoseconds (this->nanosecond) + - std::chrono::microseconds(this->microsecond) + - std::chrono::milliseconds(this->millisecond) + - std::chrono::seconds(this->second) + - std::chrono::minutes(this->minute) + - std::chrono::hours(this->hour); - } - - local_time() = default; - ~local_time() = default; - local_time(local_time const&) = default; - local_time(local_time&&) = default; - local_time& operator=(local_time const&) = default; - local_time& operator=(local_time&&) = default; -}; - -inline bool operator==(const local_time& lhs, const local_time& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); -} -inline bool operator!=(const local_time& lhs, const local_time& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_time& lhs, const local_time& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); -} -inline bool operator<=(const local_time& lhs, const local_time& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_time& lhs, const local_time& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_time& lhs, const local_time& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& time) -{ - os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.second); - if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) - { - os << '.'; - os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); - if(time.microsecond != 0 || time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); - if(time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); - } - } - } - return os; -} - -struct time_offset -{ - std::int8_t hour; // [-12, 12] - std::int8_t minute; // [-59, 59] - - time_offset(int h, int m) - : hour (static_cast(h)), - minute(static_cast(m)) - {} - - operator std::chrono::minutes() const - { - return std::chrono::minutes(this->minute) + - std::chrono::hours(this->hour); - } - - time_offset() = default; - ~time_offset() = default; - time_offset(time_offset const&) = default; - time_offset(time_offset&&) = default; - time_offset& operator=(time_offset const&) = default; - time_offset& operator=(time_offset&&) = default; -}; - -inline bool operator==(const time_offset& lhs, const time_offset& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute) == - std::make_tuple(rhs.hour, rhs.minute); -} -inline bool operator!=(const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const time_offset& lhs, const time_offset& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute) < - std::make_tuple(rhs.hour, rhs.minute); -} -inline bool operator<=(const time_offset& lhs, const time_offset& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const time_offset& offset) -{ - if(offset.hour == 0 && offset.minute == 0) - { - os << 'Z'; - return os; - } - int minute = static_cast(offset.hour) * 60 + offset.minute; - if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} - os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; - os << std::setfill('0') << std::setw(2) << minute % 60; - return os; -} - -struct local_datetime -{ - local_date date; - local_time time; - - local_datetime(local_date d, local_time t): date(d), time(t) {} - - explicit local_datetime(const std::tm& t): date(t), time(t){} - - explicit local_datetime(const std::chrono::system_clock::time_point& tp) - { - const auto t = std::chrono::system_clock::to_time_t(tp); - std::tm ltime = detail::localtime_s(&t); - - this->date = local_date(ltime); - this->time = local_time(ltime); - - // std::tm lacks subsecond information, so diff between tp and tm - // can be used to get millisecond & microsecond information. - const auto t_diff = tp - - std::chrono::system_clock::from_time_t(std::mktime(<ime)); - this->time.millisecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.microsecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.nanosecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - } - - explicit local_datetime(const std::time_t t) - : local_datetime(std::chrono::system_clock::from_time_t(t)) - {} - - operator std::chrono::system_clock::time_point() const - { - using internal_duration = - typename std::chrono::system_clock::time_point::duration; - - // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator - // of local_date and local_time independently, the conversion fails if - // it is the day when DST begins or ends. Since local_date considers the - // time is 00:00 A.M. and local_time does not consider DST because it - // does not have any date information. We need to consider both date and - // time information at the same time to convert it correctly. - - std::tm t; - t.tm_sec = static_cast(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(this->date.year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - - // std::mktime returns date as local time zone. no conversion needed - auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); - dt += std::chrono::duration_cast( - std::chrono::milliseconds(this->time.millisecond) + - std::chrono::microseconds(this->time.microsecond) + - std::chrono::nanoseconds (this->time.nanosecond)); - return dt; - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - local_datetime() = default; - ~local_datetime() = default; - local_datetime(local_datetime const&) = default; - local_datetime(local_datetime&&) = default; - local_datetime& operator=(local_datetime const&) = default; - local_datetime& operator=(local_datetime&&) = default; -}; - -inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time) == - std::make_tuple(rhs.date, rhs.time); -} -inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time) < - std::make_tuple(rhs.date, rhs.time); -} -inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_datetime& dt) -{ - os << dt.date << 'T' << dt.time; - return os; -} - -struct offset_datetime -{ - local_date date; - local_time time; - time_offset offset; - - offset_datetime(local_date d, local_time t, time_offset o) - : date(d), time(t), offset(o) - {} - offset_datetime(const local_datetime& dt, time_offset o) - : date(dt.date), time(dt.time), offset(o) - {} - explicit offset_datetime(const local_datetime& ld) - : date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) - // use the current local timezone offset - {} - explicit offset_datetime(const std::chrono::system_clock::time_point& tp) - : offset(0, 0) // use gmtime - { - const auto timet = std::chrono::system_clock::to_time_t(tp); - const auto tm = detail::gmtime_s(&timet); - this->date = local_date(tm); - this->time = local_time(tm); - } - explicit offset_datetime(const std::time_t& t) - : offset(0, 0) // use gmtime - { - const auto tm = detail::gmtime_s(&t); - this->date = local_date(tm); - this->time = local_time(tm); - } - explicit offset_datetime(const std::tm& t) - : offset(0, 0) // assume gmtime - { - this->date = local_date(t); - this->time = local_time(t); - } - - operator std::chrono::system_clock::time_point() const - { - // get date-time - using internal_duration = - typename std::chrono::system_clock::time_point::duration; - - // first, convert it to local date-time information in the same way as - // local_datetime does. later we will use time_t to adjust time offset. - std::tm t; - t.tm_sec = static_cast(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(this->date.year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - const std::time_t tp_loc = std::mktime(std::addressof(t)); - - auto tp = std::chrono::system_clock::from_time_t(tp_loc); - tp += std::chrono::duration_cast( - std::chrono::milliseconds(this->time.millisecond) + - std::chrono::microseconds(this->time.microsecond) + - std::chrono::nanoseconds (this->time.nanosecond)); - - // Since mktime uses local time zone, it should be corrected. - // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if - // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need - // to add `+09:00` to `03:00:00Z`. - // Here, it uses the time_t converted from date-time info to handle - // daylight saving time. - const auto ofs = get_local_offset(std::addressof(tp_loc)); - tp += std::chrono::hours (ofs.hour); - tp += std::chrono::minutes(ofs.minute); - - // We got `12:00:00Z` by correcting local timezone applied by mktime. - // Then we will apply the offset. Let's say `12:00:00-08:00` is given. - // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. - // So we need to subtract the offset. - tp -= std::chrono::minutes(this->offset); - return tp; - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - offset_datetime() = default; - ~offset_datetime() = default; - offset_datetime(offset_datetime const&) = default; - offset_datetime(offset_datetime&&) = default; - offset_datetime& operator=(offset_datetime const&) = default; - offset_datetime& operator=(offset_datetime&&) = default; - - private: - - static time_offset get_local_offset(const std::time_t* tp) - { - // get local timezone with the same date-time information as mktime - const auto t = detail::localtime_s(tp); - - std::array buf; - const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 - if(result != 5) - { - throw std::runtime_error("toml::offset_datetime: cannot obtain " - "timezone information of current env"); - } - const int ofs = std::atoi(buf.data()); - const int ofs_h = ofs / 100; - const int ofs_m = ofs - (ofs_h * 100); - return time_offset(ofs_h, ofs_m); - } -}; - -inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time, lhs.offset) == - std::make_tuple(rhs.date, rhs.time, rhs.offset); -} -inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time, lhs.offset) < - std::make_tuple(rhs.date, rhs.time, rhs.offset); -} -inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const offset_datetime& dt) -{ - os << dt.date << 'T' << dt.time << dt.offset; - return os; -} - -}//toml -#endif// TOML11_DATETIME diff --git a/src/toml11/toml/exception.hpp b/src/toml11/toml/exception.hpp deleted file mode 100644 index c64651d0a..000000000 --- a/src/toml11/toml/exception.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_EXCEPTION_HPP -#define TOML11_EXCEPTION_HPP -#include -#include - -#include "source_location.hpp" - -namespace toml -{ - -struct exception : public std::exception -{ - public: - explicit exception(const source_location& loc): loc_(loc) {} - virtual ~exception() noexcept override = default; - virtual const char* what() const noexcept override {return "";} - virtual source_location const& location() const noexcept {return loc_;} - - protected: - source_location loc_; -}; - -struct syntax_error : public toml::exception -{ - public: - explicit syntax_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~syntax_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -struct type_error : public toml::exception -{ - public: - explicit type_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~type_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -struct internal_error : public toml::exception -{ - public: - explicit internal_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~internal_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -} // toml -#endif // TOML_EXCEPTION diff --git a/src/toml11/toml/from.hpp b/src/toml11/toml/from.hpp deleted file mode 100644 index 10815caf5..000000000 --- a/src/toml11/toml/from.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_FROM_HPP -#define TOML11_FROM_HPP - -namespace toml -{ - -template -struct from; -// { -// static T from_toml(const toml::value& v) -// { -// // User-defined conversions ... -// } -// }; - -} // toml -#endif // TOML11_FROM_HPP diff --git a/src/toml11/toml/get.hpp b/src/toml11/toml/get.hpp deleted file mode 100644 index d7fdf553b..000000000 --- a/src/toml11/toml/get.hpp +++ /dev/null @@ -1,1117 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_GET_HPP -#define TOML11_GET_HPP -#include - -#include "from.hpp" -#include "result.hpp" -#include "value.hpp" - -namespace toml -{ - -// ============================================================================ -// exact toml::* type - -template class M, template class V> -detail::enable_if_t>::value, T> & -get(basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return T(std::move(v).template cast>::value>()); -} - -// ============================================================================ -// T == toml::value; identity transformation. - -template class M, template class V> -inline detail::enable_if_t>::value, T>& -get(basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return basic_value(std::move(v)); -} - -// ============================================================================ -// T == toml::basic_value; basic_value -> basic_value - -template class M, template class V> -inline detail::enable_if_t, - detail::negation>> - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// integer convertible from toml::Integer - -template class M, template class V> -inline detail::enable_if_t, // T is integral - detail::negation>, // but not bool - detail::negation< // but not toml::integer - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_integer()); -} - -// ============================================================================ -// floating point convertible from toml::Float - -template class M, template class V> -inline detail::enable_if_t, // T is floating_point - detail::negation< // but not toml::floating - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_floating()); -} - -// ============================================================================ -// std::string; toml uses its own toml::string, but it should be convertible to -// std::string seamlessly - -template class M, template class V> -inline detail::enable_if_t::value, std::string>& -get(basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> const& -get(const basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> -get(basic_value&& v) -{ - return std::string(std::move(v.as_string().str)); -} - -// ============================================================================ -// std::string_view - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 -template class M, template class V> -inline detail::enable_if_t::value, std::string_view> -get(const basic_value& v) -{ - return std::string_view(v.as_string().str); -} -#endif - -// ============================================================================ -// std::chrono::duration from toml::local_time. - -template class M, template class V> -inline detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return std::chrono::duration_cast( - std::chrono::nanoseconds(v.as_local_time())); -} - -// ============================================================================ -// std::chrono::system_clock::time_point from toml::datetime variants - -template class M, template class V> -inline detail::enable_if_t< - std::is_same::value, T> -get(const basic_value& v) -{ - switch(v.type()) - { - case value_t::local_date: - { - return std::chrono::system_clock::time_point(v.as_local_date()); - } - case value_t::local_datetime: - { - return std::chrono::system_clock::time_point(v.as_local_datetime()); - } - case value_t::offset_datetime: - { - return std::chrono::system_clock::time_point(v.as_offset_datetime()); - } - default: - { - throw type_error(detail::format_underline("toml::value: " - "bad_cast to std::chrono::system_clock::time_point", { - {v.location(), concat_to_string("the actual type is ", v.type())} - }), v.location()); - } - } -} - -// ============================================================================ -// forward declaration to use this recursively. ignore this and go ahead. - -// array-like type with push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // T::push_back(value) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// array-like type without push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back(...) - detail::negation< // not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// std::pair -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// std::tuple -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// map-like classes -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::table - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// T.from_toml(v) -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value&); - -// toml::from::from_toml(v) -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// T(const toml::value&) and T is not toml::basic_value, -// and it does not have `from` nor `from_toml`. -template class M, template class V> -detail::enable_if_t>, - std::is_constructible&>, - detail::negation>, - detail::negation> - >::value, T> -get(const basic_value&); - -// ============================================================================ -// array-like types; most likely STL container, like std::vector, etc. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // container.push_back(elem) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ary = v.as_array(); - - T container; - try_reserve(container, ary.size()); - - for(const auto& elem : ary) - { - container.push_back(get(elem)); - } - return container; -} - -// ============================================================================ -// std::forward_list does not have push_back, insert, or emplace. -// It has insert_after, emplace_after, push_front. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - T container; - for(const auto& elem : v.as_array()) - { - container.push_front(get(elem)); - } - container.reverse(); - return container; -} - -// ============================================================================ -// array-like types, without push_back(). most likely [std|boost]::array. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back - detail::negation< // T is not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ar = v.as_array(); - - T container; - if(ar.size() != container.size()) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), { - {v.location(), "here"} - })); - } - for(std::size_t i=0; i(ar[i]); - } - return container; -} - -// ============================================================================ -// std::pair. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using first_type = typename T::first_type; - using second_type = typename T::second_type; - - const auto& ar = v.as_array(); - if(ar.size() != 2) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::pair but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return std::make_pair(::toml::get(ar.at(0)), - ::toml::get(ar.at(1))); -} - -// ============================================================================ -// std::tuple. - -namespace detail -{ -template -T get_tuple_impl(const Array& a, index_sequence) -{ - return std::make_tuple( - ::toml::get::type>(a.at(I))...); -} -} // detail - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - const auto& ar = v.as_array(); - if(ar.size() != std::tuple_size::value) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::tuple with ", - std::tuple_size::value, " elements, but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return detail::get_tuple_impl(ar, - detail::make_index_sequence::value>{}); -} - -// ============================================================================ -// map-like types; most likely STL map, like std::map or std::unordered_map. - -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using key_type = typename T::key_type; - using mapped_type = typename T::mapped_type; - static_assert(std::is_convertible::value, - "toml::get only supports map type of which key_type is " - "convertible from std::string."); - T map; - for(const auto& kv : v.as_table()) - { - map.emplace(key_type(kv.first), get(kv.second)); - } - return map; -} - -// ============================================================================ -// user-defined, but compatible types. - -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) memfn - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value& v) -{ - T ud; - ud.from_toml(v); - return ud; -} -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return ::toml::from::from_toml(v); -} - -template class M, template class V> -detail::enable_if_t>, // T is not a toml::value - std::is_constructible&>, // T is constructible from toml::value - detail::negation>, // and T does not have T.from_toml(v); - detail::negation> // and T does not have toml::from{}; - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// find - -// ---------------------------------------------------------------------------- -// these overloads do not require to set T. and returns value itself. -template class M, template class V> -basic_value const& find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value& find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return basic_value(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -basic_value const& -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value& find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value find(basic_value&& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return basic_value(std::move(ary.at(idx))); -} - -// ---------------------------------------------------------------------------- -// find(value, key); - -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const std::size_t idx) -{ - typename basic_value::array_type ary = std::move(v).as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(std::move(ary.at(idx))); -} - -// -------------------------------------------------------------------------- -// toml::find(toml::value, toml::key, Ts&& ... keys) - -namespace detail -{ -// It suppresses warnings by -Wsign-conversion. Let's say we have the following -// code. -// ```cpp -// const auto x = toml::find(data, "array", 0); -// ``` -// Here, the type of literal number `0` is `int`. `int` is a signed integer. -// `toml::find` takes `std::size_t` as an index. So it causes implicit sign -// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0` -// suppresses the warning, but it makes user code messy. -// To suppress this warning, we need to be aware of type conversion caused -// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of -// keys can be any combination of {string-like, size_t-like}. Of course we can't -// write down all the combinations. Thus we need to use some function that -// recognize the type of argument and cast it into `std::string` or -// `std::size_t` depending on the context. -// `key_cast` does the job. It has 2 overloads. One is invoked when the -// argument type is an integer and cast the argument into `std::size_t`. The -// other is invoked when the argument type is not an integer, possibly one of -// std::string, const char[N] or const char*, and construct std::string from -// the argument. -// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to -// `toml::find(v, k)` to suppress -Wsign-conversion. - -template -enable_if_t>, - negation, bool>>>::value, std::size_t> -key_cast(T&& v) noexcept -{ - return std::size_t(v); -} -template -enable_if_t>, - negation, bool>>>>::value, std::string> -key_cast(T&& v) noexcept -{ - return std::string(std::forward(v)); -} -} // detail - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -const basic_value& -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value& -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), std::forward(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -// ============================================================================ -// get_or(value, fallback) - -template class M, template class V> -basic_value const& -get_or(const basic_value& v, const basic_value&) -{ - return v; -} -template class M, template class V> -basic_value& -get_or(basic_value& v, basic_value&) -{ - return v; -} -template class M, template class V> -basic_value -get_or(basic_value&& v, basic_value&&) -{ - return v; -} - -// ---------------------------------------------------------------------------- -// specialization for the exact toml types (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -get_or(basic_value& v, T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t, - basic_value>::value, detail::remove_cvref_t> -get_or(basic_value&& v, T&& opt) -{ - try - { - return get>(std::move(v)); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for std::string (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t, std::string>::value, - std::string> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -get_or(basic_value& v, T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - std::is_same, std::string>::value, std::string> -get_or(basic_value&& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for string literal - -template class M, template class V> -detail::enable_if_t::type>::value, std::string> -get_or(const basic_value& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) - -template class M, template class V> -detail::enable_if_t, - basic_value>>, - detail::negation>>, - detail::negation::type>> - >::value, detail::remove_cvref_t> -get_or(const basic_value& v, T&& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// =========================================================================== -// find_or(value, key, fallback) - -template class M, template class V> -basic_value const& -find_or(const basic_value& v, const key& ky, - const basic_value& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value& -find_or(basic_value& v, const toml::key& ky, basic_value& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value -find_or(basic_value&& v, const toml::key& ky, basic_value&& opt) -{ - if(!v.is_table()) {return opt;} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return opt;} - return basic_value(std::move(tab.at(ky))); -} - -// --------------------------------------------------------------------------- -// exact types (return type can be a reference) -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, - detail::remove_cvref_t> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// std::string (return type can be a reference) - -template class M, template class V> -detail::enable_if_t::value, std::string> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// string literal (deduced as std::string) -template class M, template class V> -detail::enable_if_t< - detail::is_string_literal::type>::value, - std::string> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::string(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::string(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) -template class M, template class V> -detail::enable_if_t, basic_value>>, - // T is not std::string - detail::negation>>, - // T is not a string literal - detail::negation::type>> - >::value, detail::remove_cvref_t> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// recursive find-or with type deduction (find_or(value, keys, opt)) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// --------------------------------------------------------------------------- -// recursive find_or with explicit type specialization, find_or(value, keys...) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// ============================================================================ -// expect - -template class M, template class V> -result expect(const basic_value& v) noexcept -{ - try - { - return ok(get(v)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} -template class M, template class V> -result -expect(const basic_value& v, const toml::key& k) noexcept -{ - try - { - return ok(find(v, k)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} - -} // toml -#endif// TOML11_GET diff --git a/src/toml11/toml/into.hpp b/src/toml11/toml/into.hpp deleted file mode 100644 index 74495560e..000000000 --- a/src/toml11/toml/into.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_INTO_HPP -#define TOML11_INTO_HPP - -namespace toml -{ - -template -struct into; -// { -// static toml::value into_toml(const T& user_defined_type) -// { -// // User-defined conversions ... -// } -// }; - -} // toml -#endif // TOML11_INTO_HPP diff --git a/src/toml11/toml/lexer.hpp b/src/toml11/toml/lexer.hpp deleted file mode 100644 index ea5050b8d..000000000 --- a/src/toml11/toml/lexer.hpp +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_LEXER_HPP -#define TOML11_LEXER_HPP -#include -#include -#include -#include - -#include "combinator.hpp" - -namespace toml -{ -namespace detail -{ - -// these scans contents from current location in a container of char -// and extract a region that matches their own pattern. -// to see the implementation of each component, see combinator.hpp. - -using lex_wschar = either, character<'\t'>>; -using lex_ws = repeat>; -using lex_newline = either, - sequence, character<'\n'>>>; -using lex_lower = in_range<'a', 'z'>; -using lex_upper = in_range<'A', 'Z'>; -using lex_alpha = either; -using lex_digit = in_range<'0', '9'>; -using lex_nonzero = in_range<'1', '9'>; -using lex_oct_dig = in_range<'0', '7'>; -using lex_bin_dig = in_range<'0', '1'>; -using lex_hex_dig = either, in_range<'a', 'f'>>; - -using lex_hex_prefix = sequence, character<'x'>>; -using lex_oct_prefix = sequence, character<'o'>>; -using lex_bin_prefix = sequence, character<'b'>>; -using lex_underscore = character<'_'>; -using lex_plus = character<'+'>; -using lex_minus = character<'-'>; -using lex_sign = either; - -// digit | nonzero 1*(digit | _ digit) -using lex_unsigned_dec_int = either>, at_least<1>>>, - lex_digit>; -// (+|-)? unsigned_dec_int -using lex_dec_int = sequence, lex_unsigned_dec_int>; - -// hex_prefix hex_dig *(hex_dig | _ hex_dig) -using lex_hex_int = sequence>, unlimited>>>; -// oct_prefix oct_dig *(oct_dig | _ oct_dig) -using lex_oct_int = sequence>, unlimited>>>; -// bin_prefix bin_dig *(bin_dig | _ bin_dig) -using lex_bin_int = sequence>, unlimited>>>; - -// (dec_int | hex_int | oct_int | bin_int) -using lex_integer = either; - -// =========================================================================== - -using lex_inf = sequence, character<'n'>, character<'f'>>; -using lex_nan = sequence, character<'a'>, character<'n'>>; -using lex_special_float = sequence, either>; - -using lex_zero_prefixable_int = sequence>, unlimited>>; - -using lex_fractional_part = sequence, lex_zero_prefixable_int>; - -using lex_exponent_part = sequence, character<'E'>>, - maybe, lex_zero_prefixable_int>; - -using lex_float = either>>>>; - -// =========================================================================== - -using lex_true = sequence, character<'r'>, - character<'u'>, character<'e'>>; -using lex_false = sequence, character<'a'>, character<'l'>, - character<'s'>, character<'e'>>; -using lex_boolean = either; - -// =========================================================================== - -using lex_date_fullyear = repeat>; -using lex_date_month = repeat>; -using lex_date_mday = repeat>; -using lex_time_delim = either, character<'t'>, character<' '>>; -using lex_time_hour = repeat>; -using lex_time_minute = repeat>; -using lex_time_second = repeat>; -using lex_time_secfrac = sequence, - repeat>>; - -using lex_time_numoffset = sequence, character<'-'>>, - sequence, - lex_time_minute>>; -using lex_time_offset = either, character<'z'>, - lex_time_numoffset>; - -using lex_partial_time = sequence, - lex_time_minute, character<':'>, - lex_time_second, maybe>; -using lex_full_date = sequence, - lex_date_month, character<'-'>, - lex_date_mday>; -using lex_full_time = sequence; - -using lex_offset_date_time = sequence; -using lex_local_date_time = sequence; -using lex_local_date = lex_full_date; -using lex_local_time = lex_partial_time; - -// =========================================================================== - -using lex_quotation_mark = character<'"'>; -using lex_basic_unescaped = exclude, // 0x09 (tab) is allowed - in_range<0x0A, 0x1F>, - character<0x22>, character<0x5C>, - character<0x7F>>>; - -using lex_escape = character<'\\'>; -using lex_escape_unicode_short = sequence, - repeat>>; -using lex_escape_unicode_long = sequence, - repeat>>; -using lex_escape_seq_char = either, character<'\\'>, - character<'b'>, character<'f'>, - character<'n'>, character<'r'>, - character<'t'>, - lex_escape_unicode_short, - lex_escape_unicode_long - >; -using lex_escaped = sequence; -using lex_basic_char = either; -using lex_basic_string = sequence, - lex_quotation_mark>; - -// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings -// are allowed to be used. -// After this, the following strings are *explicitly* allowed. -// - One or two `"`s in a multi-line basic string is allowed wherever it is. -// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter. -// - One or two `"`s can appear just before or after the delimiter. -// ```toml -// str4 = """Here are two quotation marks: "". Simple enough.""" -// str5 = """Here are three quotation marks: ""\".""" -// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" -// str7 = """"This," she said, "is just a pointless statement."""" -// ``` -// In the current implementation (v3.3.0), it is difficult to parse `str7` in -// the above example. It is difficult to recognize `"` at the end of string body -// collectly. It will be misunderstood as a `"""` delimiter and an additional, -// invalid `"`. Like this: -// ```console -// what(): [error] toml::parse_table: invalid line format -// --> hoge.toml -// | -// 13 | str7 = """"This," she said, "is just a pointless statement."""" -// | ^- expected newline, but got '"'. -// ``` -// As a quick workaround for this problem, `lex_ml_basic_string_delim` was -// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`. -// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s. -// In parse_ml_basic_string() function, the trailing `"`s will be attached to -// the string body. -// -using lex_ml_basic_string_delim = repeat>; -using lex_ml_basic_string_open = lex_ml_basic_string_delim; -using lex_ml_basic_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_basic_unescaped = exclude, // 0x09 is tab - in_range<0x0A, 0x1F>, - character<0x5C>, // backslash - character<0x7F>, // DEL - lex_ml_basic_string_delim>>; - -using lex_ml_basic_escaped_newline = sequence< - lex_escape, maybe, lex_newline, - repeat, unlimited>>; - -using lex_ml_basic_char = either; -using lex_ml_basic_body = repeat, - unlimited>; -using lex_ml_basic_string = sequence; - -using lex_literal_char = exclude, in_range<0x0A, 0x1F>, - character<0x7F>, character<0x27>>>; -using lex_apostrophe = character<'\''>; -using lex_literal_string = sequence, - lex_apostrophe>; - -// the same reason as above. -using lex_ml_literal_string_delim = repeat>; -using lex_ml_literal_string_open = lex_ml_literal_string_delim; -using lex_ml_literal_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_literal_char = exclude, - in_range<0x0A, 0x1F>, - character<0x7F>, - lex_ml_literal_string_delim>>; -using lex_ml_literal_body = repeat, - unlimited>; -using lex_ml_literal_string = sequence; - -using lex_string = either; - -// =========================================================================== -using lex_dot_sep = sequence, character<'.'>, maybe>; - -using lex_unquoted_key = repeat, character<'_'>>, - at_least<1>>; -using lex_quoted_key = either; -using lex_simple_key = either; -using lex_dotted_key = sequence, - at_least<1> - > - >; -using lex_key = either; - -using lex_keyval_sep = sequence, - character<'='>, - maybe>; - -using lex_std_table_open = character<'['>; -using lex_std_table_close = character<']'>; -using lex_std_table = sequence, - lex_key, - maybe, - lex_std_table_close>; - -using lex_array_table_open = sequence; -using lex_array_table_close = sequence; -using lex_array_table = sequence, - lex_key, - maybe, - lex_array_table_close>; - -using lex_utf8_1byte = in_range<0x00, 0x7F>; -using lex_utf8_2byte = sequence< - in_range(0xC2), static_cast(0xDF)>, - in_range(0x80), static_cast(0xBF)> - >; -using lex_utf8_3byte = sequence(0xE0)>, in_range(0xA0), static_cast(0xBF)>>, - sequence(0xE1), static_cast(0xEC)>, in_range(0x80), static_cast(0xBF)>>, - sequence(0xED)>, in_range(0x80), static_cast(0x9F)>>, - sequence(0xEE), static_cast(0xEF)>, in_range(0x80), static_cast(0xBF)>> - >, in_range(0x80), static_cast(0xBF)>>; -using lex_utf8_4byte = sequence(0xF0)>, in_range(0x90), static_cast(0xBF)>>, - sequence(0xF1), static_cast(0xF3)>, in_range(0x80), static_cast(0xBF)>>, - sequence(0xF4)>, in_range(0x80), static_cast(0x8F)>> - >, in_range(0x80), static_cast(0xBF)>, - in_range(0x80), static_cast(0xBF)>>; -using lex_utf8_code = either< - lex_utf8_1byte, - lex_utf8_2byte, - lex_utf8_3byte, - lex_utf8_4byte - >; - -using lex_comment_start_symbol = character<'#'>; -using lex_non_eol_ascii = either, in_range<0x20, 0x7E>>; -using lex_comment = sequence, unlimited>>; - -} // detail -} // toml -#endif // TOML_LEXER_HPP diff --git a/src/toml11/toml/literal.hpp b/src/toml11/toml/literal.hpp deleted file mode 100644 index 04fbbc13e..000000000 --- a/src/toml11/toml/literal.hpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_LITERAL_HPP -#define TOML11_LITERAL_HPP -#include "parser.hpp" - -namespace toml -{ -inline namespace literals -{ -inline namespace toml_literals -{ - -// implementation -inline ::toml::basic_value -literal_internal_impl(::toml::detail::location loc) -{ - using value_type = ::toml::basic_value< - TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; - // if there are some comments or empty lines, skip them. - using skip_line = ::toml::detail::repeat, - ::toml::detail::maybe<::toml::detail::lex_comment>, - ::toml::detail::lex_newline - >, ::toml::detail::at_least<1>>; - skip_line::invoke(loc); - - // if there are some whitespaces before a value, skip them. - using skip_ws = ::toml::detail::repeat< - ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; - skip_ws::invoke(loc); - - // to distinguish arrays and tables, first check it is a table or not. - // - // "[1,2,3]"_toml; // this is an array - // "[table]"_toml; // a table that has an empty table named "table" inside. - // "[[1,2,3]]"_toml; // this is an array of arrays - // "[[table]]"_toml; // this is a table that has an array of tables inside. - // - // "[[1]]"_toml; // this can be both... (currently it becomes a table) - // "1 = [{}]"_toml; // this is a table that has an array of table named 1. - // "[[1,]]"_toml; // this is an array of arrays. - // "[[1],]"_toml; // this also. - - const auto the_front = loc.iter(); - - const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); - loc.reset(the_front); - - const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc); - loc.reset(the_front); - - // If it is neither a table-key or a array-of-table-key, it may be a value. - if(!is_table_key && !is_aots_key) - { - if(auto data = ::toml::detail::parse_value(loc)) - { - return data.unwrap(); - } - } - - // Note that still it can be a table, because the literal might be something - // like the following. - // ```cpp - // R"( // c++11 raw string literals - // key = "value" - // int = 42 - // )"_toml; - // ``` - // It is a valid toml file. - // It should be parsed as if we parse a file with this content. - - if(auto data = ::toml::detail::parse_toml_file(loc)) - { - return data.unwrap(); - } - else // none of them. - { - throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); - } - -} - -inline ::toml::basic_value -operator"" _toml(const char* str, std::size_t len) -{ - ::toml::detail::location loc( - std::string("TOML literal encoded in a C++ code"), - std::vector(str, str + len)); - // literal length does not include the null character at the end. - return literal_internal_impl(std::move(loc)); -} - -// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers. -// So here we use the feature test macro for `char8_t` itself. -#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L -// value of u8"" literal has been changed from char to char8_t and char8_t is -// NOT compatible to char -inline ::toml::basic_value -operator"" _toml(const char8_t* str, std::size_t len) -{ - ::toml::detail::location loc( - std::string("TOML literal encoded in a C++ code"), - std::vector(reinterpret_cast(str), - reinterpret_cast(str) + len)); - return literal_internal_impl(std::move(loc)); -} -#endif - -} // toml_literals -} // literals -} // toml -#endif//TOML11_LITERAL_HPP diff --git a/src/toml11/toml/macros.hpp b/src/toml11/toml/macros.hpp deleted file mode 100644 index e8f91aecd..000000000 --- a/src/toml11/toml/macros.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef TOML11_MACROS_HPP -#define TOML11_MACROS_HPP - -#define TOML11_STRINGIZE_AUX(x) #x -#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) - -#define TOML11_CONCATENATE_AUX(x, y) x##y -#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) - -// ============================================================================ -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE - -#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE - -// ---------------------------------------------------------------------------- -// TOML11_ARGS_SIZE - -#define TOML11_INDEX_RSEQ() \ - 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ - 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -#define TOML11_ARGS_SIZE_IMPL(\ - ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ - ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ - ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ - ARG31, ARG32, N, ...) N -#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) -#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) - -// ---------------------------------------------------------------------------- -// TOML11_FOR_EACH_VA_ARGS - -#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) -#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) - -#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ - TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) - -// ---------------------------------------------------------------------------- -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE - -// use it in the following way. -// ```cpp -// namespace foo -// { -// struct Foo -// { -// std::string s; -// double d; -// int i; -// }; -// } // foo -// -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) -// ``` -// And then you can use `toml::find(file, "foo");` -// -#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ - obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); - -#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ - v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; - -#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ - namespace toml { \ - template<> \ - struct from \ - { \ - template class T, \ - template class A> \ - static NAME from_toml(const basic_value& v) \ - { \ - NAME obj; \ - TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ - return obj; \ - } \ - }; \ - template<> \ - struct into \ - { \ - static value into_toml(const NAME& obj) \ - { \ - ::toml::value v = ::toml::table{}; \ - TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ - return v; \ - } \ - }; \ - } /* toml */ - -#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE - -#endif// TOML11_MACROS_HPP diff --git a/src/toml11/toml/parser.hpp b/src/toml11/toml/parser.hpp deleted file mode 100644 index e31179918..000000000 --- a/src/toml11/toml/parser.hpp +++ /dev/null @@ -1,2364 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_PARSER_HPP -#define TOML11_PARSER_HPP -#include -#include -#include - -#include "combinator.hpp" -#include "lexer.hpp" -#include "region.hpp" -#include "result.hpp" -#include "types.hpp" -#include "value.hpp" - -#ifndef TOML11_DISABLE_STD_FILESYSTEM -#ifdef __cpp_lib_filesystem -#if __has_include() -#define TOML11_HAS_STD_FILESYSTEM -#include -#endif // has_include() -#endif // __cpp_lib_filesystem -#endif // TOML11_DISABLE_STD_FILESYSTEM - -namespace toml -{ -namespace detail -{ - -inline result, std::string> -parse_boolean(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_boolean::invoke(loc)) - { - const auto reg = token.unwrap(); - if (reg.str() == "true") {return ok(std::make_pair(true, reg));} - else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} - else // internal error. - { - throw internal_error(format_underline( - "toml::parse_boolean: internal error", - {{source_location(reg), "invalid token"}}), - source_location(reg)); - } - } - loc.reset(first); //rollback - return err(format_underline("toml::parse_boolean: ", - {{source_location(loc), "the next token is not a boolean"}})); -} - -inline result, std::string> -parse_binary_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_bin_int::invoke(loc)) - { - auto str = token.unwrap().str(); - assert(str.size() > 2); // minimum -> 0b1 - integer retval(0), base(1); - for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i) - { - if (*i == '1'){retval += base; base *= 2;} - else if(*i == '0'){base *= 2;} - else if(*i == '_'){/* do nothing. */} - else // internal error. - { - throw internal_error(format_underline( - "toml::parse_integer: internal error", - {{source_location(token.unwrap()), "invalid token"}}), - source_location(loc)); - } - } - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_binary_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_octal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_oct_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::oct >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_octal_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_hexadecimal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_hex_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::hex >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_hexadecimal_integer", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_integer(location& loc) -{ - const auto first = loc.iter(); - if(first != loc.end() && *first == '0') - { - const auto second = std::next(first); - if(second == loc.end()) // the token is just zero. - { - loc.advance(); - return ok(std::make_pair(0, region(loc, first, second))); - } - - if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100 - if(*second == 'o') {return parse_octal_integer (loc);} // 0o775 - if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE - - if(std::isdigit(*second)) - { - return err(format_underline("toml::parse_integer: " - "leading zero in an Integer is not allowed.", - {{source_location(loc), "leading zero"}})); - } - else if(std::isalpha(*second)) - { - return err(format_underline("toml::parse_integer: " - "unknown integer prefix appeared.", - {{source_location(loc), "none of 0x, 0o, 0b"}})); - } - } - - if(const auto token = lex_dec_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - - std::istringstream iss(str); - integer retval(0); - iss >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_integer: ", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_floating(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_float::invoke(loc)) - { - auto str = token.unwrap().str(); - if(str == "inf" || str == "+inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - -std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "nan" || str == "+nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - std::istringstream iss(str); - floating v(0.0); - iss >> v; - return ok(std::make_pair(v, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_floating: ", - {{source_location(loc), "the next token is not a float"}})); -} - -inline std::string read_utf8_codepoint(const region& reg, const location& loc) -{ - const auto str = reg.str().substr(1); - std::uint_least32_t codepoint; - std::istringstream iss(str); - iss >> std::hex >> codepoint; - - const auto to_char = [](const std::uint_least32_t i) noexcept -> char { - const auto uc = static_cast(i); - return *reinterpret_cast(std::addressof(uc)); - }; - - std::string character; - if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. - { - character += static_cast(codepoint); - } - else if(codepoint < 0x800) //U+0080 ... U+07FF - { - // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 - character += to_char(0xC0| codepoint >> 6); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x10000) // U+0800...U+FFFF - { - if(0xD800 <= codepoint && codepoint <= 0xDFFF) - { - throw syntax_error(format_underline( - "toml::read_utf8_codepoint: codepoints in the range " - "[0xD800, 0xDFFF] are not valid UTF-8.", {{ - source_location(loc), "not a valid UTF-8 codepoint" - }}), source_location(loc)); - } - assert(codepoint < 0xD800 || 0xDFFF < codepoint); - // 1110yyyy 10yxxxxx 10xxxxxx - character += to_char(0xE0| codepoint >> 12); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x110000) // U+010000 ... U+10FFFF - { - // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx - character += to_char(0xF0| codepoint >> 18); - character += to_char(0x80|(codepoint >> 12 & 0x3F)); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else // out of UTF-8 region - { - throw syntax_error(format_underline("toml::read_utf8_codepoint:" - " input codepoint is too large.", - {{source_location(loc), "should be in [0x00..0x10FFFF]"}}), - source_location(loc)); - } - return character; -} - -inline result parse_escape_sequence(location& loc) -{ - const auto first = loc.iter(); - if(first == loc.end() || *first != '\\') - { - return err(format_underline("toml::parse_escape_sequence: ", {{ - source_location(loc), "the next token is not a backslash \"\\\""}})); - } - loc.advance(); - switch(*loc.iter()) - { - case '\\':{loc.advance(); return ok(std::string("\\"));} - case '"' :{loc.advance(); return ok(std::string("\""));} - case 'b' :{loc.advance(); return ok(std::string("\b"));} - case 't' :{loc.advance(); return ok(std::string("\t"));} - case 'n' :{loc.advance(); return ok(std::string("\n"));} - case 'f' :{loc.advance(); return ok(std::string("\f"));} - case 'r' :{loc.advance(); return ok(std::string("\r"));} - case 'u' : - { - if(const auto token = lex_escape_unicode_short::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint uXXXX.", - {{source_location(loc), "here"}})); - } - } - case 'U': - { - if(const auto token = lex_escape_unicode_long::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint Uxxxxxxxx", - {{source_location(loc), "here"}})); - } - } - } - - const auto msg = format_underline("parse_escape_sequence: " - "unknown escape sequence appeared.", {{source_location(loc), - "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, - /* Hints = */{"if you want to write backslash as just one backslash, " - "use literal string like: regex = '<\\i\\c*\\s*>'"}); - loc.reset(first); - return err(msg); -} - -inline std::ptrdiff_t check_utf8_validity(const std::string& reg) -{ - location loc("tmp", reg); - const auto u8 = repeat::invoke(loc); - if(!u8 || loc.iter() != loc.end()) - { - const auto error_location = std::distance(loc.begin(), loc.iter()); - assert(0 <= error_location); - return error_location; - } - return -1; -} - -inline result, std::string> -parse_ml_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - std::string retval; - retval.reserve(token.unwrap().size()); - - auto delim = lex_ml_basic_string_open::invoke(inner_loc); - if(!delim) - { - throw internal_error(format_underline( - "parse_ml_basic_string: invalid token", - {{source_location(inner_loc), "should be \"\"\""}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - delim = none(); - while(!delim) - { - using lex_unescaped_seq = repeat< - either, unlimited>; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) - { - // ignore newline after escape until next non-ws char - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_ml_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - delim = lex_ml_basic_string_close::invoke(inner_loc); - } - // `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s - // at just before the delimiter. Here, we need to attach `"`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(), - [](const char c) noexcept {return c == '\"';})); - switch(delim.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "\""; break;} - case 5: {retval += "\"\""; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_basic_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_ml_basic_string: " - "the next token is not a valid multiline string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - auto quot = lex_quotation_mark::invoke(inner_loc); - if(!quot) - { - throw internal_error(format_underline("parse_basic_string: " - "invalid token", {{source_location(inner_loc), "should be \""}}), - source_location(inner_loc)); - } - - std::string retval; - retval.reserve(token.unwrap().size()); - - quot = none(); - while(!quot) - { - using lex_unescaped_seq = repeat; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - quot = lex_quotation_mark::invoke(inner_loc); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_basic_string: " - "the next token is not a valid string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_ml_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_literal_string::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_ml_literal_string_open::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - const auto body = lex_ml_literal_body::invoke(inner_loc); - - const auto close = lex_ml_literal_string_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s - // at just before the delimiter. Here, we need to attach `'`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - - std::string retval = body.unwrap().str(); - assert(std::all_of(close.unwrap().first(), close.unwrap().last(), - [](const char c) noexcept {return c == '\'';})); - switch(close.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "'"; break;} - case 5: {retval += "''"; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_literal_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval, toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_ml_literal_string: " - "the next token is not a valid multiline literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_literal_string::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_apostrophe::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto body = repeat::invoke(inner_loc); - - const auto close = lex_apostrophe::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair( - toml::string(body.unwrap().str(), toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_literal_string: " - "the next token is not a valid literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_string(location& loc) -{ - if(loc.iter() != loc.end() && *(loc.iter()) == '"') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"') - { - return parse_ml_basic_string(loc); - } - else - { - return parse_basic_string(loc); - } - } - else if(loc.iter() != loc.end() && *(loc.iter()) == '\'') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'') - { - return parse_ml_literal_string(loc); - } - else - { - return parse_literal_string(loc); - } - } - return err(format_underline("toml::parse_string: ", - {{source_location(loc), "the next token is not a string"}})); -} - -inline result, std::string> -parse_local_date(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto y = lex_date_fullyear::invoke(inner_loc); - if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_inner_local_date: invalid year format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_date_month::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid month format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto d = lex_date_mday::invoke(inner_loc); - if(!d) - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid day format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const auto year = static_cast(from_string(y.unwrap().str(), 0)); - const auto month = static_cast(from_string(m.unwrap().str(), 0)); - const auto day = static_cast(from_string(d.unwrap().str(), 0)); - - // We briefly check whether the input date is valid or not. But here, we - // only check if the RFC3339 compliance. - // Actually there are several special date that does not exist, - // because of historical reasons, such as 1582/10/5-1582/10/14 (only in - // several countries). But here, we do not care about such a complicated - // rule. It makes the code complicated and there is only low probability - // that such a specific date is needed in practice. If someone need to - // validate date accurately, that means that the one need a specialized - // library for their purpose in a different layer. - { - const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); - const auto max_day = (month == 2) ? (is_leap ? 29 : 28) : - ((month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31); - - if((month < 1 || 12 < month) || (day < 1 || max_day < day)) - { - throw syntax_error(format_underline("toml::parse_date: " - "invalid date: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - } - return ok(std::make_pair(local_date(year, static_cast(month - 1), day), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_date: ", - {{source_location(loc), "the next token is not a local_date"}})); - } -} - -inline result, std::string> -parse_local_time(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto h = lex_time_hour::invoke(inner_loc); - if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid year format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_time_minute::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid month format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto s = lex_time_second::invoke(inner_loc); - if(!s) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid second format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const int hour = from_string(h.unwrap().str(), 0); - const int minute = from_string(m.unwrap().str(), 0); - const int second = from_string(s.unwrap().str(), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute) || - (second < 0 || 60 < second)) // it may be leap second - { - throw syntax_error(format_underline("toml::parse_time: " - "invalid time: it does not conform RFC3339.", {{ - source_location(loc), "hour should be 00-23, minute should be" - " 00-59, second should be 00-60 (depending on the leap" - " second rules.)"}}), source_location(inner_loc)); - } - - local_time time(hour, minute, second, 0, 0); - - const auto before_secfrac = inner_loc.iter(); - if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) - { - auto sf = secfrac.unwrap().str(); - sf.erase(sf.begin()); // sf.front() == '.' - switch(sf.size() % 3) - { - case 2: sf += '0'; break; - case 1: sf += "00"; break; - case 0: break; - default: break; - } - if(sf.size() >= 9) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - time.nanosecond = from_string(sf.substr(6, 3), 0u); - } - else if(sf.size() >= 6) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - } - else if(sf.size() >= 3) - { - time.millisecond = from_string(sf, 0u); - time.microsecond = 0u; - } - } - else - { - if(before_secfrac != inner_loc.iter()) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid subsecond format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(time, token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_time: ", - {{source_location(loc), "the next token is not a local_time"}})); - } -} - -inline result, std::string> -parse_local_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto date = parse_local_date(inner_loc); - if(!date || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - const char delim = *(inner_loc.iter()); - if(delim != 'T' && delim != 't' && delim != ' ') - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `T` or ` ` (space)"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto time = parse_local_time(inner_loc); - if(!time) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "invalid time format"}}), - source_location(inner_loc)); - } - return ok(std::make_pair( - local_datetime(date.unwrap().first, time.unwrap().first), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_datetime: ", - {{source_location(loc), "the next token is not a local_datetime"}})); - } -} - -inline result, std::string> -parse_offset_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_offset_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto datetime = parse_local_datetime(inner_loc); - if(!datetime || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - time_offset offset(0, 0); - if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) - { - const auto str = ofs.unwrap().str(); - - const auto hour = from_string(str.substr(1,2), 0); - const auto minute = from_string(str.substr(4,2), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute)) - { - throw syntax_error(format_underline("toml::parse_offset_datetime: " - "invalid offset: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - - if(str.front() == '+') - { - offset = time_offset(hour, minute); - } - else - { - offset = time_offset(-hour, -minute); - } - } - else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `Z` or `+HH:MM`"}}), - source_location(inner_loc)); - } - return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_offset_datetime: ", - {{source_location(loc), "the next token is not a offset_datetime"}})); - } -} - -inline result, std::string> -parse_simple_key(location& loc) -{ - if(const auto bstr = parse_basic_string(loc)) - { - return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second)); - } - if(const auto lstr = parse_literal_string(loc)) - { - return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second)); - } - if(const auto bare = lex_unquoted_key::invoke(loc)) - { - const auto reg = bare.unwrap(); - return ok(std::make_pair(reg.str(), reg)); - } - return err(format_underline("toml::parse_simple_key: ", - {{source_location(loc), "the next token is not a simple key"}})); -} - -// dotted key become vector of keys -inline result, region>, std::string> -parse_key(location& loc) -{ - const auto first = loc.iter(); - // dotted key -> `foo.bar.baz` where several single keys are chained by - // dots. Whitespaces between keys and dots are allowed. - if(const auto token = lex_dotted_key::invoke(loc)) - { - const auto reg = token.unwrap(); - location inner_loc(loc.name(), reg.str()); - std::vector keys; - - while(inner_loc.iter() != inner_loc.end()) - { - lex_ws::invoke(inner_loc); - if(const auto k = parse_simple_key(inner_loc)) - { - keys.push_back(k.unwrap().first); - } - else - { - throw internal_error(format_underline( - "toml::detail::parse_key: dotted key contains invalid key", - {{source_location(inner_loc), k.unwrap_err()}}), - source_location(inner_loc)); - } - - lex_ws::invoke(inner_loc); - if(inner_loc.iter() == inner_loc.end()) - { - break; - } - else if(*inner_loc.iter() == '.') - { - inner_loc.advance(); // to skip `.` - } - else - { - throw internal_error(format_underline("toml::parse_key: " - "dotted key contains invalid key ", - {{source_location(inner_loc), "should be `.`"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(keys, reg)); - } - loc.reset(first); - - // simple_key: a single (basic_string|literal_string|bare key) - if(const auto smpl = parse_simple_key(loc)) - { - return ok(std::make_pair(std::vector(1, smpl.unwrap().first), - smpl.unwrap().second)); - } - return err(format_underline("toml::parse_key: an invalid key appeared.", - {{source_location(loc), "is not a valid key"}}, { - "bare keys : non-empty strings composed only of [A-Za-z0-9_-].", - "quoted keys: same as \"basic strings\" or 'literal strings'.", - "dotted keys: sequence of bare or quoted keys joined with a dot." - })); -} - -// forward-decl to implement parse_array and parse_table -template -result parse_value(location&); - -template -result, std::string> -parse_array(location& loc) -{ - using value_type = Value; - using array_type = typename value_type::array_type; - - const auto first = loc.iter(); - if(loc.iter() == loc.end()) - { - return err("toml::parse_array: input is empty"); - } - if(*loc.iter() != '[') - { - return err("toml::parse_array: token is not an array"); - } - loc.advance(); - - using lex_ws_comment_newline = repeat< - either, unlimited>; - - array_type retval; - while(loc.iter() != loc.end()) - { - lex_ws_comment_newline::invoke(loc); // skip - - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - - if(auto val = parse_value(loc)) - { - // After TOML v1.0.0-rc.1, array becomes to be able to have values - // with different types. So here we will omit this by default. - // - // But some of the test-suite checks if the parser accepts a hetero- - // geneous arrays, so we keep this for a while. -#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS - if(!retval.empty() && retval.front().type() != val.as_ok().type()) - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "type of elements should be the same each other.", { - {source_location(array_start_loc), "array starts here"}, - { - retval.front().location(), - "value has type " + stringize(retval.front().type()) - }, - { - val.unwrap().location(), - "value has different type, " + stringize(val.unwrap().type()) - } - }), source_location(loc)); - } -#endif - retval.push_back(std::move(val.unwrap())); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "value having invalid format appeared in an array", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "it is not a valid value."} - }), source_location(loc)); - } - - using lex_array_separator = sequence, character<','>>; - const auto sp = lex_array_separator::invoke(loc); - if(!sp) - { - lex_ws_comment_newline::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array:" - " missing array separator `,` after a value", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "should be `,`"} - }), source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_array: " - "array did not closed by `]`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -template -result, region>, Value>, std::string> -parse_key_value_pair(location& loc) -{ - using value_type = Value; - - const auto first = loc.iter(); - auto key_reg = parse_key(loc); - if(!key_reg) - { - std::string msg = std::move(key_reg.unwrap_err()); - // if the next token is keyvalue-separator, it means that there are no - // key. then we need to show error as "empty key is not allowed". - if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) - { - loc.reset(first); - msg = format_underline("toml::parse_key_value_pair: " - "empty key is not allowed.", - {{source_location(loc), "key expected before '='"}}); - } - return err(std::move(msg)); - } - - const auto kvsp = lex_keyval_sep::invoke(loc); - if(!kvsp) - { - std::string msg; - // if the line contains '=' after the invalid sequence, possibly the - // error is in the key (like, invalid character in bare key). - const auto line_end = std::find(loc.iter(), loc.end(), '\n'); - if(std::find(loc.iter(), line_end, '=') != line_end) - { - msg = format_underline("toml::parse_key_value_pair: " - "invalid format for key", - {{source_location(loc), "invalid character in key"}}, - {"Did you forget '.' to separate dotted-key?", - "Allowed characters for bare key are [0-9a-zA-Z_-]."}); - } - else // if not, the error is lack of key-value separator. - { - msg = format_underline("toml::parse_key_value_pair: " - "missing key-value separator `=`", - {{source_location(loc), "should be `=`"}}); - } - loc.reset(first); - return err(std::move(msg)); - } - - const auto after_kvsp = loc.iter(); // err msg - auto val = parse_value(loc); - if(!val) - { - std::string msg; - loc.reset(after_kvsp); - // check there is something not a comment/whitespace after `=` - if(sequence, maybe, lex_newline>::invoke(loc)) - { - loc.reset(after_kvsp); - msg = format_underline("toml::parse_key_value_pair: " - "missing value after key-value separator '='", - {{source_location(loc), "expected value, but got nothing"}}); - } - else // there is something not a comment/whitespace, so invalid format. - { - msg = std::move(val.unwrap_err()); - } - loc.reset(first); - return err(msg); - } - return ok(std::make_pair(std::move(key_reg.unwrap()), - std::move(val.unwrap()))); -} - -// for error messages. -template -std::string format_dotted_keys(InputIterator first, const InputIterator last) -{ - static_assert(std::is_same::value_type>::value,""); - - std::string retval(*first++); - for(; first != last; ++first) - { - retval += '.'; - retval += *first; - } - return retval; -} - -// forward decl for is_valid_forward_table_definition -result, region>, std::string> -parse_table_key(location& loc); -template -result, std::string> -parse_inline_table(location& loc); - -// The following toml file is allowed. -// ```toml -// [a.b.c] # here, table `a` has element `b`. -// foo = "bar" -// [a] # merge a = {baz = "qux"} to a = {b = {...}} -// baz = "qux" -// ``` -// But the following is not allowed. -// ```toml -// [a] -// b.c.foo = "bar" -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// The following is neither allowed. -// ```toml -// a = { b.c.foo = "bar"} -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// Here, it parses region of `tab->at(k)` as a table key and check the depth -// of the key. If the key region points deeper node, it would be allowed. -// Otherwise, the key points the same node. It would be rejected. -template -bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, - Iterator key_first, Iterator key_curr, Iterator key_last) -{ - // ------------------------------------------------------------------------ - // check type of the value to be inserted/merged - - std::string inserting_reg = ""; - if(const auto ptr = detail::get_region(inserting)) - { - inserting_reg = ptr->str(); - } - location inserting_def("internal", std::move(inserting_reg)); - if(const auto inlinetable = parse_inline_table(inserting_def)) - { - // check if we are overwriting existing table. - // ```toml - // # NG - // a.b = 42 - // a = {d = 3.14} - // ``` - // Inserting an inline table to a existing super-table is not allowed in - // any case. If we found it, we can reject it without further checking. - return false; - } - - // ------------------------------------------------------------------------ - // check table defined before - - std::string internal = ""; - if(const auto ptr = detail::get_region(fwd)) - { - internal = ptr->str(); - } - location def("internal", std::move(internal)); - if(const auto tabkeys = parse_table_key(def)) // [table.key] - { - // table keys always contains all the nodes from the root. - const auto& tks = tabkeys.unwrap().first; - if(std::size_t(std::distance(key_first, key_last)) == tks.size() && - std::equal(tks.begin(), tks.end(), key_first)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - if(const auto dotkeys = parse_key(def)) - { - // consider the following case. - // [a] - // b.c = {d = 42} - // [a.b.c] - // e = 2.71 - // this defines the table [a.b.c] twice. no? - - // a dotted key starts from the node representing a table in which the - // dotted key belongs to. - const auto& dks = dotkeys.unwrap().first; - if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && - std::equal(dks.begin(), dks.end(), key_curr)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - return false; -} - -template -result -insert_nested_key(typename Value::table_type& root, const Value& v, - InputIterator iter, const InputIterator last, - region key_reg, - const bool is_array_of_table = false) -{ - static_assert(std::is_same::value_type>::value,""); - - using value_type = Value; - using table_type = typename value_type::table_type; - using array_type = typename value_type::array_type; - - const auto first = iter; - assert(iter != last); - - table_type* tab = std::addressof(root); - for(; iter != last; ++iter) // search recursively - { - const key& k = *iter; - if(std::next(iter) == last) // k is the last key - { - // XXX if the value is array-of-tables, there can be several - // tables that are in the same array. in that case, we need to - // find the last element and insert it to there. - if(is_array_of_table) - { - if(tab->count(k) == 1) // there is already an array of table - { - if(tab->at(k).is_table()) - { - // show special err msg for conflicting table - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), - "\") cannot be defined"), { - {tab->at(k).location(), "table already defined"}, - {v.location(), "this conflicts with the previous table"} - }), v.location()); - } - else if(!(tab->at(k).is_array())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // the above if-else-if checks tab->at(k) is an array - auto& a = tab->at(k).as_array(); - // If table element is defined as [[array_of_tables]], it - // cannot be an empty array. If an array of tables is - // defined as `aot = []`, it cannot be appended. - if(a.empty() || !(a.front().is_table())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // avoid conflicting array of table like the following. - // ```toml - // a = [{b = 42}] # define a as an array of *inline* tables - // [[a]] # a is an array of *multi-line* tables - // b = 54 - // ``` - // Here, from the type information, these cannot be detected - // because inline table is also a table. - // But toml v0.5.0 explicitly says it is invalid. The above - // array-of-tables has a static size and appending to the - // array is invalid. - // In this library, multi-line table value has a region - // that points to the key of the table (e.g. [[a]]). By - // comparing the first two letters in key, we can detect - // the array-of-table is inline or multiline. - if(const auto ptr = detail::get_region(a.front())) - { - if(ptr->str().substr(0,2) != "[[") - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides " - "with existing array-of-tables"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value has static size")}, - {v.location(), - "appending it to the statically sized array"} - }), v.location()); - } - } - a.push_back(v); - return ok(true); - } - else // if not, we need to create the array of table - { - // XXX: Consider the following array of tables. - // ```toml - // # This is a comment. - // [[aot]] - // foo = "bar" - // ``` - // Here, the comment is for `aot`. But here, actually two - // values are defined. An array that contains tables, named - // `aot`, and the 0th element of the `aot`, `{foo = "bar"}`. - // Those two are different from each other. But both of them - // points to the same portion of the TOML file, `[[aot]]`, - // so `key_reg.comments()` returns `# This is a comment`. - // If it is assigned as a comment of `aot` defined here, the - // comment will be duplicated. Both the `aot` itself and - // the 0-th element will have the same comment. This causes - // "duplication of the same comments" bug when the data is - // serialized. - // Next, consider the following. - // ```toml - // # comment 1 - // aot = [ - // # comment 2 - // {foo = "bar"}, - // ] - // ``` - // In this case, we can distinguish those two comments. So - // here we need to add "comment 1" to the `aot` and - // "comment 2" to the 0th element of that. - // To distinguish those two, we check the key region. - std::vector comments{/* empty by default */}; - if(key_reg.str().substr(0, 2) != "[[") - { - comments = key_reg.comments(); - } - value_type aot(array_type(1, v), key_reg, std::move(comments)); - tab->insert(std::make_pair(k, aot)); - return ok(true); - } - } // end if(array of table) - - if(tab->count(k) == 1) - { - if(tab->at(k).is_table() && v.is_table()) - { - if(!is_valid_forward_table_definition( - tab->at(k), v, first, iter, last)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: table (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {tab->at(k).location(), "table already exists here"}, - {v.location(), "table defined twice"} - }), v.location()); - } - // to allow the following toml file. - // [a.b.c] - // d = 42 - // [a] - // e = 2.71 - auto& t = tab->at(k).as_table(); - for(const auto& kv : v.as_table()) - { - if(tab->at(k).contains(kv.first)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {t.at(kv.first).location(), "already exists here"}, - {v.location(), "this defined twice"} - }), v.location()); - } - t[kv.first] = kv.second; - } - detail::change_region(tab->at(k), key_reg); - return ok(true); - } - else if(v.is_table() && - tab->at(k).is_array() && - tab->at(k).as_array().size() > 0 && - tab->at(k).as_array().front().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of tables (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "array of tables defined here"}, - {v.location(), "table conflicts with the previous array of table"} - }), v.location()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "value already exists here"}, - {v.location(), "value defined twice"} - }), v.location()); - } - } - tab->insert(std::make_pair(k, v)); - return ok(true); - } - else // k is not the last one, we should insert recursively - { - // if there is no corresponding value, insert it first. - // related: you don't need to write - // # [x] - // # [x.y] - // to write - // [x.y.z] - if(tab->count(k) == 0) - { - // a table that is defined implicitly doesn't have any comments. - (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/}); - } - - // type checking... - if(tab->at(k).is_table()) - { - // According to toml-lang/toml:36d3091b3 "Clarify that inline - // tables are immutable", check if it adds key-value pair to an - // inline table. - if(const auto* ptr = get_region(tab->at(k))) - { - // here, if the value is a (multi-line) table, the region - // should be something like `[table-name]`. - if(ptr->front() == '{') - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: inserting to an inline table (", - format_dotted_keys(first, std::next(iter)), - ") but inline tables are immutable"), { - {tab->at(k).location(), "inline tables are immutable"}, - {v.location(), "inserting this"} - }), v.location()); - } - } - tab = std::addressof((*tab)[k].as_table()); - } - else if(tab->at(k).is_array()) // inserting to array-of-tables? - { - auto& a = (*tab)[k].as_array(); - if(!a.back().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {a.back().location(), concat_to_string( - "actual type is ", a.back().type())}, - {v.location(), "inserting this"} - }), v.location()); - } - tab = std::addressof(a.back().as_table()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {tab->at(k).location(), concat_to_string( - "actual type is ", tab->at(k).type())}, - {v.location(), "inserting this"} - }), v.location()); - } - } - } - return err(std::string("toml::detail::insert_nested_key: never reach here")); -} - -template -result, std::string> -parse_inline_table(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - table_type retval; - if(!(loc.iter() != loc.end() && *loc.iter() == '{')) - { - return err(format_underline("toml::parse_inline_table: ", - {{source_location(loc), "the next token is not an inline table"}})); - } - loc.advance(); - - // check if the inline table is an empty table = { } - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair(retval, region(loc, first, loc.iter()))); - } - - // it starts from "{". it should be formatted as inline-table - while(loc.iter() != loc.end()) - { - const auto kv_r = parse_key_value_pair(loc); - if(!kv_r) - { - return err(kv_r.unwrap_err()); - } - - const auto& kvpair = kv_r.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - - const auto inserted = - insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - throw internal_error("toml::parse_inline_table: " - "failed to insert value into table: " + inserted.unwrap_err(), - source_location(loc)); - } - - using lex_table_separator = sequence, character<','>>; - const auto sp = lex_table_separator::invoke(loc); - - if(!sp) - { - maybe::invoke(loc); - - if(loc.iter() == loc.end()) - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `}` ", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else if(*loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair( - retval, region(loc, first, loc.iter()))); - } - else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing curly brace `}`", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `,` ", - {{source_location(loc), "should be `,`"}}), - source_location(loc)); - } - } - else // `,` is found - { - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: trailing comma is not allowed in" - " an inline table", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_inline_table: " - "inline table did not closed by `}`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -inline result guess_number_type(const location& l) -{ - // This function tries to find some (common) mistakes by checking characters - // that follows the last character of a value. But it is often difficult - // because some non-newline characters can appear after a value. E.g. - // spaces, tabs, commas (in an array or inline table), closing brackets - // (of an array or inline table), comment-sign (#). Since this function - // does not parse further, those characters are always allowed to be there. - location loc = l; - - if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);} - loc.reset(l.iter()); - - if(lex_local_date_time::invoke(loc)) - { - // bad offset may appear after this. - if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-' - || *loc.iter() == 'Z' || *loc.iter() == 'z')) - { - return err(format_underline("bad offset: should be [+-]HH:MM or Z", - {{source_location(loc), "[+-]HH:MM or Z"}}, - {"pass: +09:00, -05:30", "fail: +9:00, -5:30"})); - } - return ok(value_t::local_datetime); - } - loc.reset(l.iter()); - - if(lex_local_date::invoke(loc)) - { - // bad time may appear after this. - // A space is allowed as a delimiter between local time. But there are - // both cases in which a space becomes valid or invalid. - // - invalid: 2019-06-16 7:00:00 - // - valid : 2019-06-16 07:00:00 - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == 'T' || c == 't') - { - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"})); - } - if('0' <= c && c <= '9') - { - return err(format_underline("bad time: missing T", - {{source_location(loc), "T or space required here"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - if(c == ' ' && std::next(loc.iter()) != loc.end() && - ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9')) - { - loc.advance(); - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - } - return ok(value_t::local_date); - } - loc.reset(l.iter()); - - if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);} - loc.reset(l.iter()); - - if(lex_float::invoke(loc)) - { - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad float: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - return ok(value_t::floating); - } - loc.reset(l.iter()); - - if(lex_integer::invoke(loc)) - { - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == '_') - { - return err(format_underline("bad integer: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if('0' <= c && c <= '9') - { - // leading zero. point '0' - loc.retrace(); - return err(format_underline("bad integer: leading zero", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if(c == ':' || c == '-') - { - return err(format_underline("bad datetime: invalid format", - {{source_location(loc), "here"}}, - {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z", - "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"})); - } - if(c == '.' || c == 'e' || c == 'E') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - } - return ok(value_t::integer); - } - if(loc.iter() != loc.end() && *loc.iter() == '.') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "integer part required before this"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad number: `_` should be surrounded by digits", - {{source_location(loc), "`_` is not surrounded by digits"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - return err(format_underline("bad format: unknown value appeared", - {{source_location(loc), "here"}})); -} - -inline result guess_value_type(const location& loc) -{ - switch(*loc.iter()) - { - case '"' : {return ok(value_t::string); } - case '\'': {return ok(value_t::string); } - case 't' : {return ok(value_t::boolean); } - case 'f' : {return ok(value_t::boolean); } - case '[' : {return ok(value_t::array); } - case '{' : {return ok(value_t::table); } - case 'i' : {return ok(value_t::floating);} // inf. - case 'n' : {return ok(value_t::floating);} // nan. - default : {return guess_number_type(loc);} - } -} - -template -result -parse_value_helper(result, std::string> rslt) -{ - if(rslt.is_ok()) - { - auto comments = rslt.as_ok().second.comments(); - return ok(Value(std::move(rslt.as_ok()), std::move(comments))); - } - else - { - return err(std::move(rslt.as_err())); - } -} - -template -result parse_value(location& loc) -{ - const auto first = loc.iter(); - if(first == loc.end()) - { - return err(format_underline("toml::parse_value: input is empty", - {{source_location(loc), ""}})); - } - - const auto type = guess_value_type(loc); - if(!type) - { - return err(type.unwrap_err()); - } - - switch(type.unwrap()) - { - case value_t::boolean : {return parse_value_helper(parse_boolean(loc) );} - case value_t::integer : {return parse_value_helper(parse_integer(loc) );} - case value_t::floating : {return parse_value_helper(parse_floating(loc) );} - case value_t::string : {return parse_value_helper(parse_string(loc) );} - case value_t::offset_datetime: {return parse_value_helper(parse_offset_datetime(loc) );} - case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} - case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} - case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} - case value_t::array : {return parse_value_helper(parse_array(loc) );} - case value_t::table : {return parse_value_helper(parse_inline_table(loc));} - default: - { - const auto msg = format_underline("toml::parse_value: " - "unknown token appeared", {{source_location(loc), "unknown"}}); - loc.reset(first); - return err(msg); - } - } -} - -inline result, region>, std::string> -parse_table_key(location& loc) -{ - if(auto token = lex_std_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_std_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `[`", - {{source_location(inner_loc), "should be `[`"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^----------- this whitespace - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_table_key: invalid key", - {{source_location(inner_loc), "not key"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^-- this whitespace - lex_ws::invoke(inner_loc); - const auto close = lex_std_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `]`", - {{source_location(inner_loc), "should be `]`"}}), - source_location(inner_loc)); - } - - // after [table.key], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline( - "toml::parse_table_key: newline required after [table.key]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -inline result, region>, std::string> -parse_array_table_key(location& loc) -{ - if(auto token = lex_array_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_array_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: no `[[`", - {{source_location(inner_loc), "should be `[[`"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: invalid key", - {{source_location(inner_loc), "not a key"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto close = lex_array_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `]]`", - {{source_location(inner_loc), "should be `]]`"}}), - source_location(inner_loc)); - } - - // after [[table.key]], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline("toml::" - "parse_array_table_key: newline required after [[table.key]]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_array_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -// parse table body (key-value pairs until the iter hits the next [tablekey]) -template -result -parse_ml_table(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - return ok(table_type{}); - } - - // XXX at lest one newline is needed. - using skip_line = repeat< - sequence, maybe, lex_newline>, at_least<1>>; - skip_line::invoke(loc); - lex_ws::invoke(loc); - - table_type tab; - while(loc.iter() != loc.end()) - { - lex_ws::invoke(loc); - const auto before = loc.iter(); - if(const auto tmp = parse_array_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - if(const auto tmp = parse_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - - if(const auto kv = parse_key_value_pair(loc)) - { - const auto& kvpair = kv.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - const auto inserted = - insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - return err(inserted.unwrap_err()); - } - } - else - { - return err(kv.unwrap_err()); - } - - // comment lines are skipped by the above function call. - // However, since the `skip_line` requires at least 1 newline, it fails - // if the file ends with ws and/or comment without newline. - // `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment` - // itself. To skip the last ws and/or comment, call lexers. - // It does not matter if these fails, so the return value is discarded. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - - // skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines - // and comments after the last key-value pairs are allowed. - const auto newline = skip_line::invoke(loc); - if(!newline && loc.iter() != loc.end()) - { - const auto before2 = loc.iter(); - lex_ws::invoke(loc); // skip whitespace - const auto msg = format_underline("toml::parse_table: " - "invalid line format", {{source_location(loc), concat_to_string( - "expected newline, but got '", show_char(*loc.iter()), "'.")}}); - loc.reset(before2); - return err(msg); - } - - // the skip_lines only matches with lines that includes newline. - // to skip the last line that includes comment and/or whitespace - // but no newline, call them one more time. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - } - return ok(tab); -} - -template -result parse_toml_file(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - // For empty files, return an empty table with an empty region (zero-length). - // Without the region, error messages would miss the filename. - return ok(value_type(table_type{}, region(loc, first, first), {})); - } - - // put the first line as a region of a file - // Here first != loc.end(), so taking std::next is okay - const region file(loc, first, std::next(loc.iter())); - - // The first successive comments that are separated from the first value - // by an empty line are for a file itself. - // ```toml - // # this is a comment for a file. - // - // key = "the first value" - // ``` - // ```toml - // # this is a comment for "the first value". - // key = "the first value" - // ``` - std::vector comments; - using lex_first_comments = sequence< - repeat, lex_comment, lex_newline>, at_least<1>>, - sequence, lex_newline> - >; - if(const auto token = lex_first_comments::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - while(inner_loc.iter() != inner_loc.end()) - { - maybe::invoke(inner_loc); // remove ws if exists - if(lex_newline::invoke(inner_loc)) - { - assert(inner_loc.iter() == inner_loc.end()); - break; // empty line found. - } - auto com = lex_comment::invoke(inner_loc).unwrap().str(); - com.erase(com.begin()); // remove # sign - comments.push_back(std::move(com)); - lex_newline::invoke(inner_loc); - } - } - - table_type data; - // root object is also a table, but without [tablename] - if(const auto tab = parse_ml_table(loc)) - { - data = std::move(tab.unwrap()); - } - else // failed (empty table is regarded as success in parse_ml_table) - { - return err(tab.unwrap_err()); - } - while(loc.iter() != loc.end()) - { - // here, the region of [table] is regarded as the table-key because - // the table body is normally too big and it is not so informative - // if the first key-value pair of the table is shown in the error - // message. - if(const auto tabkey = parse_array_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg, - /*is_array_of_table=*/ true); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - if(const auto tabkey = parse_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - return err(format_underline("toml::parse_toml_file: " - "unknown line appeared", {{source_location(loc), "unknown format"}})); - } - - return ok(Value(std::move(data), file, comments)); -} - -} // detail - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value -parse(std::istream& is, const std::string& fname = "unknown file") -{ - using value_type = basic_value; - - const auto beg = is.tellg(); - is.seekg(0, std::ios::end); - const auto end = is.tellg(); - const auto fsize = end - beg; - is.seekg(beg); - - // read whole file as a sequence of char - assert(fsize >= 0); - std::vector letters(static_cast(fsize)); - is.read(letters.data(), fsize); - - // append LF. - // Although TOML does not require LF at the EOF, to make parsing logic - // simpler, we "normalize" the content by adding LF if it does not exist. - // It also checks if the last char is CR, to avoid changing the meaning. - // This is not the *best* way to deal with the last character, but is a - // simple and quick fix. - if(!letters.empty() && letters.back() != '\n' && letters.back() != '\r') - { - letters.push_back('\n'); - } - - detail::location loc(std::move(fname), std::move(letters)); - - // skip BOM if exists. - // XXX component of BOM (like 0xEF) exceeds the representable range of - // signed char, so on some (actually, most) of the environment, these cannot - // be compared to char. However, since we are always out of luck, we need to - // check our chars are equivalent to BOM. To do this, first we need to - // convert char to unsigned char to guarantee the comparability. - if(loc.source()->size() >= 3) - { - std::array BOM; - std::memcpy(BOM.data(), loc.source()->data(), 3); - if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) - { - loc.advance(3); // BOM found. skip. - } - } - - const auto data = detail::parse_toml_file(loc); - if(!data) - { - throw syntax_error(data.unwrap_err(), source_location(loc)); - } - return data.unwrap(); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const std::string& fname) -{ - std::ifstream ifs(fname.c_str(), std::ios_base::binary); - if(!ifs.good()) - { - throw std::runtime_error("toml::parse: file open error -> " + fname); - } - return parse(ifs, fname); -} - -#ifdef TOML11_HAS_STD_FILESYSTEM -// This function just forwards `parse("filename.toml")` to std::string version -// to avoid the ambiguity in overload resolution. -// -// Both std::string and std::filesystem::path are convertible from const char*. -// Without this, both parse(std::string) and parse(std::filesystem::path) -// matches to parse("filename.toml"). This breaks the existing code. -// -// This function exactly matches to the invocation with c-string. -// So this function is preferred than others and the ambiguity disappears. -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const char* fname) -{ - return parse(std::string(fname)); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const std::filesystem::path& fpath) -{ - std::ifstream ifs(fpath, std::ios_base::binary); - if(!ifs.good()) - { - throw std::runtime_error("toml::parse: file open error -> " + - fpath.string()); - } - return parse(ifs, fpath.string()); -} -#endif // TOML11_HAS_STD_FILESYSTEM - -} // toml -#endif// TOML11_PARSER_HPP diff --git a/src/toml11/toml/region.hpp b/src/toml11/toml/region.hpp deleted file mode 100644 index 2e01e51d0..000000000 --- a/src/toml11/toml/region.hpp +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_REGION_HPP -#define TOML11_REGION_HPP -#include -#include -#include -#include -#include -#include -#include -#include "color.hpp" - -namespace toml -{ -namespace detail -{ - -// helper function to avoid std::string(0, 'c') or std::string(iter, iter) -template -std::string make_string(Iterator first, Iterator last) -{ - if(first == last) {return "";} - return std::string(first, last); -} -inline std::string make_string(std::size_t len, char c) -{ - if(len == 0) {return "";} - return std::string(len, c); -} - -// region_base is a base class of location and region that are defined below. -// it will be used to generate better error messages. -struct region_base -{ - region_base() = default; - virtual ~region_base() = default; - region_base(const region_base&) = default; - region_base(region_base&& ) = default; - region_base& operator=(const region_base&) = default; - region_base& operator=(region_base&& ) = default; - - virtual bool is_ok() const noexcept {return false;} - virtual char front() const noexcept {return '\0';} - - virtual std::string str() const {return std::string("unknown region");} - virtual std::string name() const {return std::string("unknown file");} - virtual std::string line() const {return std::string("unknown line");} - virtual std::string line_num() const {return std::string("?");} - - // length of the region - virtual std::size_t size() const noexcept {return 0;} - // number of characters in the line before the region - virtual std::size_t before() const noexcept {return 0;} - // number of characters in the line after the region - virtual std::size_t after() const noexcept {return 0;} - - virtual std::vector comments() const {return {};} - // ```toml - // # comment_before - // key = "value" # comment_inline - // ``` -}; - -// location represents a position in a container, which contains a file content. -// it can be considered as a region that contains only one character. -// -// it contains pointer to the file content and iterator that points the current -// location. -struct location final : public region_base -{ - using const_iterator = typename std::vector::const_iterator; - using difference_type = typename const_iterator::difference_type; - using source_ptr = std::shared_ptr>; - - location(std::string source_name, std::vector cont) - : source_(std::make_shared>(std::move(cont))), - line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) - {} - location(std::string source_name, const std::string& cont) - : source_(std::make_shared>(cont.begin(), cont.end())), - line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) - {} - - location(const location&) = default; - location(location&&) = default; - location& operator=(const location&) = default; - location& operator=(location&&) = default; - ~location() = default; - - bool is_ok() const noexcept override {return static_cast(source_);} - char front() const noexcept override {return *iter_;} - - // this const prohibits codes like `++(loc.iter())`. - const const_iterator iter() const noexcept {return iter_;} - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - - // XXX `location::line_num()` used to be implemented using `std::count` to - // count a number of '\n'. But with a long toml file (typically, 10k lines), - // it becomes intolerably slow because each time it generates error messages, - // it counts '\n' from thousands of characters. To workaround it, I decided - // to introduce `location::line_number_` member variable and synchronize it - // to the location changes the point to look. So an overload of `iter()` - // which returns mutable reference is removed and `advance()`, `retrace()` - // and `reset()` is added. - void advance(difference_type n = 1) noexcept - { - this->line_number_ += static_cast( - std::count(this->iter_, std::next(this->iter_, n), '\n')); - this->iter_ += n; - return; - } - void retrace(difference_type n = 1) noexcept - { - this->line_number_ -= static_cast( - std::count(std::prev(this->iter_, n), this->iter_, '\n')); - this->iter_ -= n; - return; - } - void reset(const_iterator rollback) noexcept - { - // since c++11, std::distance works in both ways for random-access - // iterators and returns a negative value if `first > last`. - if(0 <= std::distance(rollback, this->iter_)) // rollback < iter - { - this->line_number_ -= static_cast( - std::count(rollback, this->iter_, '\n')); - } - else // iter < rollback [[unlikely]] - { - this->line_number_ += static_cast( - std::count(this->iter_, rollback, '\n')); - } - this->iter_ = rollback; - return; - } - - std::string str() const override {return make_string(1, *this->iter());} - std::string name() const override {return source_name_;} - - std::string line_num() const override - { - return std::to_string(this->line_number_); - } - - std::string line() const override - { - return make_string(this->line_begin(), this->line_end()); - } - - const_iterator line_begin() const noexcept - { - using reverse_iterator = std::reverse_iterator; - return std::find(reverse_iterator(this->iter()), - reverse_iterator(this->begin()), '\n').base(); - } - const_iterator line_end() const noexcept - { - return std::find(this->iter(), this->end(), '\n'); - } - - // location is always points a character. so the size is 1. - std::size_t size() const noexcept override - { - return 1u; - } - std::size_t before() const noexcept override - { - const auto sz = std::distance(this->line_begin(), this->iter()); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->iter(), this->line_end()); - assert(sz >= 0); - return static_cast(sz); - } - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - private: - - source_ptr source_; - std::size_t line_number_; - std::string source_name_; - const_iterator iter_; -}; - -// region represents a range in a container, which contains a file content. -// -// it contains pointer to the file content and iterator that points the first -// and last location. -struct region final : public region_base -{ - using const_iterator = typename std::vector::const_iterator; - using source_ptr = std::shared_ptr>; - - // delete default constructor. source_ never be null. - region() = delete; - - explicit region(const location& loc) - : source_(loc.source()), source_name_(loc.name()), - first_(loc.iter()), last_(loc.iter()) - {} - explicit region(location&& loc) - : source_(loc.source()), source_name_(loc.name()), - first_(loc.iter()), last_(loc.iter()) - {} - - region(const location& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) - {} - region(location&& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) - {} - - region(const region&) = default; - region(region&&) = default; - region& operator=(const region&) = default; - region& operator=(region&&) = default; - ~region() = default; - - region& operator+=(const region& other) - { - // different regions cannot be concatenated - assert(this->begin() == other.begin() && this->end() == other.end() && - this->last_ == other.first_); - - this->last_ = other.last_; - return *this; - } - - bool is_ok() const noexcept override {return static_cast(source_);} - char front() const noexcept override {return *first_;} - - std::string str() const override {return make_string(first_, last_);} - std::string line() const override - { - if(this->contain_newline()) - { - return make_string(this->line_begin(), - std::find(this->line_begin(), this->last(), '\n')); - } - return make_string(this->line_begin(), this->line_end()); - } - std::string line_num() const override - { - return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); - } - - std::size_t size() const noexcept override - { - const auto sz = std::distance(first_, last_); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t before() const noexcept override - { - const auto sz = std::distance(this->line_begin(), this->first()); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->last(), this->line_end()); - assert(sz >= 0); - return static_cast(sz); - } - - bool contain_newline() const noexcept - { - return std::find(this->first(), this->last(), '\n') != this->last(); - } - - const_iterator line_begin() const noexcept - { - using reverse_iterator = std::reverse_iterator; - return std::find(reverse_iterator(this->first()), - reverse_iterator(this->begin()), '\n').base(); - } - const_iterator line_end() const noexcept - { - return std::find(this->last(), this->end(), '\n'); - } - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - const_iterator first() const noexcept {return first_;} - const_iterator last() const noexcept {return last_;} - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - std::string name() const override {return source_name_;} - - std::vector comments() const override - { - // assuming the current region (`*this`) points a value. - // ```toml - // a = "value" - // ^^^^^^^- this region - // ``` - using rev_iter = std::reverse_iterator; - - std::vector com{}; - { - // find comments just before the current region. - // ```toml - // # this should be collected. - // # this also. - // a = value # not this. - // ``` - - // # this is a comment for `a`, not array elements. - // a = [1, 2, 3, 4, 5] - if(this->first() == std::find_if(this->line_begin(), this->first(), - [](const char c) noexcept -> bool {return c == '[' || c == '{';})) - { - auto iter = this->line_begin(); // points the first character - while(iter != this->begin()) - { - iter = std::prev(iter); - - // range [line_start, iter) represents the previous line - const auto line_start = std::find( - rev_iter(iter), rev_iter(this->begin()), '\n').base(); - const auto comment_found = std::find(line_start, iter, '#'); - if(comment_found == iter) - { - break; // comment not found. - } - - // exclude the following case. - // > a = "foo" # comment // <-- this is not a comment for b but a. - // > b = "current value" - if(std::all_of(line_start, comment_found, - [](const char c) noexcept -> bool { - return c == ' ' || c == '\t'; - })) - { - // unwrap the first '#' by std::next. - auto s = make_string(std::next(comment_found), iter); - if(!s.empty() && s.back() == '\r') {s.pop_back();} - com.push_back(std::move(s)); - } - else - { - break; - } - iter = line_start; - } - } - } - - if(com.size() > 1) - { - std::reverse(com.begin(), com.end()); - } - - { - // find comments just after the current region. - // ```toml - // # not this. - // a = value # this one. - // a = [ # not this (technically difficult) - // - // ] # and this. - // ``` - // The reason why it's difficult is that it requires parsing in the - // following case. - // ```toml - // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`. - // # ... - // ] # this is apparently a comment for a. - // - // b = [ - // 3.14 ] # there is no way to add a comment to `3.14` currently. - // - // c = [ - // 3.14 # do this if you need a comment here. - // ] - // ``` - const auto comment_found = - std::find(this->last(), this->line_end(), '#'); - if(comment_found != this->line_end()) // '#' found - { - // table = {key = "value"} # what is this for? - // the above comment is not for "value", but {key="value"}. - if(comment_found == std::find_if(this->last(), comment_found, - [](const char c) noexcept -> bool { - return !(c == ' ' || c == '\t' || c == ','); - })) - { - // unwrap the first '#' by std::next. - auto s = make_string(std::next(comment_found), this->line_end()); - if(!s.empty() && s.back() == '\r') {s.pop_back();} - com.push_back(std::move(s)); - } - } - } - return com; - } - - private: - - source_ptr source_; - std::string source_name_; - const_iterator first_, last_; -}; - -} // detail -} // toml -#endif// TOML11_REGION_H diff --git a/src/toml11/toml/result.hpp b/src/toml11/toml/result.hpp deleted file mode 100644 index 77cd46c64..000000000 --- a/src/toml11/toml/result.hpp +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_RESULT_HPP -#define TOML11_RESULT_HPP -#include "traits.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace toml -{ - -template -struct success -{ - using value_type = T; - value_type value; - - explicit success(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit success(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit success(U&& v): value(std::forward(v)) {} - - template - explicit success(const success& v): value(v.value) {} - template - explicit success(success&& v): value(std::move(v.value)) {} - - ~success() = default; - success(const success&) = default; - success(success&&) = default; - success& operator=(const success&) = default; - success& operator=(success&&) = default; -}; - -template -struct failure -{ - using value_type = T; - value_type value; - - explicit failure(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit failure(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit failure(U&& v): value(std::forward(v)) {} - - template - explicit failure(const failure& v): value(v.value) {} - template - explicit failure(failure&& v): value(std::move(v.value)) {} - - ~failure() = default; - failure(const failure&) = default; - failure(failure&&) = default; - failure& operator=(const failure&) = default; - failure& operator=(failure&&) = default; -}; - -template -success::type>::type> -ok(T&& v) -{ - return success< - typename std::remove_cv::type>::type - >(std::forward(v)); -} -template -failure::type>::type> -err(T&& v) -{ - return failure< - typename std::remove_cv::type>::type - >(std::forward(v)); -} - -inline success ok(const char* literal) -{ - return success(std::string(literal)); -} -inline failure err(const char* literal) -{ - return failure(std::string(literal)); -} - - -template -struct result -{ - using value_type = T; - using error_type = E; - using success_type = success; - using failure_type = failure; - - result(const success_type& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(s); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - result(const failure_type& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(f); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - result(success_type&& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - result(failure_type&& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - - template - result(const success& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - template - result(const failure& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - template - result(success&& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - template - result(failure&& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - - result& operator=(const success_type& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(s); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - result& operator=(const failure_type& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(f); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - result& operator=(success_type&& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - result& operator=(failure_type&& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - - template - result& operator=(const success& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - template - result& operator=(const failure& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - template - result& operator=(success&& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - template - result& operator=(failure&& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - - ~result() noexcept {this->cleanup();} - - result(const result& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - result(result&& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - - template - result(const result& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - template - result(result&& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - - result& operator=(const result& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - result& operator=(result&& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - - template - result& operator=(const result& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - template - result& operator=(result&& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - - bool is_ok() const noexcept {return is_ok_;} - bool is_err() const noexcept {return !is_ok_;} - - operator bool() const noexcept {return is_ok_;} - - value_type& unwrap() & - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return this->succ.value; - } - value_type const& unwrap() const& - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return this->succ.value; - } - value_type&& unwrap() && - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return std::move(this->succ.value); - } - - value_type& unwrap_or(value_type& opt) & - { - if(is_err()) {return opt;} - return this->succ.value; - } - value_type const& unwrap_or(value_type const& opt) const& - { - if(is_err()) {return opt;} - return this->succ.value; - } - value_type unwrap_or(value_type opt) && - { - if(is_err()) {return opt;} - return this->succ.value; - } - - error_type& unwrap_err() & - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return this->fail.value; - } - error_type const& unwrap_err() const& - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return this->fail.value; - } - error_type&& unwrap_err() && - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return std::move(this->fail.value); - } - - value_type& as_ok() & noexcept {return this->succ.value;} - value_type const& as_ok() const& noexcept {return this->succ.value;} - value_type&& as_ok() && noexcept {return std::move(this->succ.value);} - - error_type& as_err() & noexcept {return this->fail.value;} - error_type const& as_err() const& noexcept {return this->fail.value;} - error_type&& as_err() && noexcept {return std::move(this->fail.value);} - - - // prerequisities - // F: T -> U - // retval: result - template - result, error_type> - map(F&& f) & - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, error_type> - map(F&& f) const& - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, error_type> - map(F&& f) && - { - if(this->is_ok()){return ok(f(std::move(this->as_ok())));} - return err(std::move(this->as_err())); - } - - // prerequisities - // F: E -> F - // retval: result - template - result> - map_err(F&& f) & - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - map_err(F&& f) const& - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - map_err(F&& f) && - { - if(this->is_err()){return err(f(std::move(this->as_err())));} - return ok(std::move(this->as_ok())); - } - - // prerequisities - // F: T -> U - // retval: U - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) & - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) const& - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) && - { - if(this->is_err()){return std::forward(opt);} - return f(std::move(this->as_ok())); - } - - // prerequisities - // F: E -> U - // retval: U - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) & - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) const& - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) && - { - if(this->is_ok()){return std::forward(opt);} - return f(std::move(this->as_err())); - } - - // prerequisities: - // F: func T -> U - // toml::err(error_type) should be convertible to U. - // normally, type U is another result and E is convertible to F - template - detail::return_type_of_t - and_then(F&& f) & - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - and_then(F&& f) const& - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - and_then(F&& f) && - { - if(this->is_ok()){return f(std::move(this->as_ok()));} - return err(std::move(this->as_err())); - } - - // prerequisities: - // F: func E -> U - // toml::ok(value_type) should be convertible to U. - // normally, type U is another result and T is convertible to S - template - detail::return_type_of_t - or_else(F&& f) & - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - or_else(F&& f) const& - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - or_else(F&& f) && - { - if(this->is_err()){return f(std::move(this->as_err()));} - return ok(std::move(this->as_ok())); - } - - // if *this is error, returns *this. otherwise, returns other. - result and_other(const result& other) const& - { - return this->is_err() ? *this : other; - } - result and_other(result&& other) && - { - return this->is_err() ? std::move(*this) : std::move(other); - } - - // if *this is okay, returns *this. otherwise, returns other. - result or_other(const result& other) const& - { - return this->is_ok() ? *this : other; - } - result or_other(result&& other) && - { - return this->is_ok() ? std::move(*this) : std::move(other); - } - - void swap(result& other) - { - result tmp(std::move(*this)); - *this = std::move(other); - other = std::move(tmp); - return ; - } - - private: - - static std::string format_error(std::exception const& excpt) - { - return std::string(excpt.what()); - } - template::value, std::nullptr_t>::type = nullptr> - static std::string format_error(U const& others) - { - std::ostringstream oss; oss << others; - return oss.str(); - } - - void cleanup() noexcept - { - if(this->is_ok_) {this->succ.~success_type();} - else {this->fail.~failure_type();} - return; - } - - private: - - bool is_ok_; - union - { - success_type succ; - failure_type fail; - }; -}; - -template -void swap(result& lhs, result& rhs) -{ - lhs.swap(rhs); - return; -} - -// this might be confusing because it eagerly evaluated, while in the other -// cases operator && and || are short-circuited. -// -// template -// inline result -// operator&&(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? rhs : lhs; -// } -// -// template -// inline result -// operator||(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? lhs : rhs; -// } - -// ---------------------------------------------------------------------------- -// re-use result as a optional with none_t - -namespace detail -{ -struct none_t {}; -inline bool operator==(const none_t&, const none_t&) noexcept {return true;} -inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} -inline bool operator< (const none_t&, const none_t&) noexcept {return false;} -inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} -inline bool operator> (const none_t&, const none_t&) noexcept {return false;} -inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const none_t&) -{ - os << "none"; - return os; -} -inline failure none() noexcept {return failure{none_t{}};} -} // detail -} // toml11 -#endif// TOML11_RESULT_H diff --git a/src/toml11/toml/serializer.hpp b/src/toml11/toml/serializer.hpp deleted file mode 100644 index 88ae775a8..000000000 --- a/src/toml11/toml/serializer.hpp +++ /dev/null @@ -1,922 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SERIALIZER_HPP -#define TOML11_SERIALIZER_HPP -#include -#include - -#include - -#include "lexer.hpp" -#include "value.hpp" - -namespace toml -{ - -// This function serialize a key. It checks a string is a bare key and -// escapes special characters if the string is not compatible to a bare key. -// ```cpp -// std::string k("non.bare.key"); // the key itself includes `.`s. -// std::string formatted = toml::format_key(k); -// assert(formatted == "\"non.bare.key\""); -// ``` -// -// This function is exposed to make it easy to write a user-defined serializer. -// Since toml restricts characters available in a bare key, generally a string -// should be escaped. But checking whether a string needs to be surrounded by -// a `"` and escaping some special character is boring. -template -std::basic_string -format_key(const std::basic_string& k) -{ - if(k.empty()) - { - return std::string("\"\""); - } - - // check the key can be a bare (unquoted) key - detail::location loc(k, std::vector(k.begin(), k.end())); - detail::lex_unquoted_key::invoke(loc); - if(loc.iter() == loc.end()) - { - return k; // all the tokens are consumed. the key is unquoted-key. - } - - //if it includes special characters, then format it in a "quoted" key. - std::basic_string serialized("\""); - for(const char c : k) - { - switch(c) - { - case '\\': {serialized += "\\\\"; break;} - case '\"': {serialized += "\\\""; break;} - case '\b': {serialized += "\\b"; break;} - case '\t': {serialized += "\\t"; break;} - case '\f': {serialized += "\\f"; break;} - case '\n': {serialized += "\\n"; break;} - case '\r': {serialized += "\\r"; break;} - default : {serialized += c; break;} - } - } - serialized += "\""; - return serialized; -} - -template -std::basic_string -format_keys(const std::vector>& keys) -{ - if(keys.empty()) - { - return std::string("\"\""); - } - - std::basic_string serialized; - for(const auto& ky : keys) - { - serialized += format_key(ky); - serialized += charT('.'); - } - serialized.pop_back(); // remove the last dot '.' - return serialized; -} - -template -struct serializer -{ - static_assert(detail::is_basic_value::value, - "toml::serializer is for toml::value and its variants, " - "toml::basic_value<...>."); - - using value_type = Value; - using key_type = typename value_type::key_type ; - using comment_type = typename value_type::comment_type ; - using boolean_type = typename value_type::boolean_type ; - using integer_type = typename value_type::integer_type ; - using floating_type = typename value_type::floating_type ; - using string_type = typename value_type::string_type ; - using local_time_type = typename value_type::local_time_type ; - using local_date_type = typename value_type::local_date_type ; - using local_datetime_type = typename value_type::local_datetime_type ; - using offset_datetime_type = typename value_type::offset_datetime_type; - using array_type = typename value_type::array_type ; - using table_type = typename value_type::table_type ; - - serializer(const std::size_t w = 80u, - const int float_prec = std::numeric_limits::max_digits10, - const bool can_be_inlined = false, - const bool no_comment = false, - std::vector ks = {}, - const bool value_has_comment = false) - : can_be_inlined_(can_be_inlined), no_comment_(no_comment), - value_has_comment_(value_has_comment && !no_comment), - float_prec_(float_prec), width_(w), keys_(std::move(ks)) - {} - ~serializer() = default; - - std::string operator()(const boolean_type& b) const - { - return b ? "true" : "false"; - } - std::string operator()(const integer_type i) const - { - return std::to_string(i); - } - std::string operator()(const floating_type f) const - { - if(std::isnan(f)) - { - if(std::signbit(f)) - { - return std::string("-nan"); - } - else - { - return std::string("nan"); - } - } - else if(!std::isfinite(f)) - { - if(std::signbit(f)) - { - return std::string("-inf"); - } - else - { - return std::string("inf"); - } - } - - const auto fmt = "%.*g"; - const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); - // +1 for null character(\0) - std::vector buf(static_cast(bsz + 1), '\0'); - std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); - - std::string token(buf.begin(), std::prev(buf.end())); - if(!token.empty() && token.back() == '.') // 1. => 1.0 - { - token += '0'; - } - - const auto e = std::find_if( - token.cbegin(), token.cend(), [](const char c) noexcept -> bool { - return c == 'e' || c == 'E'; - }); - const auto has_exponent = (token.cend() != e); - const auto has_fraction = (token.cend() != std::find( - token.cbegin(), token.cend(), '.')); - - if(!has_exponent && !has_fraction) - { - // the resulting value does not have any float specific part! - token += ".0"; - } - return token; - } - std::string operator()(const string_type& s) const - { - if(s.kind == string_t::basic) - { - if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) && - this->width_ != (std::numeric_limits::max)()) - { - // if linefeed or double-quote is contained, - // make it multiline basic string. - const auto escaped = this->escape_ml_basic_string(s.str); - std::string open("\"\"\""); - std::string close("\"\"\""); - if(escaped.find('\n') != std::string::npos || - this->width_ < escaped.size() + 6) - { - // if the string body contains newline or is enough long, - // add newlines after and before delimiters. - open += "\n"; - close = std::string("\\\n") + close; - } - return open + escaped + close; - } - - // no linefeed. try to make it oneline-string. - std::string oneline = this->escape_basic_string(s.str); - if(oneline.size() + 2 < width_ || width_ < 2) - { - const std::string quote("\""); - return quote + oneline + quote; - } - - // the line is too long compared to the specified width. - // split it into multiple lines. - std::string token("\"\"\"\n"); - while(!oneline.empty()) - { - if(oneline.size() < width_) - { - token += oneline; - oneline.clear(); - } - else if(oneline.at(width_-2) == '\\') - { - token += oneline.substr(0, width_-2); - token += "\\\n"; - oneline.erase(0, width_-2); - } - else - { - token += oneline.substr(0, width_-1); - token += "\\\n"; - oneline.erase(0, width_-1); - } - } - return token + std::string("\\\n\"\"\""); - } - else // the string `s` is literal-string. - { - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) - { - std::string open("'''"); - if(this->width_ + 6 < s.str.size()) - { - open += '\n'; // the first newline is ignored by TOML spec - } - const std::string close("'''"); - return open + s.str + close; - } - else - { - const std::string quote("'"); - return quote + s.str + quote; - } - } - } - - std::string operator()(const local_date_type& d) const - { - std::ostringstream oss; - oss << d; - return oss.str(); - } - std::string operator()(const local_time_type& t) const - { - std::ostringstream oss; - oss << t; - return oss.str(); - } - std::string operator()(const local_datetime_type& dt) const - { - std::ostringstream oss; - oss << dt; - return oss.str(); - } - std::string operator()(const offset_datetime_type& odt) const - { - std::ostringstream oss; - oss << odt; - return oss.str(); - } - - std::string operator()(const array_type& v) const - { - if(v.empty()) - { - return std::string("[]"); - } - if(this->is_array_of_tables(v)) - { - return make_array_of_tables(v); - } - - // not an array of tables. normal array. - // first, try to make it inline if none of the elements have a comment. - if( ! this->has_comment_inside(v)) - { - const auto inl = this->make_inline_array(v); - if(inl.size() < this->width_ && - std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend()) - { - return inl; - } - } - - // if the length exceeds this->width_, print multiline array. - // key = [ - // # ... - // 42, - // ... - // ] - std::string token; - std::string current_line; - token += "[\n"; - for(const auto& item : v) - { - if( ! item.comments().empty() && !no_comment_) - { - // if comment exists, the element must be the only element in the line. - // e.g. the following is not allowed. - // ```toml - // array = [ - // # comment for what? - // 1, 2, 3, 4, 5 - // ] - // ``` - if(!current_line.empty()) - { - if(current_line.back() != '\n') - { - current_line += '\n'; - } - token += current_line; - current_line.clear(); - } - for(const auto& c : item.comments()) - { - token += '#'; - token += c; - token += '\n'; - } - token += toml::visit(*this, item); - if(!token.empty() && token.back() == '\n') {token.pop_back();} - token += ",\n"; - continue; - } - std::string next_elem; - if(item.is_table()) - { - serializer ser(*this); - ser.can_be_inlined_ = true; - ser.width_ = (std::numeric_limits::max)(); - next_elem += toml::visit(ser, item); - } - else - { - next_elem += toml::visit(*this, item); - } - - // comma before newline. - if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} - - // if current line does not exceeds the width limit, continue. - if(current_line.size() + next_elem.size() + 1 < this->width_) - { - current_line += next_elem; - current_line += ','; - } - else if(current_line.empty()) - { - // if current line was empty, force put the next_elem because - // next_elem is not splittable - token += next_elem; - token += ",\n"; - // current_line is kept empty - } - else // reset current_line - { - assert(current_line.back() == ','); - token += current_line; - token += '\n'; - current_line = next_elem; - current_line += ','; - } - } - if(!current_line.empty()) - { - if(!current_line.empty() && current_line.back() != '\n') - { - current_line += '\n'; - } - token += current_line; - } - token += "]\n"; - return token; - } - - // templatize for any table-like container - std::string operator()(const table_type& v) const - { - // if an element has a comment, then it can't be inlined. - // table = {# how can we write a comment for this? key = "value"} - if(this->can_be_inlined_ && !(this->has_comment_inside(v))) - { - std::string token; - if(!this->keys_.empty()) - { - token += format_key(this->keys_.back()); - token += " = "; - } - token += this->make_inline_table(v); - if(token.size() < this->width_ && - token.end() == std::find(token.begin(), token.end(), '\n')) - { - return token; - } - } - - std::string token; - if(!keys_.empty()) - { - token += '['; - token += format_keys(keys_); - token += "]\n"; - } - token += this->make_multiline_table(v); - return token; - } - - private: - - std::string escape_basic_string(const std::string& s) const - { - //XXX assuming `s` is a valid utf-8 sequence. - std::string retval; - for(const char c : s) - { - switch(c) - { - case '\\': {retval += "\\\\"; break;} - case '\"': {retval += "\\\""; break;} - case '\b': {retval += "\\b"; break;} - case '\t': {retval += "\\t"; break;} - case '\f': {retval += "\\f"; break;} - case '\n': {retval += "\\n"; break;} - case '\r': {retval += "\\r"; break;} - default : - { - if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) - { - retval += "\\u00"; - retval += char(48 + (c / 16)); - retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); - } - else - { - retval += c; - } - } - } - } - return retval; - } - - std::string escape_ml_basic_string(const std::string& s) const - { - std::string retval; - for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i) - { - switch(*i) - { - case '\\': {retval += "\\\\"; break;} - // One or two consecutive "s are allowed. - // Later we will check there are no three consecutive "s. - // case '\"': {retval += "\\\""; break;} - case '\b': {retval += "\\b"; break;} - case '\t': {retval += "\\t"; break;} - case '\f': {retval += "\\f"; break;} - case '\n': {retval += "\n"; break;} - case '\r': - { - if(std::next(i) != e && *std::next(i) == '\n') - { - retval += "\r\n"; - ++i; - } - else - { - retval += "\\r"; - } - break; - } - default : - { - const auto c = *i; - if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) - { - retval += "\\u00"; - retval += char(48 + (c / 16)); - retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); - } - else - { - retval += c; - } - } - - } - } - // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. - // 3 consecutive `"`s are considered as a closing delimiter. - // We need to check if there are 3 or more consecutive `"`s and insert - // backslash to break them down into several short `"`s like the `str6` - // in the following example. - // ```toml - // str4 = """Here are two quotation marks: "". Simple enough.""" - // # str5 = """Here are three quotation marks: """.""" # INVALID - // str5 = """Here are three quotation marks: ""\".""" - // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" - // ``` - auto found_3_quotes = retval.find("\"\"\""); - while(found_3_quotes != std::string::npos) - { - retval.replace(found_3_quotes, 3, "\"\"\\\""); - found_3_quotes = retval.find("\"\"\""); - } - return retval; - } - - // if an element of a table or an array has a comment, it cannot be inlined. - bool has_comment_inside(const array_type& a) const noexcept - { - // if no_comment is set, comments would not be written. - if(this->no_comment_) {return false;} - - for(const auto& v : a) - { - if(!v.comments().empty()) {return true;} - } - return false; - } - bool has_comment_inside(const table_type& t) const noexcept - { - // if no_comment is set, comments would not be written. - if(this->no_comment_) {return false;} - - for(const auto& kv : t) - { - if(!kv.second.comments().empty()) {return true;} - } - return false; - } - - std::string make_inline_array(const array_type& v) const - { - assert(!has_comment_inside(v)); - std::string token; - token += '['; - bool is_first = true; - for(const auto& item : v) - { - if(is_first) {is_first = false;} else {token += ',';} - token += visit(serializer( - (std::numeric_limits::max)(), this->float_prec_, - /* inlined */ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !item.comments().empty()), item); - } - token += ']'; - return token; - } - - std::string make_inline_table(const table_type& v) const - { - assert(!has_comment_inside(v)); - assert(this->can_be_inlined_); - std::string token; - token += '{'; - bool is_first = true; - for(const auto& kv : v) - { - // in inline tables, trailing comma is not allowed (toml-lang #569). - if(is_first) {is_first = false;} else {token += ',';} - token += format_key(kv.first); - token += '='; - token += visit(serializer( - (std::numeric_limits::max)(), this->float_prec_, - /* inlined */ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - } - token += '}'; - return token; - } - - std::string make_multiline_table(const table_type& v) const - { - std::string token; - - // print non-table elements first. - // ```toml - // [foo] # a table we're writing now here - // key = "value" # <- non-table element, "key" - // # ... - // [foo.bar] # <- table element, "bar" - // ``` - // because after printing [foo.bar], the remaining non-table values will - // be assigned into [foo.bar], not [foo]. Those values should be printed - // earlier. - for(const auto& kv : v) - { - if(kv.second.is_table() || is_array_of_tables(kv.second)) - { - continue; - } - - token += write_comments(kv.second); - - const auto key_and_sep = format_key(kv.first) + " = "; - const auto residual_width = (this->width_ > key_and_sep.size()) ? - this->width_ - key_and_sep.size() : 0; - token += key_and_sep; - token += visit(serializer(residual_width, this->float_prec_, - /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - - if(token.back() != '\n') - { - token += '\n'; - } - } - - // normal tables / array of tables - - // after multiline table appeared, the other tables cannot be inline - // because the table would be assigned into the table. - // [foo] - // ... - // bar = {...} # <- bar will be a member of [foo]. - bool multiline_table_printed = false; - for(const auto& kv : v) - { - if(!kv.second.is_table() && !is_array_of_tables(kv.second)) - { - continue; // other stuff are already serialized. skip them. - } - - std::vector ks(this->keys_); - ks.push_back(kv.first); - - auto tmp = visit(serializer(this->width_, this->float_prec_, - !multiline_table_printed, this->no_comment_, ks, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - - // If it is the first time to print a multi-line table, it would be - // helpful to separate normal key-value pair and subtables by a - // newline. - // (this checks if the current key-value pair contains newlines. - // but it is not perfect because multi-line string can also contain - // a newline. in such a case, an empty line will be written) TODO - if((!multiline_table_printed) && - std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) - { - multiline_table_printed = true; - token += '\n'; // separate key-value pairs and subtables - - token += write_comments(kv.second); - token += tmp; - - // care about recursive tables (all tables in each level prints - // newline and there will be a full of newlines) - if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && - tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) - { - token += '\n'; - } - } - else - { - token += write_comments(kv.second); - token += tmp; - token += '\n'; - } - } - return token; - } - - std::string make_array_of_tables(const array_type& v) const - { - // if it's not inlined, we need to add `[[table.key]]`. - // but if it can be inlined, we can format it as the following. - // ``` - // table.key = [ - // {...}, - // # comment - // {...}, - // ] - // ``` - // This function checks if inlinization is possible or not, and then - // format the array-of-tables in a proper way. - // - // Note about comments: - // - // If the array itself has a comment (value_has_comment_ == true), we - // should try to make it inline. - // ```toml - // # comment about array - // array = [ - // # comment about table element - // {of = "table"} - // ] - // ``` - // If it is formatted as a multiline table, the two comments becomes - // indistinguishable. - // ```toml - // # comment about array - // # comment about table element - // [[array]] - // of = "table" - // ``` - // So we need to try to make it inline, and it force-inlines regardless - // of the line width limit. - // It may fail if the element of a table has comment. In that case, - // the array-of-tables will be formatted as a multiline table. - if(this->can_be_inlined_ || this->value_has_comment_) - { - std::string token; - if(!keys_.empty()) - { - token += format_key(keys_.back()); - token += " = "; - } - - bool failed = false; - token += "[\n"; - for(const auto& item : v) - { - // if an element of the table has a comment, the table - // cannot be inlined. - if(this->has_comment_inside(item.as_table())) - { - failed = true; - break; - } - // write comments for the table itself - token += write_comments(item); - - const auto t = this->make_inline_table(item.as_table()); - - if(t.size() + 1 > width_ || // +1 for the last comma {...}, - std::find(t.cbegin(), t.cend(), '\n') != t.cend()) - { - // if the value itself has a comment, ignore the line width limit - if( ! this->value_has_comment_) - { - failed = true; - break; - } - } - token += t; - token += ",\n"; - } - - if( ! failed) - { - token += "]\n"; - return token; - } - // if failed, serialize them as [[array.of.tables]]. - } - - std::string token; - for(const auto& item : v) - { - token += write_comments(item); - token += "[["; - token += format_keys(keys_); - token += "]]\n"; - token += this->make_multiline_table(item.as_table()); - } - return token; - } - - std::string write_comments(const value_type& v) const - { - std::string retval; - if(this->no_comment_) {return retval;} - - for(const auto& c : v.comments()) - { - retval += '#'; - retval += c; - retval += '\n'; - } - return retval; - } - - bool is_array_of_tables(const value_type& v) const - { - if(!v.is_array() || v.as_array().empty()) {return false;} - return is_array_of_tables(v.as_array()); - } - bool is_array_of_tables(const array_type& v) const - { - // Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to - // check all the element in an array to check if the array is an array - // of tables. - return std::all_of(v.begin(), v.end(), [](const value_type& elem) { - return elem.is_table(); - }); - } - - private: - - bool can_be_inlined_; - bool no_comment_; - bool value_has_comment_; - int float_prec_; - std::size_t width_; - std::vector keys_; -}; - -template class M, template class V> -std::string -format(const basic_value& v, std::size_t w = 80u, - int fprec = std::numeric_limits::max_digits10, - bool no_comment = false, bool force_inline = false) -{ - using value_type = basic_value; - // if value is a table, it is considered to be a root object. - // the root object can't be an inline table. - if(v.is_table()) - { - std::ostringstream oss; - if(!v.comments().empty()) - { - oss << v.comments(); - oss << '\n'; // to split the file comment from the first element - } - const auto serialized = visit(serializer(w, fprec, false, no_comment), v); - oss << serialized; - return oss.str(); - } - return visit(serializer(w, fprec, force_inline), v); -} - -namespace detail -{ -template -int comment_index(std::basic_ostream&) -{ - static const int index = std::ios_base::xalloc(); - return index; -} -} // detail - -template -std::basic_ostream& -nocomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 1; - return os; -} - -template -std::basic_ostream& -showcomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 0; - return os; -} - -template class M, template class V> -std::basic_ostream& -operator<<(std::basic_ostream& os, const basic_value& v) -{ - using value_type = basic_value; - - // get status of std::setw(). - const auto w = static_cast(os.width()); - const int fprec = static_cast(os.precision()); - os.width(0); - - // by default, iword is initialized by 0. And by default, toml11 outputs - // comments. So `0` means showcomment. 1 means nocommnet. - const bool no_comment = (1 == os.iword(detail::comment_index(os))); - - if(!no_comment && v.is_table() && !v.comments().empty()) - { - os << v.comments(); - os << '\n'; // to split the file comment from the first element - } - // the root object can't be an inline table. so pass `false`. - const auto serialized = visit(serializer(w, fprec, no_comment, false), v); - os << serialized; - - // if v is a non-table value, and has only one comment, then - // put a comment just after a value. in the following way. - // - // ```toml - // key = "value" # comment. - // ``` - // - // Since the top-level toml object is a table, one who want to put a - // non-table toml value must use this in a following way. - // - // ```cpp - // toml::value v; - // std::cout << "user-defined-key = " << v << std::endl; - // ``` - // - // In this case, it is impossible to put comments before key-value pair. - // The only way to preserve comments is to put all of them after a value. - if(!no_comment && !v.is_table() && !v.comments().empty()) - { - os << " #"; - for(const auto& c : v.comments()) {os << c;} - } - return os; -} - -} // toml -#endif// TOML11_SERIALIZER_HPP diff --git a/src/toml11/toml/source_location.hpp b/src/toml11/toml/source_location.hpp deleted file mode 100644 index fa175b5b4..000000000 --- a/src/toml11/toml/source_location.hpp +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SOURCE_LOCATION_HPP -#define TOML11_SOURCE_LOCATION_HPP -#include -#include - -#include "region.hpp" - -namespace toml -{ - -// A struct to contain location in a toml file. -// The interface imitates std::experimental::source_location, -// but not completely the same. -// -// It would be constructed by toml::value. It can be used to generate -// user-defined error messages. -// -// - std::uint_least32_t line() const noexcept -// - returns the line number where the region is on. -// - std::uint_least32_t column() const noexcept -// - returns the column number where the region starts. -// - std::uint_least32_t region() const noexcept -// - returns the size of the region. -// -// +-- line() +-- region of interest (region() == 9) -// v .---+---. -// 12 | value = "foo bar" -// ^ -// +-- column() -// -// - std::string const& file_name() const noexcept; -// - name of the file. -// - std::string const& line_str() const noexcept; -// - the whole line that contains the region of interest. -// -struct source_location -{ - public: - - source_location() - : line_num_(1), column_num_(1), region_size_(1), - file_name_("unknown file"), line_str_("") - {} - - explicit source_location(const detail::region_base* reg) - : line_num_(1), column_num_(1), region_size_(1), - file_name_("unknown file"), line_str_("") - { - if(reg) - { - if(reg->line_num() != detail::region_base().line_num()) - { - line_num_ = static_cast( - std::stoul(reg->line_num())); - } - column_num_ = static_cast(reg->before() + 1); - region_size_ = static_cast(reg->size()); - file_name_ = reg->name(); - line_str_ = reg->line(); - } - } - - explicit source_location(const detail::region& reg) - : line_num_(static_cast(std::stoul(reg.line_num()))), - column_num_(static_cast(reg.before() + 1)), - region_size_(static_cast(reg.size())), - file_name_(reg.name()), - line_str_ (reg.line()) - {} - explicit source_location(const detail::location& loc) - : line_num_(static_cast(std::stoul(loc.line_num()))), - column_num_(static_cast(loc.before() + 1)), - region_size_(static_cast(loc.size())), - file_name_(loc.name()), - line_str_ (loc.line()) - {} - - ~source_location() = default; - source_location(source_location const&) = default; - source_location(source_location &&) = default; - source_location& operator=(source_location const&) = default; - source_location& operator=(source_location &&) = default; - - std::uint_least32_t line() const noexcept {return line_num_;} - std::uint_least32_t column() const noexcept {return column_num_;} - std::uint_least32_t region() const noexcept {return region_size_;} - - std::string const& file_name() const noexcept {return file_name_;} - std::string const& line_str() const noexcept {return line_str_;} - - private: - - std::uint_least32_t line_num_; - std::uint_least32_t column_num_; - std::uint_least32_t region_size_; - std::string file_name_; - std::string line_str_; -}; - -namespace detail -{ - -// internal error message generation. -inline std::string format_underline(const std::string& message, - const std::vector>& loc_com, - const std::vector& helps = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - std::size_t line_num_width = 0; - for(const auto& lc : loc_com) - { - std::uint_least32_t line = lc.first.line(); - std::size_t digit = 0; - while(line != 0) - { - line /= 10; - digit += 1; - } - line_num_width = (std::max)(line_num_width, digit); - } - // 1 is the minimum width - line_num_width = std::max(line_num_width, 1); - - std::ostringstream retval; - - if(colorize) - { - retval << color::colorize; // turn on ANSI color - } - - // XXX - // Here, before `colorize` support, it does not output `[error]` prefix - // automatically. So some user may output it manually and this change may - // duplicate the prefix. To avoid it, check the first 7 characters and - // if it is "[error]", it removes that part from the message shown. - if(message.size() > 7 && message.substr(0, 7) == "[error]") - { - retval << color::bold << color::red << "[error]" << color::reset - << color::bold << message.substr(7) << color::reset << '\n'; - } - else - { - retval << color::bold << color::red << "[error] " << color::reset - << color::bold << message << color::reset << '\n'; - } - - const auto format_one_location = [line_num_width] - (std::ostringstream& oss, - const source_location& loc, const std::string& comment) -> void - { - oss << ' ' << color::bold << color::blue - << std::setw(static_cast(line_num_width)) - << std::right << loc.line() << " | " << color::reset - << loc.line_str() << '\n'; - - oss << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " | " << color::reset - << make_string(loc.column()-1 /*1-origin*/, ' '); - - if(loc.region() == 1) - { - // invalid - // ^------ - oss << color::bold << color::red << "^---" << color::reset; - } - else - { - // invalid - // ~~~~~~~ - const auto underline_len = (std::min)( - static_cast(loc.region()), loc.line_str().size()); - oss << color::bold << color::red - << make_string(underline_len, '~') << color::reset; - } - oss << ' '; - oss << comment; - return; - }; - - assert(!loc_com.empty()); - - // --> example.toml - // | - retval << color::bold << color::blue << " --> " << color::reset - << loc_com.front().first.file_name() << '\n'; - retval << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " |\n" << color::reset; - // 1 | key value - // | ^--- missing = - format_one_location(retval, loc_com.front().first, loc_com.front().second); - - // process the rest of the locations - for(std::size_t i=1; i filename.toml" again - { - retval << color::bold << color::blue << " --> " << color::reset - << curr.first.file_name() << '\n'; - retval << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " |\n" << color::reset; - } - - format_one_location(retval, curr.first, curr.second); - } - - if(!helps.empty()) - { - retval << '\n'; - retval << make_string(line_num_width + 1, ' '); - retval << color::bold << color::blue << " |" << color::reset; - for(const auto& help : helps) - { - retval << color::bold << "\nHint: " << color::reset; - retval << help; - } - } - return retval.str(); -} - -} // detail -} // toml -#endif// TOML11_SOURCE_LOCATION_HPP diff --git a/src/toml11/toml/storage.hpp b/src/toml11/toml/storage.hpp deleted file mode 100644 index 202f9035f..000000000 --- a/src/toml11/toml/storage.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_STORAGE_HPP -#define TOML11_STORAGE_HPP -#include "utility.hpp" - -namespace toml -{ -namespace detail -{ - -// this contains pointer and deep-copy the content if copied. -// to avoid recursive pointer. -template -struct storage -{ - using value_type = T; - - explicit storage(value_type const& v): ptr(toml::make_unique(v)) {} - explicit storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} - ~storage() = default; - storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} - storage& operator=(const storage& rhs) - { - this->ptr = toml::make_unique(*rhs.ptr); - return *this; - } - storage(storage&&) = default; - storage& operator=(storage&&) = default; - - bool is_ok() const noexcept {return static_cast(ptr);} - - value_type& value() & noexcept {return *ptr;} - value_type const& value() const& noexcept {return *ptr;} - value_type&& value() && noexcept {return std::move(*ptr);} - - private: - std::unique_ptr ptr; -}; - -} // detail -} // toml -#endif// TOML11_STORAGE_HPP diff --git a/src/toml11/toml/string.hpp b/src/toml11/toml/string.hpp deleted file mode 100644 index 5136d8c56..000000000 --- a/src/toml11/toml/string.hpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_STRING_HPP -#define TOML11_STRING_HPP -#include - -#include -#include - -#if __cplusplus >= 201703L -#if __has_include() -#define TOML11_USING_STRING_VIEW 1 -#include -#endif -#endif - -namespace toml -{ - -enum class string_t : std::uint8_t -{ - basic = 0, - literal = 1, -}; - -struct string -{ - string() = default; - ~string() = default; - string(const string& s) = default; - string(string&& s) = default; - string& operator=(const string& s) = default; - string& operator=(string&& s) = default; - - string(const std::string& s): kind(string_t::basic), str(s){} - string(const std::string& s, string_t k): kind(k), str(s){} - string(const char* s): kind(string_t::basic), str(s){} - string(const char* s, string_t k): kind(k), str(s){} - - string(std::string&& s): kind(string_t::basic), str(std::move(s)){} - string(std::string&& s, string_t k): kind(k), str(std::move(s)){} - - string& operator=(const std::string& s) - {kind = string_t::basic; str = s; return *this;} - string& operator=(std::string&& s) - {kind = string_t::basic; str = std::move(s); return *this;} - - operator std::string& () & noexcept {return str;} - operator std::string const& () const& noexcept {return str;} - operator std::string&& () && noexcept {return std::move(str);} - - string& operator+=(const char* rhs) {str += rhs; return *this;} - string& operator+=(const char rhs) {str += rhs; return *this;} - string& operator+=(const std::string& rhs) {str += rhs; return *this;} - string& operator+=(const string& rhs) {str += rhs.str; return *this;} - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 - explicit string(std::string_view s): kind(string_t::basic), str(s){} - string(std::string_view s, string_t k): kind(k), str(s){} - - string& operator=(std::string_view s) - {kind = string_t::basic; str = s; return *this;} - - explicit operator std::string_view() const noexcept - {return std::string_view(str);} - - string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} -#endif - - string_t kind; - std::string str; -}; - -inline bool operator==(const string& lhs, const string& rhs) -{ - return lhs.kind == rhs.kind && lhs.str == rhs.str; -} -inline bool operator!=(const string& lhs, const string& rhs) -{ - return !(lhs == rhs); -} -inline bool operator<(const string& lhs, const string& rhs) -{ - return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); -} -inline bool operator>(const string& lhs, const string& rhs) -{ - return rhs < lhs; -} -inline bool operator<=(const string& lhs, const string& rhs) -{ - return !(rhs < lhs); -} -inline bool operator>=(const string& lhs, const string& rhs) -{ - return !(lhs < rhs); -} - -inline bool -operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} -inline bool -operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} -inline bool -operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} -inline bool -operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} -inline bool -operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} -inline bool -operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} - -inline bool -operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} -inline bool -operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} -inline bool -operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} -inline bool -operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} -inline bool -operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} -inline bool -operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} - -inline bool -operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} -inline bool -operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} -inline bool -operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} -inline bool -operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} -inline bool -operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} -inline bool -operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} - -inline bool -operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} -inline bool -operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} -inline bool -operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} -inline bool -operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} -inline bool -operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} -inline bool -operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const string& s) -{ - if(s.kind == string_t::basic) - { - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) - { - // it contains newline. make it multiline string. - os << "\"\"\"\n"; - for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i) - { - switch(*i) - { - case '\\': {os << "\\\\"; break;} - case '\"': {os << "\\\""; break;} - case '\b': {os << "\\b"; break;} - case '\t': {os << "\\t"; break;} - case '\f': {os << "\\f"; break;} - case '\n': {os << '\n'; break;} - case '\r': - { - // since it is a multiline string, - // CRLF is not needed to be escaped. - if(std::next(i) != e && *std::next(i) == '\n') - { - os << "\r\n"; - ++i; - } - else - { - os << "\\r"; - } - break; - } - default: {os << *i; break;} - } - } - os << "\\\n\"\"\""; - return os; - } - // no newline. make it inline. - os << "\""; - for(const auto c : s.str) - { - switch(c) - { - case '\\': {os << "\\\\"; break;} - case '\"': {os << "\\\""; break;} - case '\b': {os << "\\b"; break;} - case '\t': {os << "\\t"; break;} - case '\f': {os << "\\f"; break;} - case '\n': {os << "\\n"; break;} - case '\r': {os << "\\r"; break;} - default : {os << c; break;} - } - } - os << "\""; - return os; - } - // the string `s` is literal-string. - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) - { - // contains newline or single quote. make it multiline. - os << "'''\n" << s.str << "'''"; - return os; - } - // normal literal string - os << '\'' << s.str << '\''; - return os; -} - -} // toml -#endif// TOML11_STRING_H diff --git a/src/toml11/toml/traits.hpp b/src/toml11/toml/traits.hpp deleted file mode 100644 index 5495c93b2..000000000 --- a/src/toml11/toml/traits.hpp +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TRAITS_HPP -#define TOML11_TRAITS_HPP - -#include "from.hpp" -#include "into.hpp" - -#include -#include -#include -#include -#include -#include - -#if __cplusplus >= 201703L -#if __has_include() -#include -#endif // has_include() -#endif // cplusplus >= C++17 - -namespace toml -{ -template class T, template class A> -class basic_value; - -namespace detail -{ -// --------------------------------------------------------------------------- -// check whether type T is a kind of container/map class - -struct has_iterator_impl -{ - template static std::true_type check(typename T::iterator*); - template static std::false_type check(...); -}; -struct has_value_type_impl -{ - template static std::true_type check(typename T::value_type*); - template static std::false_type check(...); -}; -struct has_key_type_impl -{ - template static std::true_type check(typename T::key_type*); - template static std::false_type check(...); -}; -struct has_mapped_type_impl -{ - template static std::true_type check(typename T::mapped_type*); - template static std::false_type check(...); -}; -struct has_reserve_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().reserve(std::declval()))*); -}; -struct has_push_back_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().push_back(std::declval()))*); -}; -struct is_comparable_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval() < std::declval())*); -}; - -struct has_from_toml_method_impl -{ - template class Tb, template class A> - static std::true_type check( - decltype(std::declval().from_toml( - std::declval<::toml::basic_value>()))*); - - template class Tb, template class A> - static std::false_type check(...); -}; -struct has_into_toml_method_impl -{ - template - static std::true_type check(decltype(std::declval().into_toml())*); - template - static std::false_type check(...); -}; - -struct has_specialized_from_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; -struct has_specialized_into_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; - - -/// Intel C++ compiler can not use decltype in parent class declaration, here -/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 -#ifdef __INTEL_COMPILER -#define decltype(...) std::enable_if::type -#endif - -template -struct has_iterator : decltype(has_iterator_impl::check(nullptr)){}; -template -struct has_value_type : decltype(has_value_type_impl::check(nullptr)){}; -template -struct has_key_type : decltype(has_key_type_impl::check(nullptr)){}; -template -struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; -template -struct has_reserve_method : decltype(has_reserve_method_impl::check(nullptr)){}; -template -struct has_push_back_method : decltype(has_push_back_method_impl::check(nullptr)){}; -template -struct is_comparable : decltype(is_comparable_impl::check(nullptr)){}; - -template class Tb, template class A> -struct has_from_toml_method -: decltype(has_from_toml_method_impl::check(nullptr)){}; - -template -struct has_into_toml_method -: decltype(has_into_toml_method_impl::check(nullptr)){}; - -template -struct has_specialized_from : decltype(has_specialized_from_impl::check(nullptr)){}; -template -struct has_specialized_into : decltype(has_specialized_into_impl::check(nullptr)){}; - -#ifdef __INTEL_COMPILER -#undef decltype -#endif - -// --------------------------------------------------------------------------- -// C++17 and/or/not - -#if __cplusplus >= 201703L - -using std::conjunction; -using std::disjunction; -using std::negation; - -#else - -template struct conjunction : std::true_type{}; -template struct conjunction : T{}; -template -struct conjunction : - std::conditional(T::value), conjunction, T>::type -{}; - -template struct disjunction : std::false_type{}; -template struct disjunction : T {}; -template -struct disjunction : - std::conditional(T::value), T, disjunction>::type -{}; - -template -struct negation : std::integral_constant(T::value)>{}; - -#endif - -// --------------------------------------------------------------------------- -// type checkers - -template struct is_std_pair : std::false_type{}; -template -struct is_std_pair> : std::true_type{}; - -template struct is_std_tuple : std::false_type{}; -template -struct is_std_tuple> : std::true_type{}; - -template struct is_std_forward_list : std::false_type{}; -template -struct is_std_forward_list> : std::true_type{}; - -template struct is_chrono_duration: std::false_type{}; -template -struct is_chrono_duration>: std::true_type{}; - -template -struct is_map : conjunction< // map satisfies all the following conditions - has_iterator, // has T::iterator - has_value_type, // has T::value_type - has_key_type, // has T::key_type - has_mapped_type // has T::mapped_type - >{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; - -template -struct is_container : conjunction< - negation>, // not a map - negation>, // not a std::string -#if __cplusplus >= 201703L -#if __has_include() - negation>, // not a std::string_view -#endif // has_include() -#endif - has_iterator, // has T::iterator - has_value_type // has T::value_type - >{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; - -template -struct is_basic_value: std::false_type{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template class M, template class V> -struct is_basic_value<::toml::basic_value>: std::true_type{}; - -// --------------------------------------------------------------------------- -// C++14 index_sequence - -#if __cplusplus >= 201402L - -using std::index_sequence; -using std::make_index_sequence; - -#else - -template struct index_sequence{}; - -template struct push_back_index_sequence{}; -template -struct push_back_index_sequence, N> -{ - typedef index_sequence type; -}; - -template -struct index_sequence_maker -{ - typedef typename push_back_index_sequence< - typename index_sequence_maker::type, N>::type type; -}; -template<> -struct index_sequence_maker<0> -{ - typedef index_sequence<0> type; -}; -template -using make_index_sequence = typename index_sequence_maker::type; - -#endif // __cplusplus >= 2014 - -// --------------------------------------------------------------------------- -// C++14 enable_if_t - -#if __cplusplus >= 201402L - -using std::enable_if_t; - -#else - -template -using enable_if_t = typename std::enable_if::type; - -#endif // __cplusplus >= 2014 - -// --------------------------------------------------------------------------- -// return_type_of_t - -#if __cplusplus >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703 - -template -using return_type_of_t = std::invoke_result_t; - -#else -// result_of is deprecated after C++17 -template -using return_type_of_t = typename std::result_of::type; - -#endif - -// --------------------------------------------------------------------------- -// is_string_literal -// -// to use this, pass `typename remove_reference::type` to T. - -template -struct is_string_literal: -disjunction< - std::is_same, - conjunction< - std::is_array, - std::is_same::type> - > - >{}; - -// --------------------------------------------------------------------------- -// C++20 remove_cvref_t - -template -struct remove_cvref -{ - using type = typename std::remove_cv< - typename std::remove_reference::type>::type; -}; - -template -using remove_cvref_t = typename remove_cvref::type; - -}// detail -}//toml -#endif // TOML_TRAITS diff --git a/src/toml11/toml/types.hpp b/src/toml11/toml/types.hpp deleted file mode 100644 index 1e420e7fd..000000000 --- a/src/toml11/toml/types.hpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TYPES_HPP -#define TOML11_TYPES_HPP -#include -#include - -#include "comments.hpp" -#include "datetime.hpp" -#include "string.hpp" -#include "traits.hpp" - -namespace toml -{ - -template class Table, // map-like class - template class Array> // vector-like class -class basic_value; - -using character = char; -using key = std::string; - -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wshadow" -#endif - -using boolean = bool; -using integer = std::int64_t; -using floating = double; // "float" is a keyword, cannot use it here. -// the following stuffs are structs defined here, so aliases are not needed. -// - string -// - offset_datetime -// - offset_datetime -// - local_datetime -// - local_date -// - local_time - -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - -// default toml::value and default array/table. these are defined after defining -// basic_value itself. -// using value = basic_value; -// using array = typename value::array_type; -// using table = typename value::table_type; - -// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in -// GCC -Wshadow=global. -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# if 7 <= __GNUC__ -# pragma GCC diagnostic ignored "-Wshadow=global" -# else // gcc-6 or older -# pragma GCC diagnostic ignored "-Wshadow" -# endif -#endif -enum class value_t : std::uint8_t -{ - empty = 0, - boolean = 1, - integer = 2, - floating = 3, - string = 4, - offset_datetime = 5, - local_datetime = 6, - local_date = 7, - local_time = 8, - array = 9, - table = 10, -}; -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - -template -inline std::basic_ostream& -operator<<(std::basic_ostream& os, value_t t) -{ - switch(t) - { - case value_t::boolean : os << "boolean"; return os; - case value_t::integer : os << "integer"; return os; - case value_t::floating : os << "floating"; return os; - case value_t::string : os << "string"; return os; - case value_t::offset_datetime : os << "offset_datetime"; return os; - case value_t::local_datetime : os << "local_datetime"; return os; - case value_t::local_date : os << "local_date"; return os; - case value_t::local_time : os << "local_time"; return os; - case value_t::array : os << "array"; return os; - case value_t::table : os << "table"; return os; - case value_t::empty : os << "empty"; return os; - default : os << "unknown"; return os; - } -} - -template, - typename alloc = std::allocator> -inline std::basic_string stringize(value_t t) -{ - std::basic_ostringstream oss; - oss << t; - return oss.str(); -} - -namespace detail -{ - -// helper to define a type that represents a value_t value. -template -using value_t_constant = std::integral_constant; - -// meta-function that convertes from value_t to the exact toml type that corresponds to. -// It takes toml::basic_value type because array and table types depend on it. -template struct enum_to_type {using type = void ;}; -template struct enum_to_type{using type = void ;}; -template struct enum_to_type{using type = boolean ;}; -template struct enum_to_type{using type = integer ;}; -template struct enum_to_type{using type = floating ;}; -template struct enum_to_type{using type = string ;}; -template struct enum_to_type{using type = offset_datetime ;}; -template struct enum_to_type{using type = local_datetime ;}; -template struct enum_to_type{using type = local_date ;}; -template struct enum_to_type{using type = local_time ;}; -template struct enum_to_type{using type = typename Value::array_type;}; -template struct enum_to_type{using type = typename Value::table_type;}; - -// meta-function that converts from an exact toml type to the enum that corresponds to. -template -struct type_to_enum : std::conditional< - std::is_same::value, // if T == array_type, - value_t_constant, // then value_t::array - typename std::conditional< // else... - std::is_same::value, // if T == table_type - value_t_constant, // then value_t::table - value_t_constant // else value_t::empty - >::type - >::type {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; - -// meta-function that checks the type T is the same as one of the toml::* types. -template -struct is_exact_toml_type : disjunction< - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same - >{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type: is_exact_toml_type{}; - -} // detail -} // toml - -#endif// TOML11_TYPES_H diff --git a/src/toml11/toml/utility.hpp b/src/toml11/toml/utility.hpp deleted file mode 100644 index 4a6b4309d..000000000 --- a/src/toml11/toml/utility.hpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_UTILITY_HPP -#define TOML11_UTILITY_HPP -#include -#include -#include - -#include "traits.hpp" - -#if __cplusplus >= 201402L -# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] -#elif defined(__GNUC__) -# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg))) -#elif defined(_MSC_VER) -# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg)) -#else -# define TOML11_MARK_AS_DEPRECATED -#endif - -namespace toml -{ - -#if __cplusplus >= 201402L - -using std::make_unique; - -#else - -template -inline std::unique_ptr make_unique(Ts&& ... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} - -#endif // __cplusplus >= 2014 - -namespace detail -{ -template -void try_reserve_impl(Container& container, std::size_t N, std::true_type) -{ - container.reserve(N); - return; -} -template -void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept -{ - return; -} -} // detail - -template -void try_reserve(Container& container, std::size_t N) -{ - if(N <= container.size()) {return;} - detail::try_reserve_impl(container, N, detail::has_reserve_method{}); - return; -} - -namespace detail -{ -inline std::string concat_to_string_impl(std::ostringstream& oss) -{ - return oss.str(); -} -template -std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) -{ - oss << std::forward(head); - return concat_to_string_impl(oss, std::forward(tail) ... ); -} -} // detail - -template -std::string concat_to_string(Ts&& ... args) -{ - std::ostringstream oss; - oss << std::boolalpha << std::fixed; - return detail::concat_to_string_impl(oss, std::forward(args) ...); -} - -template -T from_string(const std::string& str, T opt) -{ - T v(opt); - std::istringstream iss(str); - iss >> v; - return v; -} - -namespace detail -{ -#if __cplusplus >= 201402L -template -decltype(auto) last_one(T&& tail) noexcept -{ - return std::forward(tail); -} - -template -decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept -{ - return last_one(std::forward(tail)...); -} -#else // C++11 -// The following code -// ```cpp -// 1 | template -// 2 | auto last_one(T&& /*head*/, Ts&& ... tail) -// 3 | -> decltype(last_one(std::forward(tail)...)) -// 4 | { -// 5 | return last_one(std::forward(tail)...); -// 6 | } -// ``` -// does not work because the function `last_one(...)` is not yet defined at -// line #3, so `decltype()` cannot deduce the type returned from `last_one`. -// So we need to determine return type in a different way, like a meta func. - -template -struct last_one_in_pack -{ - using type = typename last_one_in_pack::type; -}; -template -struct last_one_in_pack -{ - using type = T; -}; -template -using last_one_in_pack_t = typename last_one_in_pack::type; - -template -T&& last_one(T&& tail) noexcept -{ - return std::forward(tail); -} -template -enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t> -last_one(T&& /*head*/, Ts&& ... tail) -{ - return last_one(std::forward(tail)...); -} - -#endif -} // detail - -}// toml -#endif // TOML11_UTILITY diff --git a/src/toml11/toml/value.hpp b/src/toml11/toml/value.hpp deleted file mode 100644 index 1b43db8d4..000000000 --- a/src/toml11/toml/value.hpp +++ /dev/null @@ -1,2035 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_VALUE_HPP -#define TOML11_VALUE_HPP -#include - -#include "comments.hpp" -#include "exception.hpp" -#include "into.hpp" -#include "region.hpp" -#include "source_location.hpp" -#include "storage.hpp" -#include "traits.hpp" -#include "types.hpp" -#include "utility.hpp" - -namespace toml -{ - -namespace detail -{ - -// to show error messages. not recommended for users. -template -inline region_base const* get_region(const Value& v) -{ - return v.region_info_.get(); -} - -template -void change_region(Value& v, region reg) -{ - v.region_info_ = std::make_shared(std::move(reg)); - return; -} - -template -[[noreturn]] inline void -throw_bad_cast(const std::string& funcname, value_t actual, const Value& v) -{ - throw type_error(detail::format_underline( - concat_to_string(funcname, "bad_cast to ", Expected), { - {v.location(), concat_to_string("the actual type is ", actual)} - }), v.location()); -} - -// Throw `out_of_range` from `toml::value::at()` and `toml::find()` -// after generating an error message. -// -// The implementation is a bit complicated and there are many edge-cases. -// If you are not interested in the error message generation, just skip this. -template -[[noreturn]] void -throw_key_not_found_error(const Value& v, const key& ky) -{ - // The top-level table has its region at the first character of the file. - // That means that, in the case when a key is not found in the top-level - // table, the error message points to the first character. If the file has - // its first table at the first line, the error message would be like this. - // ```console - // [error] key "a" not found - // --> example.toml - // | - // 1 | [table] - // | ^------ in this table - // ``` - // It actually points to the top-level table at the first character, - // not `[table]`. But it is too confusing. To avoid the confusion, the error - // message should explicitly say "key not found in the top-level table", - // or "the parsed file is empty" if there is no content at all (0 bytes in file). - const auto loc = v.location(); - if(loc.line() == 1 && loc.region() == 0) - { - // First line with a zero-length region means "empty file". - // The region will be generated at `parse_toml_file` function - // if the file contains no bytes. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the parsed file is empty"} - })); - } - else if(loc.line() == 1 && loc.region() == 1) - { - // Here it assumes that top-level table starts at the first character. - // The region corresponds to the top-level table will be generated at - // `parse_toml_file` function. - // It also assumes that the top-level table size is just one and - // the line number is `1`. It is always satisfied. And those conditions - // are satisfied only if the table is the top-level table. - // - // 1. one-character dot-key at the first line - // ```toml - // a.b = "c" - // ``` - // toml11 counts whole key as the table key. Here, `a.b` is the region - // of the table "a". It could be counter intuitive, but it works. - // The size of the region is 3, not 1. The above example is the shortest - // dot-key example. The size cannot be 1. - // - // 2. one-character inline-table at the first line - // ```toml - // a = {b = "c"} - // ``` - // toml11 considers the inline table body as the table region. Here, - // `{b = "c"}` is the region of the table "a". The size of the region - // is 9, not 1. The shotest inline table still has two characters, `{` - // and `}`. The size cannot be 1. - // - // 3. one-character table declaration at the first line - // ```toml - // [a] - // ``` - // toml11 considers the whole table key as the table region. Here, - // `[a]` is the table region. The size is 3, not 1. - // - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the top-level table starts here"} - })); - } - else - { - // normal table. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found"), { {loc, "in this table"} })); - } -} - -// switch by `value_t` at the compile time. -template -struct switch_cast {}; -#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \ - template<> \ - struct switch_cast \ - { \ - template \ - static typename Value::TYPE##_type& invoke(Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type const& invoke(const Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type&& invoke(Value&& v) \ - { \ - return std::move(v).as_##TYPE(); \ - } \ - }; \ - /**/ -TOML11_GENERATE_SWITCH_CASTER(boolean) -TOML11_GENERATE_SWITCH_CASTER(integer) -TOML11_GENERATE_SWITCH_CASTER(floating) -TOML11_GENERATE_SWITCH_CASTER(string) -TOML11_GENERATE_SWITCH_CASTER(offset_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_date) -TOML11_GENERATE_SWITCH_CASTER(local_time) -TOML11_GENERATE_SWITCH_CASTER(array) -TOML11_GENERATE_SWITCH_CASTER(table) - -#undef TOML11_GENERATE_SWITCH_CASTER - -}// detail - -template class Table = std::unordered_map, - template class Array = std::vector> -class basic_value -{ - template - static void assigner(T& dst, U&& v) - { - const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); - assert(tmp == std::addressof(dst)); - (void)tmp; - } - - using region_base = detail::region_base; - - template class T, - template class A> - friend class basic_value; - - public: - - using comment_type = Comment; - using key_type = ::toml::key; - using value_type = basic_value; - using boolean_type = ::toml::boolean; - using integer_type = ::toml::integer; - using floating_type = ::toml::floating; - using string_type = ::toml::string; - using local_time_type = ::toml::local_time; - using local_date_type = ::toml::local_date; - using local_datetime_type = ::toml::local_datetime; - using offset_datetime_type = ::toml::offset_datetime; - using array_type = Array; - using table_type = Table; - - public: - - basic_value() noexcept - : type_(value_t::empty), - region_info_(std::make_shared(region_base{})) - {} - ~basic_value() noexcept {this->cleanup();} - - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments_) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - basic_value(basic_value&& v) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(v.comments_)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - basic_value& operator=(const basic_value& v) - { - this->cleanup(); - this->region_info_ = v.region_info_; - this->comments_ = v.comments_; - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - return *this; - } - basic_value& operator=(basic_value&& v) - { - this->cleanup(); - this->region_info_ = std::move(v.region_info_); - this->comments_ = std::move(v.comments_); - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - return *this; - } - - // overwrite comments ---------------------------------------------------- - - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - - basic_value(basic_value&& v, std::vector com) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(com)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - - // ----------------------------------------------------------------------- - // conversion between different basic_values. - template class T, - template class A> - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments()) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value& operator=(const basic_value& v) - { - this->region_info_ = v.region_info_; - this->comments_ = comment_type(v.comments()); - this->type_ = v.type(); - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - return *this; - } - - // boolean ============================================================== - - basic_value(boolean b) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})) - { - assigner(this->boolean_, b); - } - basic_value& operator=(boolean b) - { - this->cleanup(); - this->type_ = value_t::boolean; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->boolean_, b); - return *this; - } - basic_value(boolean b, std::vector com) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->boolean_, b); - } - - // integer ============================================================== - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})) - { - assigner(this->integer_, static_cast(i)); - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(T i) - { - this->cleanup(); - this->type_ = value_t::integer; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->integer_, static_cast(i)); - return *this; - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i, std::vector com) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->integer_, static_cast(i)); - } - - // floating ============================================================= - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})) - { - assigner(this->floating_, static_cast(f)); - } - - - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(T f) - { - this->cleanup(); - this->type_ = value_t::floating; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->floating_, static_cast(f)); - return *this; - } - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, std::vector com) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->floating_, f); - } - - // string =============================================================== - - basic_value(toml::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, std::move(s)); - } - basic_value& operator=(toml::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, s); - return *this; - } - basic_value(toml::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, std::move(s)); - } - - basic_value(std::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value& operator=(std::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::move(s))); - return *this; - } - basic_value(std::string s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - basic_value(std::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value(std::string s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - - basic_value(const char* s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value& operator=(const char* s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::string(s))); - return *this; - } - basic_value(const char* s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - basic_value(const char* s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value(const char* s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 - basic_value(std::string_view s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s)); - } - basic_value& operator=(std::string_view s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(s)); - return *this; - } - basic_value(std::string_view s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s)); - } - basic_value(std::string_view s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s, kind)); - } - basic_value(std::string_view s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s, kind)); - } -#endif - - // local date =========================================================== - - basic_value(const local_date& ld) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_date_, ld); - } - basic_value& operator=(const local_date& ld) - { - this->cleanup(); - this->type_ = value_t::local_date; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_date_, ld); - return *this; - } - basic_value(const local_date& ld, std::vector com) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_date_, ld); - } - - // local time =========================================================== - - basic_value(const local_time& lt) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, lt); - } - basic_value(const local_time& lt, std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, lt); - } - basic_value& operator=(const local_time& lt) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, lt); - return *this; - } - - template - basic_value(const std::chrono::duration& dur) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value(const std::chrono::duration& dur, - std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value& operator=(const std::chrono::duration& dur) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, local_time(dur)); - return *this; - } - - // local datetime ======================================================= - - basic_value(const local_datetime& ldt) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const local_datetime& ldt, std::vector com) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_datetime_, ldt); - } - basic_value& operator=(const local_datetime& ldt) - { - this->cleanup(); - this->type_ = value_t::local_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_datetime_, ldt); - return *this; - } - - // offset datetime ====================================================== - - basic_value(const offset_datetime& odt) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const offset_datetime& odt, std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, odt); - } - basic_value& operator=(const offset_datetime& odt) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, odt); - return *this; - } - basic_value(const std::chrono::system_clock::time_point& tp) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value(const std::chrono::system_clock::time_point& tp, - std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value& operator=(const std::chrono::system_clock::time_point& tp) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, offset_datetime(tp)); - return *this; - } - - // array ================================================================ - - basic_value(const array_type& ary) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - assigner(this->array_, ary); - } - basic_value(const array_type& ary, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->array_, ary); - } - basic_value& operator=(const array_type& ary) - { - this->cleanup(); - this->type_ = value_t::array ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->array_, ary); - return *this; - } - - // array (initializer_list) ---------------------------------------------- - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(std::initializer_list list) - { - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // array (STL Containers) ------------------------------------------------ - - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& list) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // table ================================================================ - - basic_value(const table_type& tab) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - assigner(this->table_, tab); - } - basic_value(const table_type& tab, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->table_, tab); - } - basic_value& operator=(const table_type& tab) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->table_, tab); - return *this; - } - - // initializer-list ------------------------------------------------------ - - basic_value(std::initializer_list> list) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - - basic_value(std::initializer_list> list, - std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - basic_value& operator=(std::initializer_list> list) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // other table-like ----------------------------------------------------- - - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const Map& mp) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // user-defined ========================================================= - - // convert using into_toml() method ------------------------------------- - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud): basic_value(ud.into_toml()) {} - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud, std::vector com) - : basic_value(ud.into_toml(), std::move(com)) - {} - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& ud) - { - *this = ud.into_toml(); - return *this; - } - - // convert using into struct ----------------------------------------- - - template)> - basic_value(const T& ud): basic_value(::toml::into::into_toml(ud)) {} - template)> - basic_value(const T& ud, std::vector com) - : basic_value(::toml::into::into_toml(ud), std::move(com)) - {} - template)> - basic_value& operator=(const T& ud) - { - *this = ::toml::into::into_toml(ud); - return *this; - } - - // for internal use ------------------------------------------------------ - // - // Those constructors take detail::region that contains parse result. - - basic_value(boolean b, detail::region reg, std::vector cm) - : type_(value_t::boolean), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->boolean_, b); - } - template, detail::negation> - >::value, std::nullptr_t>::type = nullptr> - basic_value(T i, detail::region reg, std::vector cm) - : type_(value_t::integer), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->integer_, static_cast(i)); - } - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, detail::region reg, std::vector cm) - : type_(value_t::floating), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->floating_, static_cast(f)); - } - basic_value(toml::string s, detail::region reg, - std::vector cm) - : type_(value_t::string), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->string_, std::move(s)); - } - basic_value(const local_date& ld, detail::region reg, - std::vector cm) - : type_(value_t::local_date), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_date_, ld); - } - basic_value(const local_time& lt, detail::region reg, - std::vector cm) - : type_(value_t::local_time), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_time_, lt); - } - basic_value(const local_datetime& ldt, detail::region reg, - std::vector cm) - : type_(value_t::local_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const offset_datetime& odt, detail::region reg, - std::vector cm) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const array_type& ary, detail::region reg, - std::vector cm) - : type_(value_t::array), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->array_, ary); - } - basic_value(const table_type& tab, detail::region reg, - std::vector cm) - : type_(value_t::table), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->table_, tab); - } - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::pair parse_result, std::vector com) - : basic_value(std::move(parse_result.first), - std::move(parse_result.second), - std::move(com)) - {} - - // type checking and casting ============================================ - - template::value, - std::nullptr_t>::type = nullptr> - bool is() const noexcept - { - return detail::type_to_enum::value == this->type_; - } - bool is(value_t t) const noexcept {return t == this->type_;} - - bool is_uninitialized() const noexcept {return this->is(value_t::empty );} - bool is_boolean() const noexcept {return this->is(value_t::boolean );} - bool is_integer() const noexcept {return this->is(value_t::integer );} - bool is_floating() const noexcept {return this->is(value_t::floating );} - bool is_string() const noexcept {return this->is(value_t::string );} - bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} - bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} - bool is_local_date() const noexcept {return this->is(value_t::local_date );} - bool is_local_time() const noexcept {return this->is(value_t::local_time );} - bool is_array() const noexcept {return this->is(value_t::array );} - bool is_table() const noexcept {return this->is(value_t::table );} - - value_t type() const noexcept {return type_;} - - template - typename detail::enum_to_type::type& cast() & - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type const& cast() const& - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type&& cast() && - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(std::move(*this)); - } - - // ------------------------------------------------------------------------ - // nothrow version - - boolean const& as_boolean (const std::nothrow_t&) const& noexcept {return this->boolean_;} - integer const& as_integer (const std::nothrow_t&) const& noexcept {return this->integer_;} - floating const& as_floating (const std::nothrow_t&) const& noexcept {return this->floating_;} - string const& as_string (const std::nothrow_t&) const& noexcept {return this->string_;} - offset_datetime const& as_offset_datetime(const std::nothrow_t&) const& noexcept {return this->offset_datetime_;} - local_datetime const& as_local_datetime (const std::nothrow_t&) const& noexcept {return this->local_datetime_;} - local_date const& as_local_date (const std::nothrow_t&) const& noexcept {return this->local_date_;} - local_time const& as_local_time (const std::nothrow_t&) const& noexcept {return this->local_time_;} - array_type const& as_array (const std::nothrow_t&) const& noexcept {return this->array_.value();} - table_type const& as_table (const std::nothrow_t&) const& noexcept {return this->table_.value();} - - boolean & as_boolean (const std::nothrow_t&) & noexcept {return this->boolean_;} - integer & as_integer (const std::nothrow_t&) & noexcept {return this->integer_;} - floating & as_floating (const std::nothrow_t&) & noexcept {return this->floating_;} - string & as_string (const std::nothrow_t&) & noexcept {return this->string_;} - offset_datetime& as_offset_datetime(const std::nothrow_t&) & noexcept {return this->offset_datetime_;} - local_datetime & as_local_datetime (const std::nothrow_t&) & noexcept {return this->local_datetime_;} - local_date & as_local_date (const std::nothrow_t&) & noexcept {return this->local_date_;} - local_time & as_local_time (const std::nothrow_t&) & noexcept {return this->local_time_;} - array_type & as_array (const std::nothrow_t&) & noexcept {return this->array_.value();} - table_type & as_table (const std::nothrow_t&) & noexcept {return this->table_.value();} - - boolean && as_boolean (const std::nothrow_t&) && noexcept {return std::move(this->boolean_);} - integer && as_integer (const std::nothrow_t&) && noexcept {return std::move(this->integer_);} - floating && as_floating (const std::nothrow_t&) && noexcept {return std::move(this->floating_);} - string && as_string (const std::nothrow_t&) && noexcept {return std::move(this->string_);} - offset_datetime&& as_offset_datetime(const std::nothrow_t&) && noexcept {return std::move(this->offset_datetime_);} - local_datetime && as_local_datetime (const std::nothrow_t&) && noexcept {return std::move(this->local_datetime_);} - local_date && as_local_date (const std::nothrow_t&) && noexcept {return std::move(this->local_date_);} - local_time && as_local_time (const std::nothrow_t&) && noexcept {return std::move(this->local_time_);} - array_type && as_array (const std::nothrow_t&) && noexcept {return std::move(this->array_.value());} - table_type && as_table (const std::nothrow_t&) && noexcept {return std::move(this->table_.value());} - - // ======================================================================== - // throw version - // ------------------------------------------------------------------------ - // const reference {{{ - - boolean const& as_boolean() const& - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer const& as_integer() const& - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating const& as_floating() const& - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string const& as_string() const& - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime const& as_offset_datetime() const& - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime const& as_local_datetime() const& - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date const& as_local_date() const& - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time const& as_local_time() const& - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type const& as_array() const& - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type const& as_table() const& - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - // }}} - // ------------------------------------------------------------------------ - // nonconst reference {{{ - - boolean & as_boolean() & - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer & as_integer() & - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating & as_floating() & - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string & as_string() & - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime & as_offset_datetime() & - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime & as_local_datetime() & - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date & as_local_date() & - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time & as_local_time() & - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type & as_array() & - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type & as_table() & - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - - // }}} - // ------------------------------------------------------------------------ - // rvalue reference {{{ - - boolean && as_boolean() && - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return std::move(this->boolean_); - } - integer && as_integer() && - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return std::move(this->integer_); - } - floating && as_floating() && - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return std::move(this->floating_); - } - string && as_string() && - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return std::move(this->string_); - } - offset_datetime && as_offset_datetime() && - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return std::move(this->offset_datetime_); - } - local_datetime && as_local_datetime() && - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return std::move(this->local_datetime_); - } - local_date && as_local_date() && - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return std::move(this->local_date_); - } - local_time && as_local_time() && - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return std::move(this->local_time_); - } - array_type && as_array() && - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return std::move(this->array_.value()); - } - table_type && as_table() && - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return std::move(this->table_.value()); - } - // }}} - - // accessors ============================================================= - // - // may throw type_error or out_of_range - // - value_type& at(const key& k) - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type const& at(const key& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type& operator[](const key& k) - { - if(this->is_uninitialized()) - { - *this = table_type{}; - } - else if(!this->is_table()) // initialized, but not a table - { - detail::throw_bad_cast( - "toml::value::operator[](key): ", this->type_, *this); - } - return this->as_table(std::nothrow)[k]; - } - - value_type& at(const std::size_t idx) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array().at(idx); - } - value_type const& at(const std::size_t idx) const - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array(std::nothrow).at(idx); - } - - value_type& operator[](const std::size_t idx) noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - value_type const& operator[](const std::size_t idx) const noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - - void push_back(const value_type& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(x); - return; - } - void push_back(value_type&& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(std::move(x)); - return; - } - - template - value_type& emplace_back(Ts&& ... args) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::emplace_back(...): ", this->type_, *this); - } - this->as_array(std::nothrow).emplace_back(std::forward(args) ...); - return this->as_array(std::nothrow).back(); - } - - std::size_t size() const - { - switch(this->type_) - { - case value_t::array: - { - return this->as_array(std::nothrow).size(); - } - case value_t::table: - { - return this->as_table(std::nothrow).size(); - } - case value_t::string: - { - return this->as_string(std::nothrow).str.size(); - } - default: - { - throw type_error(detail::format_underline( - "toml::value::size(): bad_cast to container types", { - {this->location(), - concat_to_string("the actual type is ", this->type_)} - }), this->location()); - } - } - } - - std::size_t count(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::count(key): ", this->type_, *this); - } - return this->as_table(std::nothrow).count(k); - } - - bool contains(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::contains(key): ", this->type_, *this); - } - return (this->as_table(std::nothrow).count(k) != 0); - } - - source_location location() const - { - return source_location(this->region_info_.get()); - } - - comment_type const& comments() const noexcept {return this->comments_;} - comment_type& comments() noexcept {return this->comments_;} - - private: - - void cleanup() noexcept - { - switch(this->type_) - { - case value_t::string : {string_.~string(); return;} - case value_t::array : {array_.~array_storage(); return;} - case value_t::table : {table_.~table_storage(); return;} - default : return; - } - } - - // for error messages - template - friend region_base const* detail::get_region(const Value& v); - - template - friend void detail::change_region(Value& v, detail::region reg); - - private: - - using array_storage = detail::storage; - using table_storage = detail::storage; - - value_t type_; - union - { - boolean boolean_; - integer integer_; - floating floating_; - string string_; - offset_datetime offset_datetime_; - local_datetime local_datetime_; - local_date local_date_; - local_time local_time_; - array_storage array_; - table_storage table_; - }; - std::shared_ptr region_info_; - comment_type comments_; -}; - -// default toml::value and default array/table. -// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp -using value = basic_value; -using array = typename value::array_type; -using table = typename value::table_type; - -template class T, template class A> -inline bool -operator==(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()) {return false;} - if(lhs.comments() != rhs.comments()) {return false;} - - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() == rhs.as_boolean(); - } - case value_t::integer : - { - return lhs.as_integer() == rhs.as_integer(); - } - case value_t::floating : - { - return lhs.as_floating() == rhs.as_floating(); - } - case value_t::string : - { - return lhs.as_string() == rhs.as_string(); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() == rhs.as_offset_datetime(); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() == rhs.as_local_datetime(); - } - case value_t::local_date: - { - return lhs.as_local_date() == rhs.as_local_date(); - } - case value_t::local_time: - { - return lhs.as_local_time() == rhs.as_local_time(); - } - case value_t::array : - { - return lhs.as_array() == rhs.as_array(); - } - case value_t::table : - { - return lhs.as_table() == rhs.as_table(); - } - case value_t::empty : {return true; } - default: {return false;} - } -} - -template class T, template class A> -inline bool operator!=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs == rhs); -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() < rhs.as_boolean() || - (lhs.as_boolean() == rhs.as_boolean() && - lhs.comments() < rhs.comments()); - } - case value_t::integer : - { - return lhs.as_integer() < rhs.as_integer() || - (lhs.as_integer() == rhs.as_integer() && - lhs.comments() < rhs.comments()); - } - case value_t::floating : - { - return lhs.as_floating() < rhs.as_floating() || - (lhs.as_floating() == rhs.as_floating() && - lhs.comments() < rhs.comments()); - } - case value_t::string : - { - return lhs.as_string() < rhs.as_string() || - (lhs.as_string() == rhs.as_string() && - lhs.comments() < rhs.comments()); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() < rhs.as_offset_datetime() || - (lhs.as_offset_datetime() == rhs.as_offset_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() < rhs.as_local_datetime() || - (lhs.as_local_datetime() == rhs.as_local_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_date: - { - return lhs.as_local_date() < rhs.as_local_date() || - (lhs.as_local_date() == rhs.as_local_date() && - lhs.comments() < rhs.comments()); - } - case value_t::local_time: - { - return lhs.as_local_time() < rhs.as_local_time() || - (lhs.as_local_time() == rhs.as_local_time() && - lhs.comments() < rhs.comments()); - } - case value_t::array : - { - return lhs.as_array() < rhs.as_array() || - (lhs.as_array() == rhs.as_array() && - lhs.comments() < rhs.comments()); - } - case value_t::table : - { - return lhs.as_table() < rhs.as_table() || - (lhs.as_table() == rhs.as_table() && - lhs.comments() < rhs.comments()); - } - case value_t::empty : - { - return lhs.comments() < rhs.comments(); - } - default: - { - return lhs.comments() < rhs.comments(); - } - } -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<=(const basic_value& lhs, const basic_value& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs <= rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs < rhs); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const basic_value& v, const std::string& comment, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v.location(), comment}}, - std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, { - {v1.location(), comment1}, {v2.location(), comment2} - }, std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - const toml::basic_value& v3, const std::string& comment3, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v1.location(), comment1}, - {v2.location(), comment2}, {v3.location(), comment3} - }, std::move(hints), colorize); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, const toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value&& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} - case value_t::integer : {return visitor(std::move(v.as_integer ()));} - case value_t::floating : {return visitor(std::move(v.as_floating ()));} - case value_t::string : {return visitor(std::move(v.as_string ()));} - case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} - case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} - case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} - case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} - case value_t::array : {return visitor(std::move(v.as_array ()));} - case value_t::table : {return visitor(std::move(v.as_table ()));} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -}// toml -#endif// TOML11_VALUE From b87e048b3baeafd7c4d612674e6b02b9202d8272 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 23:30:38 -0400 Subject: [PATCH 410/910] Override `toml11` so it evaluates on Windows too --- package.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.nix b/package.nix index 126af6add..158696f30 100644 --- a/package.nix +++ b/package.nix @@ -226,7 +226,10 @@ in { libsodium openssl sqlite - toml11 + (toml11.overrideAttrs (old: { + # TODO change in Nixpkgs, Windows works fine. + meta.platforms = lib.platforms.all; + })) xz ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ From 3b388f6629938317d96ce72a1cfbee64372d8ce8 Mon Sep 17 00:00:00 2001 From: Harmen Date: Thu, 27 Jun 2024 10:30:21 +0200 Subject: [PATCH 411/910] string interpolation escape example (#10966) * string interpolation escape example Make it easier to find the documentation, and the example might be enough for most cases. Co-authored-by: Robert Hensing Co-authored-by: Valentin Gagarin --- .../src/language/string-interpolation.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 1e2c4ad95..7b4a5cfef 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -43,6 +43,47 @@ configureFlags = " Note that Nix expressions and strings can be arbitrarily nested; in this case the outer string contains various interpolated expressions that themselves contain strings (e.g., `"-thread"`), some of which in turn contain interpolated expressions (e.g., `${mesa}`). +To write a literal `${` in an regular string, escape it with a backslash (`\`). + +> **Example** +> +> ```nix +> "echo \${PATH}" +> ``` +> +> "echo ${PATH}" + +To write a literal `${` in an indented string, escape it with two single quotes (`''`). + +> **Example** +> +> ```nix +> '' +> echo ''${PATH} +> '' +> ``` +> +> "echo ${PATH}\n" + +`$${` can be written literally in any string. + +> **Example** +> +> In Make, `$` in file names or recipes is represented as `$$`, see [GNU `make`: Basics of Variable Reference](https://www.gnu.org/software/make/manual/html_node/Reference.html#Basics-of-Variable-References). +> This can be expressed directly in the Nix language strings: +> +> ```nix +> '' +> MAKEVAR = Hello +> all: +> @export BASHVAR=world; echo $(MAKEVAR) $${BASHVAR} +> '' +> ``` +> +> "MAKEVAR = Hello\nall:\n\t@export BASHVAR=world; echo $(MAKEVAR) $\${BASHVAR}\n" + +See the [documentation on strings][string] for details. + ### Path Rather than writing From b44909ac2244bda4c387b9a17748e8a94ada9e78 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 27 Jun 2024 10:58:59 +0200 Subject: [PATCH 412/910] add many more examples on escaping in strings (#10974) --- doc/manual/src/language/values.md | 134 +++++++++++++++++++++++++----- 1 file changed, 114 insertions(+), 20 deletions(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 4eb1887fa..ddd55a47e 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -6,19 +6,60 @@ *Strings* can be written in three ways. - The most common way is to enclose the string between double quotes, - e.g., `"foo bar"`. Strings can span multiple lines. The special - characters `"` and `\` and the character sequence `${` must be - escaped by prefixing them with a backslash (`\`). Newlines, carriage - returns and tabs can be written as `\n`, `\r` and `\t`, - respectively. - - You can include the results of other expressions into a string by enclosing them in `${ }`, a feature known as [string interpolation]. + The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. + Strings can span multiple lines. + The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. [string interpolation]: ./string-interpolation.md - The second way to write string literals is as an *indented string*, - which is enclosed between pairs of *double single-quotes*, like so: + The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): + + - Double quote (`"`) + + > **Example** + > + > ```nix + > "\"" + > ``` + > + > "\"" + + - Backslash (`\`) + + > **Example** + > + > ```nix + > "\\" + > ``` + > + > "\\" + + - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" + + > **Example** + > + > ```nix + > "\${" + > ``` + > + > "\${" + + The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > "$${" + > ``` + > + > "$\${" + + String values are output on the terminal with Nix-specific escaping. + Strings written to files will contain the characters encoded by the escaping. + + The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: ```nix '' @@ -40,18 +81,71 @@ "This is the first line.\nThis is the second line.\n This is the third line.\n" ``` - Note that the whitespace and newline following the opening `''` is - ignored if there is no non-whitespace text on the initial line. + > **Note** + > + > Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. + + > **Warning** + > + > Prefixed tab characters are not stripped. + > + > > **Example** + > > + > > The following indented string is prefixed with tabs: + > > + > > '' + > > all: + > > @echo hello + > > '' + > > + > > "\tall:\n\t\t@echo hello\n" Indented strings support [string interpolation]. - Since `${` and `''` have special meaning in indented strings, you - need a way to quote them. `$` can be escaped by prefixing it with - `''` (that is, two single quotes), i.e., `''$`. `''` can be escaped - by prefixing it with `'`, i.e., `'''`. `$` removes any special - meaning from the following `$`. Linefeed, carriage-return and tab - characters can be written as `''\n`, `''\r`, `''\t`, and `''\` - escapes any other character. + The following must be escaped to represent them in an indented string: + + - `$` is escaped by prefixing it with two single quotes (`''`) + + > **Example** + > + > ```nix + > '' + > ''$ + > '' + > ``` + > + > "$\n" + + - `''` is escaped by prefixing it with one single quote (`'`) + + > **Example** + > + > ```nix + > '' + > ''' + > '' + > ``` + > + > "''\n" + + These special characters are escaped as follows: + - Linefeed (`\n`): `''\n` + - Carriage return (`\r`): `''\r` + - Tab (`\t`): `''\t` + + `''\` escapes any other character. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > '' + > $${ + > '' + > ``` + > + > "$\${\n" Indented strings are primarily useful in that they allow multi-line string literals to follow the indentation of the enclosing Nix @@ -167,7 +261,7 @@ function and the fifth being a set. Note that lists are only lazy in values, and they are strict in length. -Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). +Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). ## Attribute Set From 26089183e66e656699c50f928d860fc5be5279ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 28 Jun 2024 15:56:53 +0200 Subject: [PATCH 413/910] maintainers: Drop thufschmitt https://github.com/NixOS/nixos-homepage/pull/1490 --- .github/CODEOWNERS | 2 +- maintainers/README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 99ca670e0..a9ca74c17 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -23,4 +23,4 @@ maintainers/*.md @fricklerhandwerk src/**/*.md @fricklerhandwerk # Libstore layer -/src/libstore @thufschmitt @ericson2314 +/src/libstore @ericson2314 diff --git a/maintainers/README.md b/maintainers/README.md index bfa0cb5a1..2a718e283 100644 --- a/maintainers/README.md +++ b/maintainers/README.md @@ -30,7 +30,6 @@ We aim to achieve this by improving the contributor experience and attracting mo ## Members - Eelco Dolstra (@edolstra) – Team lead -- Théophane Hufschmitt (@thufschmitt) - Valentin Gagarin (@fricklerhandwerk) - Thomas Bereknyei (@tomberek) - Robert Hensing (@roberth) From 9e9730ef0f9773f38d0c49749ab6d3b24843296b Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Fri, 28 Jun 2024 14:30:43 -0700 Subject: [PATCH 414/910] Test that commit-lock-file-summary and its alias work --- tests/functional/flakes/flakes.sh | 40 ++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 9d2524d15..c3cb2c661 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -19,13 +19,17 @@ flake7Dir=$TEST_ROOT/flake7 nonFlakeDir=$TEST_ROOT/nonFlake badFlakeDir=$TEST_ROOT/badFlake flakeGitBare=$TEST_ROOT/flakeGitBare +lockfileSummaryFlake=$TEST_ROOT/lockfileSummaryFlake -for repo in "$flake1Dir" "$flake2Dir" "$flake3Dir" "$flake7Dir" "$nonFlakeDir"; do +for repo in "$flake1Dir" "$flake2Dir" "$flake3Dir" "$flake7Dir" "$nonFlakeDir" "$lockfileSummaryFlake"; do # Give one repo a non-main initial branch. extraArgs= if [[ "$repo" == "$flake2Dir" ]]; then extraArgs="--initial-branch=main" fi + if [[ "$repo" == "$lockfileSummaryFlake" ]]; then + extraArgs="--initial-branch=main" + fi createGitRepo "$repo" "$extraArgs" done @@ -644,3 +648,37 @@ expectStderr 1 nix flake metadata "$flake2Dir" --no-allow-dirty --reference-lock [[ $($nonFlakeDir/shebang-inline-expr.sh baz) = "foo"$'\n'"baz" ]] [[ $($nonFlakeDir/shebang-file.sh baz) = "foo"$'\n'"baz" ]] expect 1 $nonFlakeDir/shebang-reject.sh 2>&1 | grepQuiet -F 'error: unsupported unquoted character in nix shebang: *. Use double backticks to escape?' + +# Test that the --commit-lock-file-summary flag and its alias work +cat > "$lockfileSummaryFlake/flake.nix" < Date: Fri, 28 Jun 2024 10:57:03 -0700 Subject: [PATCH 415/910] Restore commit-lock-file-summary rename for consistency It was originally renamed in https://github.com/NixOS/nix/pull/10691, but https://github.com/NixOS/nix/pull/9063 accidentally removed the new name and alias. --- src/libflake/flake-settings.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake-settings.hh index 1087c0eba..f97c175e8 100644 --- a/src/libflake/flake-settings.hh +++ b/src/libflake/flake-settings.hh @@ -37,12 +37,12 @@ struct FlakeSettings : public Config Setting commitLockFileSummary{ this, "", - "commit-lockfile-summary", + "commit-lock-file-summary", R"( The commit summary to use when committing changed flake lock files. If empty, the summary is generated based on the action performed. )", - {}, + {"commit-lockfile-summary"}, true, Xp::Flakes}; }; From fd94b74ee520f4e5acc95381e448ab31ee501426 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 13:19:04 +0200 Subject: [PATCH 416/910] Fix #10947; don't cache disallowed IFD --- src/libexpr/primops.cc | 2 +- tests/functional/flakes/eval-cache.sh | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 212441019..08f719f0f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -78,7 +78,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS if (drvs.empty()) return {}; if (isIFD && !settings.enableImportFromDerivation) - error( + error( "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", drvs.begin()->to_string(*store) ).debugThrow(); diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh index 0f8df1b91..d581a8dbd 100755 --- a/tests/functional/flakes/eval-cache.sh +++ b/tests/functional/flakes/eval-cache.sh @@ -7,12 +7,22 @@ requireGit flake1Dir="$TEST_ROOT/eval-cache-flake" createGitRepo "$flake1Dir" "" +cp ../simple.nix ../simple.builder.sh ../config.nix "$flake1Dir/" +git -C "$flake1Dir" add simple.nix simple.builder.sh config.nix +git -C "$flake1Dir" commit -m "config.nix" cat >"$flake1Dir/flake.nix" < \$out + ''; + }; + ifd = assert (import self.drv); self.drv; }; } EOF @@ -22,3 +32,8 @@ git -C "$flake1Dir" commit -m "Init" expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' + +# Conditional error should not be cached +expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \ + | grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled' +nix build "$flake1Dir#ifd" From 9b88bf8adf721d6b2d1a5666a97a0c6e9046a2d7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 13:48:17 +0200 Subject: [PATCH 417/910] Fix underflow in Printer::printAttrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code that counts the number of elided attrs incorrectly used the per-printer "global" attribute counter instead of a counter that was relevant only to the current attribute set. This bug flew under the radar because often the attribute sets aren't nested, not big enough, or we wouldn't pay attention to the numbers. I've noticed the issue because the difference underflowed. Although this behavior is tested by the functional test lang/eval-fail-bad-string-interpolation-4.nix, the underflow slipped through review. A simpler reproducer would be as follows, but I haven't added it to the test suite to keep it simple and marginally faster. ``` $ nix run nix/2.23.1 -- eval --expr '"" + (let v = { a = { a = 1; b = 2; c = 1; d = 1; e = 1; f = 1; g = 1; h = 1; }; b = { a = 1; b = 1; c = 1; }; }; in builtins.deepSeq v v)' error: … while evaluating a path segment at «string»:1:6: 1| "" + (let v = { a = { a = 1; b = 2; c = 1; d = 1; e = 1; f = 1; g = 1; h = 1; }; b = { a = 1; b = 1; c = 1; }; }; in builtins.deepSeq v v) | ^ error: cannot coerce a set to a string: { a = { a = 1; b = 2; c = 1; d = 1; e = 1; f = 1; g = 1; h = 1; }; b = { a = 1; «4294967289 attributes elided» }; } ``` --- src/libexpr/print.cc | 5 ++++- .../lang/eval-fail-bad-string-interpolation-4.err.exp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 10fe7923f..4e44fa721 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -345,11 +345,13 @@ private: auto prettyPrint = shouldPrettyPrintAttrs(sorted); + size_t currentAttrsPrinted = 0; + for (auto & i : sorted) { printSpace(prettyPrint); if (attrsPrinted >= options.maxAttrs) { - printElided(sorted.size() - attrsPrinted, "attribute", "attributes"); + printElided(sorted.size() - currentAttrsPrinted, "attribute", "attributes"); break; } @@ -358,6 +360,7 @@ private: print(*i.second, depth + 1); output << ";"; attrsPrinted++; + currentAttrsPrinted++; } decreaseIndent(); diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp index 6f907106b..b262e814d 100644 --- a/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp @@ -6,4 +6,4 @@ error: | ^ 10| - error: cannot coerce a set to a string: { a = { a = { a = { a = "ha"; b = "ha"; c = "ha"; d = "ha"; e = "ha"; f = "ha"; g = "ha"; h = "ha"; j = "ha"; }; «4294967295 attributes elided» }; «4294967294 attributes elided» }; «4294967293 attributes elided» } + error: cannot coerce a set to a string: { a = { a = { a = { a = "ha"; b = "ha"; c = "ha"; d = "ha"; e = "ha"; f = "ha"; g = "ha"; h = "ha"; j = "ha"; }; «8 attributes elided» }; «8 attributes elided» }; «8 attributes elided» } From ce1dc87711e06d1b04e444e3215ad8d35f191abd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 14:01:10 +0200 Subject: [PATCH 418/910] Refactor: rename ValuePrinter::totalAttrsPrinted Make it more distinct from the attrs printed of any specific attrset. --- src/libexpr/print.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 4e44fa721..5f6a08cfe 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -163,7 +163,7 @@ private: EvalState & state; PrintOptions options; std::optional seen; - size_t attrsPrinted = 0; + size_t totalAttrsPrinted = 0; size_t listItemsPrinted = 0; std::string indent; @@ -350,7 +350,7 @@ private: for (auto & i : sorted) { printSpace(prettyPrint); - if (attrsPrinted >= options.maxAttrs) { + if (totalAttrsPrinted >= options.maxAttrs) { printElided(sorted.size() - currentAttrsPrinted, "attribute", "attributes"); break; } @@ -359,7 +359,7 @@ private: output << " = "; print(*i.second, depth + 1); output << ";"; - attrsPrinted++; + totalAttrsPrinted++; currentAttrsPrinted++; } @@ -591,7 +591,7 @@ public: void print(Value & v) { - attrsPrinted = 0; + totalAttrsPrinted = 0; listItemsPrinted = 0; indent.clear(); From bfc54162405213b65f045a422fcb90ae62a18df1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 14:02:28 +0200 Subject: [PATCH 419/910] Refactor: rename ValuePrinter::totalListItemsPrinted --- src/libexpr/print.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 5f6a08cfe..74aa52d48 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -164,7 +164,7 @@ private: PrintOptions options; std::optional seen; size_t totalAttrsPrinted = 0; - size_t listItemsPrinted = 0; + size_t totalListItemsPrinted = 0; std::string indent; void increaseIndent() @@ -408,8 +408,8 @@ private: for (auto elem : listItems) { printSpace(prettyPrint); - if (listItemsPrinted >= options.maxListItems) { - printElided(listItems.size() - listItemsPrinted, "item", "items"); + if (totalListItemsPrinted >= options.maxListItems) { + printElided(listItems.size() - totalListItemsPrinted, "item", "items"); break; } @@ -418,7 +418,7 @@ private: } else { printNullptr(); } - listItemsPrinted++; + totalListItemsPrinted++; } decreaseIndent(); @@ -592,7 +592,7 @@ public: void print(Value & v) { totalAttrsPrinted = 0; - listItemsPrinted = 0; + totalListItemsPrinted = 0; indent.clear(); if (options.trackRepeated) { From b2c7f09b0a095ba0c0f3141a5734bf958d23b86a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 14:08:43 +0200 Subject: [PATCH 420/910] Fix underflow in Printer::printList Analogous to 9b88bf8adf7 / three commits back --- src/libexpr/print.cc | 6 +++++- .../lang/eval-fail-nested-list-items.err.exp | 9 +++++++++ tests/functional/lang/eval-fail-nested-list-items.nix | 11 +++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/functional/lang/eval-fail-nested-list-items.err.exp create mode 100644 tests/functional/lang/eval-fail-nested-list-items.nix diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 74aa52d48..2f377e588 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -405,11 +405,14 @@ private: output << "["; auto listItems = v.listItems(); auto prettyPrint = shouldPrettyPrintList(listItems); + + size_t currentListItemsPrinted = 0; + for (auto elem : listItems) { printSpace(prettyPrint); if (totalListItemsPrinted >= options.maxListItems) { - printElided(listItems.size() - totalListItemsPrinted, "item", "items"); + printElided(listItems.size() - currentListItemsPrinted, "item", "items"); break; } @@ -419,6 +422,7 @@ private: printNullptr(); } totalListItemsPrinted++; + currentListItemsPrinted++; } decreaseIndent(); diff --git a/tests/functional/lang/eval-fail-nested-list-items.err.exp b/tests/functional/lang/eval-fail-nested-list-items.err.exp new file mode 100644 index 000000000..90d439061 --- /dev/null +++ b/tests/functional/lang/eval-fail-nested-list-items.err.exp @@ -0,0 +1,9 @@ +error: + … while evaluating a path segment + at /pwd/lang/eval-fail-nested-list-items.nix:11:6: + 10| + 11| "" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v) + | ^ + 12| + + error: cannot coerce a list to a string: [ [ 1 2 3 4 5 6 7 8 ] [ 1 «3 items elided» ] ] diff --git a/tests/functional/lang/eval-fail-nested-list-items.nix b/tests/functional/lang/eval-fail-nested-list-items.nix new file mode 100644 index 000000000..af45b1dd4 --- /dev/null +++ b/tests/functional/lang/eval-fail-nested-list-items.nix @@ -0,0 +1,11 @@ +# This reproduces https://github.com/NixOS/nix/issues/10993, for lists +# $ nix run nix/2.23.1 -- eval --expr '"" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v)' +# error: +# … while evaluating a path segment +# at «string»:1:6: +# 1| "" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v) +# | ^ +# +# error: cannot coerce a list to a string: [ [ 1 2 3 4 5 6 7 8 ] [ 1 «4294967290 items elided» ] ] + +"" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v) From 72bb5301417b03f27b56677ace343289f6f40ce2 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 30 Jun 2024 19:03:15 +0530 Subject: [PATCH 421/910] use `CanonPath` in `fs-sink` and its derivatives --- src/libfetchers/git-utils.cc | 16 +++++++------- src/libstore/nar-accessor.cc | 12 +++++------ src/libutil/archive.cc | 6 +++--- src/libutil/file-system.cc | 2 +- src/libutil/fs-sink.cc | 31 +++++++++++---------------- src/libutil/fs-sink.hh | 28 ++++++++++++------------ src/libutil/git.cc | 10 ++++----- src/libutil/git.hh | 8 +++---- src/libutil/memory-source-accessor.cc | 12 +++++------ src/libutil/memory-source-accessor.hh | 6 +++--- src/libutil/tarfile.cc | 7 +++--- tests/unit/libutil/git.cc | 14 ++++++------ 12 files changed, 73 insertions(+), 79 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 2ea1e15ed..500064cee 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -851,10 +851,10 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink } void createRegularFile( - const Path & path, + const CanonPath & path, std::function func) override { - auto pathComponents = tokenizeString>(path, "/"); + auto pathComponents = tokenizeString>(path.rel(), "/"); if (!prepareDirs(pathComponents, false)) return; git_writestream * stream = nullptr; @@ -862,11 +862,11 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink throw Error("creating a blob stream object: %s", git_error_last()->message); struct CRF : CreateRegularFileSink { - const Path & path; + const CanonPath & path; GitFileSystemObjectSinkImpl & back; git_writestream * stream; bool executable = false; - CRF(const Path & path, GitFileSystemObjectSinkImpl & back, git_writestream * stream) + CRF(const CanonPath & path, GitFileSystemObjectSinkImpl & back, git_writestream * stream) : path(path), back(back), stream(stream) {} void operator () (std::string_view data) override @@ -891,15 +891,15 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink : GIT_FILEMODE_BLOB); } - void createDirectory(const Path & path) override + void createDirectory(const CanonPath & path) override { - auto pathComponents = tokenizeString>(path, "/"); + auto pathComponents = tokenizeString>(path.rel(), "/"); (void) prepareDirs(pathComponents, true); } - void createSymlink(const Path & path, const std::string & target) override + void createSymlink(const CanonPath & path, const std::string & target) override { - auto pathComponents = tokenizeString>(path, "/"); + auto pathComponents = tokenizeString>(path.rel(), "/"); if (!prepareDirs(pathComponents, false)) return; git_oid oid; diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index cecf8148f..33dde48e5 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -71,9 +71,9 @@ struct NarAccessor : public SourceAccessor : acc(acc), source(source) { } - NarMember & createMember(const Path & path, NarMember member) + NarMember & createMember(const CanonPath & path, NarMember member) { - size_t level = std::count(path.begin(), path.end(), '/'); + size_t level = std::count(path.rel().begin(), path.rel().end(), '/'); while (parents.size() > level) parents.pop(); if (parents.empty()) { @@ -83,14 +83,14 @@ struct NarAccessor : public SourceAccessor } else { if (parents.top()->stat.type != Type::tDirectory) throw Error("NAR file missing parent directory of path '%s'", path); - auto result = parents.top()->children.emplace(baseNameOf(path), std::move(member)); + auto result = parents.top()->children.emplace(baseNameOf(path.rel()), std::move(member)); auto & ref = result.first->second; parents.push(&ref); return ref; } } - void createDirectory(const Path & path) override + void createDirectory(const CanonPath & path) override { createMember(path, NarMember{ .stat = { .type = Type::tDirectory, @@ -100,7 +100,7 @@ struct NarAccessor : public SourceAccessor } }); } - void createRegularFile(const Path & path, std::function func) override + void createRegularFile(const CanonPath & path, std::function func) override { auto & nm = createMember(path, NarMember{ .stat = { .type = Type::tRegular, @@ -112,7 +112,7 @@ struct NarAccessor : public SourceAccessor func(nmc); } - void createSymlink(const Path & path, const std::string & target) override + void createSymlink(const CanonPath & path, const std::string & target) override { createMember(path, NarMember{ diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 22be392d4..3693a1ffd 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -165,7 +165,7 @@ struct CaseInsensitiveCompare }; -static void parse(FileSystemObjectSink & sink, Source & source, const Path & path) +static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath & path) { std::string s; @@ -246,7 +246,7 @@ static void parse(FileSystemObjectSink & sink, Source & source, const Path & pat } } else if (s == "node") { if (name.empty()) throw badArchive("entry name missing"); - parse(sink, source, path + "/" + name); + parse(sink, source, path / name); } else throw badArchive("unknown field " + s); } @@ -290,7 +290,7 @@ void parseDump(FileSystemObjectSink & sink, Source & source) } if (version != narVersionMagic1) throw badArchive("input doesn't look like a Nix archive"); - parse(sink, source, ""); + parse(sink, source, CanonPath{""}); } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index f75851bbd..9307a0b53 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -127,7 +127,7 @@ Path dirOf(const PathView path) } -std::string_view baseNameOf(std::string_view path) +std::string_view baseNameOf(PathView path) { if (path.empty()) return ""; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index a6a743737..b4900cf8b 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -14,7 +14,7 @@ namespace nix { void copyRecursive( SourceAccessor & accessor, const CanonPath & from, - FileSystemObjectSink & sink, const Path & to) + FileSystemObjectSink & sink, const CanonPath & to) { auto stat = accessor.lstat(from); @@ -43,7 +43,7 @@ void copyRecursive( for (auto & [name, _] : accessor.readDirectory(from)) { copyRecursive( accessor, from / name, - sink, to + "/" + name); + sink, to / name); break; } break; @@ -69,17 +69,9 @@ static RestoreSinkSettings restoreSinkSettings; static GlobalConfig::Register r1(&restoreSinkSettings); -void RestoreSink::createDirectory(const Path & path) +void RestoreSink::createDirectory(const CanonPath & path) { - Path p = dstPath + path; - if ( -#ifndef _WIN32 // TODO abstract mkdir perms for Windows - mkdir(p.c_str(), 0777) == -1 -#else - !CreateDirectoryW(pathNG(p).c_str(), NULL) -#endif - ) - throw NativeSysError("creating directory '%1%'", p); + std::filesystem::create_directory(dstPath / path.rel()); }; struct RestoreRegularFile : CreateRegularFileSink { @@ -90,13 +82,14 @@ struct RestoreRegularFile : CreateRegularFileSink { void preallocateContents(uint64_t size) override; }; -void RestoreSink::createRegularFile(const Path & path, std::function func) +void RestoreSink::createRegularFile(const CanonPath & path, std::function func) { - Path p = dstPath + path; + std::cout << "SCREAM!!!====== " << dstPath / path.rel() << std::endl; + std::filesystem::path p = dstPath / path.rel(); RestoreRegularFile crf; crf.fd = #ifdef _WIN32 - CreateFileW(pathNG(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) + CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) #else open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666) #endif @@ -141,14 +134,14 @@ void RestoreRegularFile::operator () (std::string_view data) writeFull(fd.get(), data); } -void RestoreSink::createSymlink(const Path & path, const std::string & target) +void RestoreSink::createSymlink(const CanonPath & path, const std::string & target) { - Path p = dstPath + path; + std::filesystem::path p = dstPath / path.rel(); nix::createSymlink(target, p); } -void RegularFileSink::createRegularFile(const Path & path, std::function func) +void RegularFileSink::createRegularFile(const CanonPath & path, std::function func) { struct CRF : CreateRegularFileSink { RegularFileSink & back; @@ -163,7 +156,7 @@ void RegularFileSink::createRegularFile(const Path & path, std::function func) +void NullFileSystemObjectSink::createRegularFile(const CanonPath & path, std::function func) { struct : CreateRegularFileSink { void operator () (std::string_view data) override {} diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index ae577819a..cf7d34d22 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -28,17 +28,17 @@ struct FileSystemObjectSink { virtual ~FileSystemObjectSink() = default; - virtual void createDirectory(const Path & path) = 0; + virtual void createDirectory(const CanonPath & path) = 0; /** * This function in general is no re-entrant. Only one file can be * written at a time. */ virtual void createRegularFile( - const Path & path, + const CanonPath & path, std::function) = 0; - virtual void createSymlink(const Path & path, const std::string & target) = 0; + virtual void createSymlink(const CanonPath & path, const std::string & target) = 0; }; /** @@ -46,17 +46,17 @@ struct FileSystemObjectSink */ void copyRecursive( SourceAccessor & accessor, const CanonPath & sourcePath, - FileSystemObjectSink & sink, const Path & destPath); + FileSystemObjectSink & sink, const CanonPath & destPath); /** * Ignore everything and do nothing */ struct NullFileSystemObjectSink : FileSystemObjectSink { - void createDirectory(const Path & path) override { } - void createSymlink(const Path & path, const std::string & target) override { } + void createDirectory(const CanonPath & path) override { } + void createSymlink(const CanonPath & path, const std::string & target) override { } void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; }; @@ -65,15 +65,15 @@ struct NullFileSystemObjectSink : FileSystemObjectSink */ struct RestoreSink : FileSystemObjectSink { - Path dstPath; + std::filesystem::path dstPath; - void createDirectory(const Path & path) override; + void createDirectory(const CanonPath & path) override; void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; - void createSymlink(const Path & path, const std::string & target) override; + void createSymlink(const CanonPath & path, const std::string & target) override; }; /** @@ -88,18 +88,18 @@ struct RegularFileSink : FileSystemObjectSink RegularFileSink(Sink & sink) : sink(sink) { } - void createDirectory(const Path & path) override + void createDirectory(const CanonPath & path) override { regular = false; } - void createSymlink(const Path & path, const std::string & target) override + void createSymlink(const CanonPath & path, const std::string & target) override { regular = false; } void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; }; diff --git a/src/libutil/git.cc b/src/libutil/git.cc index 8c538c988..f23df566a 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -53,7 +53,7 @@ static std::string getString(Source & source, int n) void parseBlob( FileSystemObjectSink & sink, - const Path & sinkPath, + const CanonPath & sinkPath, Source & source, BlobMode blobMode, const ExperimentalFeatureSettings & xpSettings) @@ -116,7 +116,7 @@ void parseBlob( void parseTree( FileSystemObjectSink & sink, - const Path & sinkPath, + const CanonPath & sinkPath, Source & source, std::function hook, const ExperimentalFeatureSettings & xpSettings) @@ -147,7 +147,7 @@ void parseTree( Hash hash(HashAlgorithm::SHA1); std::copy(hashs.begin(), hashs.end(), hash.hash); - hook(name, TreeEntry { + hook(CanonPath{name}, TreeEntry { .mode = mode, .hash = hash, }); @@ -171,7 +171,7 @@ ObjectType parseObjectType( void parse( FileSystemObjectSink & sink, - const Path & sinkPath, + const CanonPath & sinkPath, Source & source, BlobMode rootModeIfBlob, std::function hook, @@ -208,7 +208,7 @@ std::optional convertMode(SourceAccessor::Type type) void restore(FileSystemObjectSink & sink, Source & source, std::function hook) { - parse(sink, "", source, BlobMode::Regular, [&](Path name, TreeEntry entry) { + parse(sink, CanonPath{""}, source, BlobMode::Regular, [&](CanonPath name, TreeEntry entry) { auto [accessor, from] = hook(entry.hash); auto stat = accessor->lstat(from); auto gotOpt = convertMode(stat.type); diff --git a/src/libutil/git.hh b/src/libutil/git.hh index a65edb964..2190cc550 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -64,7 +64,7 @@ using Tree = std::map; * Implementations may seek to memoize resources (bandwidth, storage, * etc.) for the same Git hash. */ -using SinkHook = void(const Path & name, TreeEntry entry); +using SinkHook = void(const CanonPath & name, TreeEntry entry); /** * Parse the "blob " or "tree " prefix. @@ -89,13 +89,13 @@ enum struct BlobMode : RawMode }; void parseBlob( - FileSystemObjectSink & sink, const Path & sinkPath, + FileSystemObjectSink & sink, const CanonPath & sinkPath, Source & source, BlobMode blobMode, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); void parseTree( - FileSystemObjectSink & sink, const Path & sinkPath, + FileSystemObjectSink & sink, const CanonPath & sinkPath, Source & source, std::function hook, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); @@ -108,7 +108,7 @@ void parseTree( * a blob, this is ignored. */ void parse( - FileSystemObjectSink & sink, const Path & sinkPath, + FileSystemObjectSink & sink, const CanonPath & sinkPath, Source & source, BlobMode rootModeIfBlob, std::function hook, diff --git a/src/libutil/memory-source-accessor.cc b/src/libutil/memory-source-accessor.cc index b7207cffb..c4eee1031 100644 --- a/src/libutil/memory-source-accessor.cc +++ b/src/libutil/memory-source-accessor.cc @@ -124,9 +124,9 @@ SourcePath MemorySourceAccessor::addFile(CanonPath path, std::string && contents using File = MemorySourceAccessor::File; -void MemorySink::createDirectory(const Path & path) +void MemorySink::createDirectory(const CanonPath & path) { - auto * f = dst.open(CanonPath{path}, File { File::Directory { } }); + auto * f = dst.open(path, File { File::Directory { } }); if (!f) throw Error("file '%s' cannot be made because some parent file is not a directory", path); @@ -146,9 +146,9 @@ struct CreateMemoryRegularFile : CreateRegularFileSink { void preallocateContents(uint64_t size) override; }; -void MemorySink::createRegularFile(const Path & path, std::function func) +void MemorySink::createRegularFile(const CanonPath & path, std::function func) { - auto * f = dst.open(CanonPath{path}, File { File::Regular {} }); + auto * f = dst.open(path, File { File::Regular {} }); if (!f) throw Error("file '%s' cannot be made because some parent file is not a directory", path); if (auto * rp = std::get_if(&f->raw)) { @@ -173,9 +173,9 @@ void CreateMemoryRegularFile::operator () (std::string_view data) regularFile.contents += data; } -void MemorySink::createSymlink(const Path & path, const std::string & target) +void MemorySink::createSymlink(const CanonPath & path, const std::string & target) { - auto * f = dst.open(CanonPath{path}, File { File::Symlink { } }); + auto * f = dst.open(path, File { File::Symlink { } }); if (!f) throw Error("file '%s' cannot be made because some parent file is not a directory", path); if (auto * s = std::get_if(&f->raw)) diff --git a/src/libutil/memory-source-accessor.hh b/src/libutil/memory-source-accessor.hh index c8f793922..cd5146c89 100644 --- a/src/libutil/memory-source-accessor.hh +++ b/src/libutil/memory-source-accessor.hh @@ -81,13 +81,13 @@ struct MemorySink : FileSystemObjectSink MemorySink(MemorySourceAccessor & dst) : dst(dst) { } - void createDirectory(const Path & path) override; + void createDirectory(const CanonPath & path) override; void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; - void createSymlink(const Path & path, const std::string & target) override; + void createSymlink(const CanonPath & path, const std::string & target) override; }; } diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index f0e24e937..c7faedd1e 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -178,6 +178,7 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin auto path = archive_entry_pathname(entry); if (!path) throw Error("cannot get archive member name: %s", archive_error_string(archive.archive)); + auto cpath = CanonPath{path}; if (r == ARCHIVE_WARN) warn(archive_error_string(archive.archive)); else @@ -188,11 +189,11 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin switch (archive_entry_filetype(entry)) { case AE_IFDIR: - parseSink.createDirectory(path); + parseSink.createDirectory(cpath); break; case AE_IFREG: { - parseSink.createRegularFile(path, [&](auto & crf) { + parseSink.createRegularFile(cpath, [&](auto & crf) { if (archive_entry_mode(entry) & S_IXUSR) crf.isExecutable(); @@ -216,7 +217,7 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin case AE_IFLNK: { auto target = archive_entry_symlink(entry); - parseSink.createSymlink(path, target); + parseSink.createSymlink(cpath, target); break; } diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index ff934c117..9454bb675 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -67,7 +67,7 @@ TEST_F(GitTest, blob_read) { StringSink out; RegularFileSink out2 { out }; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Blob); - parseBlob(out2, "", in, BlobMode::Regular, mockXpSettings); + parseBlob(out2, CanonPath{""}, in, BlobMode::Regular, mockXpSettings); auto expected = readFile(goldenMaster("hello-world.bin")); @@ -132,8 +132,8 @@ TEST_F(GitTest, tree_read) { NullFileSystemObjectSink out; Tree got; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Tree); - parseTree(out, "", in, [&](auto & name, auto entry) { - auto name2 = name; + parseTree(out, CanonPath{""}, in, [&](auto & name, auto entry) { + auto name2 = std::string{name.rel()}; if (entry.mode == Mode::Directory) name2 += '/'; got.insert_or_assign(name2, std::move(entry)); @@ -210,14 +210,14 @@ TEST_F(GitTest, both_roundrip) { MemorySink sinkFiles2 { *files2 }; - std::function mkSinkHook; + std::function mkSinkHook; mkSinkHook = [&](auto prefix, auto & hash, auto blobMode) { StringSource in { cas[hash] }; parse( sinkFiles2, prefix, in, blobMode, - [&](const Path & name, const auto & entry) { + [&](const CanonPath & name, const auto & entry) { mkSinkHook( - prefix + "/" + name, + prefix / name, entry.hash, // N.B. this cast would not be acceptable in real // code, because it would make an assert reachable, @@ -227,7 +227,7 @@ TEST_F(GitTest, both_roundrip) { mockXpSettings); }; - mkSinkHook("", root.hash, BlobMode::Regular); + mkSinkHook(CanonPath{""}, root.hash, BlobMode::Regular); ASSERT_EQ(*files, *files2); } From 39154ed9bed23cf576c1681068677814d91496c6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 30 Jun 2024 17:10:34 +0200 Subject: [PATCH 422/910] ci.yml: Put installer cachix in verbose mode Maybe it prints something, as a workaround for https://github.com/cachix/cachix-action/issues/153, which might explain our flaky installer_test. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 103ce4ff4..c4939dd11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,6 +96,7 @@ jobs: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + cachixArgs: '-v' - id: prepare-installer run: scripts/prepare-installer-for-github-actions From 5a16bf86c518037f05bffbee0d73d715b672908b Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sun, 30 Jun 2024 17:22:04 +0100 Subject: [PATCH 423/910] doc: fix `directory` definition in nix-archive.md (#10997) * doc: fix `directory` definition in nix-archive.md Before the change the document implied that directory of a single entry contained entry: "type" "directory" "type" directory" "entry" ... After the change document should expand into: "type" "directory" "entry" ... Co-authored-by: John Ericson --- doc/manual/src/protocols/nix-archive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/protocols/nix-archive.md b/doc/manual/src/protocols/nix-archive.md index bfc523b3d..640b527f1 100644 --- a/doc/manual/src/protocols/nix-archive.md +++ b/doc/manual/src/protocols/nix-archive.md @@ -29,7 +29,7 @@ regular = [ str("executable"), str("") ], str("contents"), str(contents); symlink = str("target"), str(target); (* side condition: directory entries must be ordered by their names *) -directory = str("type"), str("directory") { directory-entry }; +directory = { directory-entry }; directory-entry = str("entry"), str("("), str("name"), str(name), str("node"), nar-obj, str(")"); ``` From e084316130a255313eb026db8bc64a653f5ffb0c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 19:28:09 +0200 Subject: [PATCH 424/910] Add mkMesonPackage for local meson packages This helper makes it easy to use filesets that include files from parent directories, which we'll need more of in https://github.com/NixOS/nix/pull/10973 --- packaging/dependencies.nix | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 88273df22..484385128 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -10,6 +10,33 @@ stdenv, versionSuffix, }: +let + inherit (pkgs) lib; + + localSourceLayer = finalAttrs: prevAttrs: + let + root = ../.; + workDirPath = + # Ideally we'd pick finalAttrs.workDir, but for now `mkDerivation` has + # the requirement that everything except passthru and meta must be + # serialized by mkDerivation, which doesn't work for this. + prevAttrs.workDir; + + workDirSubpath = lib.path.removePrefix root workDirPath; + sources = assert prevAttrs.fileset._type == "fileset"; prevAttrs.fileset; + src = lib.fileset.toSource { fileset = sources; inherit root; }; + + in + { + sourceRoot = "${src.name}/" + workDirSubpath; + inherit src; + + # Clear what `derivation` can't/shouldn't serialize; see prevAttrs.workDir. + fileset = null; + workDir = null; + }; + +in scope: { inherit stdenv versionSuffix; @@ -55,4 +82,6 @@ scope: { CONFIG_ASH_TEST y ''; }); + + mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); } From 7dd938b228c0dc5c5e42eb0efcbbe9929bde9f90 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 30 Jun 2024 19:42:05 +0200 Subject: [PATCH 425/910] libutil/package.nix: Remove .version symlink replacement solution --- src/libutil/package.nix | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 892951cdf..1996a6c4d 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -38,22 +39,22 @@ let else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - ./linux/meson.build - ./unix/meson.build - ./windows/meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../.version + ./.version + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -80,13 +81,9 @@ mkDerivation (finalAttrs: { disallowedReferences = [ boost ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix - '' - echo ${version} > .version - '' # Copy some boost libraries so we don't get all of Boost in our # closure. https://github.com/NixOS/nixpkgs/issues/45462 - + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' mkdir -p $out/lib cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib rm -f $out/lib/*.a From 93b50857edbee33c7c6522d4d5971b5081c5b7c1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 30 Jun 2024 17:25:32 +0200 Subject: [PATCH 426/910] packaging: Restore .version value altering behavior --- src/libutil/package.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 1996a6c4d..8ba6daa2a 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -81,9 +81,14 @@ mkMesonDerivation (finalAttrs: { disallowedReferences = [ boost ]; preConfigure = + # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. + '' + chmod u+w ./.version + echo ${version} > ../../.version + '' # Copy some boost libraries so we don't get all of Boost in our # closure. https://github.com/NixOS/nixpkgs/issues/45462 - lib.optionalString (!stdenv.hostPlatform.isStatic) ('' + + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' mkdir -p $out/lib cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib rm -f $out/lib/*.a From 992912f3b4a0eb8aaa7f27e5cb67e351b6ab07ac Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:12:34 +0200 Subject: [PATCH 427/910] test-support: Add TracingFileSystemObjectSink --- src/libutil/fs-sink.hh | 2 +- .../tests/tracing-file-system-object-sink.cc | 33 +++++++++++++++ .../tests/tracing-file-system-object-sink.hh | 41 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc create mode 100644 tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index 994f19960..32dcb2f01 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -45,7 +45,7 @@ struct FileSystemObjectSink * An extension of `FileSystemObjectSink` that supports file types * that are not supported by Nix's FSO model. */ -struct ExtendedFileSystemObjectSink : FileSystemObjectSink +struct ExtendedFileSystemObjectSink : virtual FileSystemObjectSink { /** * Create a hard link. The target must be the path of a previously diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc new file mode 100644 index 000000000..737e02213 --- /dev/null +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc @@ -0,0 +1,33 @@ +#include +#include "tracing-file-system-object-sink.hh" + +namespace nix::test { + +void TracingFileSystemObjectSink::createDirectory(const Path & path) +{ + std::cerr << "createDirectory(" << path << ")\n"; + sink.createDirectory(path); +} + +void TracingFileSystemObjectSink::createRegularFile(const Path & path, std::function fn) +{ + std::cerr << "createRegularFile(" << path << ")\n"; + sink.createRegularFile(path, [&](CreateRegularFileSink & crf) { + // We could wrap this and trace about the chunks of data and such + fn(crf); + }); +} + +void TracingFileSystemObjectSink::createSymlink(const Path & path, const std::string & target) +{ + std::cerr << "createSymlink(" << path << ", target: " << target << ")\n"; + sink.createSymlink(path, target); +} + +void TracingExtendedFileSystemObjectSink::createHardlink(const Path & path, const CanonPath & target) +{ + std::cerr << "createHardlink(" << path << ", target: " << target << ")\n"; + sink.createHardlink(path, target); +} + +} // namespace nix::test diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh new file mode 100644 index 000000000..9527b0be3 --- /dev/null +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh @@ -0,0 +1,41 @@ +#pragma once +#include "fs-sink.hh" + +namespace nix::test { + +/** + * A `FileSystemObjectSink` that traces calls, writing to stderr. + */ +class TracingFileSystemObjectSink : public virtual FileSystemObjectSink +{ + FileSystemObjectSink & sink; +public: + TracingFileSystemObjectSink(FileSystemObjectSink & sink) + : sink(sink) + { + } + + void createDirectory(const Path & path) override; + + void createRegularFile(const Path & path, std::function fn); + + void createSymlink(const Path & path, const std::string & target); +}; + +/** + * A `ExtendedFileSystemObjectSink` that traces calls, writing to stderr. + */ +class TracingExtendedFileSystemObjectSink : public TracingFileSystemObjectSink, public ExtendedFileSystemObjectSink +{ + ExtendedFileSystemObjectSink & sink; +public: + TracingExtendedFileSystemObjectSink(ExtendedFileSystemObjectSink & sink) + : TracingFileSystemObjectSink(sink) + , sink(sink) + { + } + + void createHardlink(const Path & path, const CanonPath & target); +}; + +} From 1fac22b16e216e3ab8850d542be587eb379605b6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:13:48 +0200 Subject: [PATCH 428/910] GitFileSystemObjectSink: Add path context to some messages --- src/libfetchers/git-utils.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index ca02fbc89..c41cfe683 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -927,7 +927,7 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink std::string_view relTargetLeft(relTarget); while (hasPrefix(relTargetLeft, "../")) { if (dir == pendingDirs.rend()) - throw Error("invalid hard link target '%s'", target); + throw Error("invalid hard link target '%s' for path '%s'", target, path); ++dir; relTargetLeft = relTargetLeft.substr(3); } @@ -940,13 +940,14 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink for (auto & c : CanonPath(relTargetLeft)) { if (auto builder = std::get_if(&curDir)) { + assert(*builder); if (!(entry = git_treebuilder_get(*builder, std::string(c).c_str()))) - throw Error("cannot find hard link target '%s'", target); + throw Error("cannot find hard link target '%s' for path '%s'", target, path); curDir = *git_tree_entry_id(entry); } else if (auto oid = std::get_if(&curDir)) { tree = lookupObject(*repo, *oid, GIT_OBJECT_TREE); if (!(entry = git_tree_entry_byname((const git_tree *) &*tree, std::string(c).c_str()))) - throw Error("cannot find hard link target '%s'", target); + throw Error("cannot find hard link target '%s' for path '%s'", target, path); curDir = *git_tree_entry_id(entry); } } From a409c1a882e65878476f58d033321eb05e163ab5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:22:26 +0200 Subject: [PATCH 429/910] Start unit testing GitFileSystemObjectSink --- tests/unit/libfetchers/git-utils.cc | 90 +++++++++++++++++++++++++++++ tests/unit/libfetchers/local.mk | 2 +- 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libfetchers/git-utils.cc diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc new file mode 100644 index 000000000..c007d1c3c --- /dev/null +++ b/tests/unit/libfetchers/git-utils.cc @@ -0,0 +1,90 @@ +#include "git-utils.hh" +#include "file-system.hh" +#include "gmock/gmock.h" +#include +#include +#include +#include +#include "fs-sink.hh" +#include "serialise.hh" + +namespace nix { + +class GitUtilsTest : public ::testing::Test +{ + // We use a single repository for all tests. + Path tmpDir; + std::unique_ptr delTmpDir; + +public: + void SetUp() override + { + tmpDir = createTempDir(); + delTmpDir = std::make_unique(tmpDir, true); + + // Create the repo with libgit2 + git_libgit2_init(); + git_repository * repo = nullptr; + auto r = git_repository_init(&repo, tmpDir.c_str(), 0); + ASSERT_EQ(r, 0); + git_repository_free(repo); + } + + void TearDown() override + { + // Destroy the AutoDelete, triggering removal + // not AutoDelete::reset(), which would cancel the deletion. + delTmpDir.reset(); + } + + ref openRepo() + { + return GitRepo::openRepo(tmpDir, true, false); + } +}; + +void writeString(CreateRegularFileSink & fileSink, std::string contents, bool executable) +{ + if (executable) + fileSink.isExecutable(); + fileSink.preallocateContents(contents.size()); + fileSink(contents); +} + +TEST_F(GitUtilsTest, sink_basic) +{ + auto repo = openRepo(); + auto sink = repo->getFileSystemObjectSink(); + + // TODO/Question: It seems a little odd that we use the tarball-like convention of requiring a top-level directory + // here + // The sync method does not document this behavior, should probably renamed because it's not very + // general, and I can't imagine that "non-conventional" archives or any other source to be handled by + // this sink. + + sink->createDirectory("foo-1.1"); + + sink->createRegularFile( + "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); + sink->createRegularFile("foo-1.1/bye", [](CreateRegularFileSink & fileSink) { + writeString(fileSink, "thanks for all the fish", false); + }); + sink->createSymlink("foo-1.1/bye-link", "bye"); + sink->createDirectory("foo-1.1/empty"); + sink->createDirectory("foo-1.1/links"); + sink->createHardlink("foo-1.1/links/foo", CanonPath("foo-1.1/hello")); + + // sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello")); + + auto result = sink->sync(); + auto accessor = repo->getAccessor(result, false); + auto entries = accessor->readDirectory(CanonPath::root); + ASSERT_EQ(entries.size(), 5); + ASSERT_EQ(accessor->readFile(CanonPath("hello")), "hello world"); + ASSERT_EQ(accessor->readFile(CanonPath("bye")), "thanks for all the fish"); + ASSERT_EQ(accessor->readLink(CanonPath("bye-link")), "bye"); + ASSERT_EQ(accessor->readDirectory(CanonPath("empty")).size(), 0); + ASSERT_EQ(accessor->readFile(CanonPath("links/foo")), "hello world"); +}; + +} // namespace nix diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk index d576d28f3..f4e56a501 100644 --- a/tests/unit/libfetchers/local.mk +++ b/tests/unit/libfetchers/local.mk @@ -29,7 +29,7 @@ libfetchers-tests_LIBS = \ libstore-test-support libutil-test-support \ libfetchers libstore libutil -libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) +libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) $(LIBGIT2_LIBS) ifdef HOST_WINDOWS # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space From f0329568b5afeddd03db4b969489e19a1bd2ee97 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:14:27 +0200 Subject: [PATCH 430/910] GitFileSystemObjectSink: catch an overflow --- src/libfetchers/git-utils.cc | 2 ++ tests/unit/libfetchers/git-utils.cc | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index c41cfe683..64fd39aed 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -931,6 +931,8 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink ++dir; relTargetLeft = relTargetLeft.substr(3); } + if (dir == pendingDirs.rend()) + throw Error("invalid hard link target '%s' for path '%s'", target, path); // Look up the remainder of the target, starting at the // top-most `git_treebuilder`. diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index c007d1c3c..3c06b593a 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -87,4 +87,24 @@ TEST_F(GitUtilsTest, sink_basic) ASSERT_EQ(accessor->readFile(CanonPath("links/foo")), "hello world"); }; +TEST_F(GitUtilsTest, sink_hardlink) +{ + auto repo = openRepo(); + auto sink = repo->getFileSystemObjectSink(); + + sink->createDirectory("foo-1.1"); + + sink->createRegularFile( + "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); + + try { + sink->createHardlink("foo-1.1/link", CanonPath("hello")); + FAIL() << "Expected an exception"; + } catch (const nix::Error & e) { + ASSERT_THAT(e.msg(), testing::HasSubstr("invalid hard link target")); + ASSERT_THAT(e.msg(), testing::HasSubstr("/hello")); + ASSERT_THAT(e.msg(), testing::HasSubstr("foo-1.1/link")); + } +}; + } // namespace nix From 6600b1c7e07a87347505a80938739b2d44f763a7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 1 Jul 2024 19:10:41 +0200 Subject: [PATCH 431/910] tests/functional/flakes/eval-cache.sh: Don't write a result symlink in the wrong location --- tests/functional/flakes/eval-cache.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh index d581a8dbd..e3fd0bbfe 100755 --- a/tests/functional/flakes/eval-cache.sh +++ b/tests/functional/flakes/eval-cache.sh @@ -36,4 +36,4 @@ expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' # Conditional error should not be cached expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \ | grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled' -nix build "$flake1Dir#ifd" +nix build --no-link "$flake1Dir#ifd" From df3e92ff964ba7e87c426f4bbd621032811ab5e1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 20:38:43 +0200 Subject: [PATCH 432/910] installerScriptForGHA: aarch64-darwin GitHub Actions seems to have magically switched architectures without changing their identifiers. See https://github.com/actions/runner-images/blob/2813ee66cbe85b31a8322ff8967148548b1a5db9/README.md#available-images Maybe they have more complete documentation elsewhere, but it seems to be incapable of selecting a runner based on architecture. --- packaging/hydra.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 5715abd8e..a1691ed38 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -105,7 +105,7 @@ in installerScriptForGHA = installScriptFor [ # Native self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" + self.hydraJobs.binaryTarball."aarch64-darwin" # Cross self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" From 101915c9b73cfe434457f380de4fbaea03430851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 2 Jul 2024 08:35:56 +0200 Subject: [PATCH 433/910] enable -Werror=unused-result Inspired by https://git.lix.systems/lix-project/lix/commit/010ff57ebb40f1a9aaff99867d2886f0e59f774a From the original PR: > We do not have any of these warnings appearing at the moment, but > it seems like a good idea to enable [[nodiscard]] checking anyway. > Once we start introducing more functions with must-use conditions we will > need such checking, and the rust stdlib has proven them very useful. --- Makefile | 2 +- src/libfetchers/meson.build | 1 + src/libstore/meson.build | 1 + src/libutil-c/meson.build | 1 + src/libutil/meson.build | 1 + src/perl/meson.build | 5 ++++- tests/unit/libutil-support/meson.build | 1 + tests/unit/libutil/meson.build | 1 + 8 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 132fe29cc..bb64a104e 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -Werror=unused-result -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index cab8d63eb..d7975ac65 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -58,6 +58,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/libstore/meson.build b/src/libstore/meson.build index a605b43e1..d9237c55a 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -164,6 +164,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index a2b020818..5de288e18 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -34,6 +34,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 9d0b28539..099c0c65f 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -143,6 +143,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/perl/meson.build b/src/perl/meson.build index 06abb4f2e..5fe7e1e28 100644 --- a/src/perl/meson.build +++ b/src/perl/meson.build @@ -23,11 +23,14 @@ nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) # set error arguments #------------------------------------------------- error_args = [ + '-Werror=unused-result', + '-Wdeprecated-copy', + '-Wdeprecated-declarations', + '-Wignored-qualifiers', '-Wno-pedantic', '-Wno-non-virtual-dtor', '-Wno-unused-parameter', '-Wno-variadic-macros', - '-Wdeprecated-declarations', '-Wno-missing-field-initializers', '-Wno-unknown-warning-option', '-Wno-unused-variable', diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build index 5912c00e0..c0345a6ee 100644 --- a/tests/unit/libutil-support/meson.build +++ b/tests/unit/libutil-support/meson.build @@ -28,6 +28,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index 56147686b..cc13c4364 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -34,6 +34,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked From fbdc5549085b1162109e8853799a627f479c5a72 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 21:19:44 -0400 Subject: [PATCH 434/910] Fix Nix shell for building Perl too --- src/perl/package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/src/perl/package.nix b/src/perl/package.nix index 85f1547b7..e1a84924c 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -40,6 +40,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { meson ninja pkg-config + perl ]; buildInputs = [ From 31257009e1562e00a4c99dd93e3e31e0c9074522 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 22:15:33 -0400 Subject: [PATCH 435/910] Meson build for libexpr and libflake --- meson.build | 8 +- packaging/components.nix | 5 +- src/libexpr/.version | 1 + src/libexpr/meson.build | 254 +++++++++++++++++++++++++ src/libexpr/meson.options | 3 + src/libexpr/package.nix | 130 +++++++++++++ src/libexpr/primops/fetchTree.cc | 2 +- src/libfetchers/meson.build | 46 ++--- src/libfetchers/package.nix | 7 +- src/libflake/.version | 1 + src/libflake/meson.build | 120 ++++++++++++ src/libflake/package.nix | 99 ++++++++++ src/libstore/meson.build | 40 ++-- src/libstore/package.nix | 3 - tests/unit/libutil-support/meson.build | 39 ++-- tests/unit/libutil/meson.build | 95 ++++----- 16 files changed, 731 insertions(+), 122 deletions(-) create mode 120000 src/libexpr/.version create mode 100644 src/libexpr/meson.build create mode 100644 src/libexpr/meson.options create mode 100644 src/libexpr/package.nix create mode 120000 src/libflake/.version create mode 100644 src/libflake/meson.build create mode 100644 src/libflake/package.nix diff --git a/meson.build b/meson.build index 085ac0865..7832eb488 100644 --- a/meson.build +++ b/meson.build @@ -9,13 +9,19 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') subproject('libfetchers') -subproject('perl') +subproject('libexpr') +subproject('libflake') + +# Docs subproject('internal-api-docs') subproject('external-api-docs') # C wrappers subproject('libutil-c') +# Language Bindings +subproject('perl') + # Testing subproject('libutil-test-support') subproject('libutil-test') diff --git a/packaging/components.nix b/packaging/components.nix index b5e47969e..01b4e826e 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -19,10 +19,13 @@ in nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-perl-bindings = callPackage ../src/perl/package.nix { }; + nix-expr = callPackage ../src/libexpr/package.nix { }; + + nix-flake = callPackage ../src/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; + nix-perl-bindings = callPackage ../src/perl/package.nix { }; } diff --git a/src/libexpr/.version b/src/libexpr/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build new file mode 100644 index 000000000..8f08decd4 --- /dev/null +++ b/src/libexpr/meson.build @@ -0,0 +1,254 @@ +project('nix-expr', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-fetchers'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') + deps_private += dependency('threads') +endif + +boost = dependency( + 'boost', + modules : ['container', 'context'], +) +# boost is a public dependency, but not a pkg-config dependency unfortunately, so we +# put in `deps_other`. +deps_other += boost + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +bdw_gc = dependency('bdw-gc', required : get_option('gc')) +if bdw_gc.found() + deps_public += bdw_gc + foreach funcspec : [ + 'pthread_attr_get_np', + 'pthread_getattr_np', + ] + define_name = 'HAVE_' + funcspec.underscorify().to_upper() + define_value = cxx.has_function(funcspec).to_int() + configdata.set(define_name, define_value) + endforeach + configdata.set('GC_THREADS', 1) +endif +configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) + +config_h = configure_file( + configuration : configdata, + output : 'config-expr.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + # '-include', 'config-fetchers.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +parser_tab = custom_target( + input : 'parser.y', + output : [ + 'parser-tab.cc', + 'parser-tab.hh', + ], + command : [ + 'bison', + '-v', + '-o', + '@OUTPUT0@', + '@INPUT@', + '-d', + ], + # NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add + # an install script below which removes parser-tab.cc. + install : true, + install_dir : get_option('includedir') / 'nix', +) + +lexer_tab = custom_target( + input : [ + 'lexer.l', + parser_tab, + ], + output : [ + 'lexer-tab.cc', + 'lexer-tab.hh', + ], + command : [ + 'flex', + '--outfile', + '@OUTPUT0@', + '--header-file=' + '@OUTPUT1@', + '@INPUT0@', + ], + # NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add + # an install script below which removes lexer-tab.cc. + install : true, + install_dir : get_option('includedir') / 'nix', +) + +generated_headers = [] +foreach header : [ + 'imported-drv-to-derivation.nix', + 'fetchurl.nix', + 'flake/call-flake.nix', + 'primops/derivation.nix', +] + generated_headers += custom_target( + command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + input : header, + output : '@PLAINNAME@.gen.hh', + ) +endforeach + + +sources = files( + 'attr-path.cc', + 'attr-set.cc', + 'eval-cache.cc', + 'eval-error.cc', + 'eval-gc.cc', + 'eval-settings.cc', + 'eval.cc', + 'function-trace.cc', + 'get-drvs.cc', + 'json-to-value.cc', + 'nixexpr.cc', + 'paths.cc', + 'primops.cc', + 'primops/context.cc', + 'primops/fetchClosure.cc', + 'primops/fetchMercurial.cc', + 'primops/fetchTree.cc', + 'primops/fromTOML.cc', + 'print-ambiguous.cc', + 'print.cc', + 'search-path.cc', + 'value-to-json.cc', + 'value-to-xml.cc', + 'value/context.cc', +) + +headers = [config_h] + files( + 'attr-path.hh', + 'attr-set.hh', + 'eval-cache.hh', + 'eval-error.hh', + 'eval-gc.hh', + 'eval-inline.hh', + 'eval-settings.hh', + 'eval.hh', + 'function-trace.hh', + 'gc-small-vector.hh', + 'get-drvs.hh', + 'json-to-value.hh', + 'nixexpr.hh', + 'parser-state.hh', + 'pos-idx.hh', + 'pos-table.hh', + 'primops.hh', + 'print-ambiguous.hh', + 'print-options.hh', + 'print.hh', + 'repl-exit-status.hh', + 'search-path.hh', + 'symbol-table.hh', + 'value-to-json.hh', + 'value-to-xml.hh', + 'value.hh', + 'value/context.hh', +) + +this_library = library( + 'nixexpr', + sources, + parser_tab, + lexer_tab, + generated_headers, + dependencies : deps_public + deps_private + deps_other, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, + libraries_private : ['-lboost_container', '-lboost_context'], +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_directories('.'), + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/src/libexpr/meson.options b/src/libexpr/meson.options new file mode 100644 index 000000000..242d30ea7 --- /dev/null +++ b/src/libexpr/meson.options @@ -0,0 +1,3 @@ +option('gc', type : 'feature', + description : 'enable garbage collection in the Nix expression evaluator (requires Boehm GC)', +) diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix new file mode 100644 index 000000000..223b04042 --- /dev/null +++ b/src/libexpr/package.nix @@ -0,0 +1,130 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nix-fetchers +, boost +, boehmgc +, nlohmann_json + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +# Whether to use garbage collection for the Nix language evaluator. +# +# If it is disabled, we just leak memory, but this is not as bad as it +# sounds so long as evaluation just takes places within short-lived +# processes. (When the process exits, the memory is reclaimed; it is +# only leaked *within* the process.) +# +# Temporarily disabled on Windows because the `GC_throw_bad_alloc` +# symbol is missing during linking. +, enableGC ? !stdenv.hostPlatform.isWindows +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-expr"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + boost + ]; + + propagatedBuildInputs = [ + nix-util + nix-store + nix-fetchers + nlohmann_json + ] ++ lib.optional enableGC boehmgc; + + disallowedReferences = [ boost ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + (lib.mesonFeature "gc" enableGC) + ]; + + env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib boost}/lib"; + } // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs that ends up in `Libs.private` + # by default, and would clash with out `disallowedReferences`. Part + # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. + '' + sed -i "$out/lib/pkgconfig/nix-expr.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index fa6b1c4b6..567b73f9a 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -1,4 +1,4 @@ -#include "libfetchers/attrs.hh" +#include "attrs.hh" #include "primops.hh" #include "eval-inline.hh" #include "eval-settings.hh" diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index d7975ac65..c17021527 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -20,27 +20,24 @@ deps_private = [ ] # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] -configdata = configuration_data() - -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -nix_store = dependency('nix-store') -if nix_store.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_store -else - deps_public += nix_store -endif - +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json @@ -113,14 +110,9 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) requires = [] -if nix_util.type_name() == 'internal' - # `requires` cannot contain declared dependencies (from the - # subproject), so we need to do this manually - requires += 'nix-util' -endif -if nix_store.type_name() == 'internal' - requires += 'nix-store' -endif +foreach dep : deps_public_subproject + requires += dep.name() +endforeach requires += deps_public import('pkgconfig').generate( @@ -138,5 +130,5 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_directories('.'), link_with : this_library, compile_args : ['-std=c++2a'], - dependencies : [nix_util, nix_store], + dependencies : deps_public_subproject + deps_public, )) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index a5583d14c..d2560255e 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-fetchers"; + pname = "nix-flake"; inherit version; src = fileset.toSource { @@ -80,11 +80,6 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs - '' - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated diff --git a/src/libflake/.version b/src/libflake/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libflake/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libflake/meson.build b/src/libflake/meson.build new file mode 100644 index 000000000..c0862a484 --- /dev/null +++ b/src/libflake/meson.build @@ -0,0 +1,120 @@ +project('nix-flake', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-fetchers'), + dependency('nix-expr'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +libgit2 = dependency('libgit2') +deps_public += libgit2 + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + # '-include', 'config-fetchers.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'flake-settings.cc', + 'flake/config.cc', + 'flake/flake.cc', + 'flake/flakeref.cc', + 'flake/url-name.cc', + 'flake/lockfile.cc', +) + +headers = files( + 'flake-settings.hh', + 'flake/flake.hh', + 'flake/flakeref.hh', + 'flake/lockfile.hh', + 'flake/url-name.hh', +) + +this_library = library( + 'nixflake', + sources, + dependencies : deps_public + deps_private + deps_other, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_directories('.'), + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/src/libflake/package.nix b/src/libflake/package.nix new file mode 100644 index 000000000..1280df7b7 --- /dev/null +++ b/src/libflake/package.nix @@ -0,0 +1,99 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nix-fetchers +, nix-expr +, nlohmann_json +, libgit2 +, man + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-flake"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-store + nix-util + nix-fetchers + nix-expr + nlohmann_json + ]; + + preConfigure = + # "Inline" .version so its not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*-tab.*" ]; + + hardeningDisable = ["fortify"]; +}) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index d9237c55a..c2384dd78 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -20,6 +20,9 @@ deps_private = [ ] # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] @@ -30,13 +33,17 @@ configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('SYSTEM', host_machine.system()) -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif +foreach nix_dep : [ + dependency('nix-util'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach run_command('ln', '-s', meson.project_build_root() / '__nothing_link_target', @@ -122,13 +129,16 @@ if enable_embedded_sandbox_shell endif generated_headers = [] -foreach header : [ 'schema.sql', 'ca-specific-schema.sql' ] +foreach header : [ + 'schema.sql', + 'ca-specific-schema.sql', +] generated_headers += custom_target( command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], input : header, output : '@PLAINNAME@.gen.hh', install : true, - install_dir : get_option('includedir') / 'nix' + install_dir : get_option('includedir') / 'nix', ) endforeach @@ -248,7 +258,7 @@ include_dirs = [ include_directories('build'), ] -headers = [config_h] +files( +headers = [config_h] + files( 'binary-cache-store.hh', 'build-result.hh', 'build/derivation-goal.hh', @@ -427,11 +437,9 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) requires = [] -if nix_util.type_name() == 'internal' - # `requires` cannot contain declared dependencies (from the - # subproject), so we need to do this manually - requires += 'nix-util' -endif +foreach dep : deps_public_subproject + requires += dep.name() +endforeach requires += deps_public import('pkgconfig').generate( @@ -450,5 +458,5 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, link_with : this_library, compile_args : ['-std=c++2a'], - dependencies : [nix_util], + dependencies : deps_public_subproject + deps_public, )) diff --git a/src/libstore/package.nix b/src/libstore/package.nix index e118f3cd2..f27dac2f6 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -23,9 +23,6 @@ # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. , withCoverageChecks ? false - -# Avoid setting things that would interfere with a functioning devShell -, forDevShell ? false }: let diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build index c0345a6ee..d5ee8eed7 100644 --- a/tests/unit/libutil-support/meson.build +++ b/tests/unit/libutil-support/meson.build @@ -20,9 +20,27 @@ deps_private = [ ] # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] +foreach nix_dep : [ + dependency('nix-util'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + add_project_arguments( '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', @@ -66,17 +84,6 @@ else linker_export_flags = [] endif -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -rapidcheck = dependency('rapidcheck') -deps_public += rapidcheck - this_library = library( 'nix-util-test-support', sources, @@ -90,7 +97,11 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -libraries_private = [] +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public import('pkgconfig').generate( this_library, @@ -99,7 +110,7 @@ import('pkgconfig').generate( description : 'Nix Package Manager', subdirs : ['nix'], extra_cflags : ['-std=c++2a'], - requires : deps_public, + requires : requires, requires_private : deps_private, ) @@ -107,5 +118,5 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, link_with : this_library, compile_args : ['-std=c++2a'], - dependencies : [], + dependencies : deps_public_subproject + deps_public, )) diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index cc13c4364..3f6e0fe65 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -18,18 +18,57 @@ cxx = meson.get_compiler('cpp') deps_private = [ ] # See note in ../nix-util/meson.build -deps_public = [ ] +deps_private_subproject = [ ] # See note in ../nix-util/meson.build deps_other = [ ] configdata = configuration_data() +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +config_h = configure_file( + configuration : configdata, + output : 'config-util-test.h', +) + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. '-include', 'config-util-test.h', - # '-include', 'config-store.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', @@ -46,14 +85,6 @@ add_project_arguments( language : 'cpp', ) -# TODO rename, because it will conflict with downstream projects -configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) - -config_h = configure_file( - configuration : configdata, - output : 'config-util-test.h', -) - sources = files( 'args.cc', 'canon-path.cc', @@ -80,52 +111,11 @@ sources = files( include_dirs = [include_directories('.')] -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif - -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -nix_util_c = dependency('nix-util-c') -if nix_util_c.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util_c -else - deps_public += nix_util_c -endif - -nix_util_test_support = dependency('nix-util-test-support') -if nix_util_test_support.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util_test_support -else - deps_public += nix_util_test_support -endif - -rapidcheck = dependency('rapidcheck') -deps_public += rapidcheck - -gtest = dependency('gtest', main : true) -deps_public += gtest this_exe = executable( 'nix-util-test', sources, - dependencies : deps_public + deps_private + deps_other, + dependencies : deps_private_subproject + deps_private + deps_other, include_directories : include_dirs, # TODO: -lrapidcheck, see ../libutil-support/build.meson link_args: linker_export_flags + ['-lrapidcheck'], @@ -139,5 +129,4 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, link_with : this_exe, compile_args : ['-std=c++2a'], - dependencies : [], )) From 4fa8068b78444f691a9a3d3c16efdcfcd540ce9a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 10:19:03 -0400 Subject: [PATCH 436/910] Mesonify other external API --- meson.build | 7 ++ src/libexpr-c/.version | 1 + src/libexpr-c/meson.build | 159 ++++++++++++++++++++++++++++++++++++ src/libexpr/meson.build | 8 +- src/libfetchers/meson.build | 4 +- src/libflake/meson.build | 6 +- src/libstore-c/.version | 1 + src/libstore-c/meson.build | 151 ++++++++++++++++++++++++++++++++++ src/libstore/meson.build | 6 +- src/libutil-c/meson.build | 81 ++++++++++++------ src/libutil/meson.build | 4 +- src/perl/lib/Nix/Store.xs | 4 +- 12 files changed, 392 insertions(+), 40 deletions(-) create mode 120000 src/libexpr-c/.version create mode 100644 src/libexpr-c/meson.build create mode 120000 src/libstore-c/.version create mode 100644 src/libstore-c/meson.build diff --git a/meson.build b/meson.build index 7832eb488..2a3932a40 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,8 @@ subproject('external-api-docs') # C wrappers subproject('libutil-c') +subproject('libstore-c') +subproject('libexpr-c') # Language Bindings subproject('perl') @@ -25,3 +27,8 @@ subproject('perl') # Testing subproject('libutil-test-support') subproject('libutil-test') +#subproject('libstore-test-support') +#subproject('libstore-test') +#subproject('libexpr-test-support') +#subproject('libexpr-test') +#subproject('libflake-test') diff --git a/src/libexpr-c/.version b/src/libexpr-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build new file mode 100644 index 000000000..5abf2b477 --- /dev/null +++ b/src/libexpr-c/meson.build @@ -0,0 +1,159 @@ +project('nix-expr-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-expr'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +foreach nix_dep : [ + dependency('nix-util-c'), + dependency('nix-store-c'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +config_h = configure_file( + configuration : configdata, + output : 'config-expr.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + + # From C libraries, for our public, installed headers too + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'nix_api_expr.cc', + 'nix_api_external.cc', + 'nix_api_value.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'nix_api_expr.h', + 'nix_api_external.h', + 'nix_api_value.h', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nixexprc', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires_public, + requires_private : requires_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 8f08decd4..34e4dec3b 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -77,16 +77,16 @@ configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) config_h = configure_file( configuration : configdata, - output : 'config-expr.h', + output : 'config-expr.hh', ) add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-include', 'config-expr.h', + '-include', 'config-expr.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index c17021527..938ee27d4 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -48,8 +48,8 @@ deps_public += libgit2 add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', # '-include', 'config-fetchers.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', diff --git a/src/libflake/meson.build b/src/libflake/meson.build index c0862a484..1c0a3ae77 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -50,10 +50,10 @@ deps_public += libgit2 add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-include', 'config-expr.h', + '-include', 'config-expr.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/libstore-c/.version b/src/libstore-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build new file mode 100644 index 000000000..049dda0e2 --- /dev/null +++ b/src/libstore-c/meson.build @@ -0,0 +1,151 @@ +project('nix-store-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +foreach nix_dep : [ + dependency('nix-util-c'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +config_h = configure_file( + configuration : configdata, + output : 'config-store.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + '-include', 'config-store.hh', + + # From C libraries, for our public, installed headers too + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'nix_api_store.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'nix_api_store.h', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nixstorec', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires_public, + requires_private : requires_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index c2384dd78..7277eb49d 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -162,14 +162,14 @@ endif config_h = configure_file( configuration : configdata, - output : 'config-store.h', + output : 'config-store.hh', ) add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 5de288e18..7ebbe3d06 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -17,19 +17,60 @@ cxx = meson.get_compiler('cpp') # See note in ../nix-util/meson.build deps_private = [ ] +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] configdata = configuration_data() +foreach nix_dep : [ + dependency('nix-util'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +foreach nix_dep : [ +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-util.h', +) + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + + # From C libraries, for our public, installed headers too '-include', 'config-util.h', - # '-include', 'config-store.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', @@ -52,13 +93,12 @@ sources = files( include_dirs = [include_directories('.')] -headers = files( +headers = [config_h] + files( 'nix_api_util.h', - 'nix_api_util_internal.h', ) if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared + # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared # objects --- see https://gcc.gnu.org/wiki/Visibility for details. # This is a temporary sledgehammer to export everything like on Unix, # and not detail with this yet. @@ -69,22 +109,6 @@ else linker_export_flags = [] endif -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -# TODO rename, because it will conflict with downstream projects -configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) - -config_h = configure_file( - configuration : configdata, - output : 'config-util.h', -) - this_library = library( 'nixutilc', sources, @@ -96,7 +120,17 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -libraries_private = [] +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public import('pkgconfig').generate( this_library, @@ -105,9 +139,8 @@ import('pkgconfig').generate( description : 'Nix Package Manager', subdirs : ['nix'], extra_cflags : ['-std=c++2a'], - requires : deps_public, - requires_private : deps_private, - libraries_private : libraries_private, + requires : requires_public, + requires_private : requires_private, ) meson.override_dependency(meson.project_name(), declare_dependency( diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 099c0c65f..c9dfee651 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -132,13 +132,13 @@ deps_public += nlohmann_json config_h = configure_file( configuration : configdata, - output : 'config-util.h', + output : 'config-util.hh', ) add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', + '-include', 'config-util.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index acce25f3a..f951437c8 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -1,5 +1,5 @@ -#include "config-util.h" -#include "config-store.h" +#include "config-util.hh" +#include "config-store.hh" #include "EXTERN.h" #include "perl.h" From 17a8c2bfce97bc857ddd519ae7054d7d930ced39 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 11:28:08 -0400 Subject: [PATCH 437/910] Unit tests and external libraries --- .gitignore | 10 +- Makefile | 19 --- Makefile.config.in | 1 - configure.ac | 22 --- doc/manual/src/contributing/hacking.md | 1 - doc/manual/src/contributing/testing.md | 10 +- maintainers/flake-module.nix | 122 ++++++++-------- meson.build | 11 +- mk/common-test.sh | 2 +- package.nix | 35 +---- packaging/components.nix | 32 ++++- src/internal-api-docs/doxygen.cfg.in | 42 +++--- src/internal-api-docs/package.nix | 1 - src/libexpr-test-support/.version | 1 + src/libexpr-test-support/meson.build | 128 +++++++++++++++++ .../libexpr-test-support}/tests/libexpr.hh | 0 .../tests/nix_api_expr.hh | 0 .../tests/value/context.cc | 0 .../tests/value/context.hh | 0 src/libexpr-test/.version | 1 + .../libexpr-test}/derived-path.cc | 0 .../libexpr-test}/error_traces.cc | 0 .../unit/libexpr => src/libexpr-test}/eval.cc | 0 .../unit/libexpr => src/libexpr-test}/json.cc | 0 .../unit/libexpr => src/libexpr-test}/main.cc | 0 src/libexpr-test/meson.build | 125 +++++++++++++++++ .../libexpr-test}/nix_api_expr.cc | 0 .../libexpr-test}/nix_api_external.cc | 0 .../libexpr-test}/nix_api_value.cc | 0 .../libexpr => src/libexpr-test}/primops.cc | 0 .../libexpr-test}/search-path.cc | 0 .../libexpr => src/libexpr-test}/trivial.cc | 0 .../libexpr-test}/value/context.cc | 0 .../libexpr-test}/value/print.cc | 0 .../libexpr-test}/value/value.cc | 0 src/libfetchers-test/.version | 1 + .../data/public-key/defaultType.json | 0 .../data/public-key/noRoundTrip.json | 0 .../data/public-key/simple.json | 0 src/libfetchers-test/meson.build | 109 +++++++++++++++ .../libfetchers-test}/public-key.cc | 0 src/libflake-test/.version | 1 + .../libflake-test}/flakeref.cc | 0 src/libflake-test/meson.build | 114 +++++++++++++++ .../libflake-test}/url-name.cc | 0 src/libstore-test-support/.version | 1 + src/libstore-test-support/meson.build | 130 ++++++++++++++++++ .../tests/derived-path.cc | 0 .../tests/derived-path.hh | 0 .../libstore-test-support}/tests/libstore.hh | 0 .../tests/nix_api_store.hh | 0 .../tests/outputs-spec.cc | 0 .../tests/outputs-spec.hh | 0 .../libstore-test-support}/tests/path.cc | 0 .../libstore-test-support}/tests/path.hh | 0 .../libstore-test-support}/tests/protocol.hh | 0 src/libstore-test/.version | 1 + .../libstore-test}/common-protocol.cc | 0 .../libstore-test}/content-address.cc | 0 .../data/common-protocol/content-address.bin | Bin .../data/common-protocol/drv-output.bin | Bin .../optional-content-address.bin | Bin .../common-protocol/optional-store-path.bin | Bin .../data/common-protocol/realisation.bin | Bin .../data/common-protocol/set.bin | Bin .../data/common-protocol/store-path.bin | Bin .../data/common-protocol/string.bin | Bin .../data/common-protocol/vector.bin | Bin .../advanced-attributes-defaults.drv | 0 .../advanced-attributes-defaults.json | 0 ...d-attributes-structured-attrs-defaults.drv | 0 ...-attributes-structured-attrs-defaults.json | 0 .../advanced-attributes-structured-attrs.drv | 0 .../advanced-attributes-structured-attrs.json | 0 .../data/derivation/advanced-attributes.drv | 0 .../derivation/bad-old-version-dyn-deps.drv | 0 .../data/derivation/bad-version.drv | 0 .../data/derivation/dynDerivationDeps.drv | 0 .../data/derivation/dynDerivationDeps.json | 0 .../data/derivation/output-caFixedFlat.json | 0 .../data/derivation/output-caFixedNAR.json | 0 .../data/derivation/output-caFixedText.json | 0 .../data/derivation/output-caFloating.json | 0 .../data/derivation/output-deferred.json | 0 .../data/derivation/output-impure.json | 0 .../derivation/output-inputAddressed.json | 0 .../libstore-test}/data/derivation/simple.drv | 0 .../data/derivation/simple.json | 0 .../libstore-test}/data/machines/bad_format | 0 .../libstore-test}/data/machines/valid | 0 .../libstore-test}/data/nar-info/impure.json | 0 .../libstore-test}/data/nar-info/pure.json | 0 .../data/path-info/empty_impure.json | 0 .../data/path-info/empty_pure.json | 0 .../libstore-test}/data/path-info/impure.json | 0 .../libstore-test}/data/path-info/pure.json | 0 .../data/serve-protocol/build-options-2.1.bin | Bin .../data/serve-protocol/build-options-2.2.bin | Bin .../data/serve-protocol/build-options-2.3.bin | Bin .../data/serve-protocol/build-options-2.7.bin | Bin .../data/serve-protocol/build-result-2.2.bin | Bin .../data/serve-protocol/build-result-2.3.bin | Bin .../data/serve-protocol/build-result-2.6.bin | Bin .../data/serve-protocol/content-address.bin | Bin .../data/serve-protocol/drv-output.bin | Bin .../serve-protocol/handshake-to-client.bin | Bin .../optional-content-address.bin | Bin .../serve-protocol/optional-store-path.bin | Bin .../data/serve-protocol/realisation.bin | Bin .../data/serve-protocol/set.bin | Bin .../data/serve-protocol/store-path.bin | Bin .../data/serve-protocol/string.bin | Bin .../unkeyed-valid-path-info-2.3.bin | Bin .../unkeyed-valid-path-info-2.4.bin | Bin .../data/serve-protocol/vector.bin | Bin .../data/store-reference/auto.txt | 0 .../data/store-reference/auto_param.txt | 0 .../data/store-reference/local_1.txt | 0 .../data/store-reference/local_2.txt | 0 .../store-reference/local_shorthand_1.txt | 0 .../store-reference/local_shorthand_2.txt | 0 .../data/store-reference/ssh.txt | 0 .../data/store-reference/unix.txt | 0 .../data/store-reference/unix_shorthand.txt | 0 .../data/worker-protocol/build-mode.bin | Bin .../worker-protocol/build-result-1.27.bin | Bin .../worker-protocol/build-result-1.28.bin | Bin .../worker-protocol/build-result-1.29.bin | Bin .../worker-protocol/build-result-1.37.bin | Bin .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin .../client-handshake-info_1_35.bin | Bin .../data/worker-protocol/content-address.bin | Bin .../worker-protocol/derived-path-1.29.bin | Bin .../worker-protocol/derived-path-1.30.bin | Bin .../data/worker-protocol/drv-output.bin | Bin .../worker-protocol/handshake-to-client.bin | Bin .../keyed-build-result-1.29.bin | Bin .../optional-content-address.bin | Bin .../worker-protocol/optional-store-path.bin | Bin .../worker-protocol/optional-trusted-flag.bin | Bin .../data/worker-protocol/realisation.bin | Bin .../data/worker-protocol/set.bin | Bin .../data/worker-protocol/store-path.bin | Bin .../data/worker-protocol/string.bin | Bin .../unkeyed-valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.16.bin | Bin .../data/worker-protocol/vector.bin | Bin .../derivation-advanced-attrs.cc | 0 .../libstore-test}/derivation.cc | 0 .../libstore-test}/derived-path.cc | 0 .../libstore-test}/downstream-placeholder.cc | 0 .../libstore-test}/machines.cc | 0 src/libstore-test/meson.build | 123 +++++++++++++++++ .../libstore-test}/nar-info-disk-cache.cc | 0 .../libstore-test}/nar-info.cc | 0 .../libstore-test}/nix_api_store.cc | 0 .../libstore-test}/outputs-spec.cc | 0 .../libstore-test}/path-info.cc | 0 .../libstore => src/libstore-test}/path.cc | 0 .../libstore-test}/references.cc | 0 .../libstore-test}/serve-protocol.cc | 0 .../libstore-test}/store-reference.cc | 0 .../libstore-test}/worker-protocol.cc | 0 src/libutil-test | 1 - src/libutil-test-support | 1 - src/libutil-test-support/.version | 1 + .../libutil-test-support}/meson.build | 3 + .../libutil-test-support}/package.nix | 0 .../tests/characterization.hh | 0 .../libutil-test-support}/tests/hash.cc | 0 .../libutil-test-support}/tests/hash.hh | 0 .../tests/nix_api_util.hh | 0 .../tests/string_callback.cc | 0 .../tests/string_callback.hh | 0 src/libutil-test/.version | 1 + .../unit/libutil => src/libutil-test}/args.cc | 0 .../libutil-test}/canon-path.cc | 0 .../libutil-test}/chunked-vector.cc | 0 .../libutil => src/libutil-test}/closure.cc | 0 .../libutil-test}/compression.cc | 0 .../libutil => src/libutil-test}/config.cc | 0 .../libutil-test}/data/git/check-data.sh | 0 .../data/git/hello-world-blob.bin | Bin .../libutil-test}/data/git/hello-world.bin | Bin .../libutil-test}/data/git/tree.bin | Bin .../libutil-test}/data/git/tree.txt | 0 .../libutil-test}/file-content-address.cc | 0 .../unit/libutil => src/libutil-test}/git.cc | 2 +- .../unit/libutil => src/libutil-test}/hash.cc | 0 .../libutil => src/libutil-test}/hilite.cc | 0 .../libutil-test}/json-utils.cc | 0 .../libutil => src/libutil-test}/logging.cc | 0 .../libutil => src/libutil-test}/lru-cache.cc | 0 .../libutil => src/libutil-test}/meson.build | 17 +-- .../libutil-test}/nix_api_util.cc | 0 .../libutil => src/libutil-test}/package.nix | 0 .../unit/libutil => src/libutil-test}/pool.cc | 0 .../libutil-test}/references.cc | 0 .../libutil => src/libutil-test}/spawn.cc | 0 .../libutil-test}/suggestions.cc | 0 .../libutil => src/libutil-test}/tests.cc | 0 .../unit/libutil => src/libutil-test}/url.cc | 0 .../libutil-test}/xml-writer.cc | 0 tests/unit/libexpr-support/local.mk | 23 ---- tests/unit/libexpr/local.mk | 45 ------ tests/unit/libfetchers/local.mk | 37 ----- tests/unit/libflake/local.mk | 43 ------ tests/unit/libstore-support/local.mk | 21 --- tests/unit/libstore/local.mk | 38 ----- tests/unit/libutil-support/.version | 1 - tests/unit/libutil-support/local.mk | 19 --- tests/unit/libutil/.version | 1 - tests/unit/libutil/local.mk | 37 ----- 215 files changed, 873 insertions(+), 461 deletions(-) create mode 120000 src/libexpr-test-support/.version create mode 100644 src/libexpr-test-support/meson.build rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/libexpr.hh (100%) rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/nix_api_expr.hh (100%) rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/value/context.cc (100%) rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/value/context.hh (100%) create mode 120000 src/libexpr-test/.version rename {tests/unit/libexpr => src/libexpr-test}/derived-path.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/error_traces.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/eval.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/json.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/main.cc (100%) create mode 100644 src/libexpr-test/meson.build rename {tests/unit/libexpr => src/libexpr-test}/nix_api_expr.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/nix_api_external.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/nix_api_value.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/primops.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/search-path.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/trivial.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/value/context.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/value/print.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/value/value.cc (100%) create mode 120000 src/libfetchers-test/.version rename {tests/unit/libfetchers => src/libfetchers-test}/data/public-key/defaultType.json (100%) rename {tests/unit/libfetchers => src/libfetchers-test}/data/public-key/noRoundTrip.json (100%) rename {tests/unit/libfetchers => src/libfetchers-test}/data/public-key/simple.json (100%) create mode 100644 src/libfetchers-test/meson.build rename {tests/unit/libfetchers => src/libfetchers-test}/public-key.cc (100%) create mode 120000 src/libflake-test/.version rename {tests/unit/libflake => src/libflake-test}/flakeref.cc (100%) create mode 100644 src/libflake-test/meson.build rename {tests/unit/libflake => src/libflake-test}/url-name.cc (100%) create mode 120000 src/libstore-test-support/.version create mode 100644 src/libstore-test-support/meson.build rename {tests/unit/libstore-support => src/libstore-test-support}/tests/derived-path.cc (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/derived-path.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/libstore.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/nix_api_store.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/outputs-spec.cc (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/outputs-spec.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/path.cc (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/path.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/protocol.hh (100%) create mode 120000 src/libstore-test/.version rename {tests/unit/libstore => src/libstore-test}/common-protocol.cc (100%) rename {tests/unit/libstore => src/libstore-test}/content-address.cc (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/drv-output.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/optional-content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/optional-store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/realisation.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/set.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/string.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/vector.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-defaults.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-defaults.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs-defaults.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs-defaults.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/bad-old-version-dyn-deps.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/bad-version.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/dynDerivationDeps.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/dynDerivationDeps.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFixedFlat.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFixedNAR.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFixedText.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFloating.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-deferred.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-inputAddressed.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/simple.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/simple.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/machines/bad_format (100%) rename {tests/unit/libstore => src/libstore-test}/data/machines/valid (100%) rename {tests/unit/libstore => src/libstore-test}/data/nar-info/impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/nar-info/pure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/empty_impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/empty_pure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/pure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.1.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.2.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.3.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.7.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-result-2.2.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-result-2.3.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-result-2.6.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/drv-output.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/handshake-to-client.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/optional-content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/optional-store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/realisation.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/set.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/string.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/unkeyed-valid-path-info-2.3.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/unkeyed-valid-path-info-2.4.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/vector.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/auto.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/auto_param.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_1.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_2.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_shorthand_1.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_shorthand_2.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/ssh.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/unix.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/unix_shorthand.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-mode.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.27.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.28.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.29.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.37.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/client-handshake-info_1_30.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/client-handshake-info_1_33.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/client-handshake-info_1_35.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/derived-path-1.29.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/derived-path-1.30.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/drv-output.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/handshake-to-client.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/keyed-build-result-1.29.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/optional-content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/optional-store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/optional-trusted-flag.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/realisation.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/set.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/string.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/unkeyed-valid-path-info-1.15.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/valid-path-info-1.15.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/valid-path-info-1.16.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/vector.bin (100%) rename {tests/unit/libstore => src/libstore-test}/derivation-advanced-attrs.cc (100%) rename {tests/unit/libstore => src/libstore-test}/derivation.cc (100%) rename {tests/unit/libstore => src/libstore-test}/derived-path.cc (100%) rename {tests/unit/libstore => src/libstore-test}/downstream-placeholder.cc (100%) rename {tests/unit/libstore => src/libstore-test}/machines.cc (100%) create mode 100644 src/libstore-test/meson.build rename {tests/unit/libstore => src/libstore-test}/nar-info-disk-cache.cc (100%) rename {tests/unit/libstore => src/libstore-test}/nar-info.cc (100%) rename {tests/unit/libstore => src/libstore-test}/nix_api_store.cc (100%) rename {tests/unit/libstore => src/libstore-test}/outputs-spec.cc (100%) rename {tests/unit/libstore => src/libstore-test}/path-info.cc (100%) rename {tests/unit/libstore => src/libstore-test}/path.cc (100%) rename {tests/unit/libstore => src/libstore-test}/references.cc (100%) rename {tests/unit/libstore => src/libstore-test}/serve-protocol.cc (100%) rename {tests/unit/libstore => src/libstore-test}/store-reference.cc (100%) rename {tests/unit/libstore => src/libstore-test}/worker-protocol.cc (100%) delete mode 120000 src/libutil-test delete mode 120000 src/libutil-test-support create mode 120000 src/libutil-test-support/.version rename {tests/unit/libutil-support => src/libutil-test-support}/meson.build (95%) rename {tests/unit/libutil-support => src/libutil-test-support}/package.nix (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/characterization.hh (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/hash.cc (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/hash.hh (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/nix_api_util.hh (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/string_callback.cc (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/string_callback.hh (100%) create mode 120000 src/libutil-test/.version rename {tests/unit/libutil => src/libutil-test}/args.cc (100%) rename {tests/unit/libutil => src/libutil-test}/canon-path.cc (100%) rename {tests/unit/libutil => src/libutil-test}/chunked-vector.cc (100%) rename {tests/unit/libutil => src/libutil-test}/closure.cc (100%) rename {tests/unit/libutil => src/libutil-test}/compression.cc (100%) rename {tests/unit/libutil => src/libutil-test}/config.cc (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/check-data.sh (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/hello-world-blob.bin (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/hello-world.bin (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/tree.bin (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/tree.txt (100%) rename {tests/unit/libutil => src/libutil-test}/file-content-address.cc (100%) rename {tests/unit/libutil => src/libutil-test}/git.cc (99%) rename {tests/unit/libutil => src/libutil-test}/hash.cc (100%) rename {tests/unit/libutil => src/libutil-test}/hilite.cc (100%) rename {tests/unit/libutil => src/libutil-test}/json-utils.cc (100%) rename {tests/unit/libutil => src/libutil-test}/logging.cc (100%) rename {tests/unit/libutil => src/libutil-test}/lru-cache.cc (100%) rename {tests/unit/libutil => src/libutil-test}/meson.build (88%) rename {tests/unit/libutil => src/libutil-test}/nix_api_util.cc (100%) rename {tests/unit/libutil => src/libutil-test}/package.nix (100%) rename {tests/unit/libutil => src/libutil-test}/pool.cc (100%) rename {tests/unit/libutil => src/libutil-test}/references.cc (100%) rename {tests/unit/libutil => src/libutil-test}/spawn.cc (100%) rename {tests/unit/libutil => src/libutil-test}/suggestions.cc (100%) rename {tests/unit/libutil => src/libutil-test}/tests.cc (100%) rename {tests/unit/libutil => src/libutil-test}/url.cc (100%) rename {tests/unit/libutil => src/libutil-test}/xml-writer.cc (100%) delete mode 100644 tests/unit/libexpr-support/local.mk delete mode 100644 tests/unit/libexpr/local.mk delete mode 100644 tests/unit/libfetchers/local.mk delete mode 100644 tests/unit/libflake/local.mk delete mode 100644 tests/unit/libstore-support/local.mk delete mode 100644 tests/unit/libstore/local.mk delete mode 120000 tests/unit/libutil-support/.version delete mode 100644 tests/unit/libutil-support/local.mk delete mode 100644 tests/unit/libutil/.version delete mode 100644 tests/unit/libutil/local.mk diff --git a/.gitignore b/.gitignore index a17b627f4..838cac335 100644 --- a/.gitignore +++ b/.gitignore @@ -49,22 +49,22 @@ perl/Makefile.config /src/libexpr/parser-tab.output /src/libexpr/nix.tbl /src/libexpr/tests -/tests/unit/libexpr/libnixexpr-tests +/src/libexpr-test/libnixexpr-tests # /src/libfetchers -/tests/unit/libfetchers/libnixfetchers-tests +/src/libfetchers-test/libnixfetchers-tests # /src/libflake -/tests/unit/libflake/libnixflake-tests +/src/libflake-test/libnixflake-tests # /src/libstore/ *.gen.* /src/libstore/tests -/tests/unit/libstore/libnixstore-tests +/src/libstore-test/libnixstore-tests # /src/libutil/ /src/libutil/tests -/tests/unit/libutil/libnixutil-tests +/src/libutil-test/libnixutil-tests /src/nix/nix diff --git a/Makefile b/Makefile index bb64a104e..a65cdbd40 100644 --- a/Makefile +++ b/Makefile @@ -38,18 +38,6 @@ makefiles += \ endif endif -ifeq ($(ENABLE_UNIT_TESTS), yes) -makefiles += \ - tests/unit/libutil/local.mk \ - tests/unit/libutil-support/local.mk \ - tests/unit/libstore/local.mk \ - tests/unit/libstore-support/local.mk \ - tests/unit/libfetchers/local.mk \ - tests/unit/libexpr/local.mk \ - tests/unit/libexpr-support/local.mk \ - tests/unit/libflake/local.mk -endif - ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ @@ -103,13 +91,6 @@ include mk/lib.mk # These must be defined after `mk/lib.mk`. Otherwise the first rule # incorrectly becomes the default target. -ifneq ($(ENABLE_UNIT_TESTS), yes) -.PHONY: check -check: - @echo "Unit tests are disabled. Configure without '--disable-unit-tests', or avoid calling 'make check'." - @exit 1 -endif - ifneq ($(ENABLE_FUNCTIONAL_TESTS), yes) .PHONY: installcheck installcheck: diff --git a/Makefile.config.in b/Makefile.config.in index 3100d2073..e131484f6 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -12,7 +12,6 @@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ ENABLE_S3 = @ENABLE_S3@ -ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ HAVE_LIBCPUID = @HAVE_LIBCPUID@ HAVE_SECCOMP = @HAVE_SECCOMP@ diff --git a/configure.ac b/configure.ac index 4f66a3efc..b9f190166 100644 --- a/configure.ac +++ b/configure.ac @@ -141,18 +141,6 @@ AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]), ENABLE_BUILD=$enableval, ENABLE_BUILD=yes) AC_SUBST(ENABLE_BUILD) -# Building without unit tests is useful for bootstrapping with a smaller footprint -# or running the tests in a separate derivation. Otherwise, we do compile and -# run them. - -AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build the tests]), - ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) -AC_SUBST(ENABLE_UNIT_TESTS) - -AS_IF( - [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], - [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) - AC_ARG_ENABLE(functional-tests, AS_HELP_STRING([--disable-functional-tests],[Do not build the tests]), ENABLE_FUNCTIONAL_TESTS=$enableval, ENABLE_FUNCTIONAL_TESTS=yes) AC_SUBST(ENABLE_FUNCTIONAL_TESTS) @@ -365,16 +353,6 @@ if test "$gc" = yes; then CFLAGS="$old_CFLAGS" fi -AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ - -# Look for gtest. -PKG_CHECK_MODULES([GTEST], [gtest_main gmock_main]) - -# Look for rapidcheck. -PKG_CHECK_MODULES([RAPIDCHECK], [rapidcheck rapidcheck_gtest]) - -]) - # Look for nlohmann/json. PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9]) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 08ba84faa..c128515e9 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -122,7 +122,6 @@ Run `make` with [`-e` / `--environment-overrides`](https://www.gnu.org/software/ The docs can take a while to build, so you may want to disable this for local development. - `ENABLE_FUNCTIONAL_TESTS=yes` to enable building the functional tests. -- `ENABLE_UNIT_TESTS=yes` to enable building the unit tests. - `OPTIMIZE=1` to enable optimizations. - `libraries=libutil programs=` to only build a specific library. diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 717deabd7..ed9c25f7a 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -59,15 +59,15 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks. > … > ``` -The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `tests/unit/${library_name_without-nix}`. -Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `tests/unit/libexpr/tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `tests/unit/libexpr-support/tests/value/context.{hh,cc}`. +The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_name_without-nix}-test`. +Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `src/libexpr-test/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `src/libexpr-test-support/tests/value/context.{hh,cc}`. Data for unit tests is stored in a `data` subdir of the directory for each unit test executable. -For example, `libnixstore` code is in `src/libstore`, and its test data is in `tests/unit/libstore/data`. -The path to the `tests/unit/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`. +For example, `libnixstore` code is in `src/libstore`, and its test data is in `src/libstore-test/data`. +The path to the `src/${library_name_without-nix}-test/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`. Note that each executable only gets the data for its tests. -The unit test libraries are in `tests/unit/${library_name_without-nix}-lib`. +The unit test libraries are in `src/${library_name_without-nix}-test-support`. All headers are in a `tests` subdirectory so they are included with `#include "tests/"`. The use of all these separate directories for the unit tests might seem inconvenient, as for example the tests are not "right next to" the part of the code they are testing. diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 8f95e788b..b78e5f63a 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^tests/unit/[^/]*/data/.*$'' + ''^src/[^/]*-test/[^/]*/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' @@ -429,65 +429,65 @@ ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/nixos/user-sandboxing/attacker\.c'' - ''^tests/unit/libexpr-support/tests/libexpr\.hh'' - ''^tests/unit/libexpr-support/tests/value/context\.cc'' - ''^tests/unit/libexpr-support/tests/value/context\.hh'' - ''^tests/unit/libexpr/derived-path\.cc'' - ''^tests/unit/libexpr/error_traces\.cc'' - ''^tests/unit/libexpr/eval\.cc'' - ''^tests/unit/libexpr/json\.cc'' - ''^tests/unit/libexpr/main\.cc'' - ''^tests/unit/libexpr/primops\.cc'' - ''^tests/unit/libexpr/search-path\.cc'' - ''^tests/unit/libexpr/trivial\.cc'' - ''^tests/unit/libexpr/value/context\.cc'' - ''^tests/unit/libexpr/value/print\.cc'' - ''^tests/unit/libfetchers/public-key\.cc'' - ''^tests/unit/libflake/flakeref\.cc'' - ''^tests/unit/libflake/url-name\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.hh'' - ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' - ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' - ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' - ''^tests/unit/libstore-support/tests/path\.cc'' - ''^tests/unit/libstore-support/tests/path\.hh'' - ''^tests/unit/libstore-support/tests/protocol\.hh'' - ''^tests/unit/libstore/common-protocol\.cc'' - ''^tests/unit/libstore/content-address\.cc'' - ''^tests/unit/libstore/derivation\.cc'' - ''^tests/unit/libstore/derived-path\.cc'' - ''^tests/unit/libstore/downstream-placeholder\.cc'' - ''^tests/unit/libstore/machines\.cc'' - ''^tests/unit/libstore/nar-info-disk-cache\.cc'' - ''^tests/unit/libstore/nar-info\.cc'' - ''^tests/unit/libstore/outputs-spec\.cc'' - ''^tests/unit/libstore/path-info\.cc'' - ''^tests/unit/libstore/path\.cc'' - ''^tests/unit/libstore/serve-protocol\.cc'' - ''^tests/unit/libstore/worker-protocol\.cc'' - ''^tests/unit/libutil-support/tests/characterization\.hh'' - ''^tests/unit/libutil-support/tests/hash\.cc'' - ''^tests/unit/libutil-support/tests/hash\.hh'' - ''^tests/unit/libutil/args\.cc'' - ''^tests/unit/libutil/canon-path\.cc'' - ''^tests/unit/libutil/chunked-vector\.cc'' - ''^tests/unit/libutil/closure\.cc'' - ''^tests/unit/libutil/compression\.cc'' - ''^tests/unit/libutil/config\.cc'' - ''^tests/unit/libutil/file-content-address\.cc'' - ''^tests/unit/libutil/git\.cc'' - ''^tests/unit/libutil/hash\.cc'' - ''^tests/unit/libutil/hilite\.cc'' - ''^tests/unit/libutil/json-utils\.cc'' - ''^tests/unit/libutil/logging\.cc'' - ''^tests/unit/libutil/lru-cache\.cc'' - ''^tests/unit/libutil/pool\.cc'' - ''^tests/unit/libutil/references\.cc'' - ''^tests/unit/libutil/suggestions\.cc'' - ''^tests/unit/libutil/tests\.cc'' - ''^tests/unit/libutil/url\.cc'' - ''^tests/unit/libutil/xml-writer\.cc'' + ''^src/libexpr-test-support/tests/libexpr\.hh'' + ''^src/libexpr-test-support/tests/value/context\.cc'' + ''^src/libexpr-test-support/tests/value/context\.hh'' + ''^src/libexpr-test/derived-path\.cc'' + ''^src/libexpr-test/error_traces\.cc'' + ''^src/libexpr-test/eval\.cc'' + ''^src/libexpr-test/json\.cc'' + ''^src/libexpr-test/main\.cc'' + ''^src/libexpr-test/primops\.cc'' + ''^src/libexpr-test/search-path\.cc'' + ''^src/libexpr-test/trivial\.cc'' + ''^src/libexpr-test/value/context\.cc'' + ''^src/libexpr-test/value/print\.cc'' + ''^src/libfetchers-test/public-key\.cc'' + ''^src/libflake-test/flakeref\.cc'' + ''^src/libflake-test/url-name\.cc'' + ''^src/libstore-test-support/tests/derived-path\.cc'' + ''^src/libstore-test-support/tests/derived-path\.hh'' + ''^src/libstore-test-support/tests/nix_api_store\.hh'' + ''^src/libstore-test-support/tests/outputs-spec\.cc'' + ''^src/libstore-test-support/tests/outputs-spec\.hh'' + ''^src/libstore-test-support/tests/path\.cc'' + ''^src/libstore-test-support/tests/path\.hh'' + ''^src/libstore-test-support/tests/protocol\.hh'' + ''^src/libstore-test/common-protocol\.cc'' + ''^src/libstore-test/content-address\.cc'' + ''^src/libstore-test/derivation\.cc'' + ''^src/libstore-test/derived-path\.cc'' + ''^src/libstore-test/downstream-placeholder\.cc'' + ''^src/libstore-test/machines\.cc'' + ''^src/libstore-test/nar-info-disk-cache\.cc'' + ''^src/libstore-test/nar-info\.cc'' + ''^src/libstore-test/outputs-spec\.cc'' + ''^src/libstore-test/path-info\.cc'' + ''^src/libstore-test/path\.cc'' + ''^src/libstore-test/serve-protocol\.cc'' + ''^src/libstore-test/worker-protocol\.cc'' + ''^src/libutil-test-support/tests/characterization\.hh'' + ''^src/libutil-test-support/tests/hash\.cc'' + ''^src/libutil-test-support/tests/hash\.hh'' + ''^src/libutil-test/args\.cc'' + ''^src/libutil-test/canon-path\.cc'' + ''^src/libutil-test/chunked-vector\.cc'' + ''^src/libutil-test/closure\.cc'' + ''^src/libutil-test/compression\.cc'' + ''^src/libutil-test/config\.cc'' + ''^src/libutil-test/file-content-address\.cc'' + ''^src/libutil-test/git\.cc'' + ''^src/libutil-test/hash\.cc'' + ''^src/libutil-test/hilite\.cc'' + ''^src/libutil-test/json-utils\.cc'' + ''^src/libutil-test/logging\.cc'' + ''^src/libutil-test/lru-cache\.cc'' + ''^src/libutil-test/pool\.cc'' + ''^src/libutil-test/references\.cc'' + ''^src/libutil-test/suggestions\.cc'' + ''^src/libutil-test/tests\.cc'' + ''^src/libutil-test/url\.cc'' + ''^src/libutil-test/xml-writer\.cc'' ]; }; shellcheck = { @@ -666,7 +666,7 @@ ''^tests/functional/user-envs\.sh$'' ''^tests/functional/why-depends\.sh$'' ''^tests/functional/zstd\.sh$'' - ''^tests/unit/libutil/data/git/check-data\.sh$'' + ''^src/libutil-test/data/git/check-data\.sh$'' ]; }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 diff --git a/meson.build b/meson.build index 2a3932a40..fb38d7ef2 100644 --- a/meson.build +++ b/meson.build @@ -27,8 +27,9 @@ subproject('perl') # Testing subproject('libutil-test-support') subproject('libutil-test') -#subproject('libstore-test-support') -#subproject('libstore-test') -#subproject('libexpr-test-support') -#subproject('libexpr-test') -#subproject('libflake-test') +subproject('libstore-test-support') +subproject('libstore-test') +subproject('libfetchers-test') +subproject('libexpr-test-support') +subproject('libexpr-test') +subproject('libflake-test') diff --git a/mk/common-test.sh b/mk/common-test.sh index c80abd381..817422c40 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -4,7 +4,7 @@ # remove file extension. test_name=$(echo -n "${test?must be defined by caller (test runner)}" | sed \ - -e "s|^tests/unit/[^/]*/data/||" \ + -e "s|^src/[^/]*-test/data/||" \ -e "s|^tests/functional/||" \ -e "s|\.sh$||" \ ) diff --git a/package.nix b/package.nix index 158696f30..0661dc080 100644 --- a/package.nix +++ b/package.nix @@ -52,10 +52,6 @@ # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true -# Run the unit tests as part of the build. See `installUnitTests` for an -# alternative to this. -, doCheck ? __forDefaults.canRunInstalled - # Run the functional tests as part of the build. , doInstallCheck ? test-client != null || __forDefaults.canRunInstalled @@ -88,11 +84,6 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to install unit tests. This is useful when cross compiling -# since we cannot run them natively during the build, but can do so -# later. -, installUnitTests ? doBuild && !__forDefaults.canExecuteHost - # For running the functional tests against a pre-built Nix. Probably # want to use in conjunction with `doBuild = false;`. , test-daemon ? null @@ -118,7 +109,7 @@ let # things which should instead be gotten via `finalAttrs` in order to # work with overriding. attrs = { - inherit doBuild doCheck doInstallCheck; + inherit doBuild doInstallCheck; }; mkDerivation = @@ -134,16 +125,11 @@ in mkDerivation (finalAttrs: let inherit (finalAttrs) - doCheck doInstallCheck ; doBuild = !finalAttrs.dontBuild; - # Either running the unit tests during the build, or installing them - # to be run later, requiresthe unit tests to be built. - buildUnitTests = doCheck || installUnitTests; - in { inherit pname version; @@ -175,10 +161,8 @@ in { (fileset.difference ./src ./src/perl) ./COPYING ./scripts/local.mk - ] ++ lib.optionals buildUnitTests [ + ] ++ lib.optionals enableManual [ ./doc/manual - ] ++ lib.optionals buildUnitTests [ - ./tests/unit ] ++ lib.optionals doInstallCheck [ ./tests/functional ])); @@ -191,8 +175,6 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && enableManual) "doc" - ++ lib.optional installUnitTests "check" - ++ lib.optional doCheck "testresults" ; nativeBuildInputs = [ @@ -234,9 +216,6 @@ in { ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ lowdown - ] ++ lib.optionals buildUnitTests [ - gtest - rapidcheck ] ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies @@ -252,7 +231,6 @@ in { ] ++ lib.optional enableGC boehmgc; dontBuild = !attrs.doBuild; - doCheck = attrs.doCheck; disallowedReferences = [ boost ]; @@ -278,18 +256,13 @@ in { configureFlags = [ (lib.enableFeature doBuild "build") - (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") - (lib.enableFeature installUnitTests "install-unit-tests") (lib.withFeatureAs true "readline-flavor" readlineFlavor) ] ++ lib.optionals (!forDevShell) [ "--sysconfdir=/etc" - ] ++ lib.optionals installUnitTests [ - "--with-check-bin-dir=${builtins.placeholder "check"}/bin" - "--with-check-lib-dir=${builtins.placeholder "check"}/lib" ] ++ lib.optionals (doBuild) [ "--with-boost=${boost}/lib" ] ++ lib.optionals (doBuild && stdenv.isLinux) [ @@ -375,10 +348,6 @@ in { platforms = lib.platforms.unix ++ lib.platforms.windows; mainProgram = "nix"; broken = !(lib.all (a: a) [ - # We cannot run or install unit tests if we don't build them or - # Nix proper (which they depend on). - (installUnitTests -> doBuild) - (doCheck -> doBuild) # The build process for the manual currently requires extracting # data from the Nix executable we are trying to document. (enableManual -> doBuild) diff --git a/packaging/components.nix b/packaging/components.nix index 01b4e826e..0189f4ca3 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -8,18 +8,40 @@ in nix = callPackage ../package.nix { }; nix-util = callPackage ../src/libutil/package.nix { }; - - nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { }; - - nix-util-test = callPackage ../tests/unit/libutil/package.nix { }; - + nix-util-test-support = callPackage ../src/libutil-test-support/package.nix { }; + nix-util-test = callPackage ../src/libutil-test/package.nix { }; nix-util-c = callPackage ../src/libutil-c/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; + nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; + nix-store-test = callPackage ../src/libstore-test/package.nix { }; + nix-store-c = callPackage ../src/libstore-c/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; + nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; + nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; + nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; + nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; + nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; + + nix-flake = callPackage ../src/libflake/package.nix { }; + nix-flake-c = callPackage ../src/libflake-c/package.nix { }; + + nix-store = callPackage ../src/libstore/package.nix { }; + nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; + nix-store-test = callPackage ../src/libstore-test/package.nix { }; + nix-store-c = callPackage ../src/libstore-c/package.nix { }; + + nix-fetchers = callPackage ../src/libfetchers/package.nix { }; + nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; + nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; + + nix-expr = callPackage ../src/libexpr/package.nix { }; + nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; + nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; + nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; diff --git a/src/internal-api-docs/doxygen.cfg.in b/src/internal-api-docs/doxygen.cfg.in index 9e7425581..395e43fe1 100644 --- a/src/internal-api-docs/doxygen.cfg.in +++ b/src/internal-api-docs/doxygen.cfg.in @@ -38,27 +38,27 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - @src@/src/libcmd \ - @src@/src/libexpr \ - @src@/src/libexpr/flake \ - @src@/tests/unit/libexpr \ - @src@/tests/unit/libexpr/value \ - @src@/tests/unit/libexpr/test \ - @src@/tests/unit/libexpr/test/value \ - @src@/src/libexpr/value \ - @src@/src/libfetchers \ - @src@/src/libmain \ - @src@/src/libstore \ - @src@/src/libstore/build \ - @src@/src/libstore/builtins \ - @src@/tests/unit/libstore \ - @src@/tests/unit/libstore/test \ - @src@/src/libutil \ - @src@/tests/unit/libutil \ - @src@/tests/unit/libutil/test \ - @src@/src/nix \ - @src@/src/nix-env \ - @src@/src/nix-store + @src@/libcmd \ + @src@/libexpr \ + @src@/libexpr/flake \ + @src@/libexpr-test \ + @src@/libexpr-test/value \ + @src@/libexpr-test-support/test \ + @src@/libexpr-test-support/test/value \ + @src@/libexpr/value \ + @src@/libfetchers \ + @src@/libmain \ + @src@/libstore \ + @src@/libstore/build \ + @src@/libstore/builtins \ + @src@/libstore-test \ + @src@/libstore-test-support/test \ + @src@/libutil \ + @src@/libutil-test \ + @src@/libutil-test-support/test \ + @src@/nix \ + @src@/nix-env \ + @src@/nix-store # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index fa54d55f3..6a3bc0501 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -28,7 +28,6 @@ stdenv.mkDerivation (finalAttrs: { # Source is not compiled, but still must be available for Doxygen # to gather comments. (cpp ../.) - (cpp ../../tests/unit) ]; }; diff --git a/src/libexpr-test-support/.version b/src/libexpr-test-support/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr-test-support/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build new file mode 100644 index 000000000..5ab0661ca --- /dev/null +++ b/src/libexpr-test-support/meson.build @@ -0,0 +1,128 @@ +project('nix-expr-test-support', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-test-support'), + dependency('nix-expr'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'tests/value/context.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'tests/libexpr.hh', + 'tests/nix_api_expr.hh', + 'tests/value/context.hh', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nix-expr-test-support', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 + # is available. See also ../libutil/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/src/libexpr-test-support/tests/libexpr.hh similarity index 100% rename from tests/unit/libexpr-support/tests/libexpr.hh rename to src/libexpr-test-support/tests/libexpr.hh diff --git a/tests/unit/libexpr-support/tests/nix_api_expr.hh b/src/libexpr-test-support/tests/nix_api_expr.hh similarity index 100% rename from tests/unit/libexpr-support/tests/nix_api_expr.hh rename to src/libexpr-test-support/tests/nix_api_expr.hh diff --git a/tests/unit/libexpr-support/tests/value/context.cc b/src/libexpr-test-support/tests/value/context.cc similarity index 100% rename from tests/unit/libexpr-support/tests/value/context.cc rename to src/libexpr-test-support/tests/value/context.cc diff --git a/tests/unit/libexpr-support/tests/value/context.hh b/src/libexpr-test-support/tests/value/context.hh similarity index 100% rename from tests/unit/libexpr-support/tests/value/context.hh rename to src/libexpr-test-support/tests/value/context.hh diff --git a/src/libexpr-test/.version b/src/libexpr-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libexpr/derived-path.cc b/src/libexpr-test/derived-path.cc similarity index 100% rename from tests/unit/libexpr/derived-path.cc rename to src/libexpr-test/derived-path.cc diff --git a/tests/unit/libexpr/error_traces.cc b/src/libexpr-test/error_traces.cc similarity index 100% rename from tests/unit/libexpr/error_traces.cc rename to src/libexpr-test/error_traces.cc diff --git a/tests/unit/libexpr/eval.cc b/src/libexpr-test/eval.cc similarity index 100% rename from tests/unit/libexpr/eval.cc rename to src/libexpr-test/eval.cc diff --git a/tests/unit/libexpr/json.cc b/src/libexpr-test/json.cc similarity index 100% rename from tests/unit/libexpr/json.cc rename to src/libexpr-test/json.cc diff --git a/tests/unit/libexpr/main.cc b/src/libexpr-test/main.cc similarity index 100% rename from tests/unit/libexpr/main.cc rename to src/libexpr-test/main.cc diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build new file mode 100644 index 000000000..d4b0db51f --- /dev/null +++ b/src/libexpr-test/meson.build @@ -0,0 +1,125 @@ +project('nix-expr-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), + dependency('nix-expr'), + dependency('nix-expr-c'), + dependency('nix-expr-test-support'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-store.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'derived-path.cc', + 'error_traces.cc', + 'eval.cc', + 'json.cc', + 'main.cc', + 'nix_api_expr.cc', + 'nix_api_external.cc', + 'nix_api_value.cc', + 'primops.cc', + 'search-path.cc', + 'trivial.cc', + 'value/context.cc', + 'value/print.cc', + 'value/value.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/src/libexpr-test/nix_api_expr.cc similarity index 100% rename from tests/unit/libexpr/nix_api_expr.cc rename to src/libexpr-test/nix_api_expr.cc diff --git a/tests/unit/libexpr/nix_api_external.cc b/src/libexpr-test/nix_api_external.cc similarity index 100% rename from tests/unit/libexpr/nix_api_external.cc rename to src/libexpr-test/nix_api_external.cc diff --git a/tests/unit/libexpr/nix_api_value.cc b/src/libexpr-test/nix_api_value.cc similarity index 100% rename from tests/unit/libexpr/nix_api_value.cc rename to src/libexpr-test/nix_api_value.cc diff --git a/tests/unit/libexpr/primops.cc b/src/libexpr-test/primops.cc similarity index 100% rename from tests/unit/libexpr/primops.cc rename to src/libexpr-test/primops.cc diff --git a/tests/unit/libexpr/search-path.cc b/src/libexpr-test/search-path.cc similarity index 100% rename from tests/unit/libexpr/search-path.cc rename to src/libexpr-test/search-path.cc diff --git a/tests/unit/libexpr/trivial.cc b/src/libexpr-test/trivial.cc similarity index 100% rename from tests/unit/libexpr/trivial.cc rename to src/libexpr-test/trivial.cc diff --git a/tests/unit/libexpr/value/context.cc b/src/libexpr-test/value/context.cc similarity index 100% rename from tests/unit/libexpr/value/context.cc rename to src/libexpr-test/value/context.cc diff --git a/tests/unit/libexpr/value/print.cc b/src/libexpr-test/value/print.cc similarity index 100% rename from tests/unit/libexpr/value/print.cc rename to src/libexpr-test/value/print.cc diff --git a/tests/unit/libexpr/value/value.cc b/src/libexpr-test/value/value.cc similarity index 100% rename from tests/unit/libexpr/value/value.cc rename to src/libexpr-test/value/value.cc diff --git a/src/libfetchers-test/.version b/src/libfetchers-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libfetchers-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libfetchers/data/public-key/defaultType.json b/src/libfetchers-test/data/public-key/defaultType.json similarity index 100% rename from tests/unit/libfetchers/data/public-key/defaultType.json rename to src/libfetchers-test/data/public-key/defaultType.json diff --git a/tests/unit/libfetchers/data/public-key/noRoundTrip.json b/src/libfetchers-test/data/public-key/noRoundTrip.json similarity index 100% rename from tests/unit/libfetchers/data/public-key/noRoundTrip.json rename to src/libfetchers-test/data/public-key/noRoundTrip.json diff --git a/tests/unit/libfetchers/data/public-key/simple.json b/src/libfetchers-test/data/public-key/simple.json similarity index 100% rename from tests/unit/libfetchers/data/public-key/simple.json rename to src/libfetchers-test/data/public-key/simple.json diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build new file mode 100644 index 000000000..be031f592 --- /dev/null +++ b/src/libfetchers-test/meson.build @@ -0,0 +1,109 @@ +project('nix-fetchers-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), + dependency('nix-fetchers'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-store.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'public-key.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libfetchers/public-key.cc b/src/libfetchers-test/public-key.cc similarity index 100% rename from tests/unit/libfetchers/public-key.cc rename to src/libfetchers-test/public-key.cc diff --git a/src/libflake-test/.version b/src/libflake-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libflake-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libflake/flakeref.cc b/src/libflake-test/flakeref.cc similarity index 100% rename from tests/unit/libflake/flakeref.cc rename to src/libflake-test/flakeref.cc diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build new file mode 100644 index 000000000..a9df80885 --- /dev/null +++ b/src/libflake-test/meson.build @@ -0,0 +1,114 @@ +project('nix-flake-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), + dependency('nix-expr'), + dependency('nix-expr-c'), + dependency('nix-expr-test-support'), + dependency('nix-flake'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'flakeref.cc', + 'url-name.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libflake/url-name.cc b/src/libflake-test/url-name.cc similarity index 100% rename from tests/unit/libflake/url-name.cc rename to src/libflake-test/url-name.cc diff --git a/src/libstore-test-support/.version b/src/libstore-test-support/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore-test-support/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build new file mode 100644 index 000000000..186d9b72a --- /dev/null +++ b/src/libstore-test-support/meson.build @@ -0,0 +1,130 @@ +project('nix-store-test-support', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-test-support'), + dependency('nix-store'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'tests/derived-path.cc', + 'tests/outputs-spec.cc', + 'tests/path.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'tests/derived-path.hh', + 'tests/libstore.hh', + 'tests/nix_api_store.hh', + 'tests/outputs-spec.hh', + 'tests/path.hh', + 'tests/protocol.hh', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nix-store-test-support', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 + # is available. See also ../libutil/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/tests/unit/libstore-support/tests/derived-path.cc b/src/libstore-test-support/tests/derived-path.cc similarity index 100% rename from tests/unit/libstore-support/tests/derived-path.cc rename to src/libstore-test-support/tests/derived-path.cc diff --git a/tests/unit/libstore-support/tests/derived-path.hh b/src/libstore-test-support/tests/derived-path.hh similarity index 100% rename from tests/unit/libstore-support/tests/derived-path.hh rename to src/libstore-test-support/tests/derived-path.hh diff --git a/tests/unit/libstore-support/tests/libstore.hh b/src/libstore-test-support/tests/libstore.hh similarity index 100% rename from tests/unit/libstore-support/tests/libstore.hh rename to src/libstore-test-support/tests/libstore.hh diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/src/libstore-test-support/tests/nix_api_store.hh similarity index 100% rename from tests/unit/libstore-support/tests/nix_api_store.hh rename to src/libstore-test-support/tests/nix_api_store.hh diff --git a/tests/unit/libstore-support/tests/outputs-spec.cc b/src/libstore-test-support/tests/outputs-spec.cc similarity index 100% rename from tests/unit/libstore-support/tests/outputs-spec.cc rename to src/libstore-test-support/tests/outputs-spec.cc diff --git a/tests/unit/libstore-support/tests/outputs-spec.hh b/src/libstore-test-support/tests/outputs-spec.hh similarity index 100% rename from tests/unit/libstore-support/tests/outputs-spec.hh rename to src/libstore-test-support/tests/outputs-spec.hh diff --git a/tests/unit/libstore-support/tests/path.cc b/src/libstore-test-support/tests/path.cc similarity index 100% rename from tests/unit/libstore-support/tests/path.cc rename to src/libstore-test-support/tests/path.cc diff --git a/tests/unit/libstore-support/tests/path.hh b/src/libstore-test-support/tests/path.hh similarity index 100% rename from tests/unit/libstore-support/tests/path.hh rename to src/libstore-test-support/tests/path.hh diff --git a/tests/unit/libstore-support/tests/protocol.hh b/src/libstore-test-support/tests/protocol.hh similarity index 100% rename from tests/unit/libstore-support/tests/protocol.hh rename to src/libstore-test-support/tests/protocol.hh diff --git a/src/libstore-test/.version b/src/libstore-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libstore/common-protocol.cc b/src/libstore-test/common-protocol.cc similarity index 100% rename from tests/unit/libstore/common-protocol.cc rename to src/libstore-test/common-protocol.cc diff --git a/tests/unit/libstore/content-address.cc b/src/libstore-test/content-address.cc similarity index 100% rename from tests/unit/libstore/content-address.cc rename to src/libstore-test/content-address.cc diff --git a/tests/unit/libstore/data/common-protocol/content-address.bin b/src/libstore-test/data/common-protocol/content-address.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/content-address.bin rename to src/libstore-test/data/common-protocol/content-address.bin diff --git a/tests/unit/libstore/data/common-protocol/drv-output.bin b/src/libstore-test/data/common-protocol/drv-output.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/drv-output.bin rename to src/libstore-test/data/common-protocol/drv-output.bin diff --git a/tests/unit/libstore/data/common-protocol/optional-content-address.bin b/src/libstore-test/data/common-protocol/optional-content-address.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/optional-content-address.bin rename to src/libstore-test/data/common-protocol/optional-content-address.bin diff --git a/tests/unit/libstore/data/common-protocol/optional-store-path.bin b/src/libstore-test/data/common-protocol/optional-store-path.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/optional-store-path.bin rename to src/libstore-test/data/common-protocol/optional-store-path.bin diff --git a/tests/unit/libstore/data/common-protocol/realisation.bin b/src/libstore-test/data/common-protocol/realisation.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/realisation.bin rename to src/libstore-test/data/common-protocol/realisation.bin diff --git a/tests/unit/libstore/data/common-protocol/set.bin b/src/libstore-test/data/common-protocol/set.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/set.bin rename to src/libstore-test/data/common-protocol/set.bin diff --git a/tests/unit/libstore/data/common-protocol/store-path.bin b/src/libstore-test/data/common-protocol/store-path.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/store-path.bin rename to src/libstore-test/data/common-protocol/store-path.bin diff --git a/tests/unit/libstore/data/common-protocol/string.bin b/src/libstore-test/data/common-protocol/string.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/string.bin rename to src/libstore-test/data/common-protocol/string.bin diff --git a/tests/unit/libstore/data/common-protocol/vector.bin b/src/libstore-test/data/common-protocol/vector.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/vector.bin rename to src/libstore-test/data/common-protocol/vector.bin diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv b/src/libstore-test/data/derivation/advanced-attributes-defaults.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv rename to src/libstore-test/data/derivation/advanced-attributes-defaults.drv diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json b/src/libstore-test/data/derivation/advanced-attributes-defaults.json similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-defaults.json rename to src/libstore-test/data/derivation/advanced-attributes-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes.drv b/src/libstore-test/data/derivation/advanced-attributes.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes.drv rename to src/libstore-test/data/derivation/advanced-attributes.drv diff --git a/tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv b/src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv similarity index 100% rename from tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv rename to src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv diff --git a/tests/unit/libstore/data/derivation/bad-version.drv b/src/libstore-test/data/derivation/bad-version.drv similarity index 100% rename from tests/unit/libstore/data/derivation/bad-version.drv rename to src/libstore-test/data/derivation/bad-version.drv diff --git a/tests/unit/libstore/data/derivation/dynDerivationDeps.drv b/src/libstore-test/data/derivation/dynDerivationDeps.drv similarity index 100% rename from tests/unit/libstore/data/derivation/dynDerivationDeps.drv rename to src/libstore-test/data/derivation/dynDerivationDeps.drv diff --git a/tests/unit/libstore/data/derivation/dynDerivationDeps.json b/src/libstore-test/data/derivation/dynDerivationDeps.json similarity index 100% rename from tests/unit/libstore/data/derivation/dynDerivationDeps.json rename to src/libstore-test/data/derivation/dynDerivationDeps.json diff --git a/tests/unit/libstore/data/derivation/output-caFixedFlat.json b/src/libstore-test/data/derivation/output-caFixedFlat.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFixedFlat.json rename to src/libstore-test/data/derivation/output-caFixedFlat.json diff --git a/tests/unit/libstore/data/derivation/output-caFixedNAR.json b/src/libstore-test/data/derivation/output-caFixedNAR.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFixedNAR.json rename to src/libstore-test/data/derivation/output-caFixedNAR.json diff --git a/tests/unit/libstore/data/derivation/output-caFixedText.json b/src/libstore-test/data/derivation/output-caFixedText.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFixedText.json rename to src/libstore-test/data/derivation/output-caFixedText.json diff --git a/tests/unit/libstore/data/derivation/output-caFloating.json b/src/libstore-test/data/derivation/output-caFloating.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFloating.json rename to src/libstore-test/data/derivation/output-caFloating.json diff --git a/tests/unit/libstore/data/derivation/output-deferred.json b/src/libstore-test/data/derivation/output-deferred.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-deferred.json rename to src/libstore-test/data/derivation/output-deferred.json diff --git a/tests/unit/libstore/data/derivation/output-impure.json b/src/libstore-test/data/derivation/output-impure.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-impure.json rename to src/libstore-test/data/derivation/output-impure.json diff --git a/tests/unit/libstore/data/derivation/output-inputAddressed.json b/src/libstore-test/data/derivation/output-inputAddressed.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-inputAddressed.json rename to src/libstore-test/data/derivation/output-inputAddressed.json diff --git a/tests/unit/libstore/data/derivation/simple.drv b/src/libstore-test/data/derivation/simple.drv similarity index 100% rename from tests/unit/libstore/data/derivation/simple.drv rename to src/libstore-test/data/derivation/simple.drv diff --git a/tests/unit/libstore/data/derivation/simple.json b/src/libstore-test/data/derivation/simple.json similarity index 100% rename from tests/unit/libstore/data/derivation/simple.json rename to src/libstore-test/data/derivation/simple.json diff --git a/tests/unit/libstore/data/machines/bad_format b/src/libstore-test/data/machines/bad_format similarity index 100% rename from tests/unit/libstore/data/machines/bad_format rename to src/libstore-test/data/machines/bad_format diff --git a/tests/unit/libstore/data/machines/valid b/src/libstore-test/data/machines/valid similarity index 100% rename from tests/unit/libstore/data/machines/valid rename to src/libstore-test/data/machines/valid diff --git a/tests/unit/libstore/data/nar-info/impure.json b/src/libstore-test/data/nar-info/impure.json similarity index 100% rename from tests/unit/libstore/data/nar-info/impure.json rename to src/libstore-test/data/nar-info/impure.json diff --git a/tests/unit/libstore/data/nar-info/pure.json b/src/libstore-test/data/nar-info/pure.json similarity index 100% rename from tests/unit/libstore/data/nar-info/pure.json rename to src/libstore-test/data/nar-info/pure.json diff --git a/tests/unit/libstore/data/path-info/empty_impure.json b/src/libstore-test/data/path-info/empty_impure.json similarity index 100% rename from tests/unit/libstore/data/path-info/empty_impure.json rename to src/libstore-test/data/path-info/empty_impure.json diff --git a/tests/unit/libstore/data/path-info/empty_pure.json b/src/libstore-test/data/path-info/empty_pure.json similarity index 100% rename from tests/unit/libstore/data/path-info/empty_pure.json rename to src/libstore-test/data/path-info/empty_pure.json diff --git a/tests/unit/libstore/data/path-info/impure.json b/src/libstore-test/data/path-info/impure.json similarity index 100% rename from tests/unit/libstore/data/path-info/impure.json rename to src/libstore-test/data/path-info/impure.json diff --git a/tests/unit/libstore/data/path-info/pure.json b/src/libstore-test/data/path-info/pure.json similarity index 100% rename from tests/unit/libstore/data/path-info/pure.json rename to src/libstore-test/data/path-info/pure.json diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.1.bin b/src/libstore-test/data/serve-protocol/build-options-2.1.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.1.bin rename to src/libstore-test/data/serve-protocol/build-options-2.1.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.2.bin b/src/libstore-test/data/serve-protocol/build-options-2.2.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.2.bin rename to src/libstore-test/data/serve-protocol/build-options-2.2.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.3.bin b/src/libstore-test/data/serve-protocol/build-options-2.3.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.3.bin rename to src/libstore-test/data/serve-protocol/build-options-2.3.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.7.bin b/src/libstore-test/data/serve-protocol/build-options-2.7.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.7.bin rename to src/libstore-test/data/serve-protocol/build-options-2.7.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-result-2.2.bin b/src/libstore-test/data/serve-protocol/build-result-2.2.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-result-2.2.bin rename to src/libstore-test/data/serve-protocol/build-result-2.2.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-result-2.3.bin b/src/libstore-test/data/serve-protocol/build-result-2.3.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-result-2.3.bin rename to src/libstore-test/data/serve-protocol/build-result-2.3.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-result-2.6.bin b/src/libstore-test/data/serve-protocol/build-result-2.6.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-result-2.6.bin rename to src/libstore-test/data/serve-protocol/build-result-2.6.bin diff --git a/tests/unit/libstore/data/serve-protocol/content-address.bin b/src/libstore-test/data/serve-protocol/content-address.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/content-address.bin rename to src/libstore-test/data/serve-protocol/content-address.bin diff --git a/tests/unit/libstore/data/serve-protocol/drv-output.bin b/src/libstore-test/data/serve-protocol/drv-output.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/drv-output.bin rename to src/libstore-test/data/serve-protocol/drv-output.bin diff --git a/tests/unit/libstore/data/serve-protocol/handshake-to-client.bin b/src/libstore-test/data/serve-protocol/handshake-to-client.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/handshake-to-client.bin rename to src/libstore-test/data/serve-protocol/handshake-to-client.bin diff --git a/tests/unit/libstore/data/serve-protocol/optional-content-address.bin b/src/libstore-test/data/serve-protocol/optional-content-address.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/optional-content-address.bin rename to src/libstore-test/data/serve-protocol/optional-content-address.bin diff --git a/tests/unit/libstore/data/serve-protocol/optional-store-path.bin b/src/libstore-test/data/serve-protocol/optional-store-path.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/optional-store-path.bin rename to src/libstore-test/data/serve-protocol/optional-store-path.bin diff --git a/tests/unit/libstore/data/serve-protocol/realisation.bin b/src/libstore-test/data/serve-protocol/realisation.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/realisation.bin rename to src/libstore-test/data/serve-protocol/realisation.bin diff --git a/tests/unit/libstore/data/serve-protocol/set.bin b/src/libstore-test/data/serve-protocol/set.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/set.bin rename to src/libstore-test/data/serve-protocol/set.bin diff --git a/tests/unit/libstore/data/serve-protocol/store-path.bin b/src/libstore-test/data/serve-protocol/store-path.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/store-path.bin rename to src/libstore-test/data/serve-protocol/store-path.bin diff --git a/tests/unit/libstore/data/serve-protocol/string.bin b/src/libstore-test/data/serve-protocol/string.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/string.bin rename to src/libstore-test/data/serve-protocol/string.bin diff --git a/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin b/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin rename to src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin diff --git a/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin b/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin rename to src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin diff --git a/tests/unit/libstore/data/serve-protocol/vector.bin b/src/libstore-test/data/serve-protocol/vector.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/vector.bin rename to src/libstore-test/data/serve-protocol/vector.bin diff --git a/tests/unit/libstore/data/store-reference/auto.txt b/src/libstore-test/data/store-reference/auto.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/auto.txt rename to src/libstore-test/data/store-reference/auto.txt diff --git a/tests/unit/libstore/data/store-reference/auto_param.txt b/src/libstore-test/data/store-reference/auto_param.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/auto_param.txt rename to src/libstore-test/data/store-reference/auto_param.txt diff --git a/tests/unit/libstore/data/store-reference/local_1.txt b/src/libstore-test/data/store-reference/local_1.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_1.txt rename to src/libstore-test/data/store-reference/local_1.txt diff --git a/tests/unit/libstore/data/store-reference/local_2.txt b/src/libstore-test/data/store-reference/local_2.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_2.txt rename to src/libstore-test/data/store-reference/local_2.txt diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_1.txt b/src/libstore-test/data/store-reference/local_shorthand_1.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_shorthand_1.txt rename to src/libstore-test/data/store-reference/local_shorthand_1.txt diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_2.txt b/src/libstore-test/data/store-reference/local_shorthand_2.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_shorthand_2.txt rename to src/libstore-test/data/store-reference/local_shorthand_2.txt diff --git a/tests/unit/libstore/data/store-reference/ssh.txt b/src/libstore-test/data/store-reference/ssh.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/ssh.txt rename to src/libstore-test/data/store-reference/ssh.txt diff --git a/tests/unit/libstore/data/store-reference/unix.txt b/src/libstore-test/data/store-reference/unix.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/unix.txt rename to src/libstore-test/data/store-reference/unix.txt diff --git a/tests/unit/libstore/data/store-reference/unix_shorthand.txt b/src/libstore-test/data/store-reference/unix_shorthand.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/unix_shorthand.txt rename to src/libstore-test/data/store-reference/unix_shorthand.txt diff --git a/tests/unit/libstore/data/worker-protocol/build-mode.bin b/src/libstore-test/data/worker-protocol/build-mode.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-mode.bin rename to src/libstore-test/data/worker-protocol/build-mode.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.27.bin b/src/libstore-test/data/worker-protocol/build-result-1.27.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.27.bin rename to src/libstore-test/data/worker-protocol/build-result-1.27.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.28.bin b/src/libstore-test/data/worker-protocol/build-result-1.28.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.28.bin rename to src/libstore-test/data/worker-protocol/build-result-1.28.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.29.bin b/src/libstore-test/data/worker-protocol/build-result-1.29.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.29.bin rename to src/libstore-test/data/worker-protocol/build-result-1.29.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.37.bin b/src/libstore-test/data/worker-protocol/build-result-1.37.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.37.bin rename to src/libstore-test/data/worker-protocol/build-result-1.37.bin diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin b/src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin rename to src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin b/src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin rename to src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin b/src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin rename to src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin diff --git a/tests/unit/libstore/data/worker-protocol/content-address.bin b/src/libstore-test/data/worker-protocol/content-address.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/content-address.bin rename to src/libstore-test/data/worker-protocol/content-address.bin diff --git a/tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin b/src/libstore-test/data/worker-protocol/derived-path-1.29.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin rename to src/libstore-test/data/worker-protocol/derived-path-1.29.bin diff --git a/tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin b/src/libstore-test/data/worker-protocol/derived-path-1.30.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin rename to src/libstore-test/data/worker-protocol/derived-path-1.30.bin diff --git a/tests/unit/libstore/data/worker-protocol/drv-output.bin b/src/libstore-test/data/worker-protocol/drv-output.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/drv-output.bin rename to src/libstore-test/data/worker-protocol/drv-output.bin diff --git a/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin b/src/libstore-test/data/worker-protocol/handshake-to-client.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/handshake-to-client.bin rename to src/libstore-test/data/worker-protocol/handshake-to-client.bin diff --git a/tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin b/src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin rename to src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin diff --git a/tests/unit/libstore/data/worker-protocol/optional-content-address.bin b/src/libstore-test/data/worker-protocol/optional-content-address.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/optional-content-address.bin rename to src/libstore-test/data/worker-protocol/optional-content-address.bin diff --git a/tests/unit/libstore/data/worker-protocol/optional-store-path.bin b/src/libstore-test/data/worker-protocol/optional-store-path.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/optional-store-path.bin rename to src/libstore-test/data/worker-protocol/optional-store-path.bin diff --git a/tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin b/src/libstore-test/data/worker-protocol/optional-trusted-flag.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin rename to src/libstore-test/data/worker-protocol/optional-trusted-flag.bin diff --git a/tests/unit/libstore/data/worker-protocol/realisation.bin b/src/libstore-test/data/worker-protocol/realisation.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/realisation.bin rename to src/libstore-test/data/worker-protocol/realisation.bin diff --git a/tests/unit/libstore/data/worker-protocol/set.bin b/src/libstore-test/data/worker-protocol/set.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/set.bin rename to src/libstore-test/data/worker-protocol/set.bin diff --git a/tests/unit/libstore/data/worker-protocol/store-path.bin b/src/libstore-test/data/worker-protocol/store-path.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/store-path.bin rename to src/libstore-test/data/worker-protocol/store-path.bin diff --git a/tests/unit/libstore/data/worker-protocol/string.bin b/src/libstore-test/data/worker-protocol/string.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/string.bin rename to src/libstore-test/data/worker-protocol/string.bin diff --git a/tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin b/src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin rename to src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin diff --git a/tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin b/src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin rename to src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin diff --git a/tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin b/src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin rename to src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin diff --git a/tests/unit/libstore/data/worker-protocol/vector.bin b/src/libstore-test/data/worker-protocol/vector.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/vector.bin rename to src/libstore-test/data/worker-protocol/vector.bin diff --git a/tests/unit/libstore/derivation-advanced-attrs.cc b/src/libstore-test/derivation-advanced-attrs.cc similarity index 100% rename from tests/unit/libstore/derivation-advanced-attrs.cc rename to src/libstore-test/derivation-advanced-attrs.cc diff --git a/tests/unit/libstore/derivation.cc b/src/libstore-test/derivation.cc similarity index 100% rename from tests/unit/libstore/derivation.cc rename to src/libstore-test/derivation.cc diff --git a/tests/unit/libstore/derived-path.cc b/src/libstore-test/derived-path.cc similarity index 100% rename from tests/unit/libstore/derived-path.cc rename to src/libstore-test/derived-path.cc diff --git a/tests/unit/libstore/downstream-placeholder.cc b/src/libstore-test/downstream-placeholder.cc similarity index 100% rename from tests/unit/libstore/downstream-placeholder.cc rename to src/libstore-test/downstream-placeholder.cc diff --git a/tests/unit/libstore/machines.cc b/src/libstore-test/machines.cc similarity index 100% rename from tests/unit/libstore/machines.cc rename to src/libstore-test/machines.cc diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build new file mode 100644 index 000000000..83d3244d4 --- /dev/null +++ b/src/libstore-test/meson.build @@ -0,0 +1,123 @@ +project('nix-store-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'common-protocol.cc', + 'content-address.cc', + 'derivation-advanced-attrs.cc', + 'derivation.cc', + 'derived-path.cc', + 'downstream-placeholder.cc', + 'machines.cc', + 'nar-info-disk-cache.cc', + 'nar-info.cc', + 'nix_api_store.cc', + 'outputs-spec.cc', + 'path-info.cc', + 'path.cc', + 'references.cc', + 'serve-protocol.cc', + 'store-reference.cc', + 'worker-protocol.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libstore/nar-info-disk-cache.cc b/src/libstore-test/nar-info-disk-cache.cc similarity index 100% rename from tests/unit/libstore/nar-info-disk-cache.cc rename to src/libstore-test/nar-info-disk-cache.cc diff --git a/tests/unit/libstore/nar-info.cc b/src/libstore-test/nar-info.cc similarity index 100% rename from tests/unit/libstore/nar-info.cc rename to src/libstore-test/nar-info.cc diff --git a/tests/unit/libstore/nix_api_store.cc b/src/libstore-test/nix_api_store.cc similarity index 100% rename from tests/unit/libstore/nix_api_store.cc rename to src/libstore-test/nix_api_store.cc diff --git a/tests/unit/libstore/outputs-spec.cc b/src/libstore-test/outputs-spec.cc similarity index 100% rename from tests/unit/libstore/outputs-spec.cc rename to src/libstore-test/outputs-spec.cc diff --git a/tests/unit/libstore/path-info.cc b/src/libstore-test/path-info.cc similarity index 100% rename from tests/unit/libstore/path-info.cc rename to src/libstore-test/path-info.cc diff --git a/tests/unit/libstore/path.cc b/src/libstore-test/path.cc similarity index 100% rename from tests/unit/libstore/path.cc rename to src/libstore-test/path.cc diff --git a/tests/unit/libstore/references.cc b/src/libstore-test/references.cc similarity index 100% rename from tests/unit/libstore/references.cc rename to src/libstore-test/references.cc diff --git a/tests/unit/libstore/serve-protocol.cc b/src/libstore-test/serve-protocol.cc similarity index 100% rename from tests/unit/libstore/serve-protocol.cc rename to src/libstore-test/serve-protocol.cc diff --git a/tests/unit/libstore/store-reference.cc b/src/libstore-test/store-reference.cc similarity index 100% rename from tests/unit/libstore/store-reference.cc rename to src/libstore-test/store-reference.cc diff --git a/tests/unit/libstore/worker-protocol.cc b/src/libstore-test/worker-protocol.cc similarity index 100% rename from tests/unit/libstore/worker-protocol.cc rename to src/libstore-test/worker-protocol.cc diff --git a/src/libutil-test b/src/libutil-test deleted file mode 120000 index 62c86f54b..000000000 --- a/src/libutil-test +++ /dev/null @@ -1 +0,0 @@ -../tests/unit/libutil/ \ No newline at end of file diff --git a/src/libutil-test-support b/src/libutil-test-support deleted file mode 120000 index f7da46d4c..000000000 --- a/src/libutil-test-support +++ /dev/null @@ -1 +0,0 @@ -../tests/unit/libutil-support/ \ No newline at end of file diff --git a/src/libutil-test-support/.version b/src/libutil-test-support/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil-test-support/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/meson.build b/src/libutil-test-support/meson.build similarity index 95% rename from tests/unit/libutil-support/meson.build rename to src/libutil-test-support/meson.build index d5ee8eed7..4a8f8b54e 100644 --- a/tests/unit/libutil-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -42,6 +42,9 @@ rapidcheck = dependency('rapidcheck') deps_public += rapidcheck add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/tests/unit/libutil-support/package.nix b/src/libutil-test-support/package.nix similarity index 100% rename from tests/unit/libutil-support/package.nix rename to src/libutil-test-support/package.nix diff --git a/tests/unit/libutil-support/tests/characterization.hh b/src/libutil-test-support/tests/characterization.hh similarity index 100% rename from tests/unit/libutil-support/tests/characterization.hh rename to src/libutil-test-support/tests/characterization.hh diff --git a/tests/unit/libutil-support/tests/hash.cc b/src/libutil-test-support/tests/hash.cc similarity index 100% rename from tests/unit/libutil-support/tests/hash.cc rename to src/libutil-test-support/tests/hash.cc diff --git a/tests/unit/libutil-support/tests/hash.hh b/src/libutil-test-support/tests/hash.hh similarity index 100% rename from tests/unit/libutil-support/tests/hash.hh rename to src/libutil-test-support/tests/hash.hh diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/src/libutil-test-support/tests/nix_api_util.hh similarity index 100% rename from tests/unit/libutil-support/tests/nix_api_util.hh rename to src/libutil-test-support/tests/nix_api_util.hh diff --git a/tests/unit/libutil-support/tests/string_callback.cc b/src/libutil-test-support/tests/string_callback.cc similarity index 100% rename from tests/unit/libutil-support/tests/string_callback.cc rename to src/libutil-test-support/tests/string_callback.cc diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/src/libutil-test-support/tests/string_callback.hh similarity index 100% rename from tests/unit/libutil-support/tests/string_callback.hh rename to src/libutil-test-support/tests/string_callback.hh diff --git a/src/libutil-test/.version b/src/libutil-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libutil/args.cc b/src/libutil-test/args.cc similarity index 100% rename from tests/unit/libutil/args.cc rename to src/libutil-test/args.cc diff --git a/tests/unit/libutil/canon-path.cc b/src/libutil-test/canon-path.cc similarity index 100% rename from tests/unit/libutil/canon-path.cc rename to src/libutil-test/canon-path.cc diff --git a/tests/unit/libutil/chunked-vector.cc b/src/libutil-test/chunked-vector.cc similarity index 100% rename from tests/unit/libutil/chunked-vector.cc rename to src/libutil-test/chunked-vector.cc diff --git a/tests/unit/libutil/closure.cc b/src/libutil-test/closure.cc similarity index 100% rename from tests/unit/libutil/closure.cc rename to src/libutil-test/closure.cc diff --git a/tests/unit/libutil/compression.cc b/src/libutil-test/compression.cc similarity index 100% rename from tests/unit/libutil/compression.cc rename to src/libutil-test/compression.cc diff --git a/tests/unit/libutil/config.cc b/src/libutil-test/config.cc similarity index 100% rename from tests/unit/libutil/config.cc rename to src/libutil-test/config.cc diff --git a/tests/unit/libutil/data/git/check-data.sh b/src/libutil-test/data/git/check-data.sh similarity index 100% rename from tests/unit/libutil/data/git/check-data.sh rename to src/libutil-test/data/git/check-data.sh diff --git a/tests/unit/libutil/data/git/hello-world-blob.bin b/src/libutil-test/data/git/hello-world-blob.bin similarity index 100% rename from tests/unit/libutil/data/git/hello-world-blob.bin rename to src/libutil-test/data/git/hello-world-blob.bin diff --git a/tests/unit/libutil/data/git/hello-world.bin b/src/libutil-test/data/git/hello-world.bin similarity index 100% rename from tests/unit/libutil/data/git/hello-world.bin rename to src/libutil-test/data/git/hello-world.bin diff --git a/tests/unit/libutil/data/git/tree.bin b/src/libutil-test/data/git/tree.bin similarity index 100% rename from tests/unit/libutil/data/git/tree.bin rename to src/libutil-test/data/git/tree.bin diff --git a/tests/unit/libutil/data/git/tree.txt b/src/libutil-test/data/git/tree.txt similarity index 100% rename from tests/unit/libutil/data/git/tree.txt rename to src/libutil-test/data/git/tree.txt diff --git a/tests/unit/libutil/file-content-address.cc b/src/libutil-test/file-content-address.cc similarity index 100% rename from tests/unit/libutil/file-content-address.cc rename to src/libutil-test/file-content-address.cc diff --git a/tests/unit/libutil/git.cc b/src/libutil-test/git.cc similarity index 99% rename from tests/unit/libutil/git.cc rename to src/libutil-test/git.cc index ff934c117..7c360d7c5 100644 --- a/tests/unit/libutil/git.cc +++ b/src/libutil-test/git.cc @@ -88,7 +88,7 @@ TEST_F(GitTest, blob_write) { /** * This data is for "shallow" tree tests. However, we use "real" hashes * so that we can check our test data in a small shell script test test - * (`tests/unit/libutil/data/git/check-data.sh`). + * (`src/libutil-test/data/git/check-data.sh`). */ const static Tree tree = { { diff --git a/tests/unit/libutil/hash.cc b/src/libutil-test/hash.cc similarity index 100% rename from tests/unit/libutil/hash.cc rename to src/libutil-test/hash.cc diff --git a/tests/unit/libutil/hilite.cc b/src/libutil-test/hilite.cc similarity index 100% rename from tests/unit/libutil/hilite.cc rename to src/libutil-test/hilite.cc diff --git a/tests/unit/libutil/json-utils.cc b/src/libutil-test/json-utils.cc similarity index 100% rename from tests/unit/libutil/json-utils.cc rename to src/libutil-test/json-utils.cc diff --git a/tests/unit/libutil/logging.cc b/src/libutil-test/logging.cc similarity index 100% rename from tests/unit/libutil/logging.cc rename to src/libutil-test/logging.cc diff --git a/tests/unit/libutil/lru-cache.cc b/src/libutil-test/lru-cache.cc similarity index 100% rename from tests/unit/libutil/lru-cache.cc rename to src/libutil-test/lru-cache.cc diff --git a/tests/unit/libutil/meson.build b/src/libutil-test/meson.build similarity index 88% rename from tests/unit/libutil/meson.build rename to src/libutil-test/meson.build index 3f6e0fe65..c3d72b976 100644 --- a/tests/unit/libutil/meson.build +++ b/src/libutil-test/meson.build @@ -23,11 +23,6 @@ deps_private_subproject = [ ] # See note in ../nix-util/meson.build deps_other = [ ] -configdata = configuration_data() - -# TODO rename, because it will conflict with downstream projects -configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) - foreach nix_dep : [ dependency('nix-util'), dependency('nix-util-c'), @@ -60,15 +55,11 @@ deps_private += rapidcheck gtest = dependency('gtest', main : true) deps_private += gtest -config_h = configure_file( - configuration : configdata, - output : 'config-util-test.h', -) - add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util-test.h', + '-include', 'config-util.hh', + '-include', 'config-util.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', @@ -113,7 +104,7 @@ include_dirs = [include_directories('.')] this_exe = executable( - 'nix-util-test', + meson.project_name(), sources, dependencies : deps_private_subproject + deps_private + deps_other, include_directories : include_dirs, @@ -123,7 +114,7 @@ this_exe = executable( install : true, ) -test('nix-util-test', this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, diff --git a/tests/unit/libutil/nix_api_util.cc b/src/libutil-test/nix_api_util.cc similarity index 100% rename from tests/unit/libutil/nix_api_util.cc rename to src/libutil-test/nix_api_util.cc diff --git a/tests/unit/libutil/package.nix b/src/libutil-test/package.nix similarity index 100% rename from tests/unit/libutil/package.nix rename to src/libutil-test/package.nix diff --git a/tests/unit/libutil/pool.cc b/src/libutil-test/pool.cc similarity index 100% rename from tests/unit/libutil/pool.cc rename to src/libutil-test/pool.cc diff --git a/tests/unit/libutil/references.cc b/src/libutil-test/references.cc similarity index 100% rename from tests/unit/libutil/references.cc rename to src/libutil-test/references.cc diff --git a/tests/unit/libutil/spawn.cc b/src/libutil-test/spawn.cc similarity index 100% rename from tests/unit/libutil/spawn.cc rename to src/libutil-test/spawn.cc diff --git a/tests/unit/libutil/suggestions.cc b/src/libutil-test/suggestions.cc similarity index 100% rename from tests/unit/libutil/suggestions.cc rename to src/libutil-test/suggestions.cc diff --git a/tests/unit/libutil/tests.cc b/src/libutil-test/tests.cc similarity index 100% rename from tests/unit/libutil/tests.cc rename to src/libutil-test/tests.cc diff --git a/tests/unit/libutil/url.cc b/src/libutil-test/url.cc similarity index 100% rename from tests/unit/libutil/url.cc rename to src/libutil-test/url.cc diff --git a/tests/unit/libutil/xml-writer.cc b/src/libutil-test/xml-writer.cc similarity index 100% rename from tests/unit/libutil/xml-writer.cc rename to src/libutil-test/xml-writer.cc diff --git a/tests/unit/libexpr-support/local.mk b/tests/unit/libexpr-support/local.mk deleted file mode 100644 index 0501de33c..000000000 --- a/tests/unit/libexpr-support/local.mk +++ /dev/null @@ -1,23 +0,0 @@ -libraries += libexpr-test-support - -libexpr-test-support_NAME = libnixexpr-test-support - -libexpr-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libexpr-test-support_INSTALL_DIR := $(checklibdir) -else - libexpr-test-support_INSTALL_DIR := -endif - -libexpr-test-support_SOURCES := \ - $(wildcard $(d)/tests/*.cc) \ - $(wildcard $(d)/tests/value/*.cc) - -libexpr-test-support_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) - -libexpr-test-support_LIBS = \ - libstore-test-support libutil-test-support \ - libexpr libstore libutil - -libexpr-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk deleted file mode 100644 index 1617e2823..000000000 --- a/tests/unit/libexpr/local.mk +++ /dev/null @@ -1,45 +0,0 @@ -check: libexpr-tests_RUN - -programs += libexpr-tests - -libexpr-tests_NAME := libnixexpr-tests - -libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml - -libexpr-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libexpr-tests_INSTALL_DIR := $(checkbindir) -else - libexpr-tests_INSTALL_DIR := -endif - -libexpr-tests_SOURCES := \ - $(wildcard $(d)/*.cc) \ - $(wildcard $(d)/value/*.cc) \ - $(wildcard $(d)/flake/*.cc) - -libexpr-tests_EXTRA_INCLUDES = \ - -I tests/unit/libexpr-support \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libexpr) \ - $(INCLUDE_libexprc) \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libstorec) \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) - -libexpr-tests_LIBS = \ - libexpr-test-support libstore-test-support libutil-test-support \ - libexpr libexprc libfetchers libstore libstorec libutil libutilc - -libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk deleted file mode 100644 index 286a59030..000000000 --- a/tests/unit/libfetchers/local.mk +++ /dev/null @@ -1,37 +0,0 @@ -check: libfetchers-tests_RUN - -programs += libfetchers-tests - -libfetchers-tests_NAME = libnixfetchers-tests - -libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml - -libfetchers-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libfetchers-tests_INSTALL_DIR := $(checkbindir) -else - libfetchers-tests_INSTALL_DIR := -endif - -libfetchers-tests_SOURCES := $(wildcard $(d)/*.cc) - -libfetchers-tests_EXTRA_INCLUDES = \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libutil) - -libfetchers-tests_CXXFLAGS += $(libfetchers-tests_EXTRA_INCLUDES) - -libfetchers-tests_LIBS = \ - libstore-test-support libutil-test-support \ - libfetchers libstore libutil - -libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk deleted file mode 100644 index 590bcf7c0..000000000 --- a/tests/unit/libflake/local.mk +++ /dev/null @@ -1,43 +0,0 @@ -check: libflake-tests_RUN - -programs += libflake-tests - -libflake-tests_NAME := libnixflake-tests - -libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml - -libflake-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libflake-tests_INSTALL_DIR := $(checkbindir) -else - libflake-tests_INSTALL_DIR := -endif - -libflake-tests_SOURCES := \ - $(wildcard $(d)/*.cc) \ - $(wildcard $(d)/value/*.cc) \ - $(wildcard $(d)/flake/*.cc) - -libflake-tests_EXTRA_INCLUDES = \ - -I tests/unit/libflake-support \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libflake) \ - $(INCLUDE_libexpr) \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libutil) \ - -libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) - -libflake-tests_LIBS = \ - libexpr-test-support libstore-test-support libutil-test-support \ - libflake libexpr libfetchers libstore libutil - -libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libstore-support/local.mk b/tests/unit/libstore-support/local.mk deleted file mode 100644 index 56dedd825..000000000 --- a/tests/unit/libstore-support/local.mk +++ /dev/null @@ -1,21 +0,0 @@ -libraries += libstore-test-support - -libstore-test-support_NAME = libnixstore-test-support - -libstore-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libstore-test-support_INSTALL_DIR := $(checklibdir) -else - libstore-test-support_INSTALL_DIR := -endif - -libstore-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) - -libstore-test-support_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) - -libstore-test-support_LIBS = \ - libutil-test-support \ - libstore libutil - -libstore-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk deleted file mode 100644 index 8d3d6b0af..000000000 --- a/tests/unit/libstore/local.mk +++ /dev/null @@ -1,38 +0,0 @@ -check: libstore-tests_RUN - -programs += libstore-tests - -libstore-tests_NAME = libnixstore-tests - -libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml - -libstore-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libstore-tests_INSTALL_DIR := $(checkbindir) -else - libstore-tests_INSTALL_DIR := -endif - -libstore-tests_SOURCES := $(wildcard $(d)/*.cc) - -libstore-tests_EXTRA_INCLUDES = \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libstore) \ - $(INCLUDE_libstorec) \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) - -libstore-tests_LIBS = \ - libstore-test-support libutil-test-support \ - libstore libstorec libutil libutilc - -libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libutil-support/.version b/tests/unit/libutil-support/.version deleted file mode 120000 index 0df9915bf..000000000 --- a/tests/unit/libutil-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/local.mk b/tests/unit/libutil-support/local.mk deleted file mode 100644 index 5f7835c9f..000000000 --- a/tests/unit/libutil-support/local.mk +++ /dev/null @@ -1,19 +0,0 @@ -libraries += libutil-test-support - -libutil-test-support_NAME = libnixutil-test-support - -libutil-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libutil-test-support_INSTALL_DIR := $(checklibdir) -else - libutil-test-support_INSTALL_DIR := -endif - -libutil-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) - -libutil-test-support_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) - -libutil-test-support_LIBS = libutil - -libutil-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libutil/.version b/tests/unit/libutil/.version deleted file mode 100644 index ad2261920..000000000 --- a/tests/unit/libutil/.version +++ /dev/null @@ -1 +0,0 @@ -2.24.0 diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk deleted file mode 100644 index 404f35cf1..000000000 --- a/tests/unit/libutil/local.mk +++ /dev/null @@ -1,37 +0,0 @@ -check: libutil-tests_RUN - -programs += libutil-tests - -libutil-tests_NAME = libnixutil-tests - -libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml - -libutil-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libutil-tests_INSTALL_DIR := $(checkbindir) -else - libutil-tests_INSTALL_DIR := -endif - -libutil-tests_SOURCES := $(wildcard $(d)/*.cc) - -libutil-tests_EXTRA_INCLUDES = \ - -I tests/unit/libutil-support \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) - -libutil-tests_LIBS = libutil-test-support libutil libutilc - -libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif - -check: $(d)/data/git/check-data.sh.test - -$(eval $(call run-test,$(d)/data/git/check-data.sh)) From a81e319528c511affd165401c2eadf2554ff5e07 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:17:13 -0400 Subject: [PATCH 438/910] Deduplicating --- meson-utils/export/meson.build | 30 ++++++++++++++++ meson-utils/meson.build | 36 +++++++++++++++++++ src/libcmd/meson-utils | 1 + src/libexpr-c/meson-utils | 1 + src/libexpr-c/meson.build | 45 ++---------------------- src/libexpr-test-support/meson-utils | 1 + src/libexpr-test-support/meson.build | 36 ++----------------- src/libexpr-test/meson-utils | 1 + src/libexpr-test/meson.build | 15 +------- src/libexpr/meson-utils | 1 + src/libexpr/meson.build | 39 +++------------------ src/libfetchers-test/meson-utils | 1 + src/libfetchers-test/meson.build | 15 +------- src/libfetchers/meson-utils | 1 + src/libfetchers/meson.build | 38 ++++---------------- src/libflake-test/meson-utils | 1 + src/libflake-test/meson.build | 15 +------- src/libflake/meson-utils | 1 + src/libflake/meson.build | 38 +++----------------- src/libmain/meson-utils | 1 + src/libstore-c/meson-utils | 1 + src/libstore-c/meson.build | 45 ++---------------------- src/libstore-test-support/meson-utils | 1 + src/libstore-test-support/meson.build | 36 ++----------------- src/libstore-test/meson-utils | 1 + src/libstore-test/meson.build | 15 +------- src/libstore/meson-utils | 1 + src/libstore/meson.build | 37 ++------------------ src/libutil-c/meson-utils | 1 + src/libutil-c/meson.build | 45 ++---------------------- src/libutil-test-support/meson-utils | 1 + src/libutil-test-support/meson.build | 36 ++----------------- src/libutil-test/meson-utils | 1 + src/libutil-test/meson.build | 15 +------- src/libutil/meson-utils | 1 + src/libutil/meson.build | 50 ++------------------------- 36 files changed, 128 insertions(+), 476 deletions(-) create mode 100644 meson-utils/export/meson.build create mode 100644 meson-utils/meson.build create mode 120000 src/libcmd/meson-utils create mode 120000 src/libexpr-c/meson-utils create mode 120000 src/libexpr-test-support/meson-utils create mode 120000 src/libexpr-test/meson-utils create mode 120000 src/libexpr/meson-utils create mode 120000 src/libfetchers-test/meson-utils create mode 120000 src/libfetchers/meson-utils create mode 120000 src/libflake-test/meson-utils create mode 120000 src/libflake/meson-utils create mode 120000 src/libmain/meson-utils create mode 120000 src/libstore-c/meson-utils create mode 120000 src/libstore-test-support/meson-utils create mode 120000 src/libstore-test/meson-utils create mode 120000 src/libstore/meson-utils create mode 120000 src/libutil-c/meson-utils create mode 120000 src/libutil-test-support/meson-utils create mode 120000 src/libutil-test/meson-utils create mode 120000 src/libutil/meson-utils diff --git a/meson-utils/export/meson.build b/meson-utils/export/meson.build new file mode 100644 index 000000000..40f6dcd59 --- /dev/null +++ b/meson-utils/export/meson.build @@ -0,0 +1,30 @@ +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires_public, + requires_private : requires_private, + libraries_private : libraries_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/meson-utils/meson.build b/meson-utils/meson.build new file mode 100644 index 000000000..89fbfdb36 --- /dev/null +++ b/meson-utils/meson.build @@ -0,0 +1,36 @@ +# These are private dependencies with pkg-config files. What private +# means is that the dependencies are used by the library but they are +# *not* used (e.g. `#include`-ed) in any installed header file, and only +# in regular source code (`*.cc`) or private, uninstalled headers. They +# are thus part of the *implementation* of the library, but not its +# *interface*. +# +# See `man pkg-config` for some details. +deps_private = [ ] + +# These are public dependencies with pkg-config files. Public is the +# opposite of private: these dependencies are used in installed header +# files. They are part of the interface (and implementation) of the +# library. +# +# N.B. This concept is mostly unrelated to our own concept of a public +# (stable) API, for consumption outside of the Nix repository. +# `libnixutil` is an unstable C++ library, whose public interface is +# likewise unstable. `libutilc` conversely is a hopefully-soon stable +# C library, whose public interface --- including public but not private +# dependencies --- will also likewise soon be stable. +# +# N.B. For distributions that care about "ABI" stablity and not just +# "API" stability, the private dependencies also matter as they can +# potentially affect the public ABI. +deps_public = [ ] + +# These are subproject deps (type == "internal"). They are other +# packages in `/src` in this repo. The private vs public distinction is +# the same as above. +deps_private_subproject = [ ] +deps_public_subproject = [ ] + +# These are dependencencies without pkg-config files. Ideally they are +# just private, but they may also be public (e.g. boost). +deps_other = [ ] diff --git a/src/libcmd/meson-utils b/src/libcmd/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libcmd/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/meson-utils b/src/libexpr-c/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr-c/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 5abf2b477..e20589484 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -14,20 +14,7 @@ project('nix-expr-c', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -128,32 +115,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires_private = [] -foreach dep : deps_private_subproject - requires_private += dep.name() -endforeach -requires_private += deps_private +libraries_private = [] -requires_public = [] -foreach dep : deps_public_subproject - requires_public += dep.name() -endforeach -requires_public += deps_public - -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires_public, - requires_private : requires_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') diff --git a/src/libexpr-test-support/meson-utils b/src/libexpr-test-support/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr-test-support/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 5ab0661ca..15138744f 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -14,17 +14,7 @@ project('nix-expr-test-support', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -103,26 +93,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libexpr-test/meson-utils b/src/libexpr-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index d4b0db51f..cf6fb477c 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -14,14 +14,7 @@ project('nix-expr-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -117,9 +110,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libexpr/meson-utils b/src/libexpr/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 34e4dec3b..401ac6673 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -14,17 +14,7 @@ project('nix-expr', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -186,6 +176,8 @@ sources = files( 'value/context.cc', ) +include_dirs = [include_directories('.')] + headers = [config_h] + files( 'attr-path.hh', 'attr-set.hh', @@ -228,27 +220,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, - libraries_private : ['-lboost_container', '-lboost_context'], -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_directories('.'), - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libfetchers-test/meson-utils b/src/libfetchers-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libfetchers-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index be031f592..ceadaf22a 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -14,14 +14,7 @@ project('nix-fetchers-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -101,9 +94,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libfetchers/meson-utils b/src/libfetchers/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libfetchers/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 938ee27d4..6954358af 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -14,17 +14,9 @@ project('nix-fetchers', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] +subdir('meson-utils') -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +configdata = configuration_data() foreach nix_dep : [ dependency('nix-util'), @@ -86,6 +78,8 @@ sources = files( 'tarball.cc', ) +include_dirs = [include_directories('.')] + headers = files( 'attrs.hh', 'cache.hh', @@ -109,26 +103,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_directories('.'), - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libflake-test/meson-utils b/src/libflake-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libflake-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index a9df80885..4ae532800 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -14,14 +14,7 @@ project('nix-flake-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -106,9 +99,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libflake/meson-utils b/src/libflake/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libflake/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 1c0a3ae77..bb85543a2 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -14,17 +14,7 @@ project('nix-flake', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -78,6 +68,8 @@ sources = files( 'flake/lockfile.cc', ) +include_dirs = [include_directories('.')] + headers = files( 'flake-settings.hh', 'flake/flake.hh', @@ -95,26 +87,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_directories('.'), - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libmain/meson-utils b/src/libmain/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libmain/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/meson-utils b/src/libstore-c/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore-c/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 049dda0e2..48efbae6f 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -14,20 +14,7 @@ project('nix-store-c', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -120,32 +107,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires_private = [] -foreach dep : deps_private_subproject - requires_private += dep.name() -endforeach -requires_private += deps_private +libraries_private = [] -requires_public = [] -foreach dep : deps_public_subproject - requires_public += dep.name() -endforeach -requires_public += deps_public - -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires_public, - requires_private : requires_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') diff --git a/src/libstore-test-support/meson-utils b/src/libstore-test-support/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore-test-support/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 186d9b72a..3f7104062 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -14,17 +14,7 @@ project('nix-store-test-support', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -105,26 +95,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libstore-test/meson-utils b/src/libstore-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 83d3244d4..ed86a0ea9 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -14,14 +14,7 @@ project('nix-store-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -115,9 +108,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libstore/meson-utils b/src/libstore/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 7277eb49d..2acd03f23 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -14,17 +14,7 @@ project('nix-store', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -436,27 +426,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = ['-lboost_container'] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, - libraries_private : ['-lboost_container'], -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libutil-c/meson-utils b/src/libutil-c/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil-c/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 7ebbe3d06..b8e25d213 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -14,20 +14,7 @@ project('nix-util-c', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -120,32 +107,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires_private = [] -foreach dep : deps_private_subproject - requires_private += dep.name() -endforeach -requires_private += deps_private +libraries_private = [] -requires_public = [] -foreach dep : deps_public_subproject - requires_public += dep.name() -endforeach -requires_public += deps_public - -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires_public, - requires_private : requires_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') diff --git a/src/libutil-test-support/meson-utils b/src/libutil-test-support/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil-test-support/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 4a8f8b54e..1dee897cb 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -14,17 +14,7 @@ project('nix-util-test-support', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -100,26 +90,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libutil-test/meson-utils b/src/libutil-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index c3d72b976..e91675455 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -14,14 +14,7 @@ project('nix-util-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -115,9 +108,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libutil/meson-utils b/src/libutil/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil/meson.build b/src/libutil/meson.build index c9dfee651..036a867db 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -14,36 +14,7 @@ project('nix-util', 'cpp', cxx = meson.get_compiler('cpp') -# These are private dependencies with pkg-config files. What private -# means is that the dependencies are used by the library but they are -# *not* used (e.g. `#include`-ed) in any installed header file, and only -# in regular source code (`*.cc`) or private, uninstalled headers. They -# are thus part of the *implementation* of the library, but not its -# *interface*. -# -# See `man pkg-config` for some details. -deps_private = [ ] - -# These are public dependencies with pkg-config files. Public is the -# opposite of private: these dependencies are used in installed header -# files. They are part of the interface (and implementation) of the -# library. -# -# N.B. This concept is mostly unrelated to our own concept of a public -# (stable) API, for consumption outside of the Nix repository. -# `libnixutil` is an unstable C++ library, whose public interface is -# likewise unstable. `libutilc` conversely is a hopefully-soon stable -# C library, whose public interface --- including public but not private -# dependencies --- will also likewise soon be stable. -# -# N.B. For distributions that care about "ABI" stablity and not just -# "API" stability, the private dependencies also matter as they can -# potentially affect the public ABI. -deps_public = [ ] - -# These are dependencencies without pkg-config files. Ideally they are -# just private, but they may also be public (e.g. boost). -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -315,21 +286,4 @@ if host_machine.system() == 'windows' libraries_private += ['-lws2_32'] endif -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : deps_public, - requires_private : deps_private, - libraries_private : libraries_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') From d902481a365c82c7620bcfd2c661589c0246fe00 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:19:42 -0400 Subject: [PATCH 439/910] Better org --- meson-utils/{ => deps-lists}/meson.build | 0 src/libexpr-c/meson.build | 2 +- src/libexpr-test-support/meson.build | 2 +- src/libexpr-test/meson.build | 2 +- src/libexpr/meson.build | 2 +- src/libfetchers-test/meson.build | 2 +- src/libfetchers/meson.build | 2 +- src/libflake-test/meson.build | 2 +- src/libflake/meson.build | 2 +- src/libstore-c/meson.build | 2 +- src/libstore-test-support/meson.build | 2 +- src/libstore-test/meson.build | 2 +- src/libstore/meson.build | 2 +- src/libutil-c/meson.build | 2 +- src/libutil-test-support/meson.build | 2 +- src/libutil-test/meson.build | 2 +- src/libutil/meson.build | 2 +- 17 files changed, 16 insertions(+), 16 deletions(-) rename meson-utils/{ => deps-lists}/meson.build (100%) diff --git a/meson-utils/meson.build b/meson-utils/deps-lists/meson.build similarity index 100% rename from meson-utils/meson.build rename to meson-utils/deps-lists/meson.build diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index e20589484..af4965b54 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -14,7 +14,7 @@ project('nix-expr-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 15138744f..2755ce8f0 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index cf6fb477c..78701d6c3 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 401ac6673..3c06710a2 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -14,7 +14,7 @@ project('nix-expr', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index ceadaf22a..98a5bb549 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 6954358af..491324c7f 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index 4ae532800..00fe90f6e 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -14,7 +14,7 @@ project('nix-flake-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libflake/meson.build b/src/libflake/meson.build index bb85543a2..18c667b54 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -14,7 +14,7 @@ project('nix-flake', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 48efbae6f..66446fa20 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -14,7 +14,7 @@ project('nix-store-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 3f7104062..9acb6b4ad 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-store-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index ed86a0ea9..0753f9e71 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -14,7 +14,7 @@ project('nix-store-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 2acd03f23..6e79a0e85 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -14,7 +14,7 @@ project('nix-store', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index b8e25d213..612138ad9 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -14,7 +14,7 @@ project('nix-util-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 1dee897cb..00e39f6ab 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-util-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index e91675455..0be920a41 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -14,7 +14,7 @@ project('nix-util-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 036a867db..337480d82 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -14,7 +14,7 @@ project('nix-util', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() From 4609ab318cc81df3c3f686cd8c444a2557f08420 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:21:00 -0400 Subject: [PATCH 440/910] Fix internal API docs --- src/internal-api-docs/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal-api-docs/meson.build b/src/internal-api-docs/meson.build index 2568c56cf..54eb7e5dd 100644 --- a/src/internal-api-docs/meson.build +++ b/src/internal-api-docs/meson.build @@ -12,7 +12,7 @@ doxygen_cfg = configure_file( configuration : { 'PROJECT_NUMBER': meson.project_version(), 'OUTPUT_DIRECTORY' : meson.current_build_dir(), - 'src' : fs.parent(fs.parent(meson.project_source_root())), + 'src' : fs.parent(fs.parent(meson.project_source_root())) / 'src', }, ) From c88f83b471030e6b065b0cb678f59f3d4696cb18 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:34:03 -0400 Subject: [PATCH 441/910] More dedup --- meson-utils/subprojects/meson.build | 19 +++++++++++++++++++ src/libexpr-c/meson.build | 25 ++++++------------------- src/libexpr-test-support/meson.build | 13 ++++--------- src/libexpr-test/meson.build | 13 ++++--------- src/libexpr/meson.build | 13 ++++--------- src/libfetchers-test/meson.build | 13 ++++--------- src/libfetchers/meson.build | 13 ++++--------- src/libflake-test/meson.build | 13 ++++--------- src/libflake/meson.build | 13 ++++--------- src/libstore-c/meson.build | 25 ++++++------------------- src/libstore-test-support/meson.build | 13 ++++--------- src/libstore-test/meson.build | 13 ++++--------- src/libstore/meson.build | 13 ++++--------- src/libutil-c/meson.build | 22 +++------------------- src/libutil-test-support/meson.build | 13 ++++--------- src/libutil-test/meson.build | 13 ++++--------- src/libutil/meson.build | 6 ++++++ 17 files changed, 88 insertions(+), 165 deletions(-) create mode 100644 meson-utils/subprojects/meson.build diff --git a/meson-utils/subprojects/meson.build b/meson-utils/subprojects/meson.build new file mode 100644 index 000000000..30a54ed91 --- /dev/null +++ b/meson-utils/subprojects/meson.build @@ -0,0 +1,19 @@ +foreach maybe_subproject_dep : deps_private_maybe_subproject + if maybe_subproject_dep.type_name() == 'internal' + deps_private_subproject += maybe_subproject_dep + # subproject sadly no good for pkg-config module + deps_other += maybe_subproject_dep + else + deps_private += maybe_subproject_dep + endif +endforeach + +foreach maybe_subproject_dep : deps_public_maybe_subproject + if maybe_subproject_dep.type_name() == 'internal' + deps_public_subproject += maybe_subproject_dep + # subproject sadly no good for pkg-config module + deps_other += maybe_subproject_dep + else + deps_public += maybe_subproject_dep + endif +endforeach diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index af4965b54..50616d0d8 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -18,32 +18,19 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-expr'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach - -foreach nix_dep : [ +deps_public_maybe_subproject = [ dependency('nix-util-c'), dependency('nix-store-c'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) config_h = configure_file( configuration : configdata, diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 2755ce8f0..5dbc6b1b5 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -16,21 +16,16 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-test-support'), dependency('nix-store'), dependency('nix-store-test-support'), dependency('nix-expr'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index 78701d6c3..cf1c48729 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -27,14 +29,7 @@ foreach nix_dep : [ dependency('nix-expr-c'), dependency('nix-expr-test-support'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 3c06710a2..30981a66e 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -18,19 +18,14 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-fetchers'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') # This is only conditional to work around # https://github.com/mesonbuild/meson/issues/13293. It should be diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 98a5bb549..7f35cbeca 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -25,14 +27,7 @@ foreach nix_dep : [ dependency('nix-store-test-support'), dependency('nix-fetchers'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 491324c7f..07ec2bc57 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -18,18 +18,13 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index 00fe90f6e..1a488ec13 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -28,14 +30,7 @@ foreach nix_dep : [ dependency('nix-expr-test-support'), dependency('nix-flake'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 18c667b54..b0c172f0c 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -16,20 +16,15 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-fetchers'), dependency('nix-expr'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 66446fa20..3e59050fd 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -18,30 +18,17 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach - -foreach nix_dep : [ +deps_public_maybe_subproject = [ dependency('nix-util-c'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) config_h = configure_file( configuration : configdata, diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 9acb6b4ad..959125561 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -16,19 +16,14 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-test-support'), dependency('nix-store'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 0753f9e71..4469c6d6a 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -24,14 +26,7 @@ foreach nix_dep : [ dependency('nix-store-c'), dependency('nix-store-test-support'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 6e79a0e85..d5dcb1931 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -23,17 +23,12 @@ configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('SYSTEM', host_machine.system()) -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') run_command('ln', '-s', meson.project_build_root() / '__nothing_link_target', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 612138ad9..35338cbbc 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -18,28 +18,12 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ dependency('nix-util'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach - -foreach nix_dep : [ +deps_public_maybe_subproject = [ ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 00e39f6ab..1b8a14355 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -16,17 +16,12 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 0be920a41..36e8c2288 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -16,19 +16,14 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 337480d82..d0e9f02c4 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -18,6 +18,12 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ +] +subdir('meson-utils/subprojects') + # Check for each of these functions, and create a define like `#define # HAVE_LUTIMES 1`. The `#define` is unconditional, 0 for not found and 1 # for found. One therefore uses it with `#if` not `#ifdef`. From d6f57f3260c9a8c66367f7cedfc256b3b0894acd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:42:43 -0400 Subject: [PATCH 442/910] More dedup --- meson-utils/export-all-symbols/meson.build | 11 +++++++++++ src/libexpr-c/meson.build | 12 +----------- src/libexpr-test-support/meson.build | 12 +----------- src/libexpr-test/meson.build | 12 +----------- src/libfetchers-test/meson.build | 12 +----------- src/libflake-test/meson.build | 12 +----------- src/libstore-c/meson.build | 12 +----------- src/libstore-test-support/meson.build | 12 +----------- src/libstore-test/meson.build | 12 +----------- src/libstore/meson.build | 7 +------ src/libutil-c/meson.build | 12 +----------- src/libutil-test-support/meson.build | 12 +----------- src/libutil-test/meson.build | 12 +----------- src/libutil/meson.build | 12 +----------- 14 files changed, 24 insertions(+), 138 deletions(-) create mode 100644 meson-utils/export-all-symbols/meson.build diff --git a/meson-utils/export-all-symbols/meson.build b/meson-utils/export-all-symbols/meson.build new file mode 100644 index 000000000..d7c086749 --- /dev/null +++ b/meson-utils/export-all-symbols/meson.build @@ -0,0 +1,11 @@ +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 50616d0d8..b242c2d8e 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -79,17 +79,7 @@ headers = [config_h] + files( 'nix_api_value.h', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixexprc', diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 5dbc6b1b5..4209a4464 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -63,17 +63,7 @@ headers = files( 'tests/value/context.hh', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nix-expr-test-support', diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index cf1c48729..11ab5c6e2 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -31,17 +31,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 7f35cbeca..846d2d70a 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -29,17 +29,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index 1a488ec13..e4665ce9c 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -32,17 +32,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 3e59050fd..b86209720 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -71,17 +71,7 @@ headers = [config_h] + files( 'nix_api_store.h', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixstorec', diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 959125561..9c798e9cb 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -65,17 +65,7 @@ headers = files( 'tests/protocol.hh', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nix-store-test-support', diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 4469c6d6a..3ec7fbbd7 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -28,17 +28,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libstore/meson.build b/src/libstore/meson.build index d5dcb1931..a552fc819 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -401,12 +401,7 @@ foreach name, value : cpp_str_defines ] endforeach -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # See note in `../nix-util/meson.build` - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixstore', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 35338cbbc..b8c74bf11 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -68,17 +68,7 @@ headers = [config_h] + files( 'nix_api_util.h', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixutilc', diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 1b8a14355..4239003c5 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -60,17 +60,7 @@ headers = files( 'tests/string_callback.hh', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nix-util-test-support', diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 36e8c2288..8d9d32151 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -25,17 +25,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libutil/meson.build b/src/libutil/meson.build index d0e9f02c4..84e47b4b4 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -257,17 +257,7 @@ else subdir('unix') endif -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixutil', From 8198888bc41b3aeff445b730556a701024eabdf2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:57:32 -0400 Subject: [PATCH 443/910] More dedup --- meson-utils/threads/meson.build | 6 ++++++ src/libexpr/meson.build | 7 +------ src/libstore/meson.build | 7 +------ src/libutil/meson.build | 7 +------ 4 files changed, 9 insertions(+), 18 deletions(-) create mode 100644 meson-utils/threads/meson.build diff --git a/meson-utils/threads/meson.build b/meson-utils/threads/meson.build new file mode 100644 index 000000000..294160de1 --- /dev/null +++ b/meson-utils/threads/meson.build @@ -0,0 +1,6 @@ +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') + deps_private += dependency('threads') +endif diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 30981a66e..8d37947b1 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -27,12 +27,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -# This is only conditional to work around -# https://github.com/mesonbuild/meson/issues/13293. It should be -# unconditional. -if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') - deps_private += dependency('threads') -endif +subdir('meson-utils/threads') boost = dependency( 'boost', diff --git a/src/libstore/meson.build b/src/libstore/meson.build index a552fc819..8ec12e42f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -67,12 +67,7 @@ has_acl_support = cxx.has_header('sys/xattr.h') \ and cxx.has_function('lremovexattr') configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int()) -# This is only conditional to work around -# https://github.com/mesonbuild/meson/issues/13293. It should be -# unconditional. -if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') - deps_private += dependency('threads') -endif +subdir('meson-utils/threads') boost = dependency( 'boost', diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 84e47b4b4..317b06da0 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -48,12 +48,7 @@ foreach funcspec : check_funcs configdata.set(define_name, define_value) endforeach -# This is only conditional to work around -# https://github.com/mesonbuild/meson/issues/13293. It should be -# unconditional. -if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') - deps_private += dependency('threads') -endif +subdir('meson-utils/threads') if host_machine.system() == 'windows' socket = cxx.find_library('ws2_32') From 8399bd6b8f86b74688a45acd4913414fcc19583b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 16:11:52 -0400 Subject: [PATCH 444/910] Dedup --- meson-utils/diagnostics/meson.build | 16 ++++++++++++++++ src/libexpr-c/meson.build | 14 ++------------ src/libexpr-test-support/meson.build | 14 ++------------ src/libexpr-test/meson.build | 14 ++------------ src/libexpr/meson.build | 15 ++------------- src/libfetchers-test/meson.build | 14 ++------------ src/libfetchers/meson.build | 15 ++------------- src/libflake-test/meson.build | 14 ++------------ src/libflake/meson.build | 14 ++------------ src/libstore-c/meson.build | 14 ++------------ src/libstore-test-support/meson.build | 14 ++------------ src/libstore-test/meson.build | 14 ++------------ src/libstore/meson.build | 15 ++------------- src/libutil-c/meson.build | 15 ++------------- src/libutil-test-support/meson.build | 15 ++------------- src/libutil-test/meson.build | 15 ++------------- src/libutil/meson.build | 15 ++------------- 17 files changed, 48 insertions(+), 199 deletions(-) create mode 100644 meson-utils/diagnostics/meson.build diff --git a/meson-utils/diagnostics/meson.build b/meson-utils/diagnostics/meson.build new file mode 100644 index 000000000..eb0c636c0 --- /dev/null +++ b/meson-utils/diagnostics/meson.build @@ -0,0 +1,16 @@ +add_project_arguments( + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Werror=unused-result', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index b242c2d8e..477640240 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -50,21 +50,11 @@ add_project_arguments( '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'nix_api_expr.cc', 'nix_api_external.cc', diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 4209a4464..ad3108a61 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -36,21 +36,11 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', '-include', 'config-expr.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'tests/value/context.cc', ) diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index 11ab5c6e2..e5bb771e3 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -48,21 +48,11 @@ add_project_arguments( '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'derived-path.cc', 'error_traces.cc', diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 8d37947b1..d99f80486 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -66,22 +66,11 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-include', 'config-expr.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + parser_tab = custom_target( input : 'parser.y', output : [ diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 846d2d70a..30056c64a 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -45,21 +45,11 @@ add_project_arguments( '-include', 'config-store.hh', '-include', 'config-util.h', '-include', 'config-store.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'public-key.cc', ) diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 07ec2bc57..16645bda3 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -38,22 +38,11 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'attrs.cc', 'cache.cc', diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index e4665ce9c..b5f0c2fb4 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -49,21 +49,11 @@ add_project_arguments( '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'flakeref.cc', 'url-name.cc', diff --git a/src/libflake/meson.build b/src/libflake/meson.build index b0c172f0c..13541fc43 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -39,21 +39,11 @@ add_project_arguments( '-include', 'config-store.hh', # '-include', 'config-fetchers.h', '-include', 'config-expr.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'flake-settings.cc', 'flake/config.cc', diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index b86209720..cd068fed2 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -46,21 +46,11 @@ add_project_arguments( # From C libraries, for our public, installed headers too '-include', 'config-util.h', '-include', 'config-store.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'nix_api_store.cc', ) diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 9c798e9cb..adf6f685e 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -33,21 +33,11 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'tests/derived-path.cc', 'tests/outputs-spec.cc', diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 3ec7fbbd7..cb561977c 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -43,21 +43,11 @@ add_project_arguments( '-include', 'config-store.hh', '-include', 'config-util.h', '-include', 'config-store.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'common-protocol.cc', 'content-address.cc', diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 8ec12e42f..d2a5acb18 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -150,22 +150,11 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'binary-cache-store.cc', 'build-result.cc', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index b8c74bf11..6c316784d 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -42,22 +42,11 @@ add_project_arguments( # From C libraries, for our public, installed headers too '-include', 'config-util.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'nix_api_util.cc', ) diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 4239003c5..d63fabce5 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -30,22 +30,11 @@ add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'tests/hash.cc', 'tests/string_callback.cc', diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 8d9d32151..43001ca68 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -38,22 +38,11 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-util.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'args.cc', 'canon-path.cc', diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 317b06da0..7a4dfaf50 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -111,22 +111,11 @@ add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'archive.cc', 'args.cc', From 0b539dea4a380235696db6265215a8cca0f33821 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 17:28:31 -0400 Subject: [PATCH 445/910] Improve boost hacks --- flake.nix | 6 +++--- package.nix | 29 +-------------------------- packaging/components.nix | 17 ---------------- packaging/dependencies.nix | 13 +++++++++++++ src/libcmd/network-proxy.cc | 5 +++-- src/libexpr/package.nix | 15 +------------- src/libstore/meson.build | 2 +- src/libstore/package.nix | 10 ---------- src/libutil/meson.build | 7 +------ src/libutil/package.nix | 39 ++----------------------------------- src/perl/package.nix | 14 +------------ 11 files changed, 26 insertions(+), 131 deletions(-) diff --git a/flake.nix b/flake.nix index 582a946c4..04fc94a55 100644 --- a/flake.nix +++ b/flake.nix @@ -207,7 +207,7 @@ # https://github.com/NixOS/nixpkgs/issues/320448 "static-" = nixpkgsFor.${system}.static; }) - (nixpkgsPrefix: nixpkgs: + (nixpkgsPrefix: nixpkgs: flatMapAttrs nixpkgs.nixComponents (pkgName: pkg: flatMapAttrs pkg.tests or {} @@ -304,8 +304,8 @@ env = { # Needed for Meson to find Boost. # https://github.com/NixOS/nixpkgs/issues/86131. - BOOST_INCLUDEDIR = "${lib.getDev pkgs.boost}/include"; - BOOST_LIBRARYDIR = "${lib.getLib pkgs.boost}/lib"; + BOOST_INCLUDEDIR = "${lib.getDev pkgs.nixDependencies.boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib pkgs.nixDependencies.boost}/lib"; # For `make format`, to work without installing pre-commit _NIX_PRE_COMMIT_HOOKS_CONFIG = "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; diff --git a/package.nix b/package.nix index 0661dc080..28d7e788f 100644 --- a/package.nix +++ b/package.nix @@ -199,7 +199,6 @@ in { ; buildInputs = lib.optionals doBuild [ - boost brotli bzip2 curl @@ -227,33 +226,12 @@ in { ; propagatedBuildInputs = [ + boost nlohmann_json ] ++ lib.optional enableGC boehmgc; dontBuild = !attrs.doBuild; - disallowedReferences = [ boost ]; - - preConfigure = lib.optionalString (doBuild && ! stdenv.hostPlatform.isStatic) ( - '' - # Copy libboost_context so we don't get all of Boost in our closure. - # https://github.com/NixOS/nixpkgs/issues/45462 - mkdir -p $out/lib - cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib - rm -f $out/lib/*.a - '' + lib.optionalString stdenv.hostPlatform.isLinux '' - chmod u+w $out/lib/*.so.* - patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* - '' + lib.optionalString stdenv.hostPlatform.isDarwin '' - for LIB in $out/lib/*.dylib; do - chmod u+w $LIB - install_name_tool -id $LIB $LIB - install_name_tool -delete_rpath ${boost}/lib/ $LIB || true - done - install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib - '' - ); - configureFlags = [ (lib.enableFeature doBuild "build") (lib.enableFeature doInstallCheck "functional-tests") @@ -295,11 +273,6 @@ in { lib.optionalString stdenv.hostPlatform.isStatic '' mkdir -p $out/nix-support echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products - '' + lib.optionalString stdenv.isDarwin '' - install_name_tool \ - -change ${boost}/lib/libboost_context.dylib \ - $out/lib/libboost_context.dylib \ - $out/lib/libnixutil.dylib '' ) + lib.optionalString enableManual '' mkdir -p ''${!outputDoc}/nix-support diff --git a/packaging/components.nix b/packaging/components.nix index 0189f4ca3..97b989f1f 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -26,23 +26,6 @@ in nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-flake = callPackage ../src/libflake/package.nix { }; - nix-flake-c = callPackage ../src/libflake-c/package.nix { }; - - nix-store = callPackage ../src/libstore/package.nix { }; - nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; - nix-store-test = callPackage ../src/libstore-test/package.nix { }; - nix-store-c = callPackage ../src/libstore-c/package.nix { }; - - nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; - nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; - - nix-expr = callPackage ../src/libexpr/package.nix { }; - nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; - nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; - nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-flake = callPackage ../src/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 484385128..b2349f02c 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -52,6 +52,19 @@ scope: { enableLargeConfig = true; }; + # Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed. + boost = (pkgs.boost.override { + extraB2Args = [ + "--with-container" + "--with-context" + "--with-coroutine" + ]; + }).overrideAttrs (old: { + # Need to remove `--with-*` to use `--with-libraries=...` + buildPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase; + installPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase; + }); + libgit2 = pkgs.libgit2.overrideAttrs (attrs: { src = inputs.libgit2; version = inputs.libgit2.lastModifiedDate; diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc index 4b7d2441f..47be311cd 100644 --- a/src/libcmd/network-proxy.cc +++ b/src/libcmd/network-proxy.cc @@ -1,7 +1,6 @@ #include "network-proxy.hh" #include -#include #include "environment-variables.hh" @@ -13,7 +12,9 @@ static StringSet getAllVariables() { StringSet variables = lowercaseVariables; for (const auto & variable : lowercaseVariables) { - variables.insert(boost::to_upper_copy(variable)); + std::string upperVariable; + std::transform(variable.begin(), variable.end(), upperVariable.begin(), [](unsigned char c) { return std::toupper(c); }); + variables.insert(std::move(upperVariable)); } return variables; } diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 223b04042..799368ee9 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -70,19 +70,14 @@ mkDerivation (finalAttrs: { pkg-config ]; - buildInputs = [ - boost - ]; - propagatedBuildInputs = [ nix-util nix-store nix-fetchers + boost nlohmann_json ] ++ lib.optional enableGC boehmgc; - disallowedReferences = [ boost ]; - preConfigure = # "Inline" .version so it's not a symlink, and includes the suffix '' @@ -104,14 +99,6 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs that ends up in `Libs.private` - # by default, and would clash with out `disallowedReferences`. Part - # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. - '' - sed -i "$out/lib/pkgconfig/nix-expr.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 diff --git a/src/libstore/meson.build b/src/libstore/meson.build index d2a5acb18..2bc3f27e4 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -400,6 +400,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -libraries_private = ['-lboost_container'] +libraries_private = [] subdir('meson-utils/export') diff --git a/src/libstore/package.nix b/src/libstore/package.nix index f27dac2f6..5af1a7815 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -86,8 +86,6 @@ mkDerivation (finalAttrs: { nlohmann_json ]; - disallowedReferences = [ boost ]; - preConfigure = # "Inline" .version so it's not a symlink, and includes the suffix '' @@ -112,14 +110,6 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs that ends up in `Libs.private` - # by default, and would clash with out `disallowedReferences`. Part - # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. - '' - sed -i "$out/lib/pkgconfig/nix-store.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 7a4dfaf50..5eee8b3b2 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -254,12 +254,7 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -# Part of how we copy boost libraries to a separate installation to -# reduce closure size. These libraries will be copied to our `$out/bin`, -# and these `-l` flags will pick them up there. -# -# https://github.com/NixOS/nixpkgs/issues/45462 -libraries_private = ['-lboost_context', '-lboost_coroutine'] +libraries_private = [] if host_machine.system() == 'windows' # `libraries_private` cannot contain ad-hoc dependencies (from # `find_library), so we need to do this manually diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 8ba6daa2a..ef5e251fb 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -65,7 +65,6 @@ mkMesonDerivation (finalAttrs: { ]; buildInputs = [ - boost brotli libsodium openssl @@ -73,37 +72,17 @@ mkMesonDerivation (finalAttrs: { ; propagatedBuildInputs = [ - boost.dev + boost libarchive nlohmann_json ]; - disallowedReferences = [ boost ]; - preConfigure = # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. '' chmod u+w ./.version echo ${version} > ../../.version - '' - # Copy some boost libraries so we don't get all of Boost in our - # closure. https://github.com/NixOS/nixpkgs/issues/45462 - + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' - mkdir -p $out/lib - cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib - rm -f $out/lib/*.a - '' + lib.optionalString stdenv.hostPlatform.isLinux '' - chmod u+w $out/lib/*.so.* - patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* - '' + lib.optionalString stdenv.hostPlatform.isDarwin '' - for LIB in $out/lib/*.dylib; do - chmod u+w $LIB - install_name_tool -id $LIB $LIB - install_name_tool -delete_rpath ${boost}/lib/ $LIB || true - done - install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib - '' - ); + ''; mesonFlags = [ (lib.mesonEnable "cpuid" stdenv.hostPlatform.isx86_64) @@ -120,20 +99,6 @@ mkMesonDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs that ends up in `Libs.private` - # by default, and would clash with out `disallowedReferences`. Part - # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. - '' - sed -i "$out/lib/pkgconfig/nix-util.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' - '' - + lib.optionalString stdenv.isDarwin '' - install_name_tool \ - -change ${boost}/lib/libboost_context.dylib \ - $out/lib/libboost_context.dylib \ - $out/lib/libnixutil.dylib - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 diff --git a/src/perl/package.nix b/src/perl/package.nix index e1a84924c..6b8487148 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -6,11 +6,6 @@ , ninja , pkg-config , nix-store -, curl -, bzip2 -, xz -, boost -, libsodium , darwin , versionSuffix ? "" }: @@ -45,14 +40,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { buildInputs = [ nix-store - curl - bzip2 - xz - perl - boost - ] - ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium - ++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security; + ]; # `perlPackages.Test2Harness` is marked broken for Darwin doCheck = !stdenv.isDarwin; From 92d3a06b252e0c373dc732eafbcb1fa40629f00e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 17:30:31 -0400 Subject: [PATCH 446/910] Remove overrides of removed flags since unit tests broken out --- flake.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/flake.nix b/flake.nix index 04fc94a55..ba526a9b8 100644 --- a/flake.nix +++ b/flake.nix @@ -145,9 +145,7 @@ nix = final.nixComponents.nix; nix_noTests = final.nix.override { - doCheck = false; doInstallCheck = false; - installUnitTests = false; }; # See https://github.com/NixOS/nixpkgs/pull/214409 From 429d6ae2b59912a7588804853e57711eb00962b6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 17:44:34 -0400 Subject: [PATCH 447/910] Add missing package.nix --- src/libexpr-c/package.nix | 94 +++++++++++++++++++++ src/libexpr-test-support/package.nix | 96 ++++++++++++++++++++++ src/libexpr-test/package.nix | 113 ++++++++++++++++++++++++++ src/libstore-c/package.nix | 94 +++++++++++++++++++++ src/libstore-test-support/package.nix | 96 ++++++++++++++++++++++ src/libstore-test/package.nix | 113 ++++++++++++++++++++++++++ src/libutil-c/package.nix | 5 -- src/libutil-test-support/package.nix | 7 +- 8 files changed, 607 insertions(+), 11 deletions(-) create mode 100644 src/libexpr-c/package.nix create mode 100644 src/libexpr-test-support/package.nix create mode 100644 src/libexpr-test/package.nix create mode 100644 src/libstore-c/package.nix create mode 100644 src/libstore-test-support/package.nix create mode 100644 src/libstore-test/package.nix diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix new file mode 100644 index 000000000..ce86780c1 --- /dev/null +++ b/src/libexpr-c/package.nix @@ -0,0 +1,94 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store-c +, nix-expr + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-expr-c"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-store-c + nix-expr + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libexpr-test-support/package.nix b/src/libexpr-test-support/package.nix new file mode 100644 index 000000000..cbc852fa5 --- /dev/null +++ b/src/libexpr-test-support/package.nix @@ -0,0 +1,96 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store-test-support +, nix-expr + +, rapidcheck + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-test-support"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-store-test-support + nix-expr + rapidcheck + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libexpr-test/package.nix b/src/libexpr-test/package.nix new file mode 100644 index 000000000..7c8c9c4d1 --- /dev/null +++ b/src/libexpr-test/package.nix @@ -0,0 +1,113 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-expr +, nix-expr-c +, nix-expr-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-expr-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-expr + nix-expr-c + nix-expr-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-expr-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix new file mode 100644 index 000000000..aedbad4a2 --- /dev/null +++ b/src/libstore-c/package.nix @@ -0,0 +1,94 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util-c +, nix-store + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store-c"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util-c + nix-store + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-test-support/package.nix b/src/libstore-test-support/package.nix new file mode 100644 index 000000000..a28f54e2a --- /dev/null +++ b/src/libstore-test-support/package.nix @@ -0,0 +1,96 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util-test-support +, nix-store + +, rapidcheck + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store-test-support"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util-test-support + nix-store + rapidcheck + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-test/package.nix b/src/libstore-test/package.nix new file mode 100644 index 000000000..b57adfea5 --- /dev/null +++ b/src/libstore-test/package.nix @@ -0,0 +1,113 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store +, nix-store-c +, nix-store-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-store + nix-store-c + nix-store-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-store-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index 05a26c17e..37f2291b5 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -55,11 +55,6 @@ mkDerivation (finalAttrs: { pkg-config ]; - buildInputs = [ - nix-util - ] - ; - propagatedBuildInputs = [ nix-util ]; diff --git a/src/libutil-test-support/package.nix b/src/libutil-test-support/package.nix index 0be0a9945..c6a0f0183 100644 --- a/src/libutil-test-support/package.nix +++ b/src/libutil-test-support/package.nix @@ -56,14 +56,9 @@ mkDerivation (finalAttrs: { pkg-config ]; - buildInputs = [ - nix-util - rapidcheck - ] - ; - propagatedBuildInputs = [ nix-util + rapidcheck ]; preConfigure = From 46ec69a483576ce095f1fde14583f174cdee2e8c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:09:32 -0400 Subject: [PATCH 448/910] Everything builds in the dev shell now --- flake.nix | 4 ++ packaging/components.nix | 8 +-- src/libexpr-c/package.nix | 2 +- src/libexpr-test/meson.build | 3 + src/libexpr/package.nix | 2 +- src/libfetchers-test/package.nix | 111 +++++++++++++++++++++++++++++++ src/libflake-test/package.nix | 111 +++++++++++++++++++++++++++++++ src/libstore-c/package.nix | 2 +- src/libstore-test/meson.build | 3 + 9 files changed, 238 insertions(+), 8 deletions(-) create mode 100644 src/libfetchers-test/package.nix create mode 100644 src/libflake-test/package.nix diff --git a/flake.nix b/flake.nix index ba526a9b8..fc46ef940 100644 --- a/flake.nix +++ b/flake.nix @@ -334,6 +334,10 @@ ++ lib.optional (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) pkgs.buildPackages.clang-tools; buildInputs = attrs.buildInputs or [] + ++ [ + pkgs.gtest + pkgs.rapidcheck + ] ++ lib.optional havePerl pkgs.perl ; }); diff --git a/packaging/components.nix b/packaging/components.nix index 97b989f1f..5576877cb 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -8,28 +8,26 @@ in nix = callPackage ../package.nix { }; nix-util = callPackage ../src/libutil/package.nix { }; + nix-util-c = callPackage ../src/libutil-c/package.nix { }; nix-util-test-support = callPackage ../src/libutil-test-support/package.nix { }; nix-util-test = callPackage ../src/libutil-test/package.nix { }; - nix-util-c = callPackage ../src/libutil-c/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; + nix-store-c = callPackage ../src/libstore-c/package.nix { }; nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; nix-store-test = callPackage ../src/libstore-test/package.nix { }; - nix-store-c = callPackage ../src/libstore-c/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; - nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; + nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; - nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; - nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; nix-perl-bindings = callPackage ../src/perl/package.nix { }; diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index ce86780c1..542be064d 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -41,7 +41,7 @@ mkDerivation (finalAttrs: { root = ./.; fileset = fileset.unions [ ./meson.build - ./meson.options + # ./meson.options (fileset.fileFilter (file: file.hasExt "cc") ./.) (fileset.fileFilter (file: file.hasExt "hh") ./.) (fileset.fileFilter (file: file.hasExt "h") ./.) diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index e5bb771e3..cc32b0a1a 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -39,6 +39,9 @@ deps_private += rapidcheck gtest = dependency('gtest', main : true) deps_private += gtest +gtest = dependency('gmock') +deps_private += gtest + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 799368ee9..14fbf0a06 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -85,7 +85,7 @@ mkDerivation (finalAttrs: { ''; mesonFlags = [ - (lib.mesonFeature "gc" enableGC) + (lib.mesonEnable "gc" enableGC) ]; env = { diff --git a/src/libfetchers-test/package.nix b/src/libfetchers-test/package.nix new file mode 100644 index 000000000..f4d3f3b73 --- /dev/null +++ b/src/libfetchers-test/package.nix @@ -0,0 +1,111 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-fetchers +, nix-store-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-fetchers-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-fetchers + nix-store-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-fetchers-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libflake-test/package.nix b/src/libflake-test/package.nix new file mode 100644 index 000000000..f03f58619 --- /dev/null +++ b/src/libflake-test/package.nix @@ -0,0 +1,111 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-flake +, nix-expr-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-flake-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-flake + nix-expr-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-flake-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index aedbad4a2..2ed78a760 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -41,7 +41,7 @@ mkDerivation (finalAttrs: { root = ./.; fileset = fileset.unions [ ./meson.build - ./meson.options + # ./meson.options (fileset.fileFilter (file: file.hasExt "cc") ./.) (fileset.fileFilter (file: file.hasExt "hh") ./.) (fileset.fileFilter (file: file.hasExt "h") ./.) diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index cb561977c..2cdbfa7b5 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -36,6 +36,9 @@ deps_private += rapidcheck gtest = dependency('gtest', main : true) deps_private += gtest +gtest = dependency('gmock') +deps_private += gtest + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. From 2c184f694be1835a50b628c0a8d9ab860583ce0e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:18:37 -0400 Subject: [PATCH 449/910] Ensure we have data dir for libexpr unit tests --- src/libexpr-test/data/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/libexpr-test/data/.gitkeep diff --git a/src/libexpr-test/data/.gitkeep b/src/libexpr-test/data/.gitkeep new file mode 100644 index 000000000..e69de29bb From 79e0ef88bf90a968c4867d28060354d1ea0f1d6d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:24:49 -0400 Subject: [PATCH 450/910] Include missing components --- packaging/components.nix | 1 + packaging/hydra.nix | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/packaging/components.nix b/packaging/components.nix index 5576877cb..73f0d24e1 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -26,6 +26,7 @@ in nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; + nix-flake-test = callPackage ../src/libflake-test/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index a1691ed38..8c212d2fb 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -40,7 +40,17 @@ let "nix-util-test-support" "nix-util-test" "nix-store" + "nix-store-c" + "nix-store-test-support" + "nix-store-test" "nix-fetchers" + "nix-fetcher-test" + "nix-expr" + "nix-expr-c" + "nix-expr-test-support" + "nix-expr-test" + "nix-flake" + "nix-flake-test" ]; in { From 6a0582d9fd258013362c435d96cc72d0e4b15320 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:28:13 -0400 Subject: [PATCH 451/910] Rename file to avoid reserved name --- .../deps-lists/meson.build | 0 .../diagnostics/meson.build | 0 .../export-all-symbols/meson.build | 0 .../export/meson.build | 0 .../subprojects/meson.build | 0 .../threads/meson.build | 0 meson.build | 4 ++-- src/libcmd/build-utils-meson | 1 + src/libcmd/meson-utils | 1 - src/libexpr-c/build-utils-meson | 1 + src/libexpr-c/meson-utils | 1 - src/libexpr-c/meson.build | 10 +++++----- src/libexpr-test-support/build-utils-meson | 1 + src/libexpr-test-support/meson-utils | 1 - src/libexpr-test-support/meson.build | 10 +++++----- src/libexpr-test/build-utils-meson | 1 + src/libexpr-test/meson-utils | 1 - src/libexpr-test/meson.build | 8 ++++---- src/libexpr/build-utils-meson | 1 + src/libexpr/meson-utils | 1 - src/libexpr/meson.build | 10 +++++----- src/libfetchers-test/build-utils-meson | 1 + src/libfetchers-test/meson-utils | 1 - src/libfetchers-test/meson.build | 8 ++++---- src/libfetchers/build-utils-meson | 1 + src/libfetchers/meson-utils | 1 - src/libfetchers/meson.build | 8 ++++---- src/libflake-test/build-utils-meson | 1 + src/libflake-test/meson-utils | 1 - src/libflake-test/meson.build | 8 ++++---- src/libflake/build-utils-meson | 1 + src/libflake/meson-utils | 1 - src/libflake/meson.build | 8 ++++---- src/libmain/build-utils-meson | 1 + src/libmain/meson-utils | 1 - src/libstore-c/build-utils-meson | 1 + src/libstore-c/meson-utils | 1 - src/libstore-c/meson.build | 10 +++++----- src/libstore-test-support/build-utils-meson | 1 + src/libstore-test-support/meson-utils | 1 - src/libstore-test-support/meson.build | 10 +++++----- src/libstore-test/build-utils-meson | 1 + src/libstore-test/meson-utils | 1 - src/libstore-test/meson.build | 8 ++++---- src/libstore/build-utils-meson | 1 + src/libstore/meson-utils | 1 - src/libstore/meson.build | 12 ++++++------ src/libutil-c/build-utils-meson | 1 + src/libutil-c/meson-utils | 1 - src/libutil-c/meson.build | 10 +++++----- src/libutil-test-support/build-utils-meson | 1 + src/libutil-test-support/meson-utils | 1 - src/libutil-test-support/meson.build | 10 +++++----- src/libutil-test/build-utils-meson | 1 + src/libutil-test/meson-utils | 1 - src/libutil-test/meson.build | 8 ++++---- src/libutil/build-utils-meson | 1 + src/libutil/meson-utils | 1 - src/libutil/meson.build | 12 ++++++------ 59 files changed, 95 insertions(+), 95 deletions(-) rename {meson-utils => build-utils-meson}/deps-lists/meson.build (100%) rename {meson-utils => build-utils-meson}/diagnostics/meson.build (100%) rename {meson-utils => build-utils-meson}/export-all-symbols/meson.build (100%) rename {meson-utils => build-utils-meson}/export/meson.build (100%) rename {meson-utils => build-utils-meson}/subprojects/meson.build (100%) rename {meson-utils => build-utils-meson}/threads/meson.build (100%) create mode 120000 src/libcmd/build-utils-meson delete mode 120000 src/libcmd/meson-utils create mode 120000 src/libexpr-c/build-utils-meson delete mode 120000 src/libexpr-c/meson-utils create mode 120000 src/libexpr-test-support/build-utils-meson delete mode 120000 src/libexpr-test-support/meson-utils create mode 120000 src/libexpr-test/build-utils-meson delete mode 120000 src/libexpr-test/meson-utils create mode 120000 src/libexpr/build-utils-meson delete mode 120000 src/libexpr/meson-utils create mode 120000 src/libfetchers-test/build-utils-meson delete mode 120000 src/libfetchers-test/meson-utils create mode 120000 src/libfetchers/build-utils-meson delete mode 120000 src/libfetchers/meson-utils create mode 120000 src/libflake-test/build-utils-meson delete mode 120000 src/libflake-test/meson-utils create mode 120000 src/libflake/build-utils-meson delete mode 120000 src/libflake/meson-utils create mode 120000 src/libmain/build-utils-meson delete mode 120000 src/libmain/meson-utils create mode 120000 src/libstore-c/build-utils-meson delete mode 120000 src/libstore-c/meson-utils create mode 120000 src/libstore-test-support/build-utils-meson delete mode 120000 src/libstore-test-support/meson-utils create mode 120000 src/libstore-test/build-utils-meson delete mode 120000 src/libstore-test/meson-utils create mode 120000 src/libstore/build-utils-meson delete mode 120000 src/libstore/meson-utils create mode 120000 src/libutil-c/build-utils-meson delete mode 120000 src/libutil-c/meson-utils create mode 120000 src/libutil-test-support/build-utils-meson delete mode 120000 src/libutil-test-support/meson-utils create mode 120000 src/libutil-test/build-utils-meson delete mode 120000 src/libutil-test/meson-utils create mode 120000 src/libutil/build-utils-meson delete mode 120000 src/libutil/meson-utils diff --git a/meson-utils/deps-lists/meson.build b/build-utils-meson/deps-lists/meson.build similarity index 100% rename from meson-utils/deps-lists/meson.build rename to build-utils-meson/deps-lists/meson.build diff --git a/meson-utils/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build similarity index 100% rename from meson-utils/diagnostics/meson.build rename to build-utils-meson/diagnostics/meson.build diff --git a/meson-utils/export-all-symbols/meson.build b/build-utils-meson/export-all-symbols/meson.build similarity index 100% rename from meson-utils/export-all-symbols/meson.build rename to build-utils-meson/export-all-symbols/meson.build diff --git a/meson-utils/export/meson.build b/build-utils-meson/export/meson.build similarity index 100% rename from meson-utils/export/meson.build rename to build-utils-meson/export/meson.build diff --git a/meson-utils/subprojects/meson.build b/build-utils-meson/subprojects/meson.build similarity index 100% rename from meson-utils/subprojects/meson.build rename to build-utils-meson/subprojects/meson.build diff --git a/meson-utils/threads/meson.build b/build-utils-meson/threads/meson.build similarity index 100% rename from meson-utils/threads/meson.build rename to build-utils-meson/threads/meson.build diff --git a/meson.build b/meson.build index fb38d7ef2..e969fc907 100644 --- a/meson.build +++ b/meson.build @@ -13,8 +13,8 @@ subproject('libexpr') subproject('libflake') # Docs -subproject('internal-api-docs') -subproject('external-api-docs') +#subproject('internal-api-docs') +#subproject('external-api-docs') # C wrappers subproject('libutil-c') diff --git a/src/libcmd/build-utils-meson b/src/libcmd/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libcmd/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libcmd/meson-utils b/src/libcmd/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libcmd/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/build-utils-meson b/src/libexpr-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr-c/meson-utils b/src/libexpr-c/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr-c/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 477640240..3c5d9e6b7 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -14,7 +14,7 @@ project('nix-expr-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -27,7 +27,7 @@ deps_public_maybe_subproject = [ dependency('nix-util-c'), dependency('nix-store-c'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) @@ -53,7 +53,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'nix_api_expr.cc', @@ -69,7 +69,7 @@ headers = [config_h] + files( 'nix_api_value.h', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixexprc', @@ -84,4 +84,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libexpr-test-support/build-utils-meson b/src/libexpr-test-support/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr-test-support/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr-test-support/meson-utils b/src/libexpr-test-support/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr-test-support/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index ad3108a61..d42b0532b 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -25,7 +25,7 @@ deps_public_maybe_subproject = [ dependency('nix-store-test-support'), dependency('nix-expr'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck @@ -39,7 +39,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'tests/value/context.cc', @@ -53,7 +53,7 @@ headers = files( 'tests/value/context.hh', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nix-expr-test-support', @@ -70,4 +70,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libexpr-test/build-utils-meson b/src/libexpr-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr-test/meson-utils b/src/libexpr-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index cc32b0a1a..f70fd0693 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -29,9 +29,9 @@ deps_public_maybe_subproject = [ dependency('nix-expr-c'), dependency('nix-expr-test-support'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -54,7 +54,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'derived-path.cc', diff --git a/src/libexpr/build-utils-meson b/src/libexpr/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr/meson-utils b/src/libexpr/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index d99f80486..fdf264604 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -14,7 +14,7 @@ project('nix-expr', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -25,9 +25,9 @@ deps_public_maybe_subproject = [ dependency('nix-store'), dependency('nix-fetchers'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/threads') +subdir('build-utils-meson/threads') boost = dependency( 'boost', @@ -69,7 +69,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') parser_tab = custom_target( input : 'parser.y', @@ -201,4 +201,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libfetchers-test/build-utils-meson b/src/libfetchers-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libfetchers-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libfetchers-test/meson-utils b/src/libfetchers-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libfetchers-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 30056c64a..e7c5b7873 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -27,9 +27,9 @@ deps_public_maybe_subproject = [ dependency('nix-store-test-support'), dependency('nix-fetchers'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -48,7 +48,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'public-key.cc', diff --git a/src/libfetchers/build-utils-meson b/src/libfetchers/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libfetchers/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libfetchers/meson-utils b/src/libfetchers/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libfetchers/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 16645bda3..257e15766 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -24,7 +24,7 @@ deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json @@ -41,7 +41,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'attrs.cc', @@ -89,4 +89,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libflake-test/build-utils-meson b/src/libflake-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libflake-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libflake-test/meson-utils b/src/libflake-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libflake-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index b5f0c2fb4..dd3f658be 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -14,7 +14,7 @@ project('nix-flake-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -30,9 +30,9 @@ deps_public_maybe_subproject = [ dependency('nix-expr-test-support'), dependency('nix-flake'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -52,7 +52,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'flakeref.cc', diff --git a/src/libflake/build-utils-meson b/src/libflake/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libflake/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libflake/meson-utils b/src/libflake/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libflake/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 13541fc43..e43d21dd3 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -14,7 +14,7 @@ project('nix-flake', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -24,7 +24,7 @@ deps_public_maybe_subproject = [ dependency('nix-fetchers'), dependency('nix-expr'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json @@ -42,7 +42,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'flake-settings.cc', @@ -74,4 +74,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libmain/build-utils-meson b/src/libmain/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libmain/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libmain/meson-utils b/src/libmain/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libmain/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/build-utils-meson b/src/libstore-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore-c/meson-utils b/src/libstore-c/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore-c/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index cd068fed2..4f2d77d9f 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -14,7 +14,7 @@ project('nix-store-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -25,7 +25,7 @@ deps_private_maybe_subproject = [ deps_public_maybe_subproject = [ dependency('nix-util-c'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) @@ -49,7 +49,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'nix_api_store.cc', @@ -61,7 +61,7 @@ headers = [config_h] + files( 'nix_api_store.h', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixstorec', @@ -76,4 +76,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libstore-test-support/build-utils-meson b/src/libstore-test-support/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore-test-support/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore-test-support/meson-utils b/src/libstore-test-support/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore-test-support/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index adf6f685e..e278bd3f8 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-store-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -23,7 +23,7 @@ deps_public_maybe_subproject = [ dependency('nix-util-test-support'), dependency('nix-store'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck @@ -36,7 +36,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'tests/derived-path.cc', @@ -55,7 +55,7 @@ headers = files( 'tests/protocol.hh', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nix-store-test-support', @@ -72,4 +72,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libstore-test/build-utils-meson b/src/libstore-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore-test/meson-utils b/src/libstore-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 2cdbfa7b5..6599b2d96 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -14,7 +14,7 @@ project('nix-store-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -26,9 +26,9 @@ deps_public_maybe_subproject = [ dependency('nix-store-c'), dependency('nix-store-test-support'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -49,7 +49,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'common-protocol.cc', diff --git a/src/libstore/build-utils-meson b/src/libstore/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore/meson-utils b/src/libstore/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 2bc3f27e4..62137ef5f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -14,7 +14,7 @@ project('nix-store', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -28,7 +28,7 @@ deps_private_maybe_subproject = [ deps_public_maybe_subproject = [ dependency('nix-util'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') run_command('ln', '-s', meson.project_build_root() / '__nothing_link_target', @@ -67,7 +67,7 @@ has_acl_support = cxx.has_header('sys/xattr.h') \ and cxx.has_function('lremovexattr') configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int()) -subdir('meson-utils/threads') +subdir('build-utils-meson/threads') boost = dependency( 'boost', @@ -153,7 +153,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'binary-cache-store.cc', @@ -385,7 +385,7 @@ foreach name, value : cpp_str_defines ] endforeach -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixstore', @@ -402,4 +402,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libutil-c/build-utils-meson b/src/libutil-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil-c/meson-utils b/src/libutil-c/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil-c/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 6c316784d..5e12186d2 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -14,7 +14,7 @@ project('nix-util-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -23,7 +23,7 @@ deps_private_maybe_subproject = [ ] deps_public_maybe_subproject = [ ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) @@ -45,7 +45,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'nix_api_util.cc', @@ -57,7 +57,7 @@ headers = [config_h] + files( 'nix_api_util.h', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixutilc', @@ -72,4 +72,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libutil-test-support/build-utils-meson b/src/libutil-test-support/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil-test-support/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil-test-support/meson-utils b/src/libutil-test-support/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil-test-support/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index d63fabce5..a36aa2a00 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -14,14 +14,14 @@ project('nix-util-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] deps_public_maybe_subproject = [ dependency('nix-util'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck @@ -33,7 +33,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'tests/hash.cc', @@ -49,7 +49,7 @@ headers = files( 'tests/string_callback.hh', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nix-util-test-support', @@ -66,4 +66,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libutil-test/build-utils-meson b/src/libutil-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil-test/meson-utils b/src/libutil-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 43001ca68..b90148f21 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -14,7 +14,7 @@ project('nix-util-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -23,9 +23,9 @@ deps_public_maybe_subproject = [ dependency('nix-util-c'), dependency('nix-util-test-support'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -41,7 +41,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'args.cc', diff --git a/src/libutil/build-utils-meson b/src/libutil/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil/meson-utils b/src/libutil/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 5eee8b3b2..c87808067 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -14,7 +14,7 @@ project('nix-util', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -22,7 +22,7 @@ deps_private_maybe_subproject = [ ] deps_public_maybe_subproject = [ ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # Check for each of these functions, and create a define like `#define # HAVE_LUTIMES 1`. The `#define` is unconditional, 0 for not found and 1 @@ -48,7 +48,7 @@ foreach funcspec : check_funcs configdata.set(define_name, define_value) endforeach -subdir('meson-utils/threads') +subdir('build-utils-meson/threads') if host_machine.system() == 'windows' socket = cxx.find_library('ws2_32') @@ -114,7 +114,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'archive.cc', @@ -241,7 +241,7 @@ else subdir('unix') endif -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixutil', @@ -261,4 +261,4 @@ if host_machine.system() == 'windows' libraries_private += ['-lws2_32'] endif -subdir('meson-utils/export') +subdir('build-utils-meson/export') From 5ba9f6cec616b26994441a8cbd766e528cd99609 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:28:24 -0400 Subject: [PATCH 452/910] Fix typo --- packaging/hydra.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 8c212d2fb..244a4ad3f 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -44,7 +44,7 @@ let "nix-store-test-support" "nix-store-test" "nix-fetchers" - "nix-fetcher-test" + "nix-fetchers-test" "nix-expr" "nix-expr-c" "nix-expr-test-support" From 479befa76d6b6d78b9304716a108ecd8c5cbae6c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 19:19:32 -0400 Subject: [PATCH 453/910] More fixes --- flake.nix | 1 + meson.build | 4 ++-- src/libexpr-c/package.nix | 4 +++- src/libexpr-test-support/package.nix | 4 +++- src/libexpr-test/meson.build | 10 ++-------- src/libexpr-test/package.nix | 4 +++- src/libexpr/{flake => }/call-flake.nix | 0 src/libexpr/eval.cc | 2 +- src/libexpr/local.mk | 2 +- src/libexpr/meson.build | 13 ++++++------- src/libexpr/package.nix | 19 ++++++++++++++++++- src/libexpr/primops/meson.build | 17 +++++++++++++++++ src/libfetchers-test/meson.build | 11 ++--------- src/libfetchers-test/package.nix | 4 +++- src/libfetchers/meson.build | 2 +- src/libfetchers/package.nix | 4 +++- src/libflake-test/meson.build | 15 ++------------- src/libflake-test/package.nix | 4 +++- src/libflake/meson.build | 3 --- src/libflake/package.nix | 4 +++- src/libstore-c/package.nix | 4 +++- src/libstore-test-support/package.nix | 4 +++- src/libstore-test/meson.build | 7 ++----- src/libstore-test/package.nix | 4 +++- src/libstore/package.nix | 4 +++- src/libutil-c/package.nix | 4 +++- src/libutil-test-support/package.nix | 4 +++- src/libutil-test/meson.build | 4 ++-- src/libutil-test/package.nix | 4 +++- src/libutil/package.nix | 2 ++ 30 files changed, 101 insertions(+), 67 deletions(-) rename src/libexpr/{flake => }/call-flake.nix (100%) create mode 100644 src/libexpr/primops/meson.build diff --git a/flake.nix b/flake.nix index fc46ef940..cfea7d386 100644 --- a/flake.nix +++ b/flake.nix @@ -324,6 +324,7 @@ ++ pkgs.nixComponents.nix-internal-api-docs.nativeBuildInputs ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs ++ [ + pkgs.buildPackages.cmake modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) diff --git a/meson.build b/meson.build index e969fc907..fb38d7ef2 100644 --- a/meson.build +++ b/meson.build @@ -13,8 +13,8 @@ subproject('libexpr') subproject('libflake') # Docs -#subproject('internal-api-docs') -#subproject('external-api-docs') +subproject('internal-api-docs') +subproject('external-api-docs') # C wrappers subproject('libutil-c') diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 542be064d..33412e218 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -62,9 +62,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr-test-support/package.nix b/src/libexpr-test-support/package.nix index cbc852fa5..ecfb2bb09 100644 --- a/src/libexpr-test-support/package.nix +++ b/src/libexpr-test-support/package.nix @@ -64,9 +64,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index f70fd0693..04b60f6d6 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -17,18 +17,12 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), - dependency('nix-store'), - dependency('nix-store-c'), - dependency('nix-store-test-support'), dependency('nix-expr'), dependency('nix-expr-c'), dependency('nix-expr-test-support'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libexpr-test/package.nix b/src/libexpr-test/package.nix index 7c8c9c4d1..12f4dd506 100644 --- a/src/libexpr-test/package.nix +++ b/src/libexpr-test/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr/flake/call-flake.nix b/src/libexpr/call-flake.nix similarity index 100% rename from src/libexpr/flake/call-flake.nix rename to src/libexpr/call-flake.nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d2be00e55..48ed66883 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -293,7 +293,7 @@ EvalState::EvalState( )} , callFlakeInternal{internalFS->addFile( CanonPath("call-flake.nix"), - #include "flake/call-flake.nix.gen.hh" + #include "call-flake.nix.gen.hh" )} , store(store) , buildStore(buildStore ? buildStore : store) diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 26958bf2c..68518e184 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -47,4 +47,4 @@ $(foreach i, $(wildcard src/libexpr/value/*.hh), \ $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh -$(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/flake/call-flake.nix.gen.hh +$(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/call-flake.nix.gen.hh diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index fdf264604..04822d179 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -55,6 +55,9 @@ if bdw_gc.found() endif configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) +toml11 = dependency('toml11', version : '>=3.7.0', method : 'cmake') +deps_other += toml11 + config_h = configure_file( configuration : configdata, output : 'config-expr.hh', @@ -117,8 +120,7 @@ generated_headers = [] foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix', - 'flake/call-flake.nix', - 'primops/derivation.nix', + 'call-flake.nix', ] generated_headers += custom_target( command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], @@ -142,11 +144,6 @@ sources = files( 'nixexpr.cc', 'paths.cc', 'primops.cc', - 'primops/context.cc', - 'primops/fetchClosure.cc', - 'primops/fetchMercurial.cc', - 'primops/fetchTree.cc', - 'primops/fromTOML.cc', 'print-ambiguous.cc', 'print.cc', 'search-path.cc', @@ -187,6 +184,8 @@ headers = [config_h] + files( 'value/context.hh', ) +subdir('primops') + this_library = library( 'nixexpr', sources, diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 14fbf0a06..855d5057e 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -5,6 +5,9 @@ , meson , ninja , pkg-config +, bison +, flex +, cmake # for resolving toml11 dep , nix-util , nix-store @@ -12,6 +15,7 @@ , boost , boehmgc , nlohmann_json +, toml11 # Configuration Options @@ -57,8 +61,12 @@ mkDerivation (finalAttrs: { fileset = fileset.unions [ ./meson.build ./meson.options + ./primops/meson.build (fileset.fileFilter (file: file.hasExt "cc") ./.) (fileset.fileFilter (file: file.hasExt "hh") ./.) + ./lexer.l + ./parser.y + (fileset.fileFilter (file: file.hasExt "nix") ./.) ]; }; @@ -68,6 +76,13 @@ mkDerivation (finalAttrs: { meson ninja pkg-config + bison + flex + cmake + ]; + + buildInputs = [ + toml11 ]; propagatedBuildInputs = [ @@ -79,9 +94,11 @@ mkDerivation (finalAttrs: { ] ++ lib.optional enableGC boehmgc; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr/primops/meson.build b/src/libexpr/primops/meson.build new file mode 100644 index 000000000..96a1dd07e --- /dev/null +++ b/src/libexpr/primops/meson.build @@ -0,0 +1,17 @@ +foreach header : [ + 'derivation.nix', +] + generated_headers += custom_target( + command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + input : header, + output : '@PLAINNAME@.gen.hh', + ) +endforeach + +sources += files( + 'context.cc', + 'fetchClosure.cc', + 'fetchMercurial.cc', + 'fetchTree.cc', + 'fromTOML.cc', +) diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index e7c5b7873..785754b34 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -17,16 +17,11 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), - dependency('nix-store'), - dependency('nix-store-c'), dependency('nix-store-test-support'), dependency('nix-fetchers'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') @@ -43,8 +38,6 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', '-include', 'config-store.hh', - '-include', 'config-util.h', - '-include', 'config-store.h', language : 'cpp', ) diff --git a/src/libfetchers-test/package.nix b/src/libfetchers-test/package.nix index f4d3f3b73..78d8ab490 100644 --- a/src/libfetchers-test/package.nix +++ b/src/libfetchers-test/package.nix @@ -67,9 +67,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 257e15766..d5703bbb3 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -30,7 +30,7 @@ nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json libgit2 = dependency('libgit2') -deps_public += libgit2 +deps_private += libgit2 add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index d2560255e..0146f5aa5 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so its not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index dd3f658be..b8221b2ad 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -17,19 +17,11 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), - dependency('nix-store'), - dependency('nix-store-c'), - dependency('nix-store-test-support'), - dependency('nix-expr'), - dependency('nix-expr-c'), dependency('nix-expr-test-support'), dependency('nix-flake'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') @@ -46,9 +38,6 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', '-include', 'config-expr.hh', - '-include', 'config-util.h', - '-include', 'config-store.h', - '-include', 'config-expr.h', language : 'cpp', ) diff --git a/src/libflake-test/package.nix b/src/libflake-test/package.nix index f03f58619..4fb190706 100644 --- a/src/libflake-test/package.nix +++ b/src/libflake-test/package.nix @@ -67,9 +67,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libflake/meson.build b/src/libflake/meson.build index e43d21dd3..30f98dce6 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -29,9 +29,6 @@ subdir('build-utils-meson/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json -libgit2 = dependency('libgit2') -deps_public += libgit2 - add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. diff --git a/src/libflake/package.nix b/src/libflake/package.nix index 1280df7b7..523da4b78 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so its not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index 2ed78a760..d0e81b1f9 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -62,9 +62,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libstore-test-support/package.nix b/src/libstore-test-support/package.nix index a28f54e2a..0f4ea73ba 100644 --- a/src/libstore-test-support/package.nix +++ b/src/libstore-test-support/package.nix @@ -64,9 +64,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 6599b2d96..bfd827b01 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -17,15 +17,12 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), dependency('nix-store'), dependency('nix-store-c'), dependency('nix-store-test-support'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libstore-test/package.nix b/src/libstore-test/package.nix index b57adfea5..0a49f1a05 100644 --- a/src/libstore-test/package.nix +++ b/src/libstore-test/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 5af1a7815..a08fabff7 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -87,9 +87,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index 37f2291b5..ba1dbe38a 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -60,9 +60,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil-test-support/package.nix b/src/libutil-test-support/package.nix index c6a0f0183..795159ebf 100644 --- a/src/libutil-test-support/package.nix +++ b/src/libutil-test-support/package.nix @@ -62,9 +62,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index b90148f21..19157cda3 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -17,12 +17,12 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libutil-test/package.nix b/src/libutil-test/package.nix index 391f8d853..396e41f3d 100644 --- a/src/libutil-test/package.nix +++ b/src/libutil-test/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil/package.nix b/src/libutil/package.nix index ef5e251fb..aff338d16 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -79,9 +79,11 @@ mkMesonDerivation (finalAttrs: { preConfigure = # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. + # Do the meson utils, without modification. '' chmod u+w ./.version echo ${version} > ../../.version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ From 17c843c5c536e6f9266003956a8251a7882ecccc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 19:30:58 -0400 Subject: [PATCH 454/910] Fix more issues --- package.nix | 5 +---- packaging/dependencies.nix | 13 ++++++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/package.nix b/package.nix index 28d7e788f..da3a069fa 100644 --- a/package.nix +++ b/package.nix @@ -207,10 +207,7 @@ in { libsodium openssl sqlite - (toml11.overrideAttrs (old: { - # TODO change in Nixpkgs, Windows works fine. - meta.platforms = lib.platforms.all; - })) + toml11 xz ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index b2349f02c..909a0a38e 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -10,6 +10,7 @@ stdenv, versionSuffix, }: + let inherit (pkgs) lib; @@ -52,7 +53,7 @@ scope: { enableLargeConfig = true; }; - # Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed. + # TODO Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed. boost = (pkgs.boost.override { extraB2Args = [ "--with-container" @@ -61,8 +62,8 @@ scope: { ]; }).overrideAttrs (old: { # Need to remove `--with-*` to use `--with-libraries=...` - buildPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase; - installPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase; + buildPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase; + installPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase; }); libgit2 = pkgs.libgit2.overrideAttrs (attrs: { @@ -96,5 +97,11 @@ scope: { ''; }); + # TODO change in Nixpkgs, Windows works fine. First commit of + # https://github.com/NixOS/nixpkgs/pull/322977 backported will fix. + toml11 = pkgs.toml11.overrideAttrs (old: { + meta.platforms = lib.platforms.all; + }); + mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); } From 7312d13acc134f57a4b959f035cac6f661c469cd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 19:35:54 -0400 Subject: [PATCH 455/910] Keep another test dir --- src/libflake-test/data/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/libflake-test/data/.gitkeep diff --git a/src/libflake-test/data/.gitkeep b/src/libflake-test/data/.gitkeep new file mode 100644 index 000000000..e69de29bb From 874ff000d4f7e661e7e95d608f6ab7083f563d6a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 20:41:03 -0400 Subject: [PATCH 456/910] Fix format --- maintainers/flake-module.nix | 2 +- src/libcmd/network-proxy.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index b78e5f63a..c0373dee4 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^src/[^/]*-test/[^/]*/data/.*$'' + ''^src/[^/]*-test/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc index 47be311cd..738bf6147 100644 --- a/src/libcmd/network-proxy.cc +++ b/src/libcmd/network-proxy.cc @@ -13,7 +13,8 @@ static StringSet getAllVariables() StringSet variables = lowercaseVariables; for (const auto & variable : lowercaseVariables) { std::string upperVariable; - std::transform(variable.begin(), variable.end(), upperVariable.begin(), [](unsigned char c) { return std::toupper(c); }); + std::transform( + variable.begin(), variable.end(), upperVariable.begin(), [](unsigned char c) { return std::toupper(c); }); variables.insert(std::move(upperVariable)); } return variables; From f7ce10dbc15635ebc652d4746c74e437a964881e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 14:26:04 -0400 Subject: [PATCH 457/910] Fix static build --- package.nix | 4 ++-- src/libstore/meson.build | 27 ++++++++++++++------------- src/libstore/package.nix | 7 +++++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/package.nix b/package.nix index da3a069fa..041786d47 100644 --- a/package.nix +++ b/package.nix @@ -33,7 +33,7 @@ , rapidcheck , sqlite , toml11 -, util-linux +, unixtools , xz , busybox-sandbox-shell ? null @@ -195,7 +195,7 @@ in { man # for testing `nix-* --help` ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. - ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux + ] ++ lib.optional stdenv.hostPlatform.isStatic unixtools.hexdump ; buildInputs = lib.optionals doBuild [ diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 62137ef5f..0686a591e 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -99,15 +99,6 @@ deps_public += nlohmann_json sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') deps_private += sqlite - -enable_embedded_sandbox_shell = get_option('embedded-sandbox-shell') -if enable_embedded_sandbox_shell - # This one goes in config.h - # The path to busybox is passed as a -D flag when compiling this_library. - # Idk why, ask the old buildsystem. - configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) -endif - generated_headers = [] foreach header : [ 'schema.sql', @@ -122,7 +113,13 @@ foreach header : [ ) endforeach -if enable_embedded_sandbox_shell +busybox = find_program(get_option('sandbox-shell'), required : false) + +if get_option('embedded-sandbox-shell') + # This one goes in config.h + # The path to busybox is passed as a -D flag when compiling this_library. + # Idk why, ask the old buildsystem. + configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) hexdump = find_program('hexdump', native : true) embedded_sandbox_shell_gen = custom_target( 'embedded-sandbox-shell.gen.hh', @@ -371,11 +368,15 @@ cpp_str_defines += { 'LSOF': lsof_path } -#if busybox.found() +if get_option('embedded-sandbox-shell') cpp_str_defines += { -# 'SANDBOX_SHELL': busybox.full_path() + 'SANDBOX_SHELL': '__embedded_sandbox_shell__' } -#endif +elif busybox.found() + cpp_str_defines += { + 'SANDBOX_SHELL': busybox.full_path() + } +endif cpp_args = [] diff --git a/src/libstore/package.nix b/src/libstore/package.nix index a08fabff7..d4859a411 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -5,6 +5,7 @@ , meson , ninja , pkg-config +, unixtools , nix-util , boost @@ -20,6 +21,8 @@ , versionSuffix ? "" +, embeddedSandboxShell ? stdenv.hostPlatform.isStatic + # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. , withCoverageChecks ? false @@ -66,7 +69,7 @@ mkDerivation (finalAttrs: { meson ninja pkg-config - ]; + ] ++ lib.optional embeddedSandboxShell unixtools.hexdump; buildInputs = [ boost @@ -96,7 +99,7 @@ mkDerivation (finalAttrs: { mesonFlags = [ (lib.mesonEnable "seccomp-sandboxing" stdenv.hostPlatform.isLinux) - (lib.mesonBool "embedded-sandbox-shell" stdenv.hostPlatform.isStatic) + (lib.mesonBool "embedded-sandbox-shell" embeddedSandboxShell) ] ++ lib.optionals stdenv.hostPlatform.isLinux [ (lib.mesonOption "sandbox-shell" "${busybox-sandbox-shell}/bin/busybox") ]; From 912c517bc067f35caab5225822ab2fb8b3ccb1fb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 14:57:10 -0400 Subject: [PATCH 458/910] Fix build of unit tests --- src/libexpr-c/meson.build | 3 +++ src/libstore-c/meson.build | 3 +++ src/libstore-test/meson.build | 3 +++ src/libstore-test/package.nix | 13 ++++++++++--- src/libutil-c/meson.build | 3 +++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 3c5d9e6b7..fa970c3a2 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -69,6 +69,9 @@ headers = [config_h] + files( 'nix_api_value.h', ) +# TODO don't install this once tests don't use it. +headers += files('nix_api_expr_internal.h') + subdir('build-utils-meson/export-all-symbols') this_library = library( diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 4f2d77d9f..93ce97960 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -61,6 +61,9 @@ headers = [config_h] + files( 'nix_api_store.h', ) +# TODO don't install this once tests don't use it. +headers += files('nix_api_store_internal.h') + subdir('build-utils-meson/export-all-symbols') this_library = library( diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index bfd827b01..6bf0a5028 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -27,6 +27,9 @@ subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') +sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') +deps_private += sqlite + rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libstore-test/package.nix b/src/libstore-test/package.nix index 0a49f1a05..e37e64886 100644 --- a/src/libstore-test/package.nix +++ b/src/libstore-test/package.nix @@ -9,6 +9,7 @@ , nix-store , nix-store-c , nix-store-test-support +, sqlite , rapidcheck , gtest @@ -64,6 +65,7 @@ mkDerivation (finalAttrs: { nix-store nix-store-c nix-store-test-support + sqlite rapidcheck gtest ]; @@ -94,10 +96,15 @@ mkDerivation (finalAttrs: { passthru = { tests = { - run = runCommand "${finalAttrs.pname}-run" { - } '' + run = let + # Inline some drv files shared with the libexpr tests + data = runCommand "${finalAttrs.pname}-test-data" {} '' + cp -r --no-preserve=mode ${./data} $out + cp -r --remove-destination ${../../tests/functional/derivation}/* $out/derivation/ + ''; + in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${data} nix-store-test touch $out ''; diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 5e12186d2..2fa1bd424 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -57,6 +57,9 @@ headers = [config_h] + files( 'nix_api_util.h', ) +# TODO don't install this once tests don't use it. +headers += files('nix_api_util_internal.h') + subdir('build-utils-meson/export-all-symbols') this_library = library( From 513f6b971855947de8ac9a344319eace77e9c2ad Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 17:02:23 -0400 Subject: [PATCH 459/910] meson: Prelink links to avoid missing C++ initializers This is the same as what the old build system did in 7eca8a16eaf74bc15a816e24005a65e5480d2a79, done for the same reasons. --- src/libexpr-c/meson.build | 1 + src/libexpr-test-support/meson.build | 1 + src/libexpr/meson.build | 1 + src/libfetchers/meson.build | 1 + src/libflake/meson.build | 1 + src/libstore-c/meson.build | 1 + src/libstore-test-support/meson.build | 1 + src/libstore/meson.build | 1 + src/libutil-c/meson.build | 1 + src/libutil-test-support/meson.build | 1 + src/libutil/meson.build | 1 + src/perl/lib/Nix/meson.build | 1 + 12 files changed, 12 insertions(+) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index fa970c3a2..fb9ade28d 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -80,6 +80,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index d42b0532b..705672204 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -63,6 +63,7 @@ this_library = library( # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 # is available. See also ../libutil/build.meson link_args: linker_export_flags + ['-lrapidcheck'], + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 04822d179..9fe7c17c4 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -193,6 +193,7 @@ this_library = library( lexer_tab, generated_headers, dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index d5703bbb3..c39fe99f3 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -82,6 +82,7 @@ this_library = library( 'nixfetchers', sources, dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 30f98dce6..d3c3d3079 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -64,6 +64,7 @@ this_library = library( 'nixflake', sources, dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 93ce97960..426f07a34 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -72,6 +72,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index e278bd3f8..ddb067c1b 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -65,6 +65,7 @@ this_library = library( # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 # is available. See also ../libutil/build.meson link_args: linker_export_flags + ['-lrapidcheck'], + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 0686a591e..f94a454da 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -396,6 +396,7 @@ this_library = library( include_directories : include_dirs, cpp_args : cpp_args, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 2fa1bd424..3f0d96282 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -68,6 +68,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index a36aa2a00..7d0e9c2fc 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -59,6 +59,7 @@ this_library = library( # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 # is available. See also ../libutil/build.meson link_args: linker_export_flags + ['-lrapidcheck'], + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libutil/meson.build b/src/libutil/meson.build index c87808067..ac2b83536 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -249,6 +249,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/perl/lib/Nix/meson.build b/src/perl/lib/Nix/meson.build index 9a79245cd..256e66096 100644 --- a/src/perl/lib/Nix/meson.build +++ b/src/perl/lib/Nix/meson.build @@ -43,6 +43,7 @@ nix_perl_store_lib = library( 'Store', sources : nix_perl_store_cc, name_prefix : '', + prelink : true, # For C++ static initializers install : true, install_mode : 'rwxr-xr-x', install_dir : join_paths(nix_perl_install_dir, 'auto', 'Nix', 'Store'), From 3ad39d2afb50e42c6479c4007da10fde9262cc68 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 17:17:20 -0400 Subject: [PATCH 460/910] Fix library name --- src/libfetchers/package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 0146f5aa5..681ffa112 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-flake"; + pname = "nix-fetchers"; inherit version; src = fileset.toSource { From 496b4a9cd2d097569ab52804559355f014fee2e3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 10:31:08 -0400 Subject: [PATCH 461/910] Move around unit test dirs to match new names --- .gitignore | 10 +- doc/manual/src/contributing/testing.md | 4 +- maintainers/flake-module.nix | 120 +++++++++--------- meson.build | 16 +-- packaging/components.nix | 16 +-- packaging/hydra.nix | 10 +- src/internal-api-docs/doxygen.cfg.in | 16 +-- .../.version | 0 .../build-utils-meson | 0 .../meson.build | 0 .../package.nix | 0 .../tests/libexpr.hh | 0 .../tests/nix_api_expr.hh | 0 .../tests/value/context.cc | 0 .../tests/value/context.hh | 0 src/{libexpr-test => nix-expr-tests}/.version | 0 .../build-utils-meson | 0 .../data/.gitkeep | 0 .../derived-path.cc | 0 .../error_traces.cc | 0 src/{libexpr-test => nix-expr-tests}/eval.cc | 0 src/{libexpr-test => nix-expr-tests}/json.cc | 0 src/{libexpr-test => nix-expr-tests}/main.cc | 0 .../meson.build | 2 +- .../nix_api_expr.cc | 0 .../nix_api_external.cc | 0 .../nix_api_value.cc | 0 .../package.nix | 4 +- .../primops.cc | 0 .../search-path.cc | 0 .../trivial.cc | 0 .../value/context.cc | 0 .../value/print.cc | 0 .../value/value.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../data/public-key/defaultType.json | 0 .../data/public-key/noRoundTrip.json | 0 .../data/public-key/simple.json | 0 .../meson.build | 2 +- .../package.nix | 4 +- .../public-key.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../data/.gitkeep | 0 .../flakeref.cc | 0 .../meson.build | 2 +- .../package.nix | 4 +- .../url-name.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../meson.build | 0 .../package.nix | 0 .../tests/derived-path.cc | 0 .../tests/derived-path.hh | 0 .../tests/libstore.hh | 0 .../tests/nix_api_store.hh | 0 .../tests/outputs-spec.cc | 0 .../tests/outputs-spec.hh | 0 .../tests/path.cc | 0 .../tests/path.hh | 0 .../tests/protocol.hh | 0 .../.version | 0 .../build-utils-meson | 0 .../common-protocol.cc | 0 .../content-address.cc | 0 .../data/common-protocol/content-address.bin | Bin .../data/common-protocol/drv-output.bin | Bin .../optional-content-address.bin | Bin .../common-protocol/optional-store-path.bin | Bin .../data/common-protocol/realisation.bin | Bin .../data/common-protocol/set.bin | Bin .../data/common-protocol/store-path.bin | Bin .../data/common-protocol/string.bin | Bin .../data/common-protocol/vector.bin | Bin .../advanced-attributes-defaults.drv | 0 .../advanced-attributes-defaults.json | 0 ...d-attributes-structured-attrs-defaults.drv | 0 ...-attributes-structured-attrs-defaults.json | 0 .../advanced-attributes-structured-attrs.drv | 0 .../advanced-attributes-structured-attrs.json | 0 .../data/derivation/advanced-attributes.drv | 0 .../derivation/bad-old-version-dyn-deps.drv | 0 .../data/derivation/bad-version.drv | 0 .../data/derivation/dynDerivationDeps.drv | 0 .../data/derivation/dynDerivationDeps.json | 0 .../data/derivation/output-caFixedFlat.json | 0 .../data/derivation/output-caFixedNAR.json | 0 .../data/derivation/output-caFixedText.json | 0 .../data/derivation/output-caFloating.json | 0 .../data/derivation/output-deferred.json | 0 .../data/derivation/output-impure.json | 0 .../derivation/output-inputAddressed.json | 0 .../data/derivation/simple.drv | 0 .../data/derivation/simple.json | 0 .../data/machines/bad_format | 0 .../data/machines/valid | 0 .../data/nar-info/impure.json | 0 .../data/nar-info/pure.json | 0 .../data/path-info/empty_impure.json | 0 .../data/path-info/empty_pure.json | 0 .../data/path-info/impure.json | 0 .../data/path-info/pure.json | 0 .../data/serve-protocol/build-options-2.1.bin | Bin .../data/serve-protocol/build-options-2.2.bin | Bin .../data/serve-protocol/build-options-2.3.bin | Bin .../data/serve-protocol/build-options-2.7.bin | Bin .../data/serve-protocol/build-result-2.2.bin | Bin .../data/serve-protocol/build-result-2.3.bin | Bin .../data/serve-protocol/build-result-2.6.bin | Bin .../data/serve-protocol/content-address.bin | Bin .../data/serve-protocol/drv-output.bin | Bin .../serve-protocol/handshake-to-client.bin | Bin .../optional-content-address.bin | Bin .../serve-protocol/optional-store-path.bin | Bin .../data/serve-protocol/realisation.bin | Bin .../data/serve-protocol/set.bin | Bin .../data/serve-protocol/store-path.bin | Bin .../data/serve-protocol/string.bin | Bin .../unkeyed-valid-path-info-2.3.bin | Bin .../unkeyed-valid-path-info-2.4.bin | Bin .../data/serve-protocol/vector.bin | Bin .../data/store-reference/auto.txt | 0 .../data/store-reference/auto_param.txt | 0 .../data/store-reference/local_1.txt | 0 .../data/store-reference/local_2.txt | 0 .../store-reference/local_shorthand_1.txt | 0 .../store-reference/local_shorthand_2.txt | 0 .../data/store-reference/ssh.txt | 0 .../data/store-reference/unix.txt | 0 .../data/store-reference/unix_shorthand.txt | 0 .../data/worker-protocol/build-mode.bin | Bin .../worker-protocol/build-result-1.27.bin | Bin .../worker-protocol/build-result-1.28.bin | Bin .../worker-protocol/build-result-1.29.bin | Bin .../worker-protocol/build-result-1.37.bin | Bin .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin .../client-handshake-info_1_35.bin | Bin .../data/worker-protocol/content-address.bin | Bin .../worker-protocol/derived-path-1.29.bin | Bin .../worker-protocol/derived-path-1.30.bin | Bin .../data/worker-protocol/drv-output.bin | Bin .../worker-protocol/handshake-to-client.bin | Bin .../keyed-build-result-1.29.bin | Bin .../optional-content-address.bin | Bin .../worker-protocol/optional-store-path.bin | Bin .../worker-protocol/optional-trusted-flag.bin | Bin .../data/worker-protocol/realisation.bin | Bin .../data/worker-protocol/set.bin | Bin .../data/worker-protocol/store-path.bin | Bin .../data/worker-protocol/string.bin | Bin .../unkeyed-valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.16.bin | Bin .../data/worker-protocol/vector.bin | Bin .../derivation-advanced-attrs.cc | 0 .../derivation.cc | 0 .../derived-path.cc | 0 .../downstream-placeholder.cc | 0 .../machines.cc | 0 .../meson.build | 2 +- .../nar-info-disk-cache.cc | 0 .../nar-info.cc | 0 .../nix_api_store.cc | 0 .../outputs-spec.cc | 0 .../package.nix | 4 +- .../path-info.cc | 0 .../path.cc | 0 .../references.cc | 0 .../serve-protocol.cc | 0 .../store-reference.cc | 0 .../worker-protocol.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../meson.build | 0 .../package.nix | 0 .../tests/characterization.hh | 0 .../tests/hash.cc | 0 .../tests/hash.hh | 0 .../tests/nix_api_util.hh | 0 .../tests/string_callback.cc | 0 .../tests/string_callback.hh | 0 src/{libutil-test => nix-util-tests}/.version | 0 src/{libutil-test => nix-util-tests}/args.cc | 0 .../build-utils-meson | 0 .../canon-path.cc | 0 .../chunked-vector.cc | 0 .../closure.cc | 0 .../compression.cc | 0 .../config.cc | 0 .../data/git/check-data.sh | 0 .../data/git/hello-world-blob.bin | Bin .../data/git/hello-world.bin | Bin .../data/git/tree.bin | Bin .../data/git/tree.txt | 0 .../file-content-address.cc | 0 src/{libutil-test => nix-util-tests}/git.cc | 2 +- src/{libutil-test => nix-util-tests}/hash.cc | 0 .../hilite.cc | 0 .../json-utils.cc | 0 .../logging.cc | 0 .../lru-cache.cc | 0 .../meson.build | 2 +- .../nix_api_util.cc | 0 .../package.nix | 4 +- src/{libutil-test => nix-util-tests}/pool.cc | 0 .../references.cc | 0 src/{libutil-test => nix-util-tests}/spawn.cc | 0 .../suggestions.cc | 0 src/{libutil-test => nix-util-tests}/tests.cc | 0 src/{libutil-test => nix-util-tests}/url.cc | 0 .../xml-writer.cc | 0 213 files changed, 112 insertions(+), 112 deletions(-) rename src/{libexpr-test-support => nix-expr-test-support}/.version (100%) rename src/{libexpr-test-support => nix-expr-test-support}/build-utils-meson (100%) rename src/{libexpr-test-support => nix-expr-test-support}/meson.build (100%) rename src/{libexpr-test-support => nix-expr-test-support}/package.nix (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/libexpr.hh (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/nix_api_expr.hh (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/value/context.cc (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/value/context.hh (100%) rename src/{libexpr-test => nix-expr-tests}/.version (100%) rename src/{libexpr-test => nix-expr-tests}/build-utils-meson (100%) rename src/{libexpr-test => nix-expr-tests}/data/.gitkeep (100%) rename src/{libexpr-test => nix-expr-tests}/derived-path.cc (100%) rename src/{libexpr-test => nix-expr-tests}/error_traces.cc (100%) rename src/{libexpr-test => nix-expr-tests}/eval.cc (100%) rename src/{libexpr-test => nix-expr-tests}/json.cc (100%) rename src/{libexpr-test => nix-expr-tests}/main.cc (100%) rename src/{libexpr-test => nix-expr-tests}/meson.build (98%) rename src/{libexpr-test => nix-expr-tests}/nix_api_expr.cc (100%) rename src/{libexpr-test => nix-expr-tests}/nix_api_external.cc (100%) rename src/{libexpr-test => nix-expr-tests}/nix_api_value.cc (100%) rename src/{libexpr-test => nix-expr-tests}/package.nix (97%) rename src/{libexpr-test => nix-expr-tests}/primops.cc (100%) rename src/{libexpr-test => nix-expr-tests}/search-path.cc (100%) rename src/{libexpr-test => nix-expr-tests}/trivial.cc (100%) rename src/{libexpr-test => nix-expr-tests}/value/context.cc (100%) rename src/{libexpr-test => nix-expr-tests}/value/print.cc (100%) rename src/{libexpr-test => nix-expr-tests}/value/value.cc (100%) rename src/{libfetchers-test => nix-fetchers-tests}/.version (100%) rename src/{libfetchers-test => nix-fetchers-tests}/build-utils-meson (100%) rename src/{libfetchers-test => nix-fetchers-tests}/data/public-key/defaultType.json (100%) rename src/{libfetchers-test => nix-fetchers-tests}/data/public-key/noRoundTrip.json (100%) rename src/{libfetchers-test => nix-fetchers-tests}/data/public-key/simple.json (100%) rename src/{libfetchers-test => nix-fetchers-tests}/meson.build (97%) rename src/{libfetchers-test => nix-fetchers-tests}/package.nix (97%) rename src/{libfetchers-test => nix-fetchers-tests}/public-key.cc (100%) rename src/{libflake-test => nix-flake-tests}/.version (100%) rename src/{libflake-test => nix-flake-tests}/build-utils-meson (100%) rename src/{libflake-test => nix-flake-tests}/data/.gitkeep (100%) rename src/{libflake-test => nix-flake-tests}/flakeref.cc (100%) rename src/{libflake-test => nix-flake-tests}/meson.build (97%) rename src/{libflake-test => nix-flake-tests}/package.nix (97%) rename src/{libflake-test => nix-flake-tests}/url-name.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/.version (100%) rename src/{libstore-test-support => nix-store-test-support}/build-utils-meson (100%) rename src/{libstore-test-support => nix-store-test-support}/meson.build (100%) rename src/{libstore-test-support => nix-store-test-support}/package.nix (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/derived-path.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/derived-path.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/libstore.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/nix_api_store.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/outputs-spec.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/outputs-spec.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/path.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/path.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/protocol.hh (100%) rename src/{libstore-test => nix-store-tests}/.version (100%) rename src/{libstore-test => nix-store-tests}/build-utils-meson (100%) rename src/{libstore-test => nix-store-tests}/common-protocol.cc (100%) rename src/{libstore-test => nix-store-tests}/content-address.cc (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/drv-output.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/optional-content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/optional-store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/realisation.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/set.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/string.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/vector.bin (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-defaults.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-defaults.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs-defaults.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs-defaults.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/bad-old-version-dyn-deps.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/bad-version.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/dynDerivationDeps.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/dynDerivationDeps.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFixedFlat.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFixedNAR.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFixedText.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFloating.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-deferred.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-inputAddressed.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/simple.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/simple.json (100%) rename src/{libstore-test => nix-store-tests}/data/machines/bad_format (100%) rename src/{libstore-test => nix-store-tests}/data/machines/valid (100%) rename src/{libstore-test => nix-store-tests}/data/nar-info/impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/nar-info/pure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/empty_impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/empty_pure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/pure.json (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.1.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.2.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.3.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.7.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-result-2.2.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-result-2.3.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-result-2.6.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/drv-output.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/handshake-to-client.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/optional-content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/optional-store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/realisation.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/set.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/string.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/unkeyed-valid-path-info-2.3.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/unkeyed-valid-path-info-2.4.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/vector.bin (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/auto.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/auto_param.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_1.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_2.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_shorthand_1.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_shorthand_2.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/ssh.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/unix.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/unix_shorthand.txt (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-mode.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.27.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.28.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.29.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.37.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/client-handshake-info_1_30.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/client-handshake-info_1_33.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/client-handshake-info_1_35.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/derived-path-1.29.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/derived-path-1.30.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/drv-output.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/handshake-to-client.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/keyed-build-result-1.29.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/optional-content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/optional-store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/optional-trusted-flag.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/realisation.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/set.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/string.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/unkeyed-valid-path-info-1.15.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/valid-path-info-1.15.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/valid-path-info-1.16.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/vector.bin (100%) rename src/{libstore-test => nix-store-tests}/derivation-advanced-attrs.cc (100%) rename src/{libstore-test => nix-store-tests}/derivation.cc (100%) rename src/{libstore-test => nix-store-tests}/derived-path.cc (100%) rename src/{libstore-test => nix-store-tests}/downstream-placeholder.cc (100%) rename src/{libstore-test => nix-store-tests}/machines.cc (100%) rename src/{libstore-test => nix-store-tests}/meson.build (98%) rename src/{libstore-test => nix-store-tests}/nar-info-disk-cache.cc (100%) rename src/{libstore-test => nix-store-tests}/nar-info.cc (100%) rename src/{libstore-test => nix-store-tests}/nix_api_store.cc (100%) rename src/{libstore-test => nix-store-tests}/outputs-spec.cc (100%) rename src/{libstore-test => nix-store-tests}/package.nix (98%) rename src/{libstore-test => nix-store-tests}/path-info.cc (100%) rename src/{libstore-test => nix-store-tests}/path.cc (100%) rename src/{libstore-test => nix-store-tests}/references.cc (100%) rename src/{libstore-test => nix-store-tests}/serve-protocol.cc (100%) rename src/{libstore-test => nix-store-tests}/store-reference.cc (100%) rename src/{libstore-test => nix-store-tests}/worker-protocol.cc (100%) rename src/{libutil-test-support => nix-util-test-support}/.version (100%) rename src/{libutil-test-support => nix-util-test-support}/build-utils-meson (100%) rename src/{libutil-test-support => nix-util-test-support}/meson.build (100%) rename src/{libutil-test-support => nix-util-test-support}/package.nix (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/characterization.hh (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/hash.cc (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/hash.hh (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/nix_api_util.hh (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/string_callback.cc (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/string_callback.hh (100%) rename src/{libutil-test => nix-util-tests}/.version (100%) rename src/{libutil-test => nix-util-tests}/args.cc (100%) rename src/{libutil-test => nix-util-tests}/build-utils-meson (100%) rename src/{libutil-test => nix-util-tests}/canon-path.cc (100%) rename src/{libutil-test => nix-util-tests}/chunked-vector.cc (100%) rename src/{libutil-test => nix-util-tests}/closure.cc (100%) rename src/{libutil-test => nix-util-tests}/compression.cc (100%) rename src/{libutil-test => nix-util-tests}/config.cc (100%) rename src/{libutil-test => nix-util-tests}/data/git/check-data.sh (100%) rename src/{libutil-test => nix-util-tests}/data/git/hello-world-blob.bin (100%) rename src/{libutil-test => nix-util-tests}/data/git/hello-world.bin (100%) rename src/{libutil-test => nix-util-tests}/data/git/tree.bin (100%) rename src/{libutil-test => nix-util-tests}/data/git/tree.txt (100%) rename src/{libutil-test => nix-util-tests}/file-content-address.cc (100%) rename src/{libutil-test => nix-util-tests}/git.cc (99%) rename src/{libutil-test => nix-util-tests}/hash.cc (100%) rename src/{libutil-test => nix-util-tests}/hilite.cc (100%) rename src/{libutil-test => nix-util-tests}/json-utils.cc (100%) rename src/{libutil-test => nix-util-tests}/logging.cc (100%) rename src/{libutil-test => nix-util-tests}/lru-cache.cc (100%) rename src/{libutil-test => nix-util-tests}/meson.build (98%) rename src/{libutil-test => nix-util-tests}/nix_api_util.cc (100%) rename src/{libutil-test => nix-util-tests}/package.nix (97%) rename src/{libutil-test => nix-util-tests}/pool.cc (100%) rename src/{libutil-test => nix-util-tests}/references.cc (100%) rename src/{libutil-test => nix-util-tests}/spawn.cc (100%) rename src/{libutil-test => nix-util-tests}/suggestions.cc (100%) rename src/{libutil-test => nix-util-tests}/tests.cc (100%) rename src/{libutil-test => nix-util-tests}/url.cc (100%) rename src/{libutil-test => nix-util-tests}/xml-writer.cc (100%) diff --git a/.gitignore b/.gitignore index 838cac335..fdfd744e5 100644 --- a/.gitignore +++ b/.gitignore @@ -49,22 +49,22 @@ perl/Makefile.config /src/libexpr/parser-tab.output /src/libexpr/nix.tbl /src/libexpr/tests -/src/libexpr-test/libnixexpr-tests +/src/nix-expr-tests/libnixexpr-tests # /src/libfetchers -/src/libfetchers-test/libnixfetchers-tests +/src/nix-fetchers-tests/libnixfetchers-tests # /src/libflake -/src/libflake-test/libnixflake-tests +/src/nix-flake-tests/libnixflake-tests # /src/libstore/ *.gen.* /src/libstore/tests -/src/libstore-test/libnixstore-tests +/src/nix-store-tests/libnixstore-tests # /src/libutil/ /src/libutil/tests -/src/libutil-test/libnixutil-tests +/src/nix-util-tests/libnixutil-tests /src/nix/nix diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index ed9c25f7a..399174de5 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -60,10 +60,10 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks. > ``` The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_name_without-nix}-test`. -Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `src/libexpr-test/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `src/libexpr-test-support/tests/value/context.{hh,cc}`. +Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `src/nix-expr-tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `src/nix-expr-test-support/tests/value/context.{hh,cc}`. Data for unit tests is stored in a `data` subdir of the directory for each unit test executable. -For example, `libnixstore` code is in `src/libstore`, and its test data is in `src/libstore-test/data`. +For example, `libnixstore` code is in `src/libstore`, and its test data is in `src/nix-store-tests/data`. The path to the `src/${library_name_without-nix}-test/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`. Note that each executable only gets the data for its tests. diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index c0373dee4..a39a70890 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -429,65 +429,65 @@ ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/nixos/user-sandboxing/attacker\.c'' - ''^src/libexpr-test-support/tests/libexpr\.hh'' - ''^src/libexpr-test-support/tests/value/context\.cc'' - ''^src/libexpr-test-support/tests/value/context\.hh'' - ''^src/libexpr-test/derived-path\.cc'' - ''^src/libexpr-test/error_traces\.cc'' - ''^src/libexpr-test/eval\.cc'' - ''^src/libexpr-test/json\.cc'' - ''^src/libexpr-test/main\.cc'' - ''^src/libexpr-test/primops\.cc'' - ''^src/libexpr-test/search-path\.cc'' - ''^src/libexpr-test/trivial\.cc'' - ''^src/libexpr-test/value/context\.cc'' - ''^src/libexpr-test/value/print\.cc'' - ''^src/libfetchers-test/public-key\.cc'' - ''^src/libflake-test/flakeref\.cc'' - ''^src/libflake-test/url-name\.cc'' - ''^src/libstore-test-support/tests/derived-path\.cc'' - ''^src/libstore-test-support/tests/derived-path\.hh'' - ''^src/libstore-test-support/tests/nix_api_store\.hh'' - ''^src/libstore-test-support/tests/outputs-spec\.cc'' - ''^src/libstore-test-support/tests/outputs-spec\.hh'' - ''^src/libstore-test-support/tests/path\.cc'' - ''^src/libstore-test-support/tests/path\.hh'' - ''^src/libstore-test-support/tests/protocol\.hh'' - ''^src/libstore-test/common-protocol\.cc'' - ''^src/libstore-test/content-address\.cc'' - ''^src/libstore-test/derivation\.cc'' - ''^src/libstore-test/derived-path\.cc'' - ''^src/libstore-test/downstream-placeholder\.cc'' - ''^src/libstore-test/machines\.cc'' - ''^src/libstore-test/nar-info-disk-cache\.cc'' - ''^src/libstore-test/nar-info\.cc'' - ''^src/libstore-test/outputs-spec\.cc'' - ''^src/libstore-test/path-info\.cc'' - ''^src/libstore-test/path\.cc'' - ''^src/libstore-test/serve-protocol\.cc'' - ''^src/libstore-test/worker-protocol\.cc'' - ''^src/libutil-test-support/tests/characterization\.hh'' - ''^src/libutil-test-support/tests/hash\.cc'' - ''^src/libutil-test-support/tests/hash\.hh'' - ''^src/libutil-test/args\.cc'' - ''^src/libutil-test/canon-path\.cc'' - ''^src/libutil-test/chunked-vector\.cc'' - ''^src/libutil-test/closure\.cc'' - ''^src/libutil-test/compression\.cc'' - ''^src/libutil-test/config\.cc'' - ''^src/libutil-test/file-content-address\.cc'' - ''^src/libutil-test/git\.cc'' - ''^src/libutil-test/hash\.cc'' - ''^src/libutil-test/hilite\.cc'' - ''^src/libutil-test/json-utils\.cc'' - ''^src/libutil-test/logging\.cc'' - ''^src/libutil-test/lru-cache\.cc'' - ''^src/libutil-test/pool\.cc'' - ''^src/libutil-test/references\.cc'' - ''^src/libutil-test/suggestions\.cc'' - ''^src/libutil-test/tests\.cc'' - ''^src/libutil-test/url\.cc'' - ''^src/libutil-test/xml-writer\.cc'' + ''^src/nix-expr-test-support/tests/libexpr\.hh'' + ''^src/nix-expr-test-support/tests/value/context\.cc'' + ''^src/nix-expr-test-support/tests/value/context\.hh'' + ''^src/nix-expr-tests/derived-path\.cc'' + ''^src/nix-expr-tests/error_traces\.cc'' + ''^src/nix-expr-tests/eval\.cc'' + ''^src/nix-expr-tests/json\.cc'' + ''^src/nix-expr-tests/main\.cc'' + ''^src/nix-expr-tests/primops\.cc'' + ''^src/nix-expr-tests/search-path\.cc'' + ''^src/nix-expr-tests/trivial\.cc'' + ''^src/nix-expr-tests/value/context\.cc'' + ''^src/nix-expr-tests/value/print\.cc'' + ''^src/nix-fetchers-tests/public-key\.cc'' + ''^src/nix-flake-tests/flakeref\.cc'' + ''^src/nix-flake-tests/url-name\.cc'' + ''^src/nix-store-test-support/tests/derived-path\.cc'' + ''^src/nix-store-test-support/tests/derived-path\.hh'' + ''^src/nix-store-test-support/tests/nix_api_store\.hh'' + ''^src/nix-store-test-support/tests/outputs-spec\.cc'' + ''^src/nix-store-test-support/tests/outputs-spec\.hh'' + ''^src/nix-store-test-support/tests/path\.cc'' + ''^src/nix-store-test-support/tests/path\.hh'' + ''^src/nix-store-test-support/tests/protocol\.hh'' + ''^src/nix-store-tests/common-protocol\.cc'' + ''^src/nix-store-tests/content-address\.cc'' + ''^src/nix-store-tests/derivation\.cc'' + ''^src/nix-store-tests/derived-path\.cc'' + ''^src/nix-store-tests/downstream-placeholder\.cc'' + ''^src/nix-store-tests/machines\.cc'' + ''^src/nix-store-tests/nar-info-disk-cache\.cc'' + ''^src/nix-store-tests/nar-info\.cc'' + ''^src/nix-store-tests/outputs-spec\.cc'' + ''^src/nix-store-tests/path-info\.cc'' + ''^src/nix-store-tests/path\.cc'' + ''^src/nix-store-tests/serve-protocol\.cc'' + ''^src/nix-store-tests/worker-protocol\.cc'' + ''^src/nix-util-test-support/tests/characterization\.hh'' + ''^src/nix-util-test-support/tests/hash\.cc'' + ''^src/nix-util-test-support/tests/hash\.hh'' + ''^src/nix-util-tests/args\.cc'' + ''^src/nix-util-tests/canon-path\.cc'' + ''^src/nix-util-tests/chunked-vector\.cc'' + ''^src/nix-util-tests/closure\.cc'' + ''^src/nix-util-tests/compression\.cc'' + ''^src/nix-util-tests/config\.cc'' + ''^src/nix-util-tests/file-content-address\.cc'' + ''^src/nix-util-tests/git\.cc'' + ''^src/nix-util-tests/hash\.cc'' + ''^src/nix-util-tests/hilite\.cc'' + ''^src/nix-util-tests/json-utils\.cc'' + ''^src/nix-util-tests/logging\.cc'' + ''^src/nix-util-tests/lru-cache\.cc'' + ''^src/nix-util-tests/pool\.cc'' + ''^src/nix-util-tests/references\.cc'' + ''^src/nix-util-tests/suggestions\.cc'' + ''^src/nix-util-tests/tests\.cc'' + ''^src/nix-util-tests/url\.cc'' + ''^src/nix-util-tests/xml-writer\.cc'' ]; }; shellcheck = { @@ -666,7 +666,7 @@ ''^tests/functional/user-envs\.sh$'' ''^tests/functional/why-depends\.sh$'' ''^tests/functional/zstd\.sh$'' - ''^src/libutil-test/data/git/check-data\.sh$'' + ''^src/nix-util-tests/data/git/check-data\.sh$'' ]; }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 diff --git a/meson.build b/meson.build index fb38d7ef2..1690bb50a 100644 --- a/meson.build +++ b/meson.build @@ -25,11 +25,11 @@ subproject('libexpr-c') subproject('perl') # Testing -subproject('libutil-test-support') -subproject('libutil-test') -subproject('libstore-test-support') -subproject('libstore-test') -subproject('libfetchers-test') -subproject('libexpr-test-support') -subproject('libexpr-test') -subproject('libflake-test') +subproject('nix-util-test-support') +subproject('nix-util-tests') +subproject('nix-store-test-support') +subproject('nix-store-tests') +subproject('nix-fetchers-tests') +subproject('nix-expr-test-support') +subproject('nix-expr-tests') +subproject('nix-flake-tests') diff --git a/packaging/components.nix b/packaging/components.nix index 73f0d24e1..db50d6b22 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -9,24 +9,24 @@ in nix-util = callPackage ../src/libutil/package.nix { }; nix-util-c = callPackage ../src/libutil-c/package.nix { }; - nix-util-test-support = callPackage ../src/libutil-test-support/package.nix { }; - nix-util-test = callPackage ../src/libutil-test/package.nix { }; + nix-util-test-support = callPackage ../src/nix-util-test-support/package.nix { }; + nix-util-tests = callPackage ../src/nix-util-tests/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; nix-store-c = callPackage ../src/libstore-c/package.nix { }; - nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; - nix-store-test = callPackage ../src/libstore-test/package.nix { }; + nix-store-test-support = callPackage ../src/nix-store-test-support/package.nix { }; + nix-store-tests = callPackage ../src/nix-store-tests/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; + nix-fetchers-tests = callPackage ../src/nix-fetchers-tests/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; - nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; + nix-expr-test-support = callPackage ../src/nix-expr-test-support/package.nix { }; + nix-expr-tests = callPackage ../src/nix-expr-tests/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; - nix-flake-test = callPackage ../src/libflake-test/package.nix { }; + nix-flake-tests = callPackage ../src/nix-flake-tests/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 244a4ad3f..97f2c59b7 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -38,19 +38,19 @@ let "nix-util" "nix-util-c" "nix-util-test-support" - "nix-util-test" + "nix-util-tests" "nix-store" "nix-store-c" "nix-store-test-support" - "nix-store-test" + "nix-store-tests" "nix-fetchers" - "nix-fetchers-test" + "nix-fetchers-tests" "nix-expr" "nix-expr-c" "nix-expr-test-support" - "nix-expr-test" + "nix-expr-tests" "nix-flake" - "nix-flake-test" + "nix-flake-tests" ]; in { diff --git a/src/internal-api-docs/doxygen.cfg.in b/src/internal-api-docs/doxygen.cfg.in index 395e43fe1..f1ef75b38 100644 --- a/src/internal-api-docs/doxygen.cfg.in +++ b/src/internal-api-docs/doxygen.cfg.in @@ -41,21 +41,21 @@ INPUT = \ @src@/libcmd \ @src@/libexpr \ @src@/libexpr/flake \ - @src@/libexpr-test \ - @src@/libexpr-test/value \ - @src@/libexpr-test-support/test \ - @src@/libexpr-test-support/test/value \ + @src@/nix-expr-tests \ + @src@/nix-expr-tests/value \ + @src@/nix-expr-test-support/test \ + @src@/nix-expr-test-support/test/value \ @src@/libexpr/value \ @src@/libfetchers \ @src@/libmain \ @src@/libstore \ @src@/libstore/build \ @src@/libstore/builtins \ - @src@/libstore-test \ - @src@/libstore-test-support/test \ + @src@/nix-store-tests \ + @src@/nix-store-test-support/test \ @src@/libutil \ - @src@/libutil-test \ - @src@/libutil-test-support/test \ + @src@/nix-util-tests \ + @src@/nix-util-test-support/test \ @src@/nix \ @src@/nix-env \ @src@/nix-store diff --git a/src/libexpr-test-support/.version b/src/nix-expr-test-support/.version similarity index 100% rename from src/libexpr-test-support/.version rename to src/nix-expr-test-support/.version diff --git a/src/libexpr-test-support/build-utils-meson b/src/nix-expr-test-support/build-utils-meson similarity index 100% rename from src/libexpr-test-support/build-utils-meson rename to src/nix-expr-test-support/build-utils-meson diff --git a/src/libexpr-test-support/meson.build b/src/nix-expr-test-support/meson.build similarity index 100% rename from src/libexpr-test-support/meson.build rename to src/nix-expr-test-support/meson.build diff --git a/src/libexpr-test-support/package.nix b/src/nix-expr-test-support/package.nix similarity index 100% rename from src/libexpr-test-support/package.nix rename to src/nix-expr-test-support/package.nix diff --git a/src/libexpr-test-support/tests/libexpr.hh b/src/nix-expr-test-support/tests/libexpr.hh similarity index 100% rename from src/libexpr-test-support/tests/libexpr.hh rename to src/nix-expr-test-support/tests/libexpr.hh diff --git a/src/libexpr-test-support/tests/nix_api_expr.hh b/src/nix-expr-test-support/tests/nix_api_expr.hh similarity index 100% rename from src/libexpr-test-support/tests/nix_api_expr.hh rename to src/nix-expr-test-support/tests/nix_api_expr.hh diff --git a/src/libexpr-test-support/tests/value/context.cc b/src/nix-expr-test-support/tests/value/context.cc similarity index 100% rename from src/libexpr-test-support/tests/value/context.cc rename to src/nix-expr-test-support/tests/value/context.cc diff --git a/src/libexpr-test-support/tests/value/context.hh b/src/nix-expr-test-support/tests/value/context.hh similarity index 100% rename from src/libexpr-test-support/tests/value/context.hh rename to src/nix-expr-test-support/tests/value/context.hh diff --git a/src/libexpr-test/.version b/src/nix-expr-tests/.version similarity index 100% rename from src/libexpr-test/.version rename to src/nix-expr-tests/.version diff --git a/src/libexpr-test/build-utils-meson b/src/nix-expr-tests/build-utils-meson similarity index 100% rename from src/libexpr-test/build-utils-meson rename to src/nix-expr-tests/build-utils-meson diff --git a/src/libexpr-test/data/.gitkeep b/src/nix-expr-tests/data/.gitkeep similarity index 100% rename from src/libexpr-test/data/.gitkeep rename to src/nix-expr-tests/data/.gitkeep diff --git a/src/libexpr-test/derived-path.cc b/src/nix-expr-tests/derived-path.cc similarity index 100% rename from src/libexpr-test/derived-path.cc rename to src/nix-expr-tests/derived-path.cc diff --git a/src/libexpr-test/error_traces.cc b/src/nix-expr-tests/error_traces.cc similarity index 100% rename from src/libexpr-test/error_traces.cc rename to src/nix-expr-tests/error_traces.cc diff --git a/src/libexpr-test/eval.cc b/src/nix-expr-tests/eval.cc similarity index 100% rename from src/libexpr-test/eval.cc rename to src/nix-expr-tests/eval.cc diff --git a/src/libexpr-test/json.cc b/src/nix-expr-tests/json.cc similarity index 100% rename from src/libexpr-test/json.cc rename to src/nix-expr-tests/json.cc diff --git a/src/libexpr-test/main.cc b/src/nix-expr-tests/main.cc similarity index 100% rename from src/libexpr-test/main.cc rename to src/nix-expr-tests/main.cc diff --git a/src/libexpr-test/meson.build b/src/nix-expr-tests/meson.build similarity index 98% rename from src/libexpr-test/meson.build rename to src/nix-expr-tests/meson.build index 04b60f6d6..04b5ae66f 100644 --- a/src/libexpr-test/meson.build +++ b/src/nix-expr-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-expr-test', 'cpp', +project('nix-expr-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libexpr-test/nix_api_expr.cc b/src/nix-expr-tests/nix_api_expr.cc similarity index 100% rename from src/libexpr-test/nix_api_expr.cc rename to src/nix-expr-tests/nix_api_expr.cc diff --git a/src/libexpr-test/nix_api_external.cc b/src/nix-expr-tests/nix_api_external.cc similarity index 100% rename from src/libexpr-test/nix_api_external.cc rename to src/nix-expr-tests/nix_api_external.cc diff --git a/src/libexpr-test/nix_api_value.cc b/src/nix-expr-tests/nix_api_value.cc similarity index 100% rename from src/libexpr-test/nix_api_value.cc rename to src/nix-expr-tests/nix_api_value.cc diff --git a/src/libexpr-test/package.nix b/src/nix-expr-tests/package.nix similarity index 97% rename from src/libexpr-test/package.nix rename to src/nix-expr-tests/package.nix index 12f4dd506..679b6fb2a 100644 --- a/src/libexpr-test/package.nix +++ b/src/nix-expr-tests/package.nix @@ -39,7 +39,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-expr-test"; + pname = "nix-expr-tests"; inherit version; src = fileset.toSource { @@ -98,7 +98,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-expr-test + nix-expr-tests touch $out ''; }; diff --git a/src/libexpr-test/primops.cc b/src/nix-expr-tests/primops.cc similarity index 100% rename from src/libexpr-test/primops.cc rename to src/nix-expr-tests/primops.cc diff --git a/src/libexpr-test/search-path.cc b/src/nix-expr-tests/search-path.cc similarity index 100% rename from src/libexpr-test/search-path.cc rename to src/nix-expr-tests/search-path.cc diff --git a/src/libexpr-test/trivial.cc b/src/nix-expr-tests/trivial.cc similarity index 100% rename from src/libexpr-test/trivial.cc rename to src/nix-expr-tests/trivial.cc diff --git a/src/libexpr-test/value/context.cc b/src/nix-expr-tests/value/context.cc similarity index 100% rename from src/libexpr-test/value/context.cc rename to src/nix-expr-tests/value/context.cc diff --git a/src/libexpr-test/value/print.cc b/src/nix-expr-tests/value/print.cc similarity index 100% rename from src/libexpr-test/value/print.cc rename to src/nix-expr-tests/value/print.cc diff --git a/src/libexpr-test/value/value.cc b/src/nix-expr-tests/value/value.cc similarity index 100% rename from src/libexpr-test/value/value.cc rename to src/nix-expr-tests/value/value.cc diff --git a/src/libfetchers-test/.version b/src/nix-fetchers-tests/.version similarity index 100% rename from src/libfetchers-test/.version rename to src/nix-fetchers-tests/.version diff --git a/src/libfetchers-test/build-utils-meson b/src/nix-fetchers-tests/build-utils-meson similarity index 100% rename from src/libfetchers-test/build-utils-meson rename to src/nix-fetchers-tests/build-utils-meson diff --git a/src/libfetchers-test/data/public-key/defaultType.json b/src/nix-fetchers-tests/data/public-key/defaultType.json similarity index 100% rename from src/libfetchers-test/data/public-key/defaultType.json rename to src/nix-fetchers-tests/data/public-key/defaultType.json diff --git a/src/libfetchers-test/data/public-key/noRoundTrip.json b/src/nix-fetchers-tests/data/public-key/noRoundTrip.json similarity index 100% rename from src/libfetchers-test/data/public-key/noRoundTrip.json rename to src/nix-fetchers-tests/data/public-key/noRoundTrip.json diff --git a/src/libfetchers-test/data/public-key/simple.json b/src/nix-fetchers-tests/data/public-key/simple.json similarity index 100% rename from src/libfetchers-test/data/public-key/simple.json rename to src/nix-fetchers-tests/data/public-key/simple.json diff --git a/src/libfetchers-test/meson.build b/src/nix-fetchers-tests/meson.build similarity index 97% rename from src/libfetchers-test/meson.build rename to src/nix-fetchers-tests/meson.build index 785754b34..c4f18e278 100644 --- a/src/libfetchers-test/meson.build +++ b/src/nix-fetchers-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-fetchers-test', 'cpp', +project('nix-fetchers-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libfetchers-test/package.nix b/src/nix-fetchers-tests/package.nix similarity index 97% rename from src/libfetchers-test/package.nix rename to src/nix-fetchers-tests/package.nix index 78d8ab490..5cf18ce33 100644 --- a/src/libfetchers-test/package.nix +++ b/src/nix-fetchers-tests/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-fetchers-test"; + pname = "nix-fetchers-tests"; inherit version; src = fileset.toSource { @@ -96,7 +96,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-fetchers-test + nix-fetchers-tests touch $out ''; }; diff --git a/src/libfetchers-test/public-key.cc b/src/nix-fetchers-tests/public-key.cc similarity index 100% rename from src/libfetchers-test/public-key.cc rename to src/nix-fetchers-tests/public-key.cc diff --git a/src/libflake-test/.version b/src/nix-flake-tests/.version similarity index 100% rename from src/libflake-test/.version rename to src/nix-flake-tests/.version diff --git a/src/libflake-test/build-utils-meson b/src/nix-flake-tests/build-utils-meson similarity index 100% rename from src/libflake-test/build-utils-meson rename to src/nix-flake-tests/build-utils-meson diff --git a/src/libflake-test/data/.gitkeep b/src/nix-flake-tests/data/.gitkeep similarity index 100% rename from src/libflake-test/data/.gitkeep rename to src/nix-flake-tests/data/.gitkeep diff --git a/src/libflake-test/flakeref.cc b/src/nix-flake-tests/flakeref.cc similarity index 100% rename from src/libflake-test/flakeref.cc rename to src/nix-flake-tests/flakeref.cc diff --git a/src/libflake-test/meson.build b/src/nix-flake-tests/meson.build similarity index 97% rename from src/libflake-test/meson.build rename to src/nix-flake-tests/meson.build index b8221b2ad..5afba2fec 100644 --- a/src/libflake-test/meson.build +++ b/src/nix-flake-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-flake-test', 'cpp', +project('nix-flake-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libflake-test/package.nix b/src/nix-flake-tests/package.nix similarity index 97% rename from src/libflake-test/package.nix rename to src/nix-flake-tests/package.nix index 4fb190706..21af753ae 100644 --- a/src/libflake-test/package.nix +++ b/src/nix-flake-tests/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-flake-test"; + pname = "nix-flake-tests"; inherit version; src = fileset.toSource { @@ -96,7 +96,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-flake-test + nix-flake-tests touch $out ''; }; diff --git a/src/libflake-test/url-name.cc b/src/nix-flake-tests/url-name.cc similarity index 100% rename from src/libflake-test/url-name.cc rename to src/nix-flake-tests/url-name.cc diff --git a/src/libstore-test-support/.version b/src/nix-store-test-support/.version similarity index 100% rename from src/libstore-test-support/.version rename to src/nix-store-test-support/.version diff --git a/src/libstore-test-support/build-utils-meson b/src/nix-store-test-support/build-utils-meson similarity index 100% rename from src/libstore-test-support/build-utils-meson rename to src/nix-store-test-support/build-utils-meson diff --git a/src/libstore-test-support/meson.build b/src/nix-store-test-support/meson.build similarity index 100% rename from src/libstore-test-support/meson.build rename to src/nix-store-test-support/meson.build diff --git a/src/libstore-test-support/package.nix b/src/nix-store-test-support/package.nix similarity index 100% rename from src/libstore-test-support/package.nix rename to src/nix-store-test-support/package.nix diff --git a/src/libstore-test-support/tests/derived-path.cc b/src/nix-store-test-support/tests/derived-path.cc similarity index 100% rename from src/libstore-test-support/tests/derived-path.cc rename to src/nix-store-test-support/tests/derived-path.cc diff --git a/src/libstore-test-support/tests/derived-path.hh b/src/nix-store-test-support/tests/derived-path.hh similarity index 100% rename from src/libstore-test-support/tests/derived-path.hh rename to src/nix-store-test-support/tests/derived-path.hh diff --git a/src/libstore-test-support/tests/libstore.hh b/src/nix-store-test-support/tests/libstore.hh similarity index 100% rename from src/libstore-test-support/tests/libstore.hh rename to src/nix-store-test-support/tests/libstore.hh diff --git a/src/libstore-test-support/tests/nix_api_store.hh b/src/nix-store-test-support/tests/nix_api_store.hh similarity index 100% rename from src/libstore-test-support/tests/nix_api_store.hh rename to src/nix-store-test-support/tests/nix_api_store.hh diff --git a/src/libstore-test-support/tests/outputs-spec.cc b/src/nix-store-test-support/tests/outputs-spec.cc similarity index 100% rename from src/libstore-test-support/tests/outputs-spec.cc rename to src/nix-store-test-support/tests/outputs-spec.cc diff --git a/src/libstore-test-support/tests/outputs-spec.hh b/src/nix-store-test-support/tests/outputs-spec.hh similarity index 100% rename from src/libstore-test-support/tests/outputs-spec.hh rename to src/nix-store-test-support/tests/outputs-spec.hh diff --git a/src/libstore-test-support/tests/path.cc b/src/nix-store-test-support/tests/path.cc similarity index 100% rename from src/libstore-test-support/tests/path.cc rename to src/nix-store-test-support/tests/path.cc diff --git a/src/libstore-test-support/tests/path.hh b/src/nix-store-test-support/tests/path.hh similarity index 100% rename from src/libstore-test-support/tests/path.hh rename to src/nix-store-test-support/tests/path.hh diff --git a/src/libstore-test-support/tests/protocol.hh b/src/nix-store-test-support/tests/protocol.hh similarity index 100% rename from src/libstore-test-support/tests/protocol.hh rename to src/nix-store-test-support/tests/protocol.hh diff --git a/src/libstore-test/.version b/src/nix-store-tests/.version similarity index 100% rename from src/libstore-test/.version rename to src/nix-store-tests/.version diff --git a/src/libstore-test/build-utils-meson b/src/nix-store-tests/build-utils-meson similarity index 100% rename from src/libstore-test/build-utils-meson rename to src/nix-store-tests/build-utils-meson diff --git a/src/libstore-test/common-protocol.cc b/src/nix-store-tests/common-protocol.cc similarity index 100% rename from src/libstore-test/common-protocol.cc rename to src/nix-store-tests/common-protocol.cc diff --git a/src/libstore-test/content-address.cc b/src/nix-store-tests/content-address.cc similarity index 100% rename from src/libstore-test/content-address.cc rename to src/nix-store-tests/content-address.cc diff --git a/src/libstore-test/data/common-protocol/content-address.bin b/src/nix-store-tests/data/common-protocol/content-address.bin similarity index 100% rename from src/libstore-test/data/common-protocol/content-address.bin rename to src/nix-store-tests/data/common-protocol/content-address.bin diff --git a/src/libstore-test/data/common-protocol/drv-output.bin b/src/nix-store-tests/data/common-protocol/drv-output.bin similarity index 100% rename from src/libstore-test/data/common-protocol/drv-output.bin rename to src/nix-store-tests/data/common-protocol/drv-output.bin diff --git a/src/libstore-test/data/common-protocol/optional-content-address.bin b/src/nix-store-tests/data/common-protocol/optional-content-address.bin similarity index 100% rename from src/libstore-test/data/common-protocol/optional-content-address.bin rename to src/nix-store-tests/data/common-protocol/optional-content-address.bin diff --git a/src/libstore-test/data/common-protocol/optional-store-path.bin b/src/nix-store-tests/data/common-protocol/optional-store-path.bin similarity index 100% rename from src/libstore-test/data/common-protocol/optional-store-path.bin rename to src/nix-store-tests/data/common-protocol/optional-store-path.bin diff --git a/src/libstore-test/data/common-protocol/realisation.bin b/src/nix-store-tests/data/common-protocol/realisation.bin similarity index 100% rename from src/libstore-test/data/common-protocol/realisation.bin rename to src/nix-store-tests/data/common-protocol/realisation.bin diff --git a/src/libstore-test/data/common-protocol/set.bin b/src/nix-store-tests/data/common-protocol/set.bin similarity index 100% rename from src/libstore-test/data/common-protocol/set.bin rename to src/nix-store-tests/data/common-protocol/set.bin diff --git a/src/libstore-test/data/common-protocol/store-path.bin b/src/nix-store-tests/data/common-protocol/store-path.bin similarity index 100% rename from src/libstore-test/data/common-protocol/store-path.bin rename to src/nix-store-tests/data/common-protocol/store-path.bin diff --git a/src/libstore-test/data/common-protocol/string.bin b/src/nix-store-tests/data/common-protocol/string.bin similarity index 100% rename from src/libstore-test/data/common-protocol/string.bin rename to src/nix-store-tests/data/common-protocol/string.bin diff --git a/src/libstore-test/data/common-protocol/vector.bin b/src/nix-store-tests/data/common-protocol/vector.bin similarity index 100% rename from src/libstore-test/data/common-protocol/vector.bin rename to src/nix-store-tests/data/common-protocol/vector.bin diff --git a/src/libstore-test/data/derivation/advanced-attributes-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-defaults.drv rename to src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv diff --git a/src/libstore-test/data/derivation/advanced-attributes-defaults.json b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.json similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-defaults.json rename to src/nix-store-tests/data/derivation/advanced-attributes-defaults.json diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json diff --git a/src/libstore-test/data/derivation/advanced-attributes.drv b/src/nix-store-tests/data/derivation/advanced-attributes.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes.drv rename to src/nix-store-tests/data/derivation/advanced-attributes.drv diff --git a/src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv b/src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv similarity index 100% rename from src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv rename to src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv diff --git a/src/libstore-test/data/derivation/bad-version.drv b/src/nix-store-tests/data/derivation/bad-version.drv similarity index 100% rename from src/libstore-test/data/derivation/bad-version.drv rename to src/nix-store-tests/data/derivation/bad-version.drv diff --git a/src/libstore-test/data/derivation/dynDerivationDeps.drv b/src/nix-store-tests/data/derivation/dynDerivationDeps.drv similarity index 100% rename from src/libstore-test/data/derivation/dynDerivationDeps.drv rename to src/nix-store-tests/data/derivation/dynDerivationDeps.drv diff --git a/src/libstore-test/data/derivation/dynDerivationDeps.json b/src/nix-store-tests/data/derivation/dynDerivationDeps.json similarity index 100% rename from src/libstore-test/data/derivation/dynDerivationDeps.json rename to src/nix-store-tests/data/derivation/dynDerivationDeps.json diff --git a/src/libstore-test/data/derivation/output-caFixedFlat.json b/src/nix-store-tests/data/derivation/output-caFixedFlat.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFixedFlat.json rename to src/nix-store-tests/data/derivation/output-caFixedFlat.json diff --git a/src/libstore-test/data/derivation/output-caFixedNAR.json b/src/nix-store-tests/data/derivation/output-caFixedNAR.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFixedNAR.json rename to src/nix-store-tests/data/derivation/output-caFixedNAR.json diff --git a/src/libstore-test/data/derivation/output-caFixedText.json b/src/nix-store-tests/data/derivation/output-caFixedText.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFixedText.json rename to src/nix-store-tests/data/derivation/output-caFixedText.json diff --git a/src/libstore-test/data/derivation/output-caFloating.json b/src/nix-store-tests/data/derivation/output-caFloating.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFloating.json rename to src/nix-store-tests/data/derivation/output-caFloating.json diff --git a/src/libstore-test/data/derivation/output-deferred.json b/src/nix-store-tests/data/derivation/output-deferred.json similarity index 100% rename from src/libstore-test/data/derivation/output-deferred.json rename to src/nix-store-tests/data/derivation/output-deferred.json diff --git a/src/libstore-test/data/derivation/output-impure.json b/src/nix-store-tests/data/derivation/output-impure.json similarity index 100% rename from src/libstore-test/data/derivation/output-impure.json rename to src/nix-store-tests/data/derivation/output-impure.json diff --git a/src/libstore-test/data/derivation/output-inputAddressed.json b/src/nix-store-tests/data/derivation/output-inputAddressed.json similarity index 100% rename from src/libstore-test/data/derivation/output-inputAddressed.json rename to src/nix-store-tests/data/derivation/output-inputAddressed.json diff --git a/src/libstore-test/data/derivation/simple.drv b/src/nix-store-tests/data/derivation/simple.drv similarity index 100% rename from src/libstore-test/data/derivation/simple.drv rename to src/nix-store-tests/data/derivation/simple.drv diff --git a/src/libstore-test/data/derivation/simple.json b/src/nix-store-tests/data/derivation/simple.json similarity index 100% rename from src/libstore-test/data/derivation/simple.json rename to src/nix-store-tests/data/derivation/simple.json diff --git a/src/libstore-test/data/machines/bad_format b/src/nix-store-tests/data/machines/bad_format similarity index 100% rename from src/libstore-test/data/machines/bad_format rename to src/nix-store-tests/data/machines/bad_format diff --git a/src/libstore-test/data/machines/valid b/src/nix-store-tests/data/machines/valid similarity index 100% rename from src/libstore-test/data/machines/valid rename to src/nix-store-tests/data/machines/valid diff --git a/src/libstore-test/data/nar-info/impure.json b/src/nix-store-tests/data/nar-info/impure.json similarity index 100% rename from src/libstore-test/data/nar-info/impure.json rename to src/nix-store-tests/data/nar-info/impure.json diff --git a/src/libstore-test/data/nar-info/pure.json b/src/nix-store-tests/data/nar-info/pure.json similarity index 100% rename from src/libstore-test/data/nar-info/pure.json rename to src/nix-store-tests/data/nar-info/pure.json diff --git a/src/libstore-test/data/path-info/empty_impure.json b/src/nix-store-tests/data/path-info/empty_impure.json similarity index 100% rename from src/libstore-test/data/path-info/empty_impure.json rename to src/nix-store-tests/data/path-info/empty_impure.json diff --git a/src/libstore-test/data/path-info/empty_pure.json b/src/nix-store-tests/data/path-info/empty_pure.json similarity index 100% rename from src/libstore-test/data/path-info/empty_pure.json rename to src/nix-store-tests/data/path-info/empty_pure.json diff --git a/src/libstore-test/data/path-info/impure.json b/src/nix-store-tests/data/path-info/impure.json similarity index 100% rename from src/libstore-test/data/path-info/impure.json rename to src/nix-store-tests/data/path-info/impure.json diff --git a/src/libstore-test/data/path-info/pure.json b/src/nix-store-tests/data/path-info/pure.json similarity index 100% rename from src/libstore-test/data/path-info/pure.json rename to src/nix-store-tests/data/path-info/pure.json diff --git a/src/libstore-test/data/serve-protocol/build-options-2.1.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.1.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.1.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.1.bin diff --git a/src/libstore-test/data/serve-protocol/build-options-2.2.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.2.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.2.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.2.bin diff --git a/src/libstore-test/data/serve-protocol/build-options-2.3.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.3.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.3.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.3.bin diff --git a/src/libstore-test/data/serve-protocol/build-options-2.7.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.7.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.7.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.7.bin diff --git a/src/libstore-test/data/serve-protocol/build-result-2.2.bin b/src/nix-store-tests/data/serve-protocol/build-result-2.2.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-result-2.2.bin rename to src/nix-store-tests/data/serve-protocol/build-result-2.2.bin diff --git a/src/libstore-test/data/serve-protocol/build-result-2.3.bin b/src/nix-store-tests/data/serve-protocol/build-result-2.3.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-result-2.3.bin rename to src/nix-store-tests/data/serve-protocol/build-result-2.3.bin diff --git a/src/libstore-test/data/serve-protocol/build-result-2.6.bin b/src/nix-store-tests/data/serve-protocol/build-result-2.6.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-result-2.6.bin rename to src/nix-store-tests/data/serve-protocol/build-result-2.6.bin diff --git a/src/libstore-test/data/serve-protocol/content-address.bin b/src/nix-store-tests/data/serve-protocol/content-address.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/content-address.bin rename to src/nix-store-tests/data/serve-protocol/content-address.bin diff --git a/src/libstore-test/data/serve-protocol/drv-output.bin b/src/nix-store-tests/data/serve-protocol/drv-output.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/drv-output.bin rename to src/nix-store-tests/data/serve-protocol/drv-output.bin diff --git a/src/libstore-test/data/serve-protocol/handshake-to-client.bin b/src/nix-store-tests/data/serve-protocol/handshake-to-client.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/handshake-to-client.bin rename to src/nix-store-tests/data/serve-protocol/handshake-to-client.bin diff --git a/src/libstore-test/data/serve-protocol/optional-content-address.bin b/src/nix-store-tests/data/serve-protocol/optional-content-address.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/optional-content-address.bin rename to src/nix-store-tests/data/serve-protocol/optional-content-address.bin diff --git a/src/libstore-test/data/serve-protocol/optional-store-path.bin b/src/nix-store-tests/data/serve-protocol/optional-store-path.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/optional-store-path.bin rename to src/nix-store-tests/data/serve-protocol/optional-store-path.bin diff --git a/src/libstore-test/data/serve-protocol/realisation.bin b/src/nix-store-tests/data/serve-protocol/realisation.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/realisation.bin rename to src/nix-store-tests/data/serve-protocol/realisation.bin diff --git a/src/libstore-test/data/serve-protocol/set.bin b/src/nix-store-tests/data/serve-protocol/set.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/set.bin rename to src/nix-store-tests/data/serve-protocol/set.bin diff --git a/src/libstore-test/data/serve-protocol/store-path.bin b/src/nix-store-tests/data/serve-protocol/store-path.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/store-path.bin rename to src/nix-store-tests/data/serve-protocol/store-path.bin diff --git a/src/libstore-test/data/serve-protocol/string.bin b/src/nix-store-tests/data/serve-protocol/string.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/string.bin rename to src/nix-store-tests/data/serve-protocol/string.bin diff --git a/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin b/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin rename to src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin diff --git a/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin b/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin rename to src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin diff --git a/src/libstore-test/data/serve-protocol/vector.bin b/src/nix-store-tests/data/serve-protocol/vector.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/vector.bin rename to src/nix-store-tests/data/serve-protocol/vector.bin diff --git a/src/libstore-test/data/store-reference/auto.txt b/src/nix-store-tests/data/store-reference/auto.txt similarity index 100% rename from src/libstore-test/data/store-reference/auto.txt rename to src/nix-store-tests/data/store-reference/auto.txt diff --git a/src/libstore-test/data/store-reference/auto_param.txt b/src/nix-store-tests/data/store-reference/auto_param.txt similarity index 100% rename from src/libstore-test/data/store-reference/auto_param.txt rename to src/nix-store-tests/data/store-reference/auto_param.txt diff --git a/src/libstore-test/data/store-reference/local_1.txt b/src/nix-store-tests/data/store-reference/local_1.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_1.txt rename to src/nix-store-tests/data/store-reference/local_1.txt diff --git a/src/libstore-test/data/store-reference/local_2.txt b/src/nix-store-tests/data/store-reference/local_2.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_2.txt rename to src/nix-store-tests/data/store-reference/local_2.txt diff --git a/src/libstore-test/data/store-reference/local_shorthand_1.txt b/src/nix-store-tests/data/store-reference/local_shorthand_1.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_shorthand_1.txt rename to src/nix-store-tests/data/store-reference/local_shorthand_1.txt diff --git a/src/libstore-test/data/store-reference/local_shorthand_2.txt b/src/nix-store-tests/data/store-reference/local_shorthand_2.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_shorthand_2.txt rename to src/nix-store-tests/data/store-reference/local_shorthand_2.txt diff --git a/src/libstore-test/data/store-reference/ssh.txt b/src/nix-store-tests/data/store-reference/ssh.txt similarity index 100% rename from src/libstore-test/data/store-reference/ssh.txt rename to src/nix-store-tests/data/store-reference/ssh.txt diff --git a/src/libstore-test/data/store-reference/unix.txt b/src/nix-store-tests/data/store-reference/unix.txt similarity index 100% rename from src/libstore-test/data/store-reference/unix.txt rename to src/nix-store-tests/data/store-reference/unix.txt diff --git a/src/libstore-test/data/store-reference/unix_shorthand.txt b/src/nix-store-tests/data/store-reference/unix_shorthand.txt similarity index 100% rename from src/libstore-test/data/store-reference/unix_shorthand.txt rename to src/nix-store-tests/data/store-reference/unix_shorthand.txt diff --git a/src/libstore-test/data/worker-protocol/build-mode.bin b/src/nix-store-tests/data/worker-protocol/build-mode.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-mode.bin rename to src/nix-store-tests/data/worker-protocol/build-mode.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.27.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.27.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.27.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.27.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.28.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.28.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.28.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.28.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.29.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.29.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.29.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.29.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.37.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.37.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.37.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.37.bin diff --git a/src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin b/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin rename to src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin diff --git a/src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin b/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin rename to src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin diff --git a/src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin b/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin rename to src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin diff --git a/src/libstore-test/data/worker-protocol/content-address.bin b/src/nix-store-tests/data/worker-protocol/content-address.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/content-address.bin rename to src/nix-store-tests/data/worker-protocol/content-address.bin diff --git a/src/libstore-test/data/worker-protocol/derived-path-1.29.bin b/src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/derived-path-1.29.bin rename to src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin diff --git a/src/libstore-test/data/worker-protocol/derived-path-1.30.bin b/src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/derived-path-1.30.bin rename to src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin diff --git a/src/libstore-test/data/worker-protocol/drv-output.bin b/src/nix-store-tests/data/worker-protocol/drv-output.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/drv-output.bin rename to src/nix-store-tests/data/worker-protocol/drv-output.bin diff --git a/src/libstore-test/data/worker-protocol/handshake-to-client.bin b/src/nix-store-tests/data/worker-protocol/handshake-to-client.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/handshake-to-client.bin rename to src/nix-store-tests/data/worker-protocol/handshake-to-client.bin diff --git a/src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin b/src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin rename to src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin diff --git a/src/libstore-test/data/worker-protocol/optional-content-address.bin b/src/nix-store-tests/data/worker-protocol/optional-content-address.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/optional-content-address.bin rename to src/nix-store-tests/data/worker-protocol/optional-content-address.bin diff --git a/src/libstore-test/data/worker-protocol/optional-store-path.bin b/src/nix-store-tests/data/worker-protocol/optional-store-path.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/optional-store-path.bin rename to src/nix-store-tests/data/worker-protocol/optional-store-path.bin diff --git a/src/libstore-test/data/worker-protocol/optional-trusted-flag.bin b/src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/optional-trusted-flag.bin rename to src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin diff --git a/src/libstore-test/data/worker-protocol/realisation.bin b/src/nix-store-tests/data/worker-protocol/realisation.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/realisation.bin rename to src/nix-store-tests/data/worker-protocol/realisation.bin diff --git a/src/libstore-test/data/worker-protocol/set.bin b/src/nix-store-tests/data/worker-protocol/set.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/set.bin rename to src/nix-store-tests/data/worker-protocol/set.bin diff --git a/src/libstore-test/data/worker-protocol/store-path.bin b/src/nix-store-tests/data/worker-protocol/store-path.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/store-path.bin rename to src/nix-store-tests/data/worker-protocol/store-path.bin diff --git a/src/libstore-test/data/worker-protocol/string.bin b/src/nix-store-tests/data/worker-protocol/string.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/string.bin rename to src/nix-store-tests/data/worker-protocol/string.bin diff --git a/src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin b/src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin rename to src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin diff --git a/src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin b/src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin rename to src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin diff --git a/src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin b/src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin rename to src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin diff --git a/src/libstore-test/data/worker-protocol/vector.bin b/src/nix-store-tests/data/worker-protocol/vector.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/vector.bin rename to src/nix-store-tests/data/worker-protocol/vector.bin diff --git a/src/libstore-test/derivation-advanced-attrs.cc b/src/nix-store-tests/derivation-advanced-attrs.cc similarity index 100% rename from src/libstore-test/derivation-advanced-attrs.cc rename to src/nix-store-tests/derivation-advanced-attrs.cc diff --git a/src/libstore-test/derivation.cc b/src/nix-store-tests/derivation.cc similarity index 100% rename from src/libstore-test/derivation.cc rename to src/nix-store-tests/derivation.cc diff --git a/src/libstore-test/derived-path.cc b/src/nix-store-tests/derived-path.cc similarity index 100% rename from src/libstore-test/derived-path.cc rename to src/nix-store-tests/derived-path.cc diff --git a/src/libstore-test/downstream-placeholder.cc b/src/nix-store-tests/downstream-placeholder.cc similarity index 100% rename from src/libstore-test/downstream-placeholder.cc rename to src/nix-store-tests/downstream-placeholder.cc diff --git a/src/libstore-test/machines.cc b/src/nix-store-tests/machines.cc similarity index 100% rename from src/libstore-test/machines.cc rename to src/nix-store-tests/machines.cc diff --git a/src/libstore-test/meson.build b/src/nix-store-tests/meson.build similarity index 98% rename from src/libstore-test/meson.build rename to src/nix-store-tests/meson.build index 6bf0a5028..2fde70d3b 100644 --- a/src/libstore-test/meson.build +++ b/src/nix-store-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-store-test', 'cpp', +project('nix-store-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libstore-test/nar-info-disk-cache.cc b/src/nix-store-tests/nar-info-disk-cache.cc similarity index 100% rename from src/libstore-test/nar-info-disk-cache.cc rename to src/nix-store-tests/nar-info-disk-cache.cc diff --git a/src/libstore-test/nar-info.cc b/src/nix-store-tests/nar-info.cc similarity index 100% rename from src/libstore-test/nar-info.cc rename to src/nix-store-tests/nar-info.cc diff --git a/src/libstore-test/nix_api_store.cc b/src/nix-store-tests/nix_api_store.cc similarity index 100% rename from src/libstore-test/nix_api_store.cc rename to src/nix-store-tests/nix_api_store.cc diff --git a/src/libstore-test/outputs-spec.cc b/src/nix-store-tests/outputs-spec.cc similarity index 100% rename from src/libstore-test/outputs-spec.cc rename to src/nix-store-tests/outputs-spec.cc diff --git a/src/libstore-test/package.nix b/src/nix-store-tests/package.nix similarity index 98% rename from src/libstore-test/package.nix rename to src/nix-store-tests/package.nix index e37e64886..dc987b3c6 100644 --- a/src/libstore-test/package.nix +++ b/src/nix-store-tests/package.nix @@ -40,7 +40,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-store-test"; + pname = "nix-store-tests"; inherit version; src = fileset.toSource { @@ -105,7 +105,7 @@ mkDerivation (finalAttrs: { in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${data} - nix-store-test + nix-store-tests touch $out ''; }; diff --git a/src/libstore-test/path-info.cc b/src/nix-store-tests/path-info.cc similarity index 100% rename from src/libstore-test/path-info.cc rename to src/nix-store-tests/path-info.cc diff --git a/src/libstore-test/path.cc b/src/nix-store-tests/path.cc similarity index 100% rename from src/libstore-test/path.cc rename to src/nix-store-tests/path.cc diff --git a/src/libstore-test/references.cc b/src/nix-store-tests/references.cc similarity index 100% rename from src/libstore-test/references.cc rename to src/nix-store-tests/references.cc diff --git a/src/libstore-test/serve-protocol.cc b/src/nix-store-tests/serve-protocol.cc similarity index 100% rename from src/libstore-test/serve-protocol.cc rename to src/nix-store-tests/serve-protocol.cc diff --git a/src/libstore-test/store-reference.cc b/src/nix-store-tests/store-reference.cc similarity index 100% rename from src/libstore-test/store-reference.cc rename to src/nix-store-tests/store-reference.cc diff --git a/src/libstore-test/worker-protocol.cc b/src/nix-store-tests/worker-protocol.cc similarity index 100% rename from src/libstore-test/worker-protocol.cc rename to src/nix-store-tests/worker-protocol.cc diff --git a/src/libutil-test-support/.version b/src/nix-util-test-support/.version similarity index 100% rename from src/libutil-test-support/.version rename to src/nix-util-test-support/.version diff --git a/src/libutil-test-support/build-utils-meson b/src/nix-util-test-support/build-utils-meson similarity index 100% rename from src/libutil-test-support/build-utils-meson rename to src/nix-util-test-support/build-utils-meson diff --git a/src/libutil-test-support/meson.build b/src/nix-util-test-support/meson.build similarity index 100% rename from src/libutil-test-support/meson.build rename to src/nix-util-test-support/meson.build diff --git a/src/libutil-test-support/package.nix b/src/nix-util-test-support/package.nix similarity index 100% rename from src/libutil-test-support/package.nix rename to src/nix-util-test-support/package.nix diff --git a/src/libutil-test-support/tests/characterization.hh b/src/nix-util-test-support/tests/characterization.hh similarity index 100% rename from src/libutil-test-support/tests/characterization.hh rename to src/nix-util-test-support/tests/characterization.hh diff --git a/src/libutil-test-support/tests/hash.cc b/src/nix-util-test-support/tests/hash.cc similarity index 100% rename from src/libutil-test-support/tests/hash.cc rename to src/nix-util-test-support/tests/hash.cc diff --git a/src/libutil-test-support/tests/hash.hh b/src/nix-util-test-support/tests/hash.hh similarity index 100% rename from src/libutil-test-support/tests/hash.hh rename to src/nix-util-test-support/tests/hash.hh diff --git a/src/libutil-test-support/tests/nix_api_util.hh b/src/nix-util-test-support/tests/nix_api_util.hh similarity index 100% rename from src/libutil-test-support/tests/nix_api_util.hh rename to src/nix-util-test-support/tests/nix_api_util.hh diff --git a/src/libutil-test-support/tests/string_callback.cc b/src/nix-util-test-support/tests/string_callback.cc similarity index 100% rename from src/libutil-test-support/tests/string_callback.cc rename to src/nix-util-test-support/tests/string_callback.cc diff --git a/src/libutil-test-support/tests/string_callback.hh b/src/nix-util-test-support/tests/string_callback.hh similarity index 100% rename from src/libutil-test-support/tests/string_callback.hh rename to src/nix-util-test-support/tests/string_callback.hh diff --git a/src/libutil-test/.version b/src/nix-util-tests/.version similarity index 100% rename from src/libutil-test/.version rename to src/nix-util-tests/.version diff --git a/src/libutil-test/args.cc b/src/nix-util-tests/args.cc similarity index 100% rename from src/libutil-test/args.cc rename to src/nix-util-tests/args.cc diff --git a/src/libutil-test/build-utils-meson b/src/nix-util-tests/build-utils-meson similarity index 100% rename from src/libutil-test/build-utils-meson rename to src/nix-util-tests/build-utils-meson diff --git a/src/libutil-test/canon-path.cc b/src/nix-util-tests/canon-path.cc similarity index 100% rename from src/libutil-test/canon-path.cc rename to src/nix-util-tests/canon-path.cc diff --git a/src/libutil-test/chunked-vector.cc b/src/nix-util-tests/chunked-vector.cc similarity index 100% rename from src/libutil-test/chunked-vector.cc rename to src/nix-util-tests/chunked-vector.cc diff --git a/src/libutil-test/closure.cc b/src/nix-util-tests/closure.cc similarity index 100% rename from src/libutil-test/closure.cc rename to src/nix-util-tests/closure.cc diff --git a/src/libutil-test/compression.cc b/src/nix-util-tests/compression.cc similarity index 100% rename from src/libutil-test/compression.cc rename to src/nix-util-tests/compression.cc diff --git a/src/libutil-test/config.cc b/src/nix-util-tests/config.cc similarity index 100% rename from src/libutil-test/config.cc rename to src/nix-util-tests/config.cc diff --git a/src/libutil-test/data/git/check-data.sh b/src/nix-util-tests/data/git/check-data.sh similarity index 100% rename from src/libutil-test/data/git/check-data.sh rename to src/nix-util-tests/data/git/check-data.sh diff --git a/src/libutil-test/data/git/hello-world-blob.bin b/src/nix-util-tests/data/git/hello-world-blob.bin similarity index 100% rename from src/libutil-test/data/git/hello-world-blob.bin rename to src/nix-util-tests/data/git/hello-world-blob.bin diff --git a/src/libutil-test/data/git/hello-world.bin b/src/nix-util-tests/data/git/hello-world.bin similarity index 100% rename from src/libutil-test/data/git/hello-world.bin rename to src/nix-util-tests/data/git/hello-world.bin diff --git a/src/libutil-test/data/git/tree.bin b/src/nix-util-tests/data/git/tree.bin similarity index 100% rename from src/libutil-test/data/git/tree.bin rename to src/nix-util-tests/data/git/tree.bin diff --git a/src/libutil-test/data/git/tree.txt b/src/nix-util-tests/data/git/tree.txt similarity index 100% rename from src/libutil-test/data/git/tree.txt rename to src/nix-util-tests/data/git/tree.txt diff --git a/src/libutil-test/file-content-address.cc b/src/nix-util-tests/file-content-address.cc similarity index 100% rename from src/libutil-test/file-content-address.cc rename to src/nix-util-tests/file-content-address.cc diff --git a/src/libutil-test/git.cc b/src/nix-util-tests/git.cc similarity index 99% rename from src/libutil-test/git.cc rename to src/nix-util-tests/git.cc index 7c360d7c5..24d24a791 100644 --- a/src/libutil-test/git.cc +++ b/src/nix-util-tests/git.cc @@ -88,7 +88,7 @@ TEST_F(GitTest, blob_write) { /** * This data is for "shallow" tree tests. However, we use "real" hashes * so that we can check our test data in a small shell script test test - * (`src/libutil-test/data/git/check-data.sh`). + * (`src/nix-util-tests/data/git/check-data.sh`). */ const static Tree tree = { { diff --git a/src/libutil-test/hash.cc b/src/nix-util-tests/hash.cc similarity index 100% rename from src/libutil-test/hash.cc rename to src/nix-util-tests/hash.cc diff --git a/src/libutil-test/hilite.cc b/src/nix-util-tests/hilite.cc similarity index 100% rename from src/libutil-test/hilite.cc rename to src/nix-util-tests/hilite.cc diff --git a/src/libutil-test/json-utils.cc b/src/nix-util-tests/json-utils.cc similarity index 100% rename from src/libutil-test/json-utils.cc rename to src/nix-util-tests/json-utils.cc diff --git a/src/libutil-test/logging.cc b/src/nix-util-tests/logging.cc similarity index 100% rename from src/libutil-test/logging.cc rename to src/nix-util-tests/logging.cc diff --git a/src/libutil-test/lru-cache.cc b/src/nix-util-tests/lru-cache.cc similarity index 100% rename from src/libutil-test/lru-cache.cc rename to src/nix-util-tests/lru-cache.cc diff --git a/src/libutil-test/meson.build b/src/nix-util-tests/meson.build similarity index 98% rename from src/libutil-test/meson.build rename to src/nix-util-tests/meson.build index 19157cda3..67ae48f53 100644 --- a/src/libutil-test/meson.build +++ b/src/nix-util-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-util-test', 'cpp', +project('nix-util-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libutil-test/nix_api_util.cc b/src/nix-util-tests/nix_api_util.cc similarity index 100% rename from src/libutil-test/nix_api_util.cc rename to src/nix-util-tests/nix_api_util.cc diff --git a/src/libutil-test/package.nix b/src/nix-util-tests/package.nix similarity index 97% rename from src/libutil-test/package.nix rename to src/nix-util-tests/package.nix index 396e41f3d..9df8153b6 100644 --- a/src/libutil-test/package.nix +++ b/src/nix-util-tests/package.nix @@ -39,7 +39,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-util-test"; + pname = "nix-util-tests"; inherit version; src = fileset.toSource { @@ -98,7 +98,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-util-test + nix-util-tests touch $out ''; }; diff --git a/src/libutil-test/pool.cc b/src/nix-util-tests/pool.cc similarity index 100% rename from src/libutil-test/pool.cc rename to src/nix-util-tests/pool.cc diff --git a/src/libutil-test/references.cc b/src/nix-util-tests/references.cc similarity index 100% rename from src/libutil-test/references.cc rename to src/nix-util-tests/references.cc diff --git a/src/libutil-test/spawn.cc b/src/nix-util-tests/spawn.cc similarity index 100% rename from src/libutil-test/spawn.cc rename to src/nix-util-tests/spawn.cc diff --git a/src/libutil-test/suggestions.cc b/src/nix-util-tests/suggestions.cc similarity index 100% rename from src/libutil-test/suggestions.cc rename to src/nix-util-tests/suggestions.cc diff --git a/src/libutil-test/tests.cc b/src/nix-util-tests/tests.cc similarity index 100% rename from src/libutil-test/tests.cc rename to src/nix-util-tests/tests.cc diff --git a/src/libutil-test/url.cc b/src/nix-util-tests/url.cc similarity index 100% rename from src/libutil-test/url.cc rename to src/nix-util-tests/url.cc diff --git a/src/libutil-test/xml-writer.cc b/src/nix-util-tests/xml-writer.cc similarity index 100% rename from src/libutil-test/xml-writer.cc rename to src/nix-util-tests/xml-writer.cc From 224c6c32560665f5514a46ebcddde644604b5c87 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 10:39:36 -0400 Subject: [PATCH 462/910] Fix test symlinks --- .../data/derivation/advanced-attributes-defaults.drv | 2 +- .../advanced-attributes-structured-attrs-defaults.drv | 2 +- .../data/derivation/advanced-attributes-structured-attrs.drv | 2 +- src/nix-store-tests/data/derivation/advanced-attributes.drv | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv index 353090ad8..f8f30ac32 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv index 11713da12..837e9a0e4 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv index 962f8ea3f..e08bb5737 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes.drv b/src/nix-store-tests/data/derivation/advanced-attributes.drv index 2a53a05ca..1dc394a0a 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes.drv \ No newline at end of file From 11dab30be9917e169d6f18e8a46999a0d62dda71 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 10:59:05 -0400 Subject: [PATCH 463/910] Update docs on the unit tests --- doc/manual/src/contributing/testing.md | 23 ++++++++++++++++++++--- src/nix-expr-tests/meson.build | 9 ++++++++- src/nix-fetchers-tests/meson.build | 9 ++++++++- src/nix-flake-tests/meson.build | 9 ++++++++- src/nix-store-tests/meson.build | 9 ++++++++- src/nix-util-tests/meson.build | 9 ++++++++- 6 files changed, 60 insertions(+), 8 deletions(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 399174de5..a96ba997b 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -76,8 +76,25 @@ there is no risk of any build-system wildcards for the library accidentally pick ### Running tests -You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. -Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable, e.g. `GTEST_FILTER='ErrorTraceTest.*' make check`. +You can run the whole testsuite with `meson test` from the Meson build directory, or the tests for a specific component with `meson test nix-store-tests`. +A environment variables that Google Test accepts are also worth knowing: + +1. [`GTEST_FILTER`](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) + + This is used for finer-grained filtering of which tests to run. + + +2. [`GTEST_BRIEF`](https://google.github.io/googletest/advanced.html#suppressing-test-passes) + + This is used to avoid logging passing tests. + +Putting the two together, one might run + +```bash +GTEST_BREIF=1 GTEST_FILTER='ErrorTraceTest.*' meson test nix-expr-tests -v +``` + +for short but comprensive output. ### Characterisation testing { #characaterisation-testing-unit } @@ -86,7 +103,7 @@ See [functional characterisation testing](#characterisation-testing-functional) Like with the functional characterisation, `_NIX_TEST_ACCEPT=1` is also used. For example: ```shell-session -$ _NIX_TEST_ACCEPT=1 make libstore-tests_RUN +$ _NIX_TEST_ACCEPT=1 meson test nix-store-tests -v ... [ SKIPPED ] WorkerProtoTest.string_read [ SKIPPED ] WorkerProtoTest.string_write diff --git a/src/nix-expr-tests/meson.build b/src/nix-expr-tests/meson.build index 04b5ae66f..71865b59f 100644 --- a/src/nix-expr-tests/meson.build +++ b/src/nix-expr-tests/meson.build @@ -81,4 +81,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-fetchers-tests/meson.build b/src/nix-fetchers-tests/meson.build index c4f18e278..b4bc77a97 100644 --- a/src/nix-fetchers-tests/meson.build +++ b/src/nix-fetchers-tests/meson.build @@ -61,4 +61,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-flake-tests/meson.build b/src/nix-flake-tests/meson.build index 5afba2fec..2d6bbca0f 100644 --- a/src/nix-flake-tests/meson.build +++ b/src/nix-flake-tests/meson.build @@ -62,4 +62,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-store-tests/meson.build b/src/nix-store-tests/meson.build index 2fde70d3b..90e7d3047 100644 --- a/src/nix-store-tests/meson.build +++ b/src/nix-store-tests/meson.build @@ -85,4 +85,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-util-tests/meson.build b/src/nix-util-tests/meson.build index 67ae48f53..4f055cabd 100644 --- a/src/nix-util-tests/meson.build +++ b/src/nix-util-tests/meson.build @@ -81,4 +81,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) From 4727d5c3c5a0c04bdf07219a167a2818a9914bcd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 11:18:33 -0400 Subject: [PATCH 464/910] Fix format blacklist --- maintainers/flake-module.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index a39a70890..007ef034f 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^src/[^/]*-test/data/.*$'' + ''^src/[^/]*-tests/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' From 4d6bc61b8d3d0278e656bcfae61489abcf40c4a8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 14:36:46 -0400 Subject: [PATCH 465/910] Fix things --- src/libexpr-c/package.nix | 49 ++++++++------------- src/libexpr/package.nix | 55 +++++++++--------------- src/libfetchers/package.nix | 45 +++++++------------- src/libflake/package.nix | 45 +++++++------------- src/libstore-c/package.nix | 49 ++++++++------------- src/libstore/package.nix | 59 ++++++++++---------------- src/libutil-c/package.nix | 49 ++++++++------------- src/libutil/package.nix | 28 +++--------- src/nix-expr-test-support/package.nix | 47 +++++++------------- src/nix-expr-tests/package.nix | 47 +++++++------------- src/nix-fetchers-tests/package.nix | 47 +++++++------------- src/nix-flake-tests/package.nix | 47 +++++++------------- src/nix-store-test-support/package.nix | 47 +++++++------------- src/nix-store-tests/package.nix | 47 +++++++------------- src/nix-util-test-support/package.nix | 47 +++++++------------- src/nix-util-tests/package.nix | 47 +++++++------------- 16 files changed, 258 insertions(+), 497 deletions(-) diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 33412e218..81e42cf6a 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -12,41 +13,30 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-expr-c"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "h") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; outputs = [ "out" "dev" ]; @@ -65,8 +55,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -80,8 +70,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -89,8 +78,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 855d5057e..d4296bc07 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -21,10 +22,6 @@ , versionSuffix ? "" -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false - # Whether to use garbage collection for the Nix language evaluator. # # If it is disabled, we just leak memory, but this is not as bad as it @@ -41,34 +38,27 @@ let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-expr"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - ./primops/meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ./lexer.l - ./parser.y - (fileset.fileFilter (file: file.hasExt "nix") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + ./primops/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ./lexer.l + ./parser.y + (fileset.fileFilter (file: file.hasExt "nix") ./.) + ]; outputs = [ "out" "dev" ]; @@ -97,8 +87,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -118,8 +108,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -127,8 +116,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 681ffa112..7786a4f35 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -15,40 +16,28 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false - }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-fetchers"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +61,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { @@ -86,7 +75,7 @@ mkDerivation (finalAttrs: { # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated # to work with `strictDeps`. - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -94,8 +83,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*-tab.*" ]; - - hardeningDisable = ["fortify"]; }) diff --git a/src/libflake/package.nix b/src/libflake/package.nix index 523da4b78..f0609d5d5 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -17,40 +18,28 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false - }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-flake"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +61,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { @@ -86,7 +75,7 @@ mkDerivation (finalAttrs: { # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated # to work with `strictDeps`. - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -94,8 +83,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*-tab.*" ]; - - hardeningDisable = ["fortify"]; }) diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index d0e81b1f9..c14cf955d 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -12,41 +13,30 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store-c"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "h") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; outputs = [ "out" "dev" ]; @@ -65,8 +55,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -80,8 +70,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -89,8 +78,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libstore/package.nix b/src/libstore/package.nix index d4859a411..df92b5b28 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -22,46 +23,35 @@ , versionSuffix ? "" , embeddedSandboxShell ? stdenv.hostPlatform.isStatic - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - ./linux/meson.build - ./unix/meson.build - ./windows/meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "sb") ./.) - (fileset.fileFilter (file: file.hasExt "md") ./.) - (fileset.fileFilter (file: file.hasExt "sql") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "sb") ./.) + (fileset.fileFilter (file: file.hasExt "md") ./.) + (fileset.fileFilter (file: file.hasExt "sql") ./.) + ]; outputs = [ "out" "dev" ]; @@ -93,8 +83,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -117,8 +107,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -126,8 +115,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index ba1dbe38a..f92cb036c 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -11,41 +12,30 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-c"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "h") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; outputs = [ "out" "dev" ]; @@ -63,8 +53,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -78,8 +68,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -87,8 +76,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index aff338d16..74d4d7853 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -18,25 +18,12 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in mkMesonDerivation (finalAttrs: { @@ -45,6 +32,8 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson ../../.version ./.version ./meson.build @@ -78,12 +67,14 @@ mkMesonDerivation (finalAttrs: { ]; preConfigure = - # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. + # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. + # + # TODO: change release process to add `pre` in `.version`, remove it + # before tagging, and restore after. '' chmod u+w ./.version echo ${version} > ../../.version - cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ @@ -103,8 +94,7 @@ mkMesonDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -112,8 +102,4 @@ mkMesonDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-expr-test-support/package.nix b/src/nix-expr-test-support/package.nix index ecfb2bb09..aec0e7663 100644 --- a/src/nix-expr-test-support/package.nix +++ b/src/nix-expr-test-support/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -14,40 +15,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-test-support"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -67,8 +57,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -82,8 +72,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -91,8 +80,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-expr-tests/package.nix b/src/nix-expr-tests/package.nix index 679b6fb2a..ddd79fd55 100644 --- a/src/nix-expr-tests/package.nix +++ b/src/nix-expr-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -17,40 +18,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-expr-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +62,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -87,8 +77,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -108,8 +97,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-fetchers-tests/package.nix b/src/nix-fetchers-tests/package.nix index 5cf18ce33..759743a8b 100644 --- a/src/nix-fetchers-tests/package.nix +++ b/src/nix-fetchers-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -16,40 +17,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-fetchers-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -70,8 +60,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -85,8 +75,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -106,8 +95,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-flake-tests/package.nix b/src/nix-flake-tests/package.nix index 21af753ae..a7783593a 100644 --- a/src/nix-flake-tests/package.nix +++ b/src/nix-flake-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -16,40 +17,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-flake-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -70,8 +60,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -85,8 +75,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -106,8 +95,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-store-test-support/package.nix b/src/nix-store-test-support/package.nix index 0f4ea73ba..250f29b86 100644 --- a/src/nix-store-test-support/package.nix +++ b/src/nix-store-test-support/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -14,40 +15,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store-test-support"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -67,8 +57,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -82,8 +72,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -91,8 +80,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-store-tests/package.nix b/src/nix-store-tests/package.nix index dc987b3c6..e6750771f 100644 --- a/src/nix-store-tests/package.nix +++ b/src/nix-store-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -18,40 +19,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -74,8 +64,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -89,8 +79,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -115,8 +104,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-util-test-support/package.nix b/src/nix-util-test-support/package.nix index 795159ebf..42a56d58f 100644 --- a/src/nix-util-test-support/package.nix +++ b/src/nix-util-test-support/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -13,40 +14,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-test-support"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -65,8 +55,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -80,8 +70,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -89,8 +78,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-util-tests/package.nix b/src/nix-util-tests/package.nix index 9df8153b6..2491d8722 100644 --- a/src/nix-util-tests/package.nix +++ b/src/nix-util-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -17,40 +18,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +62,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -87,8 +77,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -108,8 +97,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) From 11946817f0858115f8afbfacd5b65d47552d37a7 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 14:31:04 -0400 Subject: [PATCH 466/910] fileset for store unit test data --- src/nix-store-tests/package.nix | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/nix-store-tests/package.nix b/src/nix-store-tests/package.nix index e6750771f..243b9f149 100644 --- a/src/nix-store-tests/package.nix +++ b/src/nix-store-tests/package.nix @@ -86,14 +86,18 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = let - # Inline some drv files shared with the libexpr tests - data = runCommand "${finalAttrs.pname}-test-data" {} '' - cp -r --no-preserve=mode ${./data} $out - cp -r --remove-destination ${../../tests/functional/derivation}/* $out/derivation/ - ''; + # Some data is shared with the functional tests: they create it, + # we consume it. + data = lib.fileset.toSource { + root = ../..; + fileset = lib.fileset.unions [ + ./data + ../../tests/functional/derivation + ]; + }; in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${data} + export _NIX_TEST_UNIT_DATA=${data + "/src/nix-store-test/data"} nix-store-tests touch $out ''; From 451f8a8c19e2ab95999553f5bf3a1fb056877933 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 00:25:57 -0400 Subject: [PATCH 467/910] Put back files for now We'll revert this sometime later --- .gitignore | 10 +- maintainers/flake-module.nix | 122 +++++++++--------- packaging/components.nix | 16 +-- src/nix-expr-test-support | 1 + src/nix-expr-test-support/.version | 1 - src/nix-expr-test-support/build-utils-meson | 1 - src/nix-expr-tests | 1 + src/nix-expr-tests/.version | 1 - src/nix-expr-tests/build-utils-meson | 1 - src/nix-fetchers-tests | 1 + src/nix-fetchers-tests/.version | 1 - src/nix-fetchers-tests/build-utils-meson | 1 - src/nix-flake-tests | 1 + src/nix-flake-tests/.version | 1 - src/nix-flake-tests/build-utils-meson | 1 - src/nix-store-test-support | 1 + src/nix-store-test-support/.version | 1 - src/nix-store-test-support/build-utils-meson | 1 - src/nix-store-tests | 1 + src/nix-store-tests/.version | 1 - src/nix-store-tests/build-utils-meson | 1 - .../advanced-attributes-defaults.drv | 1 - ...d-attributes-structured-attrs-defaults.drv | 1 - .../advanced-attributes-structured-attrs.drv | 1 - .../data/derivation/advanced-attributes.drv | 1 - src/nix-util-test-support | 1 + src/nix-util-test-support/.version | 1 - src/nix-util-test-support/build-utils-meson | 1 - src/nix-util-tests | 1 + src/nix-util-tests/.version | 1 - src/nix-util-tests/build-utils-meson | 1 - tests/unit/libexpr-support/.version | 1 + tests/unit/libexpr-support/build-utils-meson | 1 + .../unit/libexpr-support}/meson.build | 0 .../unit/libexpr-support}/package.nix | 6 +- .../unit/libexpr-support}/tests/libexpr.hh | 0 .../libexpr-support}/tests/nix_api_expr.hh | 0 .../libexpr-support}/tests/value/context.cc | 0 .../libexpr-support}/tests/value/context.hh | 0 tests/unit/libexpr/.version | 1 + tests/unit/libexpr/build-utils-meson | 1 + .../unit/libexpr}/data/.gitkeep | 0 .../unit/libexpr}/derived-path.cc | 0 .../unit/libexpr}/error_traces.cc | 0 .../unit/libexpr}/eval.cc | 0 .../unit/libexpr}/json.cc | 0 .../unit/libexpr}/main.cc | 0 .../unit/libexpr}/meson.build | 0 .../unit/libexpr}/nix_api_expr.cc | 0 .../unit/libexpr}/nix_api_external.cc | 0 .../unit/libexpr}/nix_api_value.cc | 0 .../unit/libexpr}/package.nix | 6 +- .../unit/libexpr}/primops.cc | 0 .../unit/libexpr}/search-path.cc | 0 .../unit/libexpr}/trivial.cc | 0 .../unit/libexpr}/value/context.cc | 0 .../unit/libexpr}/value/print.cc | 0 .../unit/libexpr}/value/value.cc | 0 tests/unit/libfetchers/.version | 1 + tests/unit/libfetchers/build-utils-meson | 1 + .../data/public-key/defaultType.json | 0 .../data/public-key/noRoundTrip.json | 0 .../libfetchers}/data/public-key/simple.json | 0 .../unit/libfetchers}/meson.build | 0 .../unit/libfetchers}/package.nix | 6 +- .../unit/libfetchers}/public-key.cc | 0 tests/unit/libflake/.version | 1 + tests/unit/libflake/build-utils-meson | 1 + .../unit/libflake}/data/.gitkeep | 0 .../unit/libflake}/flakeref.cc | 0 .../unit/libflake}/meson.build | 0 .../unit/libflake}/package.nix | 6 +- .../unit/libflake}/url-name.cc | 0 tests/unit/libstore-support/.version | 1 + tests/unit/libstore-support/build-utils-meson | 1 + .../unit/libstore-support}/meson.build | 0 .../unit/libstore-support}/package.nix | 6 +- .../libstore-support}/tests/derived-path.cc | 0 .../libstore-support}/tests/derived-path.hh | 0 .../unit/libstore-support}/tests/libstore.hh | 0 .../libstore-support}/tests/nix_api_store.hh | 0 .../libstore-support}/tests/outputs-spec.cc | 0 .../libstore-support}/tests/outputs-spec.hh | 0 .../unit/libstore-support}/tests/path.cc | 0 .../unit/libstore-support}/tests/path.hh | 0 .../unit/libstore-support}/tests/protocol.hh | 0 tests/unit/libstore/.version | 1 + tests/unit/libstore/build-utils-meson | 1 + .../unit/libstore}/common-protocol.cc | 0 .../unit/libstore}/content-address.cc | 0 .../data/common-protocol/content-address.bin | Bin .../data/common-protocol/drv-output.bin | Bin .../optional-content-address.bin | Bin .../common-protocol/optional-store-path.bin | Bin .../data/common-protocol/realisation.bin | Bin .../libstore}/data/common-protocol/set.bin | Bin .../data/common-protocol/store-path.bin | Bin .../libstore}/data/common-protocol/string.bin | Bin .../libstore}/data/common-protocol/vector.bin | Bin .../advanced-attributes-defaults.drv | 1 + .../advanced-attributes-defaults.json | 0 ...d-attributes-structured-attrs-defaults.drv | 1 + ...-attributes-structured-attrs-defaults.json | 0 .../advanced-attributes-structured-attrs.drv | 1 + .../advanced-attributes-structured-attrs.json | 0 .../data/derivation/advanced-attributes.drv | 1 + .../derivation/bad-old-version-dyn-deps.drv | 0 .../libstore}/data/derivation/bad-version.drv | 0 .../data/derivation/dynDerivationDeps.drv | 0 .../data/derivation/dynDerivationDeps.json | 0 .../data/derivation/output-caFixedFlat.json | 0 .../data/derivation/output-caFixedNAR.json | 0 .../data/derivation/output-caFixedText.json | 0 .../data/derivation/output-caFloating.json | 0 .../data/derivation/output-deferred.json | 0 .../data/derivation/output-impure.json | 0 .../derivation/output-inputAddressed.json | 0 .../unit/libstore}/data/derivation/simple.drv | 0 .../libstore}/data/derivation/simple.json | 0 .../unit/libstore}/data/machines/bad_format | 0 .../unit/libstore}/data/machines/valid | 0 .../unit/libstore}/data/nar-info/impure.json | 0 .../unit/libstore}/data/nar-info/pure.json | 0 .../data/path-info/empty_impure.json | 0 .../libstore}/data/path-info/empty_pure.json | 0 .../unit/libstore}/data/path-info/impure.json | 0 .../unit/libstore}/data/path-info/pure.json | 0 .../data/serve-protocol/build-options-2.1.bin | Bin .../data/serve-protocol/build-options-2.2.bin | Bin .../data/serve-protocol/build-options-2.3.bin | Bin .../data/serve-protocol/build-options-2.7.bin | Bin .../data/serve-protocol/build-result-2.2.bin | Bin .../data/serve-protocol/build-result-2.3.bin | Bin .../data/serve-protocol/build-result-2.6.bin | Bin .../data/serve-protocol/content-address.bin | Bin .../data/serve-protocol/drv-output.bin | Bin .../serve-protocol/handshake-to-client.bin | Bin .../optional-content-address.bin | Bin .../serve-protocol/optional-store-path.bin | Bin .../data/serve-protocol/realisation.bin | Bin .../libstore}/data/serve-protocol/set.bin | Bin .../data/serve-protocol/store-path.bin | Bin .../libstore}/data/serve-protocol/string.bin | Bin .../unkeyed-valid-path-info-2.3.bin | Bin .../unkeyed-valid-path-info-2.4.bin | Bin .../libstore}/data/serve-protocol/vector.bin | Bin .../libstore}/data/store-reference/auto.txt | 0 .../data/store-reference/auto_param.txt | 0 .../data/store-reference/local_1.txt | 0 .../data/store-reference/local_2.txt | 0 .../store-reference/local_shorthand_1.txt | 0 .../store-reference/local_shorthand_2.txt | 0 .../libstore}/data/store-reference/ssh.txt | 0 .../libstore}/data/store-reference/unix.txt | 0 .../data/store-reference/unix_shorthand.txt | 0 .../data/worker-protocol/build-mode.bin | Bin .../worker-protocol/build-result-1.27.bin | Bin .../worker-protocol/build-result-1.28.bin | Bin .../worker-protocol/build-result-1.29.bin | Bin .../worker-protocol/build-result-1.37.bin | Bin .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin .../client-handshake-info_1_35.bin | Bin .../data/worker-protocol/content-address.bin | Bin .../worker-protocol/derived-path-1.29.bin | Bin .../worker-protocol/derived-path-1.30.bin | Bin .../data/worker-protocol/drv-output.bin | Bin .../worker-protocol/handshake-to-client.bin | Bin .../keyed-build-result-1.29.bin | Bin .../optional-content-address.bin | Bin .../worker-protocol/optional-store-path.bin | Bin .../worker-protocol/optional-trusted-flag.bin | Bin .../data/worker-protocol/realisation.bin | Bin .../libstore}/data/worker-protocol/set.bin | Bin .../data/worker-protocol/store-path.bin | Bin .../libstore}/data/worker-protocol/string.bin | Bin .../unkeyed-valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.16.bin | Bin .../libstore}/data/worker-protocol/vector.bin | Bin .../libstore}/derivation-advanced-attrs.cc | 0 .../unit/libstore}/derivation.cc | 0 .../unit/libstore}/derived-path.cc | 0 .../unit/libstore}/downstream-placeholder.cc | 0 .../unit/libstore}/machines.cc | 0 .../unit/libstore}/meson.build | 0 .../unit/libstore}/nar-info-disk-cache.cc | 0 .../unit/libstore}/nar-info.cc | 0 .../unit/libstore}/nix_api_store.cc | 0 .../unit/libstore}/outputs-spec.cc | 0 .../unit/libstore}/package.nix | 10 +- .../unit/libstore}/path-info.cc | 0 .../unit/libstore}/path.cc | 0 .../unit/libstore}/references.cc | 0 .../unit/libstore}/serve-protocol.cc | 0 .../unit/libstore}/store-reference.cc | 0 .../unit/libstore}/worker-protocol.cc | 0 tests/unit/libutil-support/.version | 1 + tests/unit/libutil-support/build-utils-meson | 1 + .../unit/libutil-support}/meson.build | 0 .../unit/libutil-support}/package.nix | 6 +- .../tests/characterization.hh | 0 .../unit/libutil-support}/tests/hash.cc | 0 .../unit/libutil-support}/tests/hash.hh | 0 .../libutil-support}/tests/nix_api_util.hh | 0 .../libutil-support}/tests/string_callback.cc | 0 .../libutil-support}/tests/string_callback.hh | 0 tests/unit/libutil/.version | 1 + .../unit/libutil}/args.cc | 0 tests/unit/libutil/build-utils-meson | 1 + .../unit/libutil}/canon-path.cc | 0 .../unit/libutil}/chunked-vector.cc | 0 .../unit/libutil}/closure.cc | 0 .../unit/libutil}/compression.cc | 0 .../unit/libutil}/config.cc | 0 .../unit/libutil}/data/git/check-data.sh | 0 .../libutil}/data/git/hello-world-blob.bin | Bin .../unit/libutil}/data/git/hello-world.bin | Bin .../unit/libutil}/data/git/tree.bin | Bin .../unit/libutil}/data/git/tree.txt | 0 .../unit/libutil}/file-content-address.cc | 0 .../unit/libutil}/git.cc | 2 +- .../unit/libutil}/hash.cc | 0 .../unit/libutil}/hilite.cc | 0 .../unit/libutil}/json-utils.cc | 0 .../unit/libutil}/logging.cc | 0 .../unit/libutil}/lru-cache.cc | 0 .../unit/libutil}/meson.build | 0 .../unit/libutil}/nix_api_util.cc | 0 .../unit/libutil}/package.nix | 6 +- .../unit/libutil}/pool.cc | 0 .../unit/libutil}/references.cc | 0 .../unit/libutil}/spawn.cc | 0 .../unit/libutil}/suggestions.cc | 0 .../unit/libutil}/tests.cc | 0 .../unit/libutil}/url.cc | 0 .../unit/libutil}/xml-writer.cc | 0 237 files changed, 129 insertions(+), 121 deletions(-) create mode 120000 src/nix-expr-test-support delete mode 120000 src/nix-expr-test-support/.version delete mode 120000 src/nix-expr-test-support/build-utils-meson create mode 120000 src/nix-expr-tests delete mode 120000 src/nix-expr-tests/.version delete mode 120000 src/nix-expr-tests/build-utils-meson create mode 120000 src/nix-fetchers-tests delete mode 120000 src/nix-fetchers-tests/.version delete mode 120000 src/nix-fetchers-tests/build-utils-meson create mode 120000 src/nix-flake-tests delete mode 120000 src/nix-flake-tests/.version delete mode 120000 src/nix-flake-tests/build-utils-meson create mode 120000 src/nix-store-test-support delete mode 120000 src/nix-store-test-support/.version delete mode 120000 src/nix-store-test-support/build-utils-meson create mode 120000 src/nix-store-tests delete mode 120000 src/nix-store-tests/.version delete mode 120000 src/nix-store-tests/build-utils-meson delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes.drv create mode 120000 src/nix-util-test-support delete mode 120000 src/nix-util-test-support/.version delete mode 120000 src/nix-util-test-support/build-utils-meson create mode 120000 src/nix-util-tests delete mode 120000 src/nix-util-tests/.version delete mode 120000 src/nix-util-tests/build-utils-meson create mode 120000 tests/unit/libexpr-support/.version create mode 120000 tests/unit/libexpr-support/build-utils-meson rename {src/nix-expr-test-support => tests/unit/libexpr-support}/meson.build (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/package.nix (93%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/libexpr.hh (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/nix_api_expr.hh (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/value/context.cc (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/value/context.hh (100%) create mode 120000 tests/unit/libexpr/.version create mode 120000 tests/unit/libexpr/build-utils-meson rename {src/nix-expr-tests => tests/unit/libexpr}/data/.gitkeep (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/derived-path.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/error_traces.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/eval.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/json.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/main.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/meson.build (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/nix_api_expr.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/nix_api_external.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/nix_api_value.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/package.nix (94%) rename {src/nix-expr-tests => tests/unit/libexpr}/primops.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/search-path.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/trivial.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/value/context.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/value/print.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/value/value.cc (100%) create mode 120000 tests/unit/libfetchers/.version create mode 120000 tests/unit/libfetchers/build-utils-meson rename {src/nix-fetchers-tests => tests/unit/libfetchers}/data/public-key/defaultType.json (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/data/public-key/noRoundTrip.json (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/data/public-key/simple.json (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/meson.build (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/package.nix (94%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/public-key.cc (100%) create mode 120000 tests/unit/libflake/.version create mode 120000 tests/unit/libflake/build-utils-meson rename {src/nix-flake-tests => tests/unit/libflake}/data/.gitkeep (100%) rename {src/nix-flake-tests => tests/unit/libflake}/flakeref.cc (100%) rename {src/nix-flake-tests => tests/unit/libflake}/meson.build (100%) rename {src/nix-flake-tests => tests/unit/libflake}/package.nix (94%) rename {src/nix-flake-tests => tests/unit/libflake}/url-name.cc (100%) create mode 120000 tests/unit/libstore-support/.version create mode 120000 tests/unit/libstore-support/build-utils-meson rename {src/nix-store-test-support => tests/unit/libstore-support}/meson.build (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/package.nix (93%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/derived-path.cc (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/derived-path.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/libstore.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/nix_api_store.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/outputs-spec.cc (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/outputs-spec.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/path.cc (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/path.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/protocol.hh (100%) create mode 120000 tests/unit/libstore/.version create mode 120000 tests/unit/libstore/build-utils-meson rename {src/nix-store-tests => tests/unit/libstore}/common-protocol.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/content-address.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/drv-output.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/optional-content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/optional-store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/realisation.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/set.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/string.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/vector.bin (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/advanced-attributes-defaults.json (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/advanced-attributes-structured-attrs-defaults.json (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/advanced-attributes-structured-attrs.json (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/bad-old-version-dyn-deps.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/bad-version.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/dynDerivationDeps.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/dynDerivationDeps.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFixedFlat.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFixedNAR.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFixedText.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFloating.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-deferred.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-inputAddressed.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/simple.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/simple.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/machines/bad_format (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/machines/valid (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/nar-info/impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/nar-info/pure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/empty_impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/empty_pure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/pure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.1.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.2.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.3.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.7.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-result-2.2.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-result-2.3.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-result-2.6.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/drv-output.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/handshake-to-client.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/optional-content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/optional-store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/realisation.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/set.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/string.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/unkeyed-valid-path-info-2.3.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/unkeyed-valid-path-info-2.4.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/vector.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/auto.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/auto_param.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_1.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_2.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_shorthand_1.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_shorthand_2.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/ssh.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/unix.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/unix_shorthand.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-mode.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.27.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.28.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.29.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.37.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/client-handshake-info_1_30.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/client-handshake-info_1_33.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/client-handshake-info_1_35.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/derived-path-1.29.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/derived-path-1.30.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/drv-output.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/handshake-to-client.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/keyed-build-result-1.29.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/optional-content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/optional-store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/optional-trusted-flag.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/realisation.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/set.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/string.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/unkeyed-valid-path-info-1.15.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/valid-path-info-1.15.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/valid-path-info-1.16.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/vector.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/derivation-advanced-attrs.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/derivation.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/derived-path.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/downstream-placeholder.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/machines.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/meson.build (100%) rename {src/nix-store-tests => tests/unit/libstore}/nar-info-disk-cache.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/nar-info.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/nix_api_store.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/outputs-spec.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/package.nix (90%) rename {src/nix-store-tests => tests/unit/libstore}/path-info.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/path.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/references.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/serve-protocol.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/store-reference.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/worker-protocol.cc (100%) create mode 120000 tests/unit/libutil-support/.version create mode 120000 tests/unit/libutil-support/build-utils-meson rename {src/nix-util-test-support => tests/unit/libutil-support}/meson.build (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/package.nix (93%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/characterization.hh (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/hash.cc (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/hash.hh (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/nix_api_util.hh (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/string_callback.cc (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/string_callback.hh (100%) create mode 120000 tests/unit/libutil/.version rename {src/nix-util-tests => tests/unit/libutil}/args.cc (100%) create mode 120000 tests/unit/libutil/build-utils-meson rename {src/nix-util-tests => tests/unit/libutil}/canon-path.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/chunked-vector.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/closure.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/compression.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/config.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/check-data.sh (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/hello-world-blob.bin (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/hello-world.bin (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/tree.bin (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/tree.txt (100%) rename {src/nix-util-tests => tests/unit/libutil}/file-content-address.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/git.cc (99%) rename {src/nix-util-tests => tests/unit/libutil}/hash.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/hilite.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/json-utils.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/logging.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/lru-cache.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/meson.build (100%) rename {src/nix-util-tests => tests/unit/libutil}/nix_api_util.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/package.nix (94%) rename {src/nix-util-tests => tests/unit/libutil}/pool.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/references.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/spawn.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/suggestions.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/tests.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/url.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/xml-writer.cc (100%) diff --git a/.gitignore b/.gitignore index fdfd744e5..a17b627f4 100644 --- a/.gitignore +++ b/.gitignore @@ -49,22 +49,22 @@ perl/Makefile.config /src/libexpr/parser-tab.output /src/libexpr/nix.tbl /src/libexpr/tests -/src/nix-expr-tests/libnixexpr-tests +/tests/unit/libexpr/libnixexpr-tests # /src/libfetchers -/src/nix-fetchers-tests/libnixfetchers-tests +/tests/unit/libfetchers/libnixfetchers-tests # /src/libflake -/src/nix-flake-tests/libnixflake-tests +/tests/unit/libflake/libnixflake-tests # /src/libstore/ *.gen.* /src/libstore/tests -/src/nix-store-tests/libnixstore-tests +/tests/unit/libstore/libnixstore-tests # /src/libutil/ /src/libutil/tests -/src/nix-util-tests/libnixutil-tests +/tests/unit/libutil/libnixutil-tests /src/nix/nix diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 007ef034f..8f95e788b 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^src/[^/]*-tests/data/.*$'' + ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' @@ -429,65 +429,65 @@ ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/nixos/user-sandboxing/attacker\.c'' - ''^src/nix-expr-test-support/tests/libexpr\.hh'' - ''^src/nix-expr-test-support/tests/value/context\.cc'' - ''^src/nix-expr-test-support/tests/value/context\.hh'' - ''^src/nix-expr-tests/derived-path\.cc'' - ''^src/nix-expr-tests/error_traces\.cc'' - ''^src/nix-expr-tests/eval\.cc'' - ''^src/nix-expr-tests/json\.cc'' - ''^src/nix-expr-tests/main\.cc'' - ''^src/nix-expr-tests/primops\.cc'' - ''^src/nix-expr-tests/search-path\.cc'' - ''^src/nix-expr-tests/trivial\.cc'' - ''^src/nix-expr-tests/value/context\.cc'' - ''^src/nix-expr-tests/value/print\.cc'' - ''^src/nix-fetchers-tests/public-key\.cc'' - ''^src/nix-flake-tests/flakeref\.cc'' - ''^src/nix-flake-tests/url-name\.cc'' - ''^src/nix-store-test-support/tests/derived-path\.cc'' - ''^src/nix-store-test-support/tests/derived-path\.hh'' - ''^src/nix-store-test-support/tests/nix_api_store\.hh'' - ''^src/nix-store-test-support/tests/outputs-spec\.cc'' - ''^src/nix-store-test-support/tests/outputs-spec\.hh'' - ''^src/nix-store-test-support/tests/path\.cc'' - ''^src/nix-store-test-support/tests/path\.hh'' - ''^src/nix-store-test-support/tests/protocol\.hh'' - ''^src/nix-store-tests/common-protocol\.cc'' - ''^src/nix-store-tests/content-address\.cc'' - ''^src/nix-store-tests/derivation\.cc'' - ''^src/nix-store-tests/derived-path\.cc'' - ''^src/nix-store-tests/downstream-placeholder\.cc'' - ''^src/nix-store-tests/machines\.cc'' - ''^src/nix-store-tests/nar-info-disk-cache\.cc'' - ''^src/nix-store-tests/nar-info\.cc'' - ''^src/nix-store-tests/outputs-spec\.cc'' - ''^src/nix-store-tests/path-info\.cc'' - ''^src/nix-store-tests/path\.cc'' - ''^src/nix-store-tests/serve-protocol\.cc'' - ''^src/nix-store-tests/worker-protocol\.cc'' - ''^src/nix-util-test-support/tests/characterization\.hh'' - ''^src/nix-util-test-support/tests/hash\.cc'' - ''^src/nix-util-test-support/tests/hash\.hh'' - ''^src/nix-util-tests/args\.cc'' - ''^src/nix-util-tests/canon-path\.cc'' - ''^src/nix-util-tests/chunked-vector\.cc'' - ''^src/nix-util-tests/closure\.cc'' - ''^src/nix-util-tests/compression\.cc'' - ''^src/nix-util-tests/config\.cc'' - ''^src/nix-util-tests/file-content-address\.cc'' - ''^src/nix-util-tests/git\.cc'' - ''^src/nix-util-tests/hash\.cc'' - ''^src/nix-util-tests/hilite\.cc'' - ''^src/nix-util-tests/json-utils\.cc'' - ''^src/nix-util-tests/logging\.cc'' - ''^src/nix-util-tests/lru-cache\.cc'' - ''^src/nix-util-tests/pool\.cc'' - ''^src/nix-util-tests/references\.cc'' - ''^src/nix-util-tests/suggestions\.cc'' - ''^src/nix-util-tests/tests\.cc'' - ''^src/nix-util-tests/url\.cc'' - ''^src/nix-util-tests/xml-writer\.cc'' + ''^tests/unit/libexpr-support/tests/libexpr\.hh'' + ''^tests/unit/libexpr-support/tests/value/context\.cc'' + ''^tests/unit/libexpr-support/tests/value/context\.hh'' + ''^tests/unit/libexpr/derived-path\.cc'' + ''^tests/unit/libexpr/error_traces\.cc'' + ''^tests/unit/libexpr/eval\.cc'' + ''^tests/unit/libexpr/json\.cc'' + ''^tests/unit/libexpr/main\.cc'' + ''^tests/unit/libexpr/primops\.cc'' + ''^tests/unit/libexpr/search-path\.cc'' + ''^tests/unit/libexpr/trivial\.cc'' + ''^tests/unit/libexpr/value/context\.cc'' + ''^tests/unit/libexpr/value/print\.cc'' + ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libflake/flakeref\.cc'' + ''^tests/unit/libflake/url-name\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.hh'' + ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' + ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' + ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' + ''^tests/unit/libstore-support/tests/path\.cc'' + ''^tests/unit/libstore-support/tests/path\.hh'' + ''^tests/unit/libstore-support/tests/protocol\.hh'' + ''^tests/unit/libstore/common-protocol\.cc'' + ''^tests/unit/libstore/content-address\.cc'' + ''^tests/unit/libstore/derivation\.cc'' + ''^tests/unit/libstore/derived-path\.cc'' + ''^tests/unit/libstore/downstream-placeholder\.cc'' + ''^tests/unit/libstore/machines\.cc'' + ''^tests/unit/libstore/nar-info-disk-cache\.cc'' + ''^tests/unit/libstore/nar-info\.cc'' + ''^tests/unit/libstore/outputs-spec\.cc'' + ''^tests/unit/libstore/path-info\.cc'' + ''^tests/unit/libstore/path\.cc'' + ''^tests/unit/libstore/serve-protocol\.cc'' + ''^tests/unit/libstore/worker-protocol\.cc'' + ''^tests/unit/libutil-support/tests/characterization\.hh'' + ''^tests/unit/libutil-support/tests/hash\.cc'' + ''^tests/unit/libutil-support/tests/hash\.hh'' + ''^tests/unit/libutil/args\.cc'' + ''^tests/unit/libutil/canon-path\.cc'' + ''^tests/unit/libutil/chunked-vector\.cc'' + ''^tests/unit/libutil/closure\.cc'' + ''^tests/unit/libutil/compression\.cc'' + ''^tests/unit/libutil/config\.cc'' + ''^tests/unit/libutil/file-content-address\.cc'' + ''^tests/unit/libutil/git\.cc'' + ''^tests/unit/libutil/hash\.cc'' + ''^tests/unit/libutil/hilite\.cc'' + ''^tests/unit/libutil/json-utils\.cc'' + ''^tests/unit/libutil/logging\.cc'' + ''^tests/unit/libutil/lru-cache\.cc'' + ''^tests/unit/libutil/pool\.cc'' + ''^tests/unit/libutil/references\.cc'' + ''^tests/unit/libutil/suggestions\.cc'' + ''^tests/unit/libutil/tests\.cc'' + ''^tests/unit/libutil/url\.cc'' + ''^tests/unit/libutil/xml-writer\.cc'' ]; }; shellcheck = { @@ -666,7 +666,7 @@ ''^tests/functional/user-envs\.sh$'' ''^tests/functional/why-depends\.sh$'' ''^tests/functional/zstd\.sh$'' - ''^src/nix-util-tests/data/git/check-data\.sh$'' + ''^tests/unit/libutil/data/git/check-data\.sh$'' ]; }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 diff --git a/packaging/components.nix b/packaging/components.nix index db50d6b22..e9e95c028 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -9,24 +9,24 @@ in nix-util = callPackage ../src/libutil/package.nix { }; nix-util-c = callPackage ../src/libutil-c/package.nix { }; - nix-util-test-support = callPackage ../src/nix-util-test-support/package.nix { }; - nix-util-tests = callPackage ../src/nix-util-tests/package.nix { }; + nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { }; + nix-util-tests = callPackage ../tests/unit/libutil/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; nix-store-c = callPackage ../src/libstore-c/package.nix { }; - nix-store-test-support = callPackage ../src/nix-store-test-support/package.nix { }; - nix-store-tests = callPackage ../src/nix-store-tests/package.nix { }; + nix-store-test-support = callPackage ../tests/unit/libstore-support/package.nix { }; + nix-store-tests = callPackage ../tests/unit/libstore/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-fetchers-tests = callPackage ../src/nix-fetchers-tests/package.nix { }; + nix-fetchers-tests = callPackage ../tests/unit/libfetchers/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-expr-test-support = callPackage ../src/nix-expr-test-support/package.nix { }; - nix-expr-tests = callPackage ../src/nix-expr-tests/package.nix { }; + nix-expr-test-support = callPackage ../tests/unit/libexpr-support/package.nix { }; + nix-expr-tests = callPackage ../tests/unit/libexpr/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; - nix-flake-tests = callPackage ../src/nix-flake-tests/package.nix { }; + nix-flake-tests = callPackage ../tests/unit/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/src/nix-expr-test-support b/src/nix-expr-test-support new file mode 120000 index 000000000..427b80dff --- /dev/null +++ b/src/nix-expr-test-support @@ -0,0 +1 @@ +../tests/unit/libexpr-support \ No newline at end of file diff --git a/src/nix-expr-test-support/.version b/src/nix-expr-test-support/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-expr-test-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-expr-test-support/build-utils-meson b/src/nix-expr-test-support/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-expr-test-support/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-expr-tests b/src/nix-expr-tests new file mode 120000 index 000000000..3af7110d3 --- /dev/null +++ b/src/nix-expr-tests @@ -0,0 +1 @@ +../tests/unit/libexpr \ No newline at end of file diff --git a/src/nix-expr-tests/.version b/src/nix-expr-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-expr-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-expr-tests/build-utils-meson b/src/nix-expr-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-expr-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-fetchers-tests b/src/nix-fetchers-tests new file mode 120000 index 000000000..80e4b68ae --- /dev/null +++ b/src/nix-fetchers-tests @@ -0,0 +1 @@ +../tests/unit/libfetchers \ No newline at end of file diff --git a/src/nix-fetchers-tests/.version b/src/nix-fetchers-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-fetchers-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-fetchers-tests/build-utils-meson b/src/nix-fetchers-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-fetchers-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-flake-tests b/src/nix-flake-tests new file mode 120000 index 000000000..bb2d49400 --- /dev/null +++ b/src/nix-flake-tests @@ -0,0 +1 @@ +../tests/unit/libflake \ No newline at end of file diff --git a/src/nix-flake-tests/.version b/src/nix-flake-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-flake-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-flake-tests/build-utils-meson b/src/nix-flake-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-flake-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-store-test-support b/src/nix-store-test-support new file mode 120000 index 000000000..af4befd90 --- /dev/null +++ b/src/nix-store-test-support @@ -0,0 +1 @@ +../tests/unit/libstore-support \ No newline at end of file diff --git a/src/nix-store-test-support/.version b/src/nix-store-test-support/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-store-test-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-store-test-support/build-utils-meson b/src/nix-store-test-support/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-store-test-support/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-store-tests b/src/nix-store-tests new file mode 120000 index 000000000..fc9b910af --- /dev/null +++ b/src/nix-store-tests @@ -0,0 +1 @@ +../tests/unit/libstore \ No newline at end of file diff --git a/src/nix-store-tests/.version b/src/nix-store-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-store-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-store-tests/build-utils-meson b/src/nix-store-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-store-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv deleted file mode 120000 index f8f30ac32..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv deleted file mode 120000 index 837e9a0e4..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv deleted file mode 120000 index e08bb5737..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes.drv b/src/nix-store-tests/data/derivation/advanced-attributes.drv deleted file mode 120000 index 1dc394a0a..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes.drv \ No newline at end of file diff --git a/src/nix-util-test-support b/src/nix-util-test-support new file mode 120000 index 000000000..4b25930eb --- /dev/null +++ b/src/nix-util-test-support @@ -0,0 +1 @@ +../tests/unit/libutil-support \ No newline at end of file diff --git a/src/nix-util-test-support/.version b/src/nix-util-test-support/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-util-test-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-util-test-support/build-utils-meson b/src/nix-util-test-support/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-util-test-support/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-util-tests b/src/nix-util-tests new file mode 120000 index 000000000..e1138411a --- /dev/null +++ b/src/nix-util-tests @@ -0,0 +1 @@ +../tests/unit/libutil \ No newline at end of file diff --git a/src/nix-util-tests/.version b/src/nix-util-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-util-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-util-tests/build-utils-meson b/src/nix-util-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-util-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/tests/unit/libexpr-support/.version b/tests/unit/libexpr-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libexpr-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libexpr-support/build-utils-meson b/tests/unit/libexpr-support/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libexpr-support/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-expr-test-support/meson.build b/tests/unit/libexpr-support/meson.build similarity index 100% rename from src/nix-expr-test-support/meson.build rename to tests/unit/libexpr-support/meson.build diff --git a/src/nix-expr-test-support/package.nix b/tests/unit/libexpr-support/package.nix similarity index 93% rename from src/nix-expr-test-support/package.nix rename to tests/unit/libexpr-support/package.nix index aec0e7663..f32cf2615 100644 --- a/src/nix-expr-test-support/package.nix +++ b/tests/unit/libexpr-support/package.nix @@ -29,9 +29,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -58,7 +58,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-expr-test-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh similarity index 100% rename from src/nix-expr-test-support/tests/libexpr.hh rename to tests/unit/libexpr-support/tests/libexpr.hh diff --git a/src/nix-expr-test-support/tests/nix_api_expr.hh b/tests/unit/libexpr-support/tests/nix_api_expr.hh similarity index 100% rename from src/nix-expr-test-support/tests/nix_api_expr.hh rename to tests/unit/libexpr-support/tests/nix_api_expr.hh diff --git a/src/nix-expr-test-support/tests/value/context.cc b/tests/unit/libexpr-support/tests/value/context.cc similarity index 100% rename from src/nix-expr-test-support/tests/value/context.cc rename to tests/unit/libexpr-support/tests/value/context.cc diff --git a/src/nix-expr-test-support/tests/value/context.hh b/tests/unit/libexpr-support/tests/value/context.hh similarity index 100% rename from src/nix-expr-test-support/tests/value/context.hh rename to tests/unit/libexpr-support/tests/value/context.hh diff --git a/tests/unit/libexpr/.version b/tests/unit/libexpr/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libexpr/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libexpr/build-utils-meson b/tests/unit/libexpr/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libexpr/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-expr-tests/data/.gitkeep b/tests/unit/libexpr/data/.gitkeep similarity index 100% rename from src/nix-expr-tests/data/.gitkeep rename to tests/unit/libexpr/data/.gitkeep diff --git a/src/nix-expr-tests/derived-path.cc b/tests/unit/libexpr/derived-path.cc similarity index 100% rename from src/nix-expr-tests/derived-path.cc rename to tests/unit/libexpr/derived-path.cc diff --git a/src/nix-expr-tests/error_traces.cc b/tests/unit/libexpr/error_traces.cc similarity index 100% rename from src/nix-expr-tests/error_traces.cc rename to tests/unit/libexpr/error_traces.cc diff --git a/src/nix-expr-tests/eval.cc b/tests/unit/libexpr/eval.cc similarity index 100% rename from src/nix-expr-tests/eval.cc rename to tests/unit/libexpr/eval.cc diff --git a/src/nix-expr-tests/json.cc b/tests/unit/libexpr/json.cc similarity index 100% rename from src/nix-expr-tests/json.cc rename to tests/unit/libexpr/json.cc diff --git a/src/nix-expr-tests/main.cc b/tests/unit/libexpr/main.cc similarity index 100% rename from src/nix-expr-tests/main.cc rename to tests/unit/libexpr/main.cc diff --git a/src/nix-expr-tests/meson.build b/tests/unit/libexpr/meson.build similarity index 100% rename from src/nix-expr-tests/meson.build rename to tests/unit/libexpr/meson.build diff --git a/src/nix-expr-tests/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc similarity index 100% rename from src/nix-expr-tests/nix_api_expr.cc rename to tests/unit/libexpr/nix_api_expr.cc diff --git a/src/nix-expr-tests/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc similarity index 100% rename from src/nix-expr-tests/nix_api_external.cc rename to tests/unit/libexpr/nix_api_external.cc diff --git a/src/nix-expr-tests/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc similarity index 100% rename from src/nix-expr-tests/nix_api_value.cc rename to tests/unit/libexpr/nix_api_value.cc diff --git a/src/nix-expr-tests/package.nix b/tests/unit/libexpr/package.nix similarity index 94% rename from src/nix-expr-tests/package.nix rename to tests/unit/libexpr/package.nix index ddd79fd55..1667dc77e 100644 --- a/src/nix-expr-tests/package.nix +++ b/tests/unit/libexpr/package.nix @@ -32,9 +32,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -63,7 +63,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-expr-tests/primops.cc b/tests/unit/libexpr/primops.cc similarity index 100% rename from src/nix-expr-tests/primops.cc rename to tests/unit/libexpr/primops.cc diff --git a/src/nix-expr-tests/search-path.cc b/tests/unit/libexpr/search-path.cc similarity index 100% rename from src/nix-expr-tests/search-path.cc rename to tests/unit/libexpr/search-path.cc diff --git a/src/nix-expr-tests/trivial.cc b/tests/unit/libexpr/trivial.cc similarity index 100% rename from src/nix-expr-tests/trivial.cc rename to tests/unit/libexpr/trivial.cc diff --git a/src/nix-expr-tests/value/context.cc b/tests/unit/libexpr/value/context.cc similarity index 100% rename from src/nix-expr-tests/value/context.cc rename to tests/unit/libexpr/value/context.cc diff --git a/src/nix-expr-tests/value/print.cc b/tests/unit/libexpr/value/print.cc similarity index 100% rename from src/nix-expr-tests/value/print.cc rename to tests/unit/libexpr/value/print.cc diff --git a/src/nix-expr-tests/value/value.cc b/tests/unit/libexpr/value/value.cc similarity index 100% rename from src/nix-expr-tests/value/value.cc rename to tests/unit/libexpr/value/value.cc diff --git a/tests/unit/libfetchers/.version b/tests/unit/libfetchers/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libfetchers/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libfetchers/build-utils-meson b/tests/unit/libfetchers/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libfetchers/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-fetchers-tests/data/public-key/defaultType.json b/tests/unit/libfetchers/data/public-key/defaultType.json similarity index 100% rename from src/nix-fetchers-tests/data/public-key/defaultType.json rename to tests/unit/libfetchers/data/public-key/defaultType.json diff --git a/src/nix-fetchers-tests/data/public-key/noRoundTrip.json b/tests/unit/libfetchers/data/public-key/noRoundTrip.json similarity index 100% rename from src/nix-fetchers-tests/data/public-key/noRoundTrip.json rename to tests/unit/libfetchers/data/public-key/noRoundTrip.json diff --git a/src/nix-fetchers-tests/data/public-key/simple.json b/tests/unit/libfetchers/data/public-key/simple.json similarity index 100% rename from src/nix-fetchers-tests/data/public-key/simple.json rename to tests/unit/libfetchers/data/public-key/simple.json diff --git a/src/nix-fetchers-tests/meson.build b/tests/unit/libfetchers/meson.build similarity index 100% rename from src/nix-fetchers-tests/meson.build rename to tests/unit/libfetchers/meson.build diff --git a/src/nix-fetchers-tests/package.nix b/tests/unit/libfetchers/package.nix similarity index 94% rename from src/nix-fetchers-tests/package.nix rename to tests/unit/libfetchers/package.nix index 759743a8b..e9daacaeb 100644 --- a/src/nix-fetchers-tests/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -31,9 +31,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -61,7 +61,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-fetchers-tests/public-key.cc b/tests/unit/libfetchers/public-key.cc similarity index 100% rename from src/nix-fetchers-tests/public-key.cc rename to tests/unit/libfetchers/public-key.cc diff --git a/tests/unit/libflake/.version b/tests/unit/libflake/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libflake/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libflake/build-utils-meson b/tests/unit/libflake/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libflake/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-flake-tests/data/.gitkeep b/tests/unit/libflake/data/.gitkeep similarity index 100% rename from src/nix-flake-tests/data/.gitkeep rename to tests/unit/libflake/data/.gitkeep diff --git a/src/nix-flake-tests/flakeref.cc b/tests/unit/libflake/flakeref.cc similarity index 100% rename from src/nix-flake-tests/flakeref.cc rename to tests/unit/libflake/flakeref.cc diff --git a/src/nix-flake-tests/meson.build b/tests/unit/libflake/meson.build similarity index 100% rename from src/nix-flake-tests/meson.build rename to tests/unit/libflake/meson.build diff --git a/src/nix-flake-tests/package.nix b/tests/unit/libflake/package.nix similarity index 94% rename from src/nix-flake-tests/package.nix rename to tests/unit/libflake/package.nix index a7783593a..c2bcc8eb8 100644 --- a/src/nix-flake-tests/package.nix +++ b/tests/unit/libflake/package.nix @@ -31,9 +31,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -61,7 +61,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-flake-tests/url-name.cc b/tests/unit/libflake/url-name.cc similarity index 100% rename from src/nix-flake-tests/url-name.cc rename to tests/unit/libflake/url-name.cc diff --git a/tests/unit/libstore-support/.version b/tests/unit/libstore-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libstore-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libstore-support/build-utils-meson b/tests/unit/libstore-support/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libstore-support/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-store-test-support/meson.build b/tests/unit/libstore-support/meson.build similarity index 100% rename from src/nix-store-test-support/meson.build rename to tests/unit/libstore-support/meson.build diff --git a/src/nix-store-test-support/package.nix b/tests/unit/libstore-support/package.nix similarity index 93% rename from src/nix-store-test-support/package.nix rename to tests/unit/libstore-support/package.nix index 250f29b86..f3a5bfc82 100644 --- a/src/nix-store-test-support/package.nix +++ b/tests/unit/libstore-support/package.nix @@ -29,9 +29,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -58,7 +58,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-store-test-support/tests/derived-path.cc b/tests/unit/libstore-support/tests/derived-path.cc similarity index 100% rename from src/nix-store-test-support/tests/derived-path.cc rename to tests/unit/libstore-support/tests/derived-path.cc diff --git a/src/nix-store-test-support/tests/derived-path.hh b/tests/unit/libstore-support/tests/derived-path.hh similarity index 100% rename from src/nix-store-test-support/tests/derived-path.hh rename to tests/unit/libstore-support/tests/derived-path.hh diff --git a/src/nix-store-test-support/tests/libstore.hh b/tests/unit/libstore-support/tests/libstore.hh similarity index 100% rename from src/nix-store-test-support/tests/libstore.hh rename to tests/unit/libstore-support/tests/libstore.hh diff --git a/src/nix-store-test-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh similarity index 100% rename from src/nix-store-test-support/tests/nix_api_store.hh rename to tests/unit/libstore-support/tests/nix_api_store.hh diff --git a/src/nix-store-test-support/tests/outputs-spec.cc b/tests/unit/libstore-support/tests/outputs-spec.cc similarity index 100% rename from src/nix-store-test-support/tests/outputs-spec.cc rename to tests/unit/libstore-support/tests/outputs-spec.cc diff --git a/src/nix-store-test-support/tests/outputs-spec.hh b/tests/unit/libstore-support/tests/outputs-spec.hh similarity index 100% rename from src/nix-store-test-support/tests/outputs-spec.hh rename to tests/unit/libstore-support/tests/outputs-spec.hh diff --git a/src/nix-store-test-support/tests/path.cc b/tests/unit/libstore-support/tests/path.cc similarity index 100% rename from src/nix-store-test-support/tests/path.cc rename to tests/unit/libstore-support/tests/path.cc diff --git a/src/nix-store-test-support/tests/path.hh b/tests/unit/libstore-support/tests/path.hh similarity index 100% rename from src/nix-store-test-support/tests/path.hh rename to tests/unit/libstore-support/tests/path.hh diff --git a/src/nix-store-test-support/tests/protocol.hh b/tests/unit/libstore-support/tests/protocol.hh similarity index 100% rename from src/nix-store-test-support/tests/protocol.hh rename to tests/unit/libstore-support/tests/protocol.hh diff --git a/tests/unit/libstore/.version b/tests/unit/libstore/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libstore/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libstore/build-utils-meson b/tests/unit/libstore/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libstore/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-store-tests/common-protocol.cc b/tests/unit/libstore/common-protocol.cc similarity index 100% rename from src/nix-store-tests/common-protocol.cc rename to tests/unit/libstore/common-protocol.cc diff --git a/src/nix-store-tests/content-address.cc b/tests/unit/libstore/content-address.cc similarity index 100% rename from src/nix-store-tests/content-address.cc rename to tests/unit/libstore/content-address.cc diff --git a/src/nix-store-tests/data/common-protocol/content-address.bin b/tests/unit/libstore/data/common-protocol/content-address.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/content-address.bin rename to tests/unit/libstore/data/common-protocol/content-address.bin diff --git a/src/nix-store-tests/data/common-protocol/drv-output.bin b/tests/unit/libstore/data/common-protocol/drv-output.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/drv-output.bin rename to tests/unit/libstore/data/common-protocol/drv-output.bin diff --git a/src/nix-store-tests/data/common-protocol/optional-content-address.bin b/tests/unit/libstore/data/common-protocol/optional-content-address.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/optional-content-address.bin rename to tests/unit/libstore/data/common-protocol/optional-content-address.bin diff --git a/src/nix-store-tests/data/common-protocol/optional-store-path.bin b/tests/unit/libstore/data/common-protocol/optional-store-path.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/optional-store-path.bin rename to tests/unit/libstore/data/common-protocol/optional-store-path.bin diff --git a/src/nix-store-tests/data/common-protocol/realisation.bin b/tests/unit/libstore/data/common-protocol/realisation.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/realisation.bin rename to tests/unit/libstore/data/common-protocol/realisation.bin diff --git a/src/nix-store-tests/data/common-protocol/set.bin b/tests/unit/libstore/data/common-protocol/set.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/set.bin rename to tests/unit/libstore/data/common-protocol/set.bin diff --git a/src/nix-store-tests/data/common-protocol/store-path.bin b/tests/unit/libstore/data/common-protocol/store-path.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/store-path.bin rename to tests/unit/libstore/data/common-protocol/store-path.bin diff --git a/src/nix-store-tests/data/common-protocol/string.bin b/tests/unit/libstore/data/common-protocol/string.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/string.bin rename to tests/unit/libstore/data/common-protocol/string.bin diff --git a/src/nix-store-tests/data/common-protocol/vector.bin b/tests/unit/libstore/data/common-protocol/vector.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/vector.bin rename to tests/unit/libstore/data/common-protocol/vector.bin diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv new file mode 120000 index 000000000..353090ad8 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json similarity index 100% rename from src/nix-store-tests/data/derivation/advanced-attributes-defaults.json rename to tests/unit/libstore/data/derivation/advanced-attributes-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv new file mode 120000 index 000000000..11713da12 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json similarity index 100% rename from src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json rename to tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv new file mode 120000 index 000000000..962f8ea3f --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json similarity index 100% rename from src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json rename to tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes.drv b/tests/unit/libstore/data/derivation/advanced-attributes.drv new file mode 120000 index 000000000..2a53a05ca --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv b/tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv similarity index 100% rename from src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv rename to tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv diff --git a/src/nix-store-tests/data/derivation/bad-version.drv b/tests/unit/libstore/data/derivation/bad-version.drv similarity index 100% rename from src/nix-store-tests/data/derivation/bad-version.drv rename to tests/unit/libstore/data/derivation/bad-version.drv diff --git a/src/nix-store-tests/data/derivation/dynDerivationDeps.drv b/tests/unit/libstore/data/derivation/dynDerivationDeps.drv similarity index 100% rename from src/nix-store-tests/data/derivation/dynDerivationDeps.drv rename to tests/unit/libstore/data/derivation/dynDerivationDeps.drv diff --git a/src/nix-store-tests/data/derivation/dynDerivationDeps.json b/tests/unit/libstore/data/derivation/dynDerivationDeps.json similarity index 100% rename from src/nix-store-tests/data/derivation/dynDerivationDeps.json rename to tests/unit/libstore/data/derivation/dynDerivationDeps.json diff --git a/src/nix-store-tests/data/derivation/output-caFixedFlat.json b/tests/unit/libstore/data/derivation/output-caFixedFlat.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFixedFlat.json rename to tests/unit/libstore/data/derivation/output-caFixedFlat.json diff --git a/src/nix-store-tests/data/derivation/output-caFixedNAR.json b/tests/unit/libstore/data/derivation/output-caFixedNAR.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFixedNAR.json rename to tests/unit/libstore/data/derivation/output-caFixedNAR.json diff --git a/src/nix-store-tests/data/derivation/output-caFixedText.json b/tests/unit/libstore/data/derivation/output-caFixedText.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFixedText.json rename to tests/unit/libstore/data/derivation/output-caFixedText.json diff --git a/src/nix-store-tests/data/derivation/output-caFloating.json b/tests/unit/libstore/data/derivation/output-caFloating.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFloating.json rename to tests/unit/libstore/data/derivation/output-caFloating.json diff --git a/src/nix-store-tests/data/derivation/output-deferred.json b/tests/unit/libstore/data/derivation/output-deferred.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-deferred.json rename to tests/unit/libstore/data/derivation/output-deferred.json diff --git a/src/nix-store-tests/data/derivation/output-impure.json b/tests/unit/libstore/data/derivation/output-impure.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-impure.json rename to tests/unit/libstore/data/derivation/output-impure.json diff --git a/src/nix-store-tests/data/derivation/output-inputAddressed.json b/tests/unit/libstore/data/derivation/output-inputAddressed.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-inputAddressed.json rename to tests/unit/libstore/data/derivation/output-inputAddressed.json diff --git a/src/nix-store-tests/data/derivation/simple.drv b/tests/unit/libstore/data/derivation/simple.drv similarity index 100% rename from src/nix-store-tests/data/derivation/simple.drv rename to tests/unit/libstore/data/derivation/simple.drv diff --git a/src/nix-store-tests/data/derivation/simple.json b/tests/unit/libstore/data/derivation/simple.json similarity index 100% rename from src/nix-store-tests/data/derivation/simple.json rename to tests/unit/libstore/data/derivation/simple.json diff --git a/src/nix-store-tests/data/machines/bad_format b/tests/unit/libstore/data/machines/bad_format similarity index 100% rename from src/nix-store-tests/data/machines/bad_format rename to tests/unit/libstore/data/machines/bad_format diff --git a/src/nix-store-tests/data/machines/valid b/tests/unit/libstore/data/machines/valid similarity index 100% rename from src/nix-store-tests/data/machines/valid rename to tests/unit/libstore/data/machines/valid diff --git a/src/nix-store-tests/data/nar-info/impure.json b/tests/unit/libstore/data/nar-info/impure.json similarity index 100% rename from src/nix-store-tests/data/nar-info/impure.json rename to tests/unit/libstore/data/nar-info/impure.json diff --git a/src/nix-store-tests/data/nar-info/pure.json b/tests/unit/libstore/data/nar-info/pure.json similarity index 100% rename from src/nix-store-tests/data/nar-info/pure.json rename to tests/unit/libstore/data/nar-info/pure.json diff --git a/src/nix-store-tests/data/path-info/empty_impure.json b/tests/unit/libstore/data/path-info/empty_impure.json similarity index 100% rename from src/nix-store-tests/data/path-info/empty_impure.json rename to tests/unit/libstore/data/path-info/empty_impure.json diff --git a/src/nix-store-tests/data/path-info/empty_pure.json b/tests/unit/libstore/data/path-info/empty_pure.json similarity index 100% rename from src/nix-store-tests/data/path-info/empty_pure.json rename to tests/unit/libstore/data/path-info/empty_pure.json diff --git a/src/nix-store-tests/data/path-info/impure.json b/tests/unit/libstore/data/path-info/impure.json similarity index 100% rename from src/nix-store-tests/data/path-info/impure.json rename to tests/unit/libstore/data/path-info/impure.json diff --git a/src/nix-store-tests/data/path-info/pure.json b/tests/unit/libstore/data/path-info/pure.json similarity index 100% rename from src/nix-store-tests/data/path-info/pure.json rename to tests/unit/libstore/data/path-info/pure.json diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.1.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.1.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.1.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.1.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.2.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.2.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.2.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.2.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.3.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.3.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.3.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.3.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.7.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.7.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.7.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.7.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-result-2.2.bin b/tests/unit/libstore/data/serve-protocol/build-result-2.2.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-result-2.2.bin rename to tests/unit/libstore/data/serve-protocol/build-result-2.2.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-result-2.3.bin b/tests/unit/libstore/data/serve-protocol/build-result-2.3.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-result-2.3.bin rename to tests/unit/libstore/data/serve-protocol/build-result-2.3.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-result-2.6.bin b/tests/unit/libstore/data/serve-protocol/build-result-2.6.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-result-2.6.bin rename to tests/unit/libstore/data/serve-protocol/build-result-2.6.bin diff --git a/src/nix-store-tests/data/serve-protocol/content-address.bin b/tests/unit/libstore/data/serve-protocol/content-address.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/content-address.bin rename to tests/unit/libstore/data/serve-protocol/content-address.bin diff --git a/src/nix-store-tests/data/serve-protocol/drv-output.bin b/tests/unit/libstore/data/serve-protocol/drv-output.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/drv-output.bin rename to tests/unit/libstore/data/serve-protocol/drv-output.bin diff --git a/src/nix-store-tests/data/serve-protocol/handshake-to-client.bin b/tests/unit/libstore/data/serve-protocol/handshake-to-client.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/handshake-to-client.bin rename to tests/unit/libstore/data/serve-protocol/handshake-to-client.bin diff --git a/src/nix-store-tests/data/serve-protocol/optional-content-address.bin b/tests/unit/libstore/data/serve-protocol/optional-content-address.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/optional-content-address.bin rename to tests/unit/libstore/data/serve-protocol/optional-content-address.bin diff --git a/src/nix-store-tests/data/serve-protocol/optional-store-path.bin b/tests/unit/libstore/data/serve-protocol/optional-store-path.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/optional-store-path.bin rename to tests/unit/libstore/data/serve-protocol/optional-store-path.bin diff --git a/src/nix-store-tests/data/serve-protocol/realisation.bin b/tests/unit/libstore/data/serve-protocol/realisation.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/realisation.bin rename to tests/unit/libstore/data/serve-protocol/realisation.bin diff --git a/src/nix-store-tests/data/serve-protocol/set.bin b/tests/unit/libstore/data/serve-protocol/set.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/set.bin rename to tests/unit/libstore/data/serve-protocol/set.bin diff --git a/src/nix-store-tests/data/serve-protocol/store-path.bin b/tests/unit/libstore/data/serve-protocol/store-path.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/store-path.bin rename to tests/unit/libstore/data/serve-protocol/store-path.bin diff --git a/src/nix-store-tests/data/serve-protocol/string.bin b/tests/unit/libstore/data/serve-protocol/string.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/string.bin rename to tests/unit/libstore/data/serve-protocol/string.bin diff --git a/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin b/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin rename to tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin diff --git a/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin b/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin rename to tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin diff --git a/src/nix-store-tests/data/serve-protocol/vector.bin b/tests/unit/libstore/data/serve-protocol/vector.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/vector.bin rename to tests/unit/libstore/data/serve-protocol/vector.bin diff --git a/src/nix-store-tests/data/store-reference/auto.txt b/tests/unit/libstore/data/store-reference/auto.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/auto.txt rename to tests/unit/libstore/data/store-reference/auto.txt diff --git a/src/nix-store-tests/data/store-reference/auto_param.txt b/tests/unit/libstore/data/store-reference/auto_param.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/auto_param.txt rename to tests/unit/libstore/data/store-reference/auto_param.txt diff --git a/src/nix-store-tests/data/store-reference/local_1.txt b/tests/unit/libstore/data/store-reference/local_1.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_1.txt rename to tests/unit/libstore/data/store-reference/local_1.txt diff --git a/src/nix-store-tests/data/store-reference/local_2.txt b/tests/unit/libstore/data/store-reference/local_2.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_2.txt rename to tests/unit/libstore/data/store-reference/local_2.txt diff --git a/src/nix-store-tests/data/store-reference/local_shorthand_1.txt b/tests/unit/libstore/data/store-reference/local_shorthand_1.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_shorthand_1.txt rename to tests/unit/libstore/data/store-reference/local_shorthand_1.txt diff --git a/src/nix-store-tests/data/store-reference/local_shorthand_2.txt b/tests/unit/libstore/data/store-reference/local_shorthand_2.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_shorthand_2.txt rename to tests/unit/libstore/data/store-reference/local_shorthand_2.txt diff --git a/src/nix-store-tests/data/store-reference/ssh.txt b/tests/unit/libstore/data/store-reference/ssh.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/ssh.txt rename to tests/unit/libstore/data/store-reference/ssh.txt diff --git a/src/nix-store-tests/data/store-reference/unix.txt b/tests/unit/libstore/data/store-reference/unix.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/unix.txt rename to tests/unit/libstore/data/store-reference/unix.txt diff --git a/src/nix-store-tests/data/store-reference/unix_shorthand.txt b/tests/unit/libstore/data/store-reference/unix_shorthand.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/unix_shorthand.txt rename to tests/unit/libstore/data/store-reference/unix_shorthand.txt diff --git a/src/nix-store-tests/data/worker-protocol/build-mode.bin b/tests/unit/libstore/data/worker-protocol/build-mode.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-mode.bin rename to tests/unit/libstore/data/worker-protocol/build-mode.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.27.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.27.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.27.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.27.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.28.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.28.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.28.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.28.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.29.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.29.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.29.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.29.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.37.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.37.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.37.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.37.bin diff --git a/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin rename to tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin diff --git a/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin rename to tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin diff --git a/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin rename to tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin diff --git a/src/nix-store-tests/data/worker-protocol/content-address.bin b/tests/unit/libstore/data/worker-protocol/content-address.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/content-address.bin rename to tests/unit/libstore/data/worker-protocol/content-address.bin diff --git a/src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin b/tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin rename to tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin diff --git a/src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin b/tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin rename to tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin diff --git a/src/nix-store-tests/data/worker-protocol/drv-output.bin b/tests/unit/libstore/data/worker-protocol/drv-output.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/drv-output.bin rename to tests/unit/libstore/data/worker-protocol/drv-output.bin diff --git a/src/nix-store-tests/data/worker-protocol/handshake-to-client.bin b/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/handshake-to-client.bin rename to tests/unit/libstore/data/worker-protocol/handshake-to-client.bin diff --git a/src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin b/tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin rename to tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin diff --git a/src/nix-store-tests/data/worker-protocol/optional-content-address.bin b/tests/unit/libstore/data/worker-protocol/optional-content-address.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/optional-content-address.bin rename to tests/unit/libstore/data/worker-protocol/optional-content-address.bin diff --git a/src/nix-store-tests/data/worker-protocol/optional-store-path.bin b/tests/unit/libstore/data/worker-protocol/optional-store-path.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/optional-store-path.bin rename to tests/unit/libstore/data/worker-protocol/optional-store-path.bin diff --git a/src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin b/tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin rename to tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin diff --git a/src/nix-store-tests/data/worker-protocol/realisation.bin b/tests/unit/libstore/data/worker-protocol/realisation.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/realisation.bin rename to tests/unit/libstore/data/worker-protocol/realisation.bin diff --git a/src/nix-store-tests/data/worker-protocol/set.bin b/tests/unit/libstore/data/worker-protocol/set.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/set.bin rename to tests/unit/libstore/data/worker-protocol/set.bin diff --git a/src/nix-store-tests/data/worker-protocol/store-path.bin b/tests/unit/libstore/data/worker-protocol/store-path.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/store-path.bin rename to tests/unit/libstore/data/worker-protocol/store-path.bin diff --git a/src/nix-store-tests/data/worker-protocol/string.bin b/tests/unit/libstore/data/worker-protocol/string.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/string.bin rename to tests/unit/libstore/data/worker-protocol/string.bin diff --git a/src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin b/tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin rename to tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin diff --git a/src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin b/tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin rename to tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin diff --git a/src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin b/tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin rename to tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin diff --git a/src/nix-store-tests/data/worker-protocol/vector.bin b/tests/unit/libstore/data/worker-protocol/vector.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/vector.bin rename to tests/unit/libstore/data/worker-protocol/vector.bin diff --git a/src/nix-store-tests/derivation-advanced-attrs.cc b/tests/unit/libstore/derivation-advanced-attrs.cc similarity index 100% rename from src/nix-store-tests/derivation-advanced-attrs.cc rename to tests/unit/libstore/derivation-advanced-attrs.cc diff --git a/src/nix-store-tests/derivation.cc b/tests/unit/libstore/derivation.cc similarity index 100% rename from src/nix-store-tests/derivation.cc rename to tests/unit/libstore/derivation.cc diff --git a/src/nix-store-tests/derived-path.cc b/tests/unit/libstore/derived-path.cc similarity index 100% rename from src/nix-store-tests/derived-path.cc rename to tests/unit/libstore/derived-path.cc diff --git a/src/nix-store-tests/downstream-placeholder.cc b/tests/unit/libstore/downstream-placeholder.cc similarity index 100% rename from src/nix-store-tests/downstream-placeholder.cc rename to tests/unit/libstore/downstream-placeholder.cc diff --git a/src/nix-store-tests/machines.cc b/tests/unit/libstore/machines.cc similarity index 100% rename from src/nix-store-tests/machines.cc rename to tests/unit/libstore/machines.cc diff --git a/src/nix-store-tests/meson.build b/tests/unit/libstore/meson.build similarity index 100% rename from src/nix-store-tests/meson.build rename to tests/unit/libstore/meson.build diff --git a/src/nix-store-tests/nar-info-disk-cache.cc b/tests/unit/libstore/nar-info-disk-cache.cc similarity index 100% rename from src/nix-store-tests/nar-info-disk-cache.cc rename to tests/unit/libstore/nar-info-disk-cache.cc diff --git a/src/nix-store-tests/nar-info.cc b/tests/unit/libstore/nar-info.cc similarity index 100% rename from src/nix-store-tests/nar-info.cc rename to tests/unit/libstore/nar-info.cc diff --git a/src/nix-store-tests/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc similarity index 100% rename from src/nix-store-tests/nix_api_store.cc rename to tests/unit/libstore/nix_api_store.cc diff --git a/src/nix-store-tests/outputs-spec.cc b/tests/unit/libstore/outputs-spec.cc similarity index 100% rename from src/nix-store-tests/outputs-spec.cc rename to tests/unit/libstore/outputs-spec.cc diff --git a/src/nix-store-tests/package.nix b/tests/unit/libstore/package.nix similarity index 90% rename from src/nix-store-tests/package.nix rename to tests/unit/libstore/package.nix index 243b9f149..663e8ba43 100644 --- a/src/nix-store-tests/package.nix +++ b/tests/unit/libstore/package.nix @@ -33,9 +33,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -65,7 +65,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ @@ -92,12 +92,12 @@ mkMesonDerivation (finalAttrs: { root = ../..; fileset = lib.fileset.unions [ ./data - ../../tests/functional/derivation + ../../functional/derivation ]; }; in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${data + "/src/nix-store-test/data"} + export _NIX_TEST_UNIT_DATA=${data + "/unit/libstore/data"} nix-store-tests touch $out ''; diff --git a/src/nix-store-tests/path-info.cc b/tests/unit/libstore/path-info.cc similarity index 100% rename from src/nix-store-tests/path-info.cc rename to tests/unit/libstore/path-info.cc diff --git a/src/nix-store-tests/path.cc b/tests/unit/libstore/path.cc similarity index 100% rename from src/nix-store-tests/path.cc rename to tests/unit/libstore/path.cc diff --git a/src/nix-store-tests/references.cc b/tests/unit/libstore/references.cc similarity index 100% rename from src/nix-store-tests/references.cc rename to tests/unit/libstore/references.cc diff --git a/src/nix-store-tests/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc similarity index 100% rename from src/nix-store-tests/serve-protocol.cc rename to tests/unit/libstore/serve-protocol.cc diff --git a/src/nix-store-tests/store-reference.cc b/tests/unit/libstore/store-reference.cc similarity index 100% rename from src/nix-store-tests/store-reference.cc rename to tests/unit/libstore/store-reference.cc diff --git a/src/nix-store-tests/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc similarity index 100% rename from src/nix-store-tests/worker-protocol.cc rename to tests/unit/libstore/worker-protocol.cc diff --git a/tests/unit/libutil-support/.version b/tests/unit/libutil-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libutil-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/build-utils-meson b/tests/unit/libutil-support/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libutil-support/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-util-test-support/meson.build b/tests/unit/libutil-support/meson.build similarity index 100% rename from src/nix-util-test-support/meson.build rename to tests/unit/libutil-support/meson.build diff --git a/src/nix-util-test-support/package.nix b/tests/unit/libutil-support/package.nix similarity index 93% rename from src/nix-util-test-support/package.nix rename to tests/unit/libutil-support/package.nix index 42a56d58f..431fe91c6 100644 --- a/src/nix-util-test-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -28,9 +28,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -56,7 +56,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-util-test-support/tests/characterization.hh b/tests/unit/libutil-support/tests/characterization.hh similarity index 100% rename from src/nix-util-test-support/tests/characterization.hh rename to tests/unit/libutil-support/tests/characterization.hh diff --git a/src/nix-util-test-support/tests/hash.cc b/tests/unit/libutil-support/tests/hash.cc similarity index 100% rename from src/nix-util-test-support/tests/hash.cc rename to tests/unit/libutil-support/tests/hash.cc diff --git a/src/nix-util-test-support/tests/hash.hh b/tests/unit/libutil-support/tests/hash.hh similarity index 100% rename from src/nix-util-test-support/tests/hash.hh rename to tests/unit/libutil-support/tests/hash.hh diff --git a/src/nix-util-test-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh similarity index 100% rename from src/nix-util-test-support/tests/nix_api_util.hh rename to tests/unit/libutil-support/tests/nix_api_util.hh diff --git a/src/nix-util-test-support/tests/string_callback.cc b/tests/unit/libutil-support/tests/string_callback.cc similarity index 100% rename from src/nix-util-test-support/tests/string_callback.cc rename to tests/unit/libutil-support/tests/string_callback.cc diff --git a/src/nix-util-test-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh similarity index 100% rename from src/nix-util-test-support/tests/string_callback.hh rename to tests/unit/libutil-support/tests/string_callback.hh diff --git a/tests/unit/libutil/.version b/tests/unit/libutil/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libutil/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/src/nix-util-tests/args.cc b/tests/unit/libutil/args.cc similarity index 100% rename from src/nix-util-tests/args.cc rename to tests/unit/libutil/args.cc diff --git a/tests/unit/libutil/build-utils-meson b/tests/unit/libutil/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libutil/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-util-tests/canon-path.cc b/tests/unit/libutil/canon-path.cc similarity index 100% rename from src/nix-util-tests/canon-path.cc rename to tests/unit/libutil/canon-path.cc diff --git a/src/nix-util-tests/chunked-vector.cc b/tests/unit/libutil/chunked-vector.cc similarity index 100% rename from src/nix-util-tests/chunked-vector.cc rename to tests/unit/libutil/chunked-vector.cc diff --git a/src/nix-util-tests/closure.cc b/tests/unit/libutil/closure.cc similarity index 100% rename from src/nix-util-tests/closure.cc rename to tests/unit/libutil/closure.cc diff --git a/src/nix-util-tests/compression.cc b/tests/unit/libutil/compression.cc similarity index 100% rename from src/nix-util-tests/compression.cc rename to tests/unit/libutil/compression.cc diff --git a/src/nix-util-tests/config.cc b/tests/unit/libutil/config.cc similarity index 100% rename from src/nix-util-tests/config.cc rename to tests/unit/libutil/config.cc diff --git a/src/nix-util-tests/data/git/check-data.sh b/tests/unit/libutil/data/git/check-data.sh similarity index 100% rename from src/nix-util-tests/data/git/check-data.sh rename to tests/unit/libutil/data/git/check-data.sh diff --git a/src/nix-util-tests/data/git/hello-world-blob.bin b/tests/unit/libutil/data/git/hello-world-blob.bin similarity index 100% rename from src/nix-util-tests/data/git/hello-world-blob.bin rename to tests/unit/libutil/data/git/hello-world-blob.bin diff --git a/src/nix-util-tests/data/git/hello-world.bin b/tests/unit/libutil/data/git/hello-world.bin similarity index 100% rename from src/nix-util-tests/data/git/hello-world.bin rename to tests/unit/libutil/data/git/hello-world.bin diff --git a/src/nix-util-tests/data/git/tree.bin b/tests/unit/libutil/data/git/tree.bin similarity index 100% rename from src/nix-util-tests/data/git/tree.bin rename to tests/unit/libutil/data/git/tree.bin diff --git a/src/nix-util-tests/data/git/tree.txt b/tests/unit/libutil/data/git/tree.txt similarity index 100% rename from src/nix-util-tests/data/git/tree.txt rename to tests/unit/libutil/data/git/tree.txt diff --git a/src/nix-util-tests/file-content-address.cc b/tests/unit/libutil/file-content-address.cc similarity index 100% rename from src/nix-util-tests/file-content-address.cc rename to tests/unit/libutil/file-content-address.cc diff --git a/src/nix-util-tests/git.cc b/tests/unit/libutil/git.cc similarity index 99% rename from src/nix-util-tests/git.cc rename to tests/unit/libutil/git.cc index 24d24a791..ff934c117 100644 --- a/src/nix-util-tests/git.cc +++ b/tests/unit/libutil/git.cc @@ -88,7 +88,7 @@ TEST_F(GitTest, blob_write) { /** * This data is for "shallow" tree tests. However, we use "real" hashes * so that we can check our test data in a small shell script test test - * (`src/nix-util-tests/data/git/check-data.sh`). + * (`tests/unit/libutil/data/git/check-data.sh`). */ const static Tree tree = { { diff --git a/src/nix-util-tests/hash.cc b/tests/unit/libutil/hash.cc similarity index 100% rename from src/nix-util-tests/hash.cc rename to tests/unit/libutil/hash.cc diff --git a/src/nix-util-tests/hilite.cc b/tests/unit/libutil/hilite.cc similarity index 100% rename from src/nix-util-tests/hilite.cc rename to tests/unit/libutil/hilite.cc diff --git a/src/nix-util-tests/json-utils.cc b/tests/unit/libutil/json-utils.cc similarity index 100% rename from src/nix-util-tests/json-utils.cc rename to tests/unit/libutil/json-utils.cc diff --git a/src/nix-util-tests/logging.cc b/tests/unit/libutil/logging.cc similarity index 100% rename from src/nix-util-tests/logging.cc rename to tests/unit/libutil/logging.cc diff --git a/src/nix-util-tests/lru-cache.cc b/tests/unit/libutil/lru-cache.cc similarity index 100% rename from src/nix-util-tests/lru-cache.cc rename to tests/unit/libutil/lru-cache.cc diff --git a/src/nix-util-tests/meson.build b/tests/unit/libutil/meson.build similarity index 100% rename from src/nix-util-tests/meson.build rename to tests/unit/libutil/meson.build diff --git a/src/nix-util-tests/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc similarity index 100% rename from src/nix-util-tests/nix_api_util.cc rename to tests/unit/libutil/nix_api_util.cc diff --git a/src/nix-util-tests/package.nix b/tests/unit/libutil/package.nix similarity index 94% rename from src/nix-util-tests/package.nix rename to tests/unit/libutil/package.nix index 2491d8722..6bfa571d8 100644 --- a/src/nix-util-tests/package.nix +++ b/tests/unit/libutil/package.nix @@ -32,9 +32,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -63,7 +63,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-util-tests/pool.cc b/tests/unit/libutil/pool.cc similarity index 100% rename from src/nix-util-tests/pool.cc rename to tests/unit/libutil/pool.cc diff --git a/src/nix-util-tests/references.cc b/tests/unit/libutil/references.cc similarity index 100% rename from src/nix-util-tests/references.cc rename to tests/unit/libutil/references.cc diff --git a/src/nix-util-tests/spawn.cc b/tests/unit/libutil/spawn.cc similarity index 100% rename from src/nix-util-tests/spawn.cc rename to tests/unit/libutil/spawn.cc diff --git a/src/nix-util-tests/suggestions.cc b/tests/unit/libutil/suggestions.cc similarity index 100% rename from src/nix-util-tests/suggestions.cc rename to tests/unit/libutil/suggestions.cc diff --git a/src/nix-util-tests/tests.cc b/tests/unit/libutil/tests.cc similarity index 100% rename from src/nix-util-tests/tests.cc rename to tests/unit/libutil/tests.cc diff --git a/src/nix-util-tests/url.cc b/tests/unit/libutil/url.cc similarity index 100% rename from src/nix-util-tests/url.cc rename to tests/unit/libutil/url.cc diff --git a/src/nix-util-tests/xml-writer.cc b/tests/unit/libutil/xml-writer.cc similarity index 100% rename from src/nix-util-tests/xml-writer.cc rename to tests/unit/libutil/xml-writer.cc From b0bc2a97bfe007fbc32f584ed9de5e7cb75a521c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 14:40:34 -0400 Subject: [PATCH 468/910] Put unit tests back in old build system for now --- Makefile | 19 ++++++++++++ Makefile.config.in | 1 + configure.ac | 22 ++++++++++++++ package.nix | 33 +++++++++++++++++++- tests/unit/libexpr-support/local.mk | 23 ++++++++++++++ tests/unit/libexpr/local.mk | 45 ++++++++++++++++++++++++++++ tests/unit/libfetchers/local.mk | 37 +++++++++++++++++++++++ tests/unit/libflake/local.mk | 43 ++++++++++++++++++++++++++ tests/unit/libstore-support/local.mk | 21 +++++++++++++ tests/unit/libstore/local.mk | 38 +++++++++++++++++++++++ tests/unit/libutil-support/local.mk | 19 ++++++++++++ tests/unit/libutil/local.mk | 37 +++++++++++++++++++++++ 12 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libexpr-support/local.mk create mode 100644 tests/unit/libexpr/local.mk create mode 100644 tests/unit/libfetchers/local.mk create mode 100644 tests/unit/libflake/local.mk create mode 100644 tests/unit/libstore-support/local.mk create mode 100644 tests/unit/libstore/local.mk create mode 100644 tests/unit/libutil-support/local.mk create mode 100644 tests/unit/libutil/local.mk diff --git a/Makefile b/Makefile index a65cdbd40..bb64a104e 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,18 @@ makefiles += \ endif endif +ifeq ($(ENABLE_UNIT_TESTS), yes) +makefiles += \ + tests/unit/libutil/local.mk \ + tests/unit/libutil-support/local.mk \ + tests/unit/libstore/local.mk \ + tests/unit/libstore-support/local.mk \ + tests/unit/libfetchers/local.mk \ + tests/unit/libexpr/local.mk \ + tests/unit/libexpr-support/local.mk \ + tests/unit/libflake/local.mk +endif + ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ @@ -91,6 +103,13 @@ include mk/lib.mk # These must be defined after `mk/lib.mk`. Otherwise the first rule # incorrectly becomes the default target. +ifneq ($(ENABLE_UNIT_TESTS), yes) +.PHONY: check +check: + @echo "Unit tests are disabled. Configure without '--disable-unit-tests', or avoid calling 'make check'." + @exit 1 +endif + ifneq ($(ENABLE_FUNCTIONAL_TESTS), yes) .PHONY: installcheck installcheck: diff --git a/Makefile.config.in b/Makefile.config.in index e131484f6..3100d2073 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -12,6 +12,7 @@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ ENABLE_S3 = @ENABLE_S3@ +ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ HAVE_LIBCPUID = @HAVE_LIBCPUID@ HAVE_SECCOMP = @HAVE_SECCOMP@ diff --git a/configure.ac b/configure.ac index b9f190166..4f66a3efc 100644 --- a/configure.ac +++ b/configure.ac @@ -141,6 +141,18 @@ AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]), ENABLE_BUILD=$enableval, ENABLE_BUILD=yes) AC_SUBST(ENABLE_BUILD) +# Building without unit tests is useful for bootstrapping with a smaller footprint +# or running the tests in a separate derivation. Otherwise, we do compile and +# run them. + +AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build the tests]), + ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) +AC_SUBST(ENABLE_UNIT_TESTS) + +AS_IF( + [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], + [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) + AC_ARG_ENABLE(functional-tests, AS_HELP_STRING([--disable-functional-tests],[Do not build the tests]), ENABLE_FUNCTIONAL_TESTS=$enableval, ENABLE_FUNCTIONAL_TESTS=yes) AC_SUBST(ENABLE_FUNCTIONAL_TESTS) @@ -353,6 +365,16 @@ if test "$gc" = yes; then CFLAGS="$old_CFLAGS" fi +AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ + +# Look for gtest. +PKG_CHECK_MODULES([GTEST], [gtest_main gmock_main]) + +# Look for rapidcheck. +PKG_CHECK_MODULES([RAPIDCHECK], [rapidcheck rapidcheck_gtest]) + +]) + # Look for nlohmann/json. PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9]) diff --git a/package.nix b/package.nix index 041786d47..c3e565399 100644 --- a/package.nix +++ b/package.nix @@ -52,6 +52,10 @@ # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true +# Run the unit tests as part of the build. See `installUnitTests` for an +# alternative to this. +, doCheck ? __forDefaults.canRunInstalled + # Run the functional tests as part of the build. , doInstallCheck ? test-client != null || __forDefaults.canRunInstalled @@ -84,6 +88,11 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" +# Whether to install unit tests. This is useful when cross compiling +# since we cannot run them natively during the build, but can do so +# later. +, installUnitTests ? doBuild && !__forDefaults.canExecuteHost + # For running the functional tests against a pre-built Nix. Probably # want to use in conjunction with `doBuild = false;`. , test-daemon ? null @@ -109,7 +118,7 @@ let # things which should instead be gotten via `finalAttrs` in order to # work with overriding. attrs = { - inherit doBuild doInstallCheck; + inherit doBuild doCheck doInstallCheck; }; mkDerivation = @@ -125,11 +134,16 @@ in mkDerivation (finalAttrs: let inherit (finalAttrs) + doCheck doInstallCheck ; doBuild = !finalAttrs.dontBuild; + # Either running the unit tests during the build, or installing them + # to be run later, requiresthe unit tests to be built. + buildUnitTests = doCheck || installUnitTests; + in { inherit pname version; @@ -163,6 +177,8 @@ in { ./scripts/local.mk ] ++ lib.optionals enableManual [ ./doc/manual + ] ++ lib.optionals buildUnitTests [ + ./tests/unit ] ++ lib.optionals doInstallCheck [ ./tests/functional ])); @@ -175,6 +191,8 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && enableManual) "doc" + ++ lib.optional installUnitTests "check" + ++ lib.optional doCheck "testresults" ; nativeBuildInputs = [ @@ -212,6 +230,9 @@ in { ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ lowdown + ] ++ lib.optionals buildUnitTests [ + gtest + rapidcheck ] ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies @@ -228,16 +249,22 @@ in { ] ++ lib.optional enableGC boehmgc; dontBuild = !attrs.doBuild; + doCheck = attrs.doCheck; configureFlags = [ (lib.enableFeature doBuild "build") + (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") + (lib.enableFeature installUnitTests "install-unit-tests") (lib.withFeatureAs true "readline-flavor" readlineFlavor) ] ++ lib.optionals (!forDevShell) [ "--sysconfdir=/etc" + ] ++ lib.optionals installUnitTests [ + "--with-check-bin-dir=${builtins.placeholder "check"}/bin" + "--with-check-lib-dir=${builtins.placeholder "check"}/lib" ] ++ lib.optionals (doBuild) [ "--with-boost=${boost}/lib" ] ++ lib.optionals (doBuild && stdenv.isLinux) [ @@ -318,6 +345,10 @@ in { platforms = lib.platforms.unix ++ lib.platforms.windows; mainProgram = "nix"; broken = !(lib.all (a: a) [ + # We cannot run or install unit tests if we don't build them or + # Nix proper (which they depend on). + (installUnitTests -> doBuild) + (doCheck -> doBuild) # The build process for the manual currently requires extracting # data from the Nix executable we are trying to document. (enableManual -> doBuild) diff --git a/tests/unit/libexpr-support/local.mk b/tests/unit/libexpr-support/local.mk new file mode 100644 index 000000000..0501de33c --- /dev/null +++ b/tests/unit/libexpr-support/local.mk @@ -0,0 +1,23 @@ +libraries += libexpr-test-support + +libexpr-test-support_NAME = libnixexpr-test-support + +libexpr-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libexpr-test-support_INSTALL_DIR := $(checklibdir) +else + libexpr-test-support_INSTALL_DIR := +endif + +libexpr-test-support_SOURCES := \ + $(wildcard $(d)/tests/*.cc) \ + $(wildcard $(d)/tests/value/*.cc) + +libexpr-test-support_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) + +libexpr-test-support_LIBS = \ + libstore-test-support libutil-test-support \ + libexpr libstore libutil + +libexpr-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk new file mode 100644 index 000000000..1617e2823 --- /dev/null +++ b/tests/unit/libexpr/local.mk @@ -0,0 +1,45 @@ +check: libexpr-tests_RUN + +programs += libexpr-tests + +libexpr-tests_NAME := libnixexpr-tests + +libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml + +libexpr-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libexpr-tests_INSTALL_DIR := $(checkbindir) +else + libexpr-tests_INSTALL_DIR := +endif + +libexpr-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libexpr-tests_EXTRA_INCLUDES = \ + -I tests/unit/libexpr-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libexprc) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) + +libexpr-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libexpr libexprc libfetchers libstore libstorec libutil libutilc + +libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk new file mode 100644 index 000000000..286a59030 --- /dev/null +++ b/tests/unit/libfetchers/local.mk @@ -0,0 +1,37 @@ +check: libfetchers-tests_RUN + +programs += libfetchers-tests + +libfetchers-tests_NAME = libnixfetchers-tests + +libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml + +libfetchers-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libfetchers-tests_INSTALL_DIR := $(checkbindir) +else + libfetchers-tests_INSTALL_DIR := +endif + +libfetchers-tests_SOURCES := $(wildcard $(d)/*.cc) + +libfetchers-tests_EXTRA_INCLUDES = \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) + +libfetchers-tests_CXXFLAGS += $(libfetchers-tests_EXTRA_INCLUDES) + +libfetchers-tests_LIBS = \ + libstore-test-support libutil-test-support \ + libfetchers libstore libutil + +libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk new file mode 100644 index 000000000..590bcf7c0 --- /dev/null +++ b/tests/unit/libflake/local.mk @@ -0,0 +1,43 @@ +check: libflake-tests_RUN + +programs += libflake-tests + +libflake-tests_NAME := libnixflake-tests + +libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml + +libflake-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libflake-tests_INSTALL_DIR := $(checkbindir) +else + libflake-tests_INSTALL_DIR := +endif + +libflake-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libflake-tests_EXTRA_INCLUDES = \ + -I tests/unit/libflake-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libflake) \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) \ + +libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) + +libflake-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libflake libexpr libfetchers libstore libutil + +libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libstore-support/local.mk b/tests/unit/libstore-support/local.mk new file mode 100644 index 000000000..56dedd825 --- /dev/null +++ b/tests/unit/libstore-support/local.mk @@ -0,0 +1,21 @@ +libraries += libstore-test-support + +libstore-test-support_NAME = libnixstore-test-support + +libstore-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libstore-test-support_INSTALL_DIR := $(checklibdir) +else + libstore-test-support_INSTALL_DIR := +endif + +libstore-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) + +libstore-test-support_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) + +libstore-test-support_LIBS = \ + libutil-test-support \ + libstore libutil + +libstore-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk new file mode 100644 index 000000000..8d3d6b0af --- /dev/null +++ b/tests/unit/libstore/local.mk @@ -0,0 +1,38 @@ +check: libstore-tests_RUN + +programs += libstore-tests + +libstore-tests_NAME = libnixstore-tests + +libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml + +libstore-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libstore-tests_INSTALL_DIR := $(checkbindir) +else + libstore-tests_INSTALL_DIR := +endif + +libstore-tests_SOURCES := $(wildcard $(d)/*.cc) + +libstore-tests_EXTRA_INCLUDES = \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libstore) \ + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) + +libstore-tests_LIBS = \ + libstore-test-support libutil-test-support \ + libstore libstorec libutil libutilc + +libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libutil-support/local.mk b/tests/unit/libutil-support/local.mk new file mode 100644 index 000000000..5f7835c9f --- /dev/null +++ b/tests/unit/libutil-support/local.mk @@ -0,0 +1,19 @@ +libraries += libutil-test-support + +libutil-test-support_NAME = libnixutil-test-support + +libutil-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libutil-test-support_INSTALL_DIR := $(checklibdir) +else + libutil-test-support_INSTALL_DIR := +endif + +libutil-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) + +libutil-test-support_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) + +libutil-test-support_LIBS = libutil + +libutil-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk new file mode 100644 index 000000000..404f35cf1 --- /dev/null +++ b/tests/unit/libutil/local.mk @@ -0,0 +1,37 @@ +check: libutil-tests_RUN + +programs += libutil-tests + +libutil-tests_NAME = libnixutil-tests + +libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml + +libutil-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libutil-tests_INSTALL_DIR := $(checkbindir) +else + libutil-tests_INSTALL_DIR := +endif + +libutil-tests_SOURCES := $(wildcard $(d)/*.cc) + +libutil-tests_EXTRA_INCLUDES = \ + -I tests/unit/libutil-support \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) + +libutil-tests_LIBS = libutil-test-support libutil libutilc + +libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif + +check: $(d)/data/git/check-data.sh.test + +$(eval $(call run-test,$(d)/data/git/check-data.sh)) From caabdb06d1a8b7d75928b9c35d796a4da5355314 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Tue, 2 Jul 2024 20:55:46 -0500 Subject: [PATCH 469/910] preserve early sequoia migration script --- scripts/sequoia-nixbld-user-migration.sh | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 scripts/sequoia-nixbld-user-migration.sh diff --git a/scripts/sequoia-nixbld-user-migration.sh b/scripts/sequoia-nixbld-user-migration.sh new file mode 100644 index 000000000..778a95688 --- /dev/null +++ b/scripts/sequoia-nixbld-user-migration.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +((NEW_NIX_FIRST_BUILD_UID=331)) + +id_available(){ + dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null +} + +change_nixbld_names_and_ids(){ + local name uid next_id + ((next_id=NEW_NIX_FIRST_BUILD_UID)) + echo "Attempting to migrate _nixbld users." + echo "Each _nixbld# user should have its UID moved to $next_id+" + while read -r name uid; do + echo " Checking $name (uid: $uid)" + # iterate for a clean ID + while id_available "$next_id"; do + ((next_id++)) + if ((next_id >= 400)); then + echo "We've hit UID 400 without placing all of your users :(" + echo "You should use the commands in this script as a starting" + echo "point to review your UID-space and manually move the" + echo "remaining users (or delete them, if you don't need them)." + exit 1 + fi + done + + # first 2 are cleanup, it's OK if they aren't here + sudo dscl . delete "/Users/$name" dsAttrTypeNative:_writers_passwd &>/dev/null || true + sudo dscl . change "/Users/$name" NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true + sudo dscl . change "/Users/$name" UniqueID "$uid" "$next_id" + echo " $name migrated to uid: $next_id" + done < <(dscl . list /Users UniqueID | grep _nixbld | sort -n -k2) +} + +change_nixbld_names_and_ids From a713476790c62a4c3b22ebc17bbd46ef75e96eb8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 3 Jul 2024 09:03:41 +0200 Subject: [PATCH 470/910] docs: split types from syntax (#11013) move together all syntactic and semantic information into one page, and add a page on data types, which in turn links to the syntax and semantics. also split out the note on scoping rules into its own page. Co-authored-by: Ryan Hendrickson --- doc/manual/redirects.js | 14 +- doc/manual/src/SUMMARY.md.in | 7 +- doc/manual/src/_redirects | 2 + doc/manual/src/command-ref/env-common.md | 2 +- doc/manual/src/command-ref/nix-env/install.md | 2 +- doc/manual/src/command-ref/opt-common.md | 2 +- doc/manual/src/glossary.md | 8 +- doc/manual/src/language/constructs.md | 486 ---------- .../src/language/constructs/lookup-path.md | 2 +- doc/manual/src/language/derivations.md | 10 +- doc/manual/src/language/index.md | 60 +- doc/manual/src/language/operators.md | 16 +- doc/manual/src/language/scope.md | 14 + doc/manual/src/language/string-context.md | 2 +- .../src/language/string-interpolation.md | 6 +- doc/manual/src/language/syntax.md | 841 ++++++++++++++++++ doc/manual/src/language/types.md | 91 ++ doc/manual/src/language/values.md | 374 -------- src/libcmd/common-eval-args.cc | 2 +- src/libexpr/primops.cc | 23 +- 20 files changed, 1029 insertions(+), 935 deletions(-) delete mode 100644 doc/manual/src/language/constructs.md create mode 100644 doc/manual/src/language/scope.md create mode 100644 doc/manual/src/language/syntax.md create mode 100644 doc/manual/src/language/types.md delete mode 100644 doc/manual/src/language/values.md diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index f7b479106..0f9f91b03 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -238,12 +238,12 @@ const redirects = { "attr-system": "language/derivations.html#attr-system", "ssec-derivation": "language/derivations.html", "ch-expression-language": "language/index.html", - "sec-constructs": "language/constructs.html", - "sect-let-language": "language/constructs.html#let-language", - "ss-functions": "language/constructs.html#functions", + "sec-constructs": "language/syntax.html", + "sect-let-language": "language/syntax.html#let-expressions", + "ss-functions": "language/syntax.html#functions", "sec-language-operators": "language/operators.html", "table-operators": "language/operators.html", - "ssec-values": "language/values.html", + "ssec-values": "language/types.html", "gloss-closure": "glossary.html#gloss-closure", "gloss-derivation": "glossary.html#gloss-derivation", "gloss-deriver": "glossary.html#gloss-deriver", @@ -335,11 +335,15 @@ const redirects = { "ssec-relnotes-2.2": "release-notes/rl-2.2.html", "ssec-relnotes-2.3": "release-notes/rl-2.3.html", }, - "language/values.html": { + "language/types.html": { "simple-values": "#primitives", "lists": "#list", "strings": "#string", "attribute-sets": "#attribute-set", + "type-number": "#type-int", + }, + "language/syntax.html": { + "scoping-rules": "scoping.html", }, "installation/installing-binary.html": { "linux": "uninstall.html#linux", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index cb54a3822..6e5c1aee1 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -25,11 +25,12 @@ - [Store Types](store/types/index.md) {{#include ./store/types/SUMMARY.md}} - [Nix Language](language/index.md) - - [Data Types](language/values.md) - - [Language Constructs](language/constructs.md) + - [Data Types](language/types.md) + - [String context](language/string-context.md) + - [Syntax and semantics](language/syntax.md) + - [Scoping rules](language/scope.md) - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) - - [String context](language/string-context.md) - [Operators](language/operators.md) - [Derivations](language/derivations.md) - [Advanced Attributes](language/advanced-attributes.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index 7f8edca8e..c52ca0ddd 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -27,6 +27,8 @@ /expressions/language-operators /language/operators 301! /expressions/language-values /language/values 301! /expressions/* /language/:splat 301! +/language/values /language/types 301! +/language/constructs /language/syntax 301! /installation/installation /installation 301! diff --git a/doc/manual/src/command-ref/env-common.md b/doc/manual/src/command-ref/env-common.md index 34e0dbfbd..d3f5f9c14 100644 --- a/doc/manual/src/command-ref/env-common.md +++ b/doc/manual/src/command-ref/env-common.md @@ -10,7 +10,7 @@ Most Nix commands interpret the following environment variables: - [`NIX_PATH`](#env-NIX_PATH) A colon-separated list of directories used to look up the location of Nix - expressions using [paths](@docroot@/language/values.md#type-path) + expressions using [paths](@docroot@/language/types.md#type-path) enclosed in angle brackets (i.e., ``), e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index 76aa70f71..748dd1e7a 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -57,7 +57,7 @@ The arguments *args* map to store paths in a number of possible ways: easy way to copy user environment elements from one profile to another. -- If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument. +- If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/syntax.md#functions) that are called with the [default Nix expression] as their single argument. The derivations returned by those function calls are installed. This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. diff --git a/doc/manual/src/command-ref/opt-common.md b/doc/manual/src/command-ref/opt-common.md index 114b292f9..a42909e2d 100644 --- a/doc/manual/src/command-ref/opt-common.md +++ b/doc/manual/src/command-ref/opt-common.md @@ -143,7 +143,7 @@ Most Nix commands accept the following command-line options: This option is accepted by `nix-env`, `nix-instantiate`, `nix-shell` and `nix-build`. When evaluating Nix expressions, the expression evaluator will automatically try to call functions that it encounters. - It can automatically call functions for which every argument has a [default value](@docroot@/language/constructs.md#functions) (e.g., `{ argName ? defaultValue }: ...`). + It can automatically call functions for which every argument has a [default value](@docroot@/language/syntax.md#functions) (e.g., `{ argName ? defaultValue }: ...`). With `--arg`, you can also call functions that have arguments without a default value (or override a default value). That is, if the evaluator encounters a function with an argument named *name*, it will call it with value *value*. diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 4c9b2c52e..f65ada63a 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -312,7 +312,7 @@ - [package attribute set]{#package-attribute-set} - An [attribute set](@docroot@/language/values.md#attribute-set) containing the attribute `type = "derivation";` (derivation for historical reasons), as well as other attributes, such as + An [attribute set](@docroot@/language/types.md#attribute-set) containing the attribute `type = "derivation";` (derivation for historical reasons), as well as other attributes, such as - attributes that refer to the files of a [package], typically in the form of [derivation outputs](#output), - attributes that declare something about how the package is supposed to be installed or used, - other metadata or arbitrary attributes. @@ -325,9 +325,9 @@ See [String interpolation](./language/string-interpolation.md) for details. - [string]: ./language/values.md#type-string - [path]: ./language/values.md#type-path - [attribute name]: ./language/values.md#attribute-set + [string]: ./language/types.md#type-string + [path]: ./language/types.md#type-path + [attribute name]: ./language/types.md#attribute-set - [base directory]{#gloss-base-directory} diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md deleted file mode 100644 index 491d221b3..000000000 --- a/doc/manual/src/language/constructs.md +++ /dev/null @@ -1,486 +0,0 @@ -# Language Constructs - -## Recursive sets - -Recursive sets are like normal [attribute sets](./values.md#attribute-set), but the attributes can refer to each other. - -> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}` - -Example: - -```nix -rec { - x = y; - y = 123; -}.x -``` - -This evaluates to `123`. - -Note that without `rec` the binding `x = y;` would -refer to the variable `y` in the surrounding scope, if one exists, and -would be invalid if no such variable exists. That is, in a normal -(non-recursive) set, attributes are not added to the lexical scope; in a -recursive set, they are. - -Recursive sets of course introduce the danger of infinite recursion. For -example, the expression - -```nix -rec { - x = y; - y = x; -}.x -``` - -will crash with an `infinite recursion encountered` error message. - -## Let-expressions - -A let-expression allows you to define local variables for an expression. - -> *let-in* = `let` [ *identifier* = *expr* ]... `in` *expr* - -Example: - -```nix -let - x = "foo"; - y = "bar"; -in x + y -``` - -This evaluates to `"foobar"`. - -## Inheriting attributes - -When defining an [attribute set](./values.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes). -This can be shortened using the `inherit` keyword. - -Example: - -```nix -let x = 123; in -{ - inherit x; - y = 456; -} -``` - -is equivalent to - -```nix -let x = 123; in -{ - x = x; - y = 456; -} -``` - -and both evaluate to `{ x = 123; y = 456; }`. - -> **Note** -> -> This works because `x` is added to the lexical scope by the `let` construct. - -It is also possible to inherit attributes from another attribute set. - -Example: - -In this fragment from `all-packages.nix`, - -```nix -graphviz = (import ../tools/graphics/graphviz) { - inherit fetchurl stdenv libpng libjpeg expat x11 yacc; - inherit (xorg) libXaw; -}; - -xorg = { - libX11 = ...; - libXaw = ...; - ... -} - -libpng = ...; -libjpg = ...; -... -``` - -the set used in the function call to the function defined in -`../tools/graphics/graphviz` inherits a number of variables from the -surrounding scope (`fetchurl` ... `yacc`), but also inherits `libXaw` -(the X Athena Widgets) from the `xorg` set. - -Summarizing the fragment - -```nix -... -inherit x y z; -inherit (src-set) a b c; -... -``` - -is equivalent to - -```nix -... -x = x; y = y; z = z; -a = src-set.a; b = src-set.b; c = src-set.c; -... -``` - -when used while defining local variables in a let-expression or while -defining a set. - -In a `let` expression, `inherit` can be used to selectively bring specific attributes of a set into scope. For example - - -```nix -let - x = { a = 1; b = 2; }; - inherit (builtins) attrNames; -in -{ - names = attrNames x; -} -``` - -is equivalent to - -```nix -let - x = { a = 1; b = 2; }; -in -{ - names = builtins.attrNames x; -} -``` - -both evaluate to `{ names = [ "a" "b" ]; }`. - -## Functions - -Functions have the following form: - -```nix -pattern: body -``` - -The pattern specifies what the argument of the function must look like, -and binds variables in the body to (parts of) the argument. There are -three kinds of patterns: - - - If a pattern is a single identifier, then the function matches any - argument. Example: - - ```nix - let negate = x: !x; - concat = x: y: x + y; - in if negate true then concat "foo" "bar" else "" - ``` - - Note that `concat` is a function that takes one argument and returns - a function that takes another argument. This allows partial - parameterisation (i.e., only filling some of the arguments of a - function); e.g., - - ```nix - map (concat "foo") [ "bar" "bla" "abc" ] - ``` - - evaluates to `[ "foobar" "foobla" "fooabc" ]`. - - - A *set pattern* of the form `{ name1, name2, …, nameN }` matches a - set containing the listed attributes, and binds the values of those - attributes to variables in the function body. For example, the - function - - ```nix - { x, y, z }: z + y + x - ``` - - can only be called with a set containing exactly the attributes `x`, - `y` and `z`. No other attributes are allowed. If you want to allow - additional arguments, you can use an ellipsis (`...`): - - ```nix - { x, y, z, ... }: z + y + x - ``` - - This works on any set that contains at least the three named - attributes. - - It is possible to provide *default values* for attributes, in - which case they are allowed to be missing. A default value is - specified by writing `name ? e`, where *e* is an arbitrary - expression. For example, - - ```nix - { x, y ? "foo", z ? "bar" }: z + y + x - ``` - - specifies a function that only requires an attribute named `x`, but - optionally accepts `y` and `z`. - - - An `@`-pattern provides a means of referring to the whole value - being matched: - - ```nix - args@{ x, y, z, ... }: z + y + x + args.a - ``` - - but can also be written as: - - ```nix - { x, y, z, ... } @ args: z + y + x + args.a - ``` - - Here `args` is bound to the argument *as passed*, which is further - matched against the pattern `{ x, y, z, ... }`. - The `@`-pattern makes mainly sense with an ellipsis(`...`) as - you can access attribute names as `a`, using `args.a`, which was - given as an additional attribute to the function. - - > **Warning** - > - > `args@` binds the name `args` to the attribute set that is passed to the function. - > In particular, `args` does *not* include any default values specified with `?` in the function's set pattern. - > - > For instance - > - > ```nix - > let - > f = args@{ a ? 23, ... }: [ a args ]; - > in - > f {} - > ``` - > - > is equivalent to - > - > ```nix - > let - > f = args @ { ... }: [ (args.a or 23) args ]; - > in - > f {} - > ``` - > - > and both expressions will evaluate to: - > - > ```nix - > [ 23 {} ] - > ``` - -Note that functions do not have names. If you want to give them a name, -you can bind them to an attribute, e.g., - -```nix -let concat = { x, y }: x + y; -in concat { x = "foo"; y = "bar"; } -``` - -## Conditionals - -Conditionals look like this: - -```nix -if e1 then e2 else e3 -``` - -where *e1* is an expression that should evaluate to a Boolean value -(`true` or `false`). - -## Assertions - -Assertions are generally used to check that certain requirements on or -between features and dependencies hold. They look like this: - -```nix -assert e1; e2 -``` - -where *e1* is an expression that should evaluate to a Boolean value. If -it evaluates to `true`, *e2* is returned; otherwise expression -evaluation is aborted and a backtrace is printed. - -Here is a Nix expression for the Subversion package that shows how -assertions can be used:. - -```nix -{ localServer ? false -, httpServer ? false -, sslSupport ? false -, pythonBindings ? false -, javaSwigBindings ? false -, javahlBindings ? false -, stdenv, fetchurl -, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null -}: - -assert localServer -> db4 != null; ① -assert httpServer -> httpd != null && httpd.expat == expat; ② -assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); ③ -assert pythonBindings -> swig != null && swig.pythonSupport; -assert javaSwigBindings -> swig != null && swig.javaSupport; -assert javahlBindings -> j2sdk != null; - -stdenv.mkDerivation { - name = "subversion-1.1.1"; - ... - openssl = if sslSupport then openssl else null; ④ - ... -} -``` - -The points of interest are: - -1. This assertion states that if Subversion is to have support for - local repositories, then Berkeley DB is needed. So if the Subversion - function is called with the `localServer` argument set to `true` but - the `db4` argument set to `null`, then the evaluation fails. - - Note that `->` is the [logical - implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication) - Boolean operation. - -2. This is a more subtle condition: if Subversion is built with Apache - (`httpServer`) support, then the Expat library (an XML library) used - by Subversion should be same as the one used by Apache. This is - because in this configuration Subversion code ends up being linked - with Apache code, and if the Expat libraries do not match, a build- - or runtime link error or incompatibility might occur. - -3. This assertion says that in order for Subversion to have SSL support - (so that it can access `https` URLs), an OpenSSL library must be - passed. Additionally, it says that *if* Apache support is enabled, - then Apache's OpenSSL should match Subversion's. (Note that if - Apache support is not enabled, we don't care about Apache's - OpenSSL.) - -4. The conditional here is not really related to assertions, but is - worth pointing out: it ensures that if SSL support is disabled, then - the Subversion derivation is not dependent on OpenSSL, even if a - non-`null` value was passed. This prevents an unnecessary rebuild of - Subversion if OpenSSL changes. - -## With-expressions - -A *with-expression*, - -```nix -with e1; e2 -``` - -introduces the set *e1* into the lexical scope of the expression *e2*. -For instance, - -```nix -let as = { x = "foo"; y = "bar"; }; -in with as; x + y -``` - -evaluates to `"foobar"` since the `with` adds the `x` and `y` attributes -of `as` to the lexical scope in the expression `x + y`. The most common -use of `with` is in conjunction with the `import` function. E.g., - -```nix -with (import ./definitions.nix); ... -``` - -makes all attributes defined in the file `definitions.nix` available as -if they were defined locally in a `let`-expression. - -The bindings introduced by `with` do not shadow bindings introduced by -other means, e.g. - -```nix -let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ... -``` - -establishes the same scope as - -```nix -let a = 1; in let a = 2; in let a = 3; in let a = 4; in ... -``` - -Variables coming from outer `with` expressions *are* shadowed: - -```nix -with { a = "outer"; }; -with { a = "inner"; }; -a -``` - -Does evaluate to `"inner"`. - -## Comments - -- Inline comments start with `#` and run until the end of the line. - - > **Example** - > - > ```nix - > # A number - > 2 # Equals 1 + 1 - > ``` - > - > ```console - > 2 - > ``` - -- Block comments start with `/*` and run until the next occurrence of `*/`. - - > **Example** - > - > ```nix - > /* - > Block comments - > can span multiple lines. - > */ "hello" - > ``` - > - > ```console - > "hello" - > ``` - - This means that block comments cannot be nested. - - > **Example** - > - > ```nix - > /* /* nope */ */ 1 - > ``` - > - > ```console - > error: syntax error, unexpected '*' - > - > at «string»:1:15: - > - > 1| /* /* nope */ * - > | ^ - > ``` - - Consider escaping nested comments and unescaping them in post-processing. - - > **Example** - > - > ```nix - > /* /* nested *\/ */ 1 - > ``` - > - > ```console - > 1 - > ``` - -## Scoping rules - -Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. - -* primary scope --- explicitly-bound variables - * [`let`](#let-expressions) - * [`inherit`](#inheriting-attributes) - * function arguments - -* secondary scope --- implicitly-bound variables - * [`with`](#with-expressions) -Primary scope takes precedence over secondary scope. -See [`with`](#with-expressions) for a detailed example. diff --git a/doc/manual/src/language/constructs/lookup-path.md b/doc/manual/src/language/constructs/lookup-path.md index e87d2922b..11278f3a8 100644 --- a/doc/manual/src/language/constructs/lookup-path.md +++ b/doc/manual/src/language/constructs/lookup-path.md @@ -4,7 +4,7 @@ > > *lookup-path* = `<` *identifier* [ `/` *identifier* ]... `>` -A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/values.md#type-path) if the identifier matches a search path entry. +A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/types.md#type-path) if the identifier matches a search path entry. The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index b95900cdd..8879fe706 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -12,7 +12,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect ### Required -- [`name`]{#attr-name} ([String](@docroot@/language/values.md#type-string)) +- [`name`]{#attr-name} ([String](@docroot@/language/types.md#type-string)) A symbolic name for the derivation. It is added to the [store path] of the corresponding [store derivation] as well as to its [output paths](@docroot@/glossary.md#gloss-output-path). @@ -31,7 +31,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > The store derivation's path will be `/nix/store/-hello.drv`. > The [output](#attr-outputs) paths will be of the form `/nix/store/-hello[-]` -- [`system`]{#attr-system} ([String](@docroot@/language/values.md#type-string)) +- [`system`]{#attr-system} ([String](@docroot@/language/types.md#type-string)) The system type on which the [`builder`](#attr-builder) executable is meant to be run. @@ -66,7 +66,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > > [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation. -- [`builder`]{#attr-builder} ([Path](@docroot@/language/values.md#type-path) | [String](@docroot@/language/values.md#type-string)) +- [`builder`]{#attr-builder} ([Path](@docroot@/language/types.md#type-path) | [String](@docroot@/language/types.md#type-string)) Path to an executable that will perform the build. @@ -113,7 +113,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect ### Optional -- [`args`]{#attr-args} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string)) +- [`args`]{#attr-args} ([List](@docroot@/language/types.md#list) of [String](@docroot@/language/types.md#type-string)) Default: `[ ]` @@ -132,7 +132,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > }; > ``` -- [`outputs`]{#attr-outputs} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string)) +- [`outputs`]{#attr-outputs} ([List](@docroot@/language/types.md#list) of [String](@docroot@/language/types.md#type-string)) Default: `[ "out" ]` diff --git a/doc/manual/src/language/index.md b/doc/manual/src/language/index.md index 3694480d7..2bfdbb8a0 100644 --- a/doc/manual/src/language/index.md +++ b/doc/manual/src/language/index.md @@ -53,7 +53,7 @@ This is an incomplete overview of language features, by example. - *Basic values ([primitives](@docroot@/language/values.md#primitives))* + *Basic values ([primitives](@docroot@/language/types.md#primitives))* @@ -71,7 +71,7 @@ This is an incomplete overview of language features, by example. - A [string](@docroot@/language/values.md#type-string) + A [string](@docroot@/language/types.md#type-string) @@ -102,7 +102,7 @@ This is an incomplete overview of language features, by example. - A [comment](@docroot@/language/constructs.md#comments). + A [comment](@docroot@/language/syntax.md#comments). @@ -130,7 +130,7 @@ This is an incomplete overview of language features, by example. - [Booleans](@docroot@/language/values.md#type-boolean) + [Booleans](@docroot@/language/types.md#type-boolean) @@ -142,7 +142,7 @@ This is an incomplete overview of language features, by example. - [Null](@docroot@/language/values.md#type-null) value + [Null](@docroot@/language/types.md#type-null) value @@ -154,7 +154,7 @@ This is an incomplete overview of language features, by example. - An [integer](@docroot@/language/values.md#type-number) + An [integer](@docroot@/language/types.md#type-int) @@ -166,7 +166,7 @@ This is an incomplete overview of language features, by example. - A [floating point number](@docroot@/language/values.md#type-number) + A [floating point number](@docroot@/language/types.md#type-float) @@ -178,7 +178,7 @@ This is an incomplete overview of language features, by example. - An absolute [path](@docroot@/language/values.md#type-path) + An absolute [path](@docroot@/language/types.md#type-path) @@ -190,7 +190,7 @@ This is an incomplete overview of language features, by example. - A [path](@docroot@/language/values.md#type-path) relative to the file containing this Nix expression + A [path](@docroot@/language/types.md#type-path) relative to the file containing this Nix expression @@ -202,7 +202,7 @@ This is an incomplete overview of language features, by example. - A home [path](@docroot@/language/values.md#type-path). Evaluates to the `"/.config"`. + A home [path](@docroot@/language/types.md#type-path). Evaluates to the `"/.config"`. @@ -238,7 +238,7 @@ This is an incomplete overview of language features, by example. - An [attribute set](@docroot@/language/values.md#attribute-set) with attributes named `x` and `y` + An [attribute set](@docroot@/language/types.md#attribute-set) with attributes named `x` and `y` @@ -262,7 +262,7 @@ This is an incomplete overview of language features, by example. - A [recursive set](@docroot@/language/constructs.md#recursive-sets), equivalent to `{ x = "foo"; y = "foobar"; }`. + A [recursive set](@docroot@/language/syntax.md#recursive-sets), equivalent to `{ x = "foo"; y = "foobar"; }`. @@ -278,7 +278,7 @@ This is an incomplete overview of language features, by example. - [Lists](@docroot@/language/values.md#list) with three elements. + [Lists](@docroot@/language/types.md#list) with three elements. @@ -362,7 +362,7 @@ This is an incomplete overview of language features, by example. - [Attribute selection](@docroot@/language/values.md#attribute-set) (evaluates to `1`) + [Attribute selection](@docroot@/language/types.md#attribute-set) (evaluates to `1`) @@ -374,7 +374,7 @@ This is an incomplete overview of language features, by example. - [Attribute selection](@docroot@/language/values.md#attribute-set) with default (evaluates to `3`) + [Attribute selection](@docroot@/language/types.md#attribute-set) with default (evaluates to `3`) @@ -410,7 +410,7 @@ This is an incomplete overview of language features, by example. - [Conditional expression](@docroot@/language/constructs.md#conditionals). + [Conditional expression](@docroot@/language/syntax.md#conditionals). @@ -422,7 +422,7 @@ This is an incomplete overview of language features, by example. - [Assertion](@docroot@/language/constructs.md#assertions) check (evaluates to `"yes!"`). + [Assertion](@docroot@/language/syntax.md#assertions) check (evaluates to `"yes!"`). @@ -434,7 +434,7 @@ This is an incomplete overview of language features, by example. - Variable definition. See [`let`-expressions](@docroot@/language/constructs.md#let-expressions). + Variable definition. See [`let`-expressions](@docroot@/language/syntax.md#let-expressions). @@ -448,7 +448,7 @@ This is an incomplete overview of language features, by example. Add all attributes from the given set to the scope (evaluates to `1`). - See [`with`-expressions](@docroot@/language/constructs.md#with-expressions) for details and shadowing caveats. + See [`with`-expressions](@docroot@/language/syntax.md#with-expressions) for details and shadowing caveats. @@ -462,7 +462,7 @@ This is an incomplete overview of language features, by example. Adds the variables to the current scope (attribute set or `let` binding). Desugars to `pkgs = pkgs; src = src;`. - See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). + See [Inheriting attributes](@docroot@/language/syntax.md#inheriting-attributes). @@ -476,14 +476,14 @@ This is an incomplete overview of language features, by example. Adds the attributes, from the attribute set in parentheses, to the current scope (attribute set or `let` binding). Desugars to `lib = pkgs.lib; stdenv = pkgs.stdenv;`. - See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). + See [Inheriting attributes](@docroot@/language/syntax.md#inheriting-attributes). - *[Functions](@docroot@/language/constructs.md#functions) (lambdas)* + *[Functions](@docroot@/language/syntax.md#functions) (lambdas)* @@ -500,7 +500,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects an integer and returns it increased by 1. + A [function](@docroot@/language/syntax.md#functions) that expects an integer and returns it increased by 1. @@ -512,7 +512,7 @@ This is an incomplete overview of language features, by example. - Curried [function](@docroot@/language/constructs.md#functions), equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. + Curried [function](@docroot@/language/syntax.md#functions), equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. @@ -524,7 +524,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) call (evaluates to 101) + A [function](@docroot@/language/syntax.md#functions) call (evaluates to 101) @@ -536,7 +536,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) bound to a variable and subsequently called by name (evaluates to 103) + A [function](@docroot@/language/syntax.md#functions) bound to a variable and subsequently called by name (evaluates to 103) @@ -548,7 +548,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and concatenates them + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attributes `x` and `y` and concatenates them @@ -560,7 +560,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` @@ -572,7 +572,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and ignores any other attributes + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attributes `x` and `y` and ignores any other attributes @@ -586,7 +586,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y`, and binds the whole set to `args` + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attributes `x` and `y`, and binds the whole set to `args` diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 9e5ab52a2..9660a764d 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -27,11 +27,11 @@ | Logical disjunction (`OR`) | *bool* \|\| *bool* | left | 13 | | [Logical implication] | *bool* `->` *bool* | right | 14 | -[string]: ./values.md#type-string -[path]: ./values.md#type-path -[number]: ./values.md#type-number -[list]: ./values.md#list -[attribute set]: ./values.md#attribute-set +[string]: ./types.md#type-string +[path]: ./types.md#type-path +[number]: ./types.md#type-float +[list]: ./types.md#list +[attribute set]: ./types.md#attribute-set ## Attribute selection @@ -42,7 +42,7 @@ Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*. If the attribute doesn’t exist, return the *expr* after `or` if provided, otherwise abort evaluation. -An attribute path is a dot-separated list of [attribute names](./values.md#attribute-set). +An attribute path is a dot-separated list of [attribute names](./types.md#attribute-set). > **Syntax** > @@ -61,7 +61,7 @@ The result is a [Boolean] value. See also: [`builtins.hasAttr`](@docroot@/language/builtins.md#builtins-hasAttr) -[Boolean]: ./values.md#type-boolean +[Boolean]: ./types.md#type-boolean [Has attribute]: #has-attribute @@ -172,7 +172,7 @@ All comparison operators are implemented in terms of `<`, and the following equi - Numbers are type-compatible, see [arithmetic] operators. - Floating point numbers only differ up to a limited precision. -[function]: ./constructs.md#functions +[function]: ./syntax.md#functions [Equality]: #equality diff --git a/doc/manual/src/language/scope.md b/doc/manual/src/language/scope.md new file mode 100644 index 000000000..5c6aed38d --- /dev/null +++ b/doc/manual/src/language/scope.md @@ -0,0 +1,14 @@ +# Scoping rules + +Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. + +* primary scope: explicitly-bound variables + * [`let`](./syntax.md#let-expressions) + * [`inherit`](./syntax.md#inheriting-attributes) + * [function](./syntax.md#functions) arguments + +* secondary scope: implicitly-bound variables + * [`with`](./syntax.md#with-expressions) + +Primary scope takes precedence over secondary scope. +See [`with`](./syntax.md#with-expressions) for a detailed example. diff --git a/doc/manual/src/language/string-context.md b/doc/manual/src/language/string-context.md index 88ae0d8b0..6a3482cfd 100644 --- a/doc/manual/src/language/string-context.md +++ b/doc/manual/src/language/string-context.md @@ -111,7 +111,7 @@ It creates an [attribute set] representing the string context, which can be insp [`builtins.hasContext`]: ./builtins.md#builtins-hasContext [`builtins.getContext`]: ./builtins.md#builtins-getContext -[attribute set]: ./values.md#attribute-set +[attribute set]: ./types.md#attribute-set ## Clearing string contexts diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 7b4a5cfef..1778bdfa0 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -4,9 +4,9 @@ String interpolation is a language feature where a [string], [path], or [attribu Such a construct is called *interpolated string*, and the expression inside is an [interpolated expression](#interpolated-expression). -[string]: ./values.md#type-string -[path]: ./values.md#type-path -[attribute set]: ./values.md#attribute-set +[string]: ./types.md#type-string +[path]: ./types.md#type-path +[attribute set]: ./types.md#attribute-set ## Examples diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md new file mode 100644 index 000000000..238c502f9 --- /dev/null +++ b/doc/manual/src/language/syntax.md @@ -0,0 +1,841 @@ +# Language Constructs + +This section covers syntax and semantics of the Nix language. + +## Basic Literals + +### String {#string-literal} + + *Strings* can be written in three ways. + + The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. + Strings can span multiple lines. + The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. + + [string interpolation]: ./string-interpolation.md + + The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): + + - Double quote (`"`) + + > **Example** + > + > ```nix + > "\"" + > ``` + > + > "\"" + + - Backslash (`\`) + + > **Example** + > + > ```nix + > "\\" + > ``` + > + > "\\" + + - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" + + > **Example** + > + > ```nix + > "\${" + > ``` + > + > "\${" + + The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > "$${" + > ``` + > + > "$\${" + + String values are output on the terminal with Nix-specific escaping. + Strings written to files will contain the characters encoded by the escaping. + + The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: + + ```nix + '' + This is the first line. + This is the second line. + This is the third line. + '' + ``` + + This kind of string literal intelligently strips indentation from + the start of each line. To be precise, it strips from each line a + number of spaces equal to the minimal indentation of the string as a + whole (disregarding the indentation of empty lines). For instance, + the first and second line are indented two spaces, while the third + line is indented four spaces. Thus, two spaces are stripped from + each line, so the resulting string is + + ```nix + "This is the first line.\nThis is the second line.\n This is the third line.\n" + ``` + + > **Note** + > + > Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. + + > **Warning** + > + > Prefixed tab characters are not stripped. + > + > > **Example** + > > + > > The following indented string is prefixed with tabs: + > > + > > '' + > > all: + > > @echo hello + > > '' + > > + > > "\tall:\n\t\t@echo hello\n" + + Indented strings support [string interpolation]. + + The following must be escaped to represent them in an indented string: + + - `$` is escaped by prefixing it with two single quotes (`''`) + + > **Example** + > + > ```nix + > '' + > ''$ + > '' + > ``` + > + > "$\n" + + - `''` is escaped by prefixing it with one single quote (`'`) + + > **Example** + > + > ```nix + > '' + > ''' + > '' + > ``` + > + > "''\n" + + These special characters are escaped as follows: + - Linefeed (`\n`): `''\n` + - Carriage return (`\r`): `''\r` + - Tab (`\t`): `''\t` + + `''\` escapes any other character. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > '' + > $${ + > '' + > ``` + > + > "$\${\n" + + Indented strings are primarily useful in that they allow multi-line + string literals to follow the indentation of the enclosing Nix + expression, and that less escaping is typically necessary for + strings representing languages such as shell scripts and + configuration files because `''` is much less common than `"`. + Example: + + ```nix + stdenv.mkDerivation { + ... + postInstall = + '' + mkdir $out/bin $out/etc + cp foo $out/bin + echo "Hello World" > $out/etc/foo.conf + ${if enableBar then "cp bar $out/bin" else ""} + ''; + ... + } + ``` + + Finally, as a convenience, *URIs* as defined in appendix B of + [RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as + is*, without quotes. For instance, the string + `"http://example.org/foo.tar.bz2"` can also be written as + `http://example.org/foo.tar.bz2`. + +### Number {#number-literal} + + + + Numbers, which can be *integers* (like `123`) or *floating point* + (like `123.43` or `.27e13`). + + See [arithmetic] and [comparison] operators for semantics. + + [arithmetic]: ./operators.md#arithmetic + [comparison]: ./operators.md#comparison + +### Path {#path-literal} + + *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. + + Paths are suitable for referring to local files, and are often preferable over strings. + - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. + - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). + - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. + - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. + + A path literal must contain at least one slash to be recognised as such. + For instance, `builder.sh` is not a path: + it's parsed as an expression that selects the attribute `sh` from the variable `builder`. + + Path literals may also refer to absolute paths by starting with a slash. + + > **Note** + > + > Absolute paths make expressions less portable. + > In the case where a function translates a path literal into an absolute path string for a configuration file, it is recommended to write a string literal instead. + > This avoids some confusion about whether files at that location will be used during evaluation. + > It also avoids unintentional situations where some function might try to copy everything at the location into the store. + + If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. + For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. + Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. + + Paths can be used in [string interpolation] and string concatenation. + For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. + + Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. + For example, assume you used a file path in an interpolated string during a `nix repl` session. + Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. + + [store path]: @docroot@/store/store-path.md + + Path literals can also include [string interpolation], besides being [interpolated into other expressions]. + + [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions + + At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. + + `a.${foo}/b.${bar}` is a syntactically valid number division operation. + `./a.${foo}/b.${bar}` is a path. + + [Lookup path](./constructs/lookup-path.md) literals such as `` also resolve to path values. + +## List {#list-literal} + +Lists are formed by enclosing a whitespace-separated list of values +between square brackets. For example, + +```nix +[ 123 ./foo.nix "abc" (f { x = y; }) ] +``` + +defines a list of four elements, the last being the result of a call to +the function `f`. Note that function calls have to be enclosed in +parentheses. If they had been omitted, e.g., + +```nix +[ 123 ./foo.nix "abc" f { x = y; } ] +``` + +the result would be a list of five elements, the fourth one being a +function and the fifth being a set. + +Note that lists are only lazy in values, and they are strict in length. + +Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). + +## Attribute Set {#attrs-literal} + +An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). + +An attribute name can be an identifier or a [string](#string). +An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`). + +> **Syntax** +> +> *name* = *identifier* | *string* \ +> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*` + +Names and values are separated by an equal sign (`=`). +Each value is an arbitrary expression terminated by a semicolon (`;`). + +> **Syntax** +> +> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}` + +Attributes can appear in any order. +An attribute name may only occur once. + +Example: + +```nix +{ + x = 123; + text = "Hello"; + y = f { bla = 456; }; +} +``` + +This defines a set with attributes named `x`, `text`, `y`. + +Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection). + +Example: + +```nix +{ a = "Foo"; b = "Bar"; }.a +``` + +This evaluates to `"Foo"`. + +It is possible to provide a default value in an attribute selection using the `or` keyword. + +Example: + +```nix +{ a = "Foo"; b = "Bar"; }.c or "Xyzzy" +``` + +```nix +{ a = "Foo"; b = "Bar"; }.c.d.e.f.g or "Xyzzy" +``` + +will both evaluate to `"Xyzzy"` because there is no `c` attribute in the set. + +You can use arbitrary double-quoted strings as attribute names: + +```nix +{ "$!@#?" = 123; }."$!@#?" +``` + +```nix +let bar = "bar"; in +{ "foo ${bar}" = 123; }."foo ${bar}" +``` + +Both will evaluate to `123`. + +Attribute names support [string interpolation]: + +```nix +let bar = "foo"; in +{ foo = 123; }.${bar} +``` + +```nix +let bar = "foo"; in +{ ${bar} = 123; }.foo +``` + +Both will evaluate to `123`. + +In the special case where an attribute name inside of a set declaration +evaluates to `null` (which is normally an error, as `null` cannot be coerced to +a string), that attribute is simply not added to the set: + +```nix +{ ${if foo then "bar" else null} = true; } +``` + +This will evaluate to `{}` if `foo` evaluates to `false`. + +A set that has a `__functor` attribute whose value is callable (i.e. is +itself a function or a set with a `__functor` attribute whose value is +callable) can be applied as if it were a function, with the set itself +passed in first , e.g., + +```nix +let add = { __functor = self: x: x + self.x; }; + inc = add // { x = 1; }; +in inc 1 +``` + +evaluates to `2`. This can be used to attach metadata to a function +without the caller needing to treat it specially, or to implement a form +of object-oriented programming, for example. + +## Recursive sets + +Recursive sets are like normal [attribute sets](./types.md#attribute-set), but the attributes can refer to each other. + +> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}` + +Example: + +```nix +rec { + x = y; + y = 123; +}.x +``` + +This evaluates to `123`. + +Note that without `rec` the binding `x = y;` would +refer to the variable `y` in the surrounding scope, if one exists, and +would be invalid if no such variable exists. That is, in a normal +(non-recursive) set, attributes are not added to the lexical scope; in a +recursive set, they are. + +Recursive sets of course introduce the danger of infinite recursion. For +example, the expression + +```nix +rec { + x = y; + y = x; +}.x +``` + +will crash with an `infinite recursion encountered` error message. + +## Let-expressions + +A let-expression allows you to define local variables for an expression. + +> *let-in* = `let` [ *identifier* = *expr* ]... `in` *expr* + +Example: + +```nix +let + x = "foo"; + y = "bar"; +in x + y +``` + +This evaluates to `"foobar"`. + +## Inheriting attributes + +When defining an [attribute set](./types.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes). +This can be shortened using the `inherit` keyword. + +Example: + +```nix +let x = 123; in +{ + inherit x; + y = 456; +} +``` + +is equivalent to + +```nix +let x = 123; in +{ + x = x; + y = 456; +} +``` + +and both evaluate to `{ x = 123; y = 456; }`. + +> **Note** +> +> This works because `x` is added to the lexical scope by the `let` construct. + +It is also possible to inherit attributes from another attribute set. + +Example: + +In this fragment from `all-packages.nix`, + +```nix +graphviz = (import ../tools/graphics/graphviz) { + inherit fetchurl stdenv libpng libjpeg expat x11 yacc; + inherit (xorg) libXaw; +}; + +xorg = { + libX11 = ...; + libXaw = ...; + ... +} + +libpng = ...; +libjpg = ...; +... +``` + +the set used in the function call to the function defined in +`../tools/graphics/graphviz` inherits a number of variables from the +surrounding scope (`fetchurl` ... `yacc`), but also inherits `libXaw` +(the X Athena Widgets) from the `xorg` set. + +Summarizing the fragment + +```nix +... +inherit x y z; +inherit (src-set) a b c; +... +``` + +is equivalent to + +```nix +... +x = x; y = y; z = z; +a = src-set.a; b = src-set.b; c = src-set.c; +... +``` + +when used while defining local variables in a let-expression or while +defining a set. + +In a `let` expression, `inherit` can be used to selectively bring specific attributes of a set into scope. For example + + +```nix +let + x = { a = 1; b = 2; }; + inherit (builtins) attrNames; +in +{ + names = attrNames x; +} +``` + +is equivalent to + +```nix +let + x = { a = 1; b = 2; }; +in +{ + names = builtins.attrNames x; +} +``` + +both evaluate to `{ names = [ "a" "b" ]; }`. + +## Functions + +Functions have the following form: + +```nix +pattern: body +``` + +The pattern specifies what the argument of the function must look like, +and binds variables in the body to (parts of) the argument. There are +three kinds of patterns: + + - If a pattern is a single identifier, then the function matches any + argument. Example: + + ```nix + let negate = x: !x; + concat = x: y: x + y; + in if negate true then concat "foo" "bar" else "" + ``` + + Note that `concat` is a function that takes one argument and returns + a function that takes another argument. This allows partial + parameterisation (i.e., only filling some of the arguments of a + function); e.g., + + ```nix + map (concat "foo") [ "bar" "bla" "abc" ] + ``` + + evaluates to `[ "foobar" "foobla" "fooabc" ]`. + + - A *set pattern* of the form `{ name1, name2, …, nameN }` matches a + set containing the listed attributes, and binds the values of those + attributes to variables in the function body. For example, the + function + + ```nix + { x, y, z }: z + y + x + ``` + + can only be called with a set containing exactly the attributes `x`, + `y` and `z`. No other attributes are allowed. If you want to allow + additional arguments, you can use an ellipsis (`...`): + + ```nix + { x, y, z, ... }: z + y + x + ``` + + This works on any set that contains at least the three named + attributes. + + It is possible to provide *default values* for attributes, in + which case they are allowed to be missing. A default value is + specified by writing `name ? e`, where *e* is an arbitrary + expression. For example, + + ```nix + { x, y ? "foo", z ? "bar" }: z + y + x + ``` + + specifies a function that only requires an attribute named `x`, but + optionally accepts `y` and `z`. + + - An `@`-pattern provides a means of referring to the whole value + being matched: + + ```nix + args@{ x, y, z, ... }: z + y + x + args.a + ``` + + but can also be written as: + + ```nix + { x, y, z, ... } @ args: z + y + x + args.a + ``` + + Here `args` is bound to the argument *as passed*, which is further + matched against the pattern `{ x, y, z, ... }`. + The `@`-pattern makes mainly sense with an ellipsis(`...`) as + you can access attribute names as `a`, using `args.a`, which was + given as an additional attribute to the function. + + > **Warning** + > + > `args@` binds the name `args` to the attribute set that is passed to the function. + > In particular, `args` does *not* include any default values specified with `?` in the function's set pattern. + > + > For instance + > + > ```nix + > let + > f = args@{ a ? 23, ... }: [ a args ]; + > in + > f {} + > ``` + > + > is equivalent to + > + > ```nix + > let + > f = args @ { ... }: [ (args.a or 23) args ]; + > in + > f {} + > ``` + > + > and both expressions will evaluate to: + > + > ```nix + > [ 23 {} ] + > ``` + +Note that functions do not have names. If you want to give them a name, +you can bind them to an attribute, e.g., + +```nix +let concat = { x, y }: x + y; +in concat { x = "foo"; y = "bar"; } +``` + +## Conditionals + +Conditionals look like this: + +```nix +if e1 then e2 else e3 +``` + +where *e1* is an expression that should evaluate to a Boolean value +(`true` or `false`). + +## Assertions + +Assertions are generally used to check that certain requirements on or +between features and dependencies hold. They look like this: + +```nix +assert e1; e2 +``` + +where *e1* is an expression that should evaluate to a Boolean value. If +it evaluates to `true`, *e2* is returned; otherwise expression +evaluation is aborted and a backtrace is printed. + +Here is a Nix expression for the Subversion package that shows how +assertions can be used:. + +```nix +{ localServer ? false +, httpServer ? false +, sslSupport ? false +, pythonBindings ? false +, javaSwigBindings ? false +, javahlBindings ? false +, stdenv, fetchurl +, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null +}: + +assert localServer -> db4 != null; ① +assert httpServer -> httpd != null && httpd.expat == expat; ② +assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); ③ +assert pythonBindings -> swig != null && swig.pythonSupport; +assert javaSwigBindings -> swig != null && swig.javaSupport; +assert javahlBindings -> j2sdk != null; + +stdenv.mkDerivation { + name = "subversion-1.1.1"; + ... + openssl = if sslSupport then openssl else null; ④ + ... +} +``` + +The points of interest are: + +1. This assertion states that if Subversion is to have support for + local repositories, then Berkeley DB is needed. So if the Subversion + function is called with the `localServer` argument set to `true` but + the `db4` argument set to `null`, then the evaluation fails. + + Note that `->` is the [logical + implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication) + Boolean operation. + +2. This is a more subtle condition: if Subversion is built with Apache + (`httpServer`) support, then the Expat library (an XML library) used + by Subversion should be same as the one used by Apache. This is + because in this configuration Subversion code ends up being linked + with Apache code, and if the Expat libraries do not match, a build- + or runtime link error or incompatibility might occur. + +3. This assertion says that in order for Subversion to have SSL support + (so that it can access `https` URLs), an OpenSSL library must be + passed. Additionally, it says that *if* Apache support is enabled, + then Apache's OpenSSL should match Subversion's. (Note that if + Apache support is not enabled, we don't care about Apache's + OpenSSL.) + +4. The conditional here is not really related to assertions, but is + worth pointing out: it ensures that if SSL support is disabled, then + the Subversion derivation is not dependent on OpenSSL, even if a + non-`null` value was passed. This prevents an unnecessary rebuild of + Subversion if OpenSSL changes. + +## With-expressions + +A *with-expression*, + +```nix +with e1; e2 +``` + +introduces the set *e1* into the lexical scope of the expression *e2*. +For instance, + +```nix +let as = { x = "foo"; y = "bar"; }; +in with as; x + y +``` + +evaluates to `"foobar"` since the `with` adds the `x` and `y` attributes +of `as` to the lexical scope in the expression `x + y`. The most common +use of `with` is in conjunction with the `import` function. E.g., + +```nix +with (import ./definitions.nix); ... +``` + +makes all attributes defined in the file `definitions.nix` available as +if they were defined locally in a `let`-expression. + +The bindings introduced by `with` do not shadow bindings introduced by +other means, e.g. + +```nix +let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ... +``` + +establishes the same scope as + +```nix +let a = 1; in let a = 2; in let a = 3; in let a = 4; in ... +``` + +Variables coming from outer `with` expressions *are* shadowed: + +```nix +with { a = "outer"; }; +with { a = "inner"; }; +a +``` + +Does evaluate to `"inner"`. + +## Comments + +- Inline comments start with `#` and run until the end of the line. + + > **Example** + > + > ```nix + > # A number + > 2 # Equals 1 + 1 + > ``` + > + > ```console + > 2 + > ``` + +- Block comments start with `/*` and run until the next occurrence of `*/`. + + > **Example** + > + > ```nix + > /* + > Block comments + > can span multiple lines. + > */ "hello" + > ``` + > + > ```console + > "hello" + > ``` + + This means that block comments cannot be nested. + + > **Example** + > + > ```nix + > /* /* nope */ */ 1 + > ``` + > + > ```console + > error: syntax error, unexpected '*' + > + > at «string»:1:15: + > + > 1| /* /* nope */ * + > | ^ + > ``` + + Consider escaping nested comments and unescaping them in post-processing. + + > **Example** + > + > ```nix + > /* /* nested *\/ */ 1 + > ``` + > + > ```console + > 1 + > ``` diff --git a/doc/manual/src/language/types.md b/doc/manual/src/language/types.md new file mode 100644 index 000000000..1b3e6b247 --- /dev/null +++ b/doc/manual/src/language/types.md @@ -0,0 +1,91 @@ +# Data Types + +Every value in the Nix language has one of the following types: + +* [Integer](#type-int) +* [Float](#type-float) +* [Boolean](#type-bool) +* [String](#type-string) +* [Path](#type-path) +* [Null](#type-null) +* [Attribute set](#type-attrs) +* [List](#type-list) +* [Function](#type-function) +* [External](#type-external) + +## Primitives + +### Integer {#type-int} + +An _integer_ in the Nix language is a signed 64-bit integer. + +Non-negative integers can be expressed as [integer literals](syntax.md#number-literal). +Negative integers are created with the [arithmetic negation operator](./operators.md#arithmetic). +The function [`builtins.isInt`](builtins.md#builtins-isInt) can be used to determine if a value is an integer. + +### Float {#type-float} + +A _float_ in the Nix language is a 64-bit [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) floating-point number. + +Most non-negative floats can be expressed as [float literals](syntax.md#number-literal). +Negative floats are created with the [arithmetic negation operator](./operators.md#arithmetic). +The function [`builtins.isFloat`](builtins.md#builtins-isFloat) can be used to determine if a value is a float. + +### Boolean {#type-bool} + +A _boolean_ in the Nix language is one of _true_ or _false_. + + + +These values are available as attributes of [`builtins`](builtin-constants.md#builtins-builtins) as [`builtins.true`](builtin-constants.md#builtins-true) and [`builtins.false`](builtin-constants.md#builtins-false). +The function [`builtins.isBool`](builtins.md#builtins-isBool) can be used to determine if a value is a boolean. + +### String {#type-string} + +A _string_ in the Nix language is an immutable, finite-length sequence of bytes, along with a [string context](string-context.md). +Nix does not assume or support working natively with character encodings. + +String values without string context can be expressed as [string literals](syntax.md#string-literal). +The function [`builtins.isString`](builtins.md#builtins-isString) can be used to determine if a value is a string. + +### Path {#type-path} + + + +The function [`builtins.isPath`](builtins.md#builtins-isPath) can be used to determine if a value is a path. + +### Null {#type-null} + +There is a single value of type _null_ in the Nix language. + + + +This value is available as an attribute on the [`builtins`](builtin-constants.md#builtins-builtins) attribute set as [`builtins.null`](builtin-constants.md#builtins-null). + +## Compound values + +### Attribute set {#type-attrs} + + + +An attribute set can be constructed with an [attribute set literal](syntax.md#attrs-literal). +The function [`builtins.isAttrs`](builtins.md#builtins-isAttrs) can be used to determine if a value is an attribute set. + +### List {#type-list} + + + +A list can be constructed with a [list literal](syntax.md#list-literal). +The function [`builtins.isList`](builtins.md#builtins-isList) can be used to determine if a value is a list. + +## Function {#type-function} + + + +A function can be constructed with a [function expression](syntax.md#functions). +The function [`builtins.isFunction`](builtins.md#builtins-isFunction) can be used to determine if a value is a function. + +## External {#type-external} + +An _external_ value is an opaque value created by a Nix [plugin](../command-ref/conf-file.md#conf-plugin-files). +Such a value can be substituted in Nix expressions but only created and used by plugin code. diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md deleted file mode 100644 index ddd55a47e..000000000 --- a/doc/manual/src/language/values.md +++ /dev/null @@ -1,374 +0,0 @@ -# Data Types - -## Primitives - -- String - - *Strings* can be written in three ways. - - The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. - Strings can span multiple lines. - The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. - - [string interpolation]: ./string-interpolation.md - - The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): - - - Double quote (`"`) - - > **Example** - > - > ```nix - > "\"" - > ``` - > - > "\"" - - - Backslash (`\`) - - > **Example** - > - > ```nix - > "\\" - > ``` - > - > "\\" - - - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" - - > **Example** - > - > ```nix - > "\${" - > ``` - > - > "\${" - - The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. - - A "double-dollar-curly" (`$${`) can be written literally. - - > **Example** - > - > ```nix - > "$${" - > ``` - > - > "$\${" - - String values are output on the terminal with Nix-specific escaping. - Strings written to files will contain the characters encoded by the escaping. - - The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: - - ```nix - '' - This is the first line. - This is the second line. - This is the third line. - '' - ``` - - This kind of string literal intelligently strips indentation from - the start of each line. To be precise, it strips from each line a - number of spaces equal to the minimal indentation of the string as a - whole (disregarding the indentation of empty lines). For instance, - the first and second line are indented two spaces, while the third - line is indented four spaces. Thus, two spaces are stripped from - each line, so the resulting string is - - ```nix - "This is the first line.\nThis is the second line.\n This is the third line.\n" - ``` - - > **Note** - > - > Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. - - > **Warning** - > - > Prefixed tab characters are not stripped. - > - > > **Example** - > > - > > The following indented string is prefixed with tabs: - > > - > > '' - > > all: - > > @echo hello - > > '' - > > - > > "\tall:\n\t\t@echo hello\n" - - Indented strings support [string interpolation]. - - The following must be escaped to represent them in an indented string: - - - `$` is escaped by prefixing it with two single quotes (`''`) - - > **Example** - > - > ```nix - > '' - > ''$ - > '' - > ``` - > - > "$\n" - - - `''` is escaped by prefixing it with one single quote (`'`) - - > **Example** - > - > ```nix - > '' - > ''' - > '' - > ``` - > - > "''\n" - - These special characters are escaped as follows: - - Linefeed (`\n`): `''\n` - - Carriage return (`\r`): `''\r` - - Tab (`\t`): `''\t` - - `''\` escapes any other character. - - A "double-dollar-curly" (`$${`) can be written literally. - - > **Example** - > - > ```nix - > '' - > $${ - > '' - > ``` - > - > "$\${\n" - - Indented strings are primarily useful in that they allow multi-line - string literals to follow the indentation of the enclosing Nix - expression, and that less escaping is typically necessary for - strings representing languages such as shell scripts and - configuration files because `''` is much less common than `"`. - Example: - - ```nix - stdenv.mkDerivation { - ... - postInstall = - '' - mkdir $out/bin $out/etc - cp foo $out/bin - echo "Hello World" > $out/etc/foo.conf - ${if enableBar then "cp bar $out/bin" else ""} - ''; - ... - } - ``` - - Finally, as a convenience, *URIs* as defined in appendix B of - [RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as - is*, without quotes. For instance, the string - `"http://example.org/foo.tar.bz2"` can also be written as - `http://example.org/foo.tar.bz2`. - -- Number - - Numbers, which can be *integers* (like `123`) or *floating point* - (like `123.43` or `.27e13`). - - See [arithmetic] and [comparison] operators for semantics. - - [arithmetic]: ./operators.md#arithmetic - [comparison]: ./operators.md#comparison - -- Path - - *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - - Paths are suitable for referring to local files, and are often preferable over strings. - - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. - - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). - - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. - - A path literal must contain at least one slash to be recognised as such. - For instance, `builder.sh` is not a path: - it's parsed as an expression that selects the attribute `sh` from the variable `builder`. - - Path literals may also refer to absolute paths by starting with a slash. - - > **Note** - > - > Absolute paths make expressions less portable. - > In the case where a function translates a path literal into an absolute path string for a configuration file, it is recommended to write a string literal instead. - > This avoids some confusion about whether files at that location will be used during evaluation. - > It also avoids unintentional situations where some function might try to copy everything at the location into the store. - - If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. - For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. - Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. - - Paths can be used in [string interpolation] and string concatenation. - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. - - Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. - For example, assume you used a file path in an interpolated string during a `nix repl` session. - Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. - - [store path]: @docroot@/store/store-path.md - - Path literals can also include [string interpolation], besides being [interpolated into other expressions]. - - [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions - - At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. - - `a.${foo}/b.${bar}` is a syntactically valid number division operation. - `./a.${foo}/b.${bar}` is a path. - - [Lookup path](./constructs/lookup-path.md) literals such as `` also resolve to path values. - -- Boolean - - *Booleans* with values `true` and `false`. - -- Null - - The null value, denoted as `null`. - -## List - -Lists are formed by enclosing a whitespace-separated list of values -between square brackets. For example, - -```nix -[ 123 ./foo.nix "abc" (f { x = y; }) ] -``` - -defines a list of four elements, the last being the result of a call to -the function `f`. Note that function calls have to be enclosed in -parentheses. If they had been omitted, e.g., - -```nix -[ 123 ./foo.nix "abc" f { x = y; } ] -``` - -the result would be a list of five elements, the fourth one being a -function and the fifth being a set. - -Note that lists are only lazy in values, and they are strict in length. - -Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). - -## Attribute Set - -An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). - -An attribute name can be an identifier or a [string](#string). -An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`). - -> **Syntax** -> -> *name* = *identifier* | *string* \ -> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*` - -Names and values are separated by an equal sign (`=`). -Each value is an arbitrary expression terminated by a semicolon (`;`). - -> **Syntax** -> -> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}` - -Attributes can appear in any order. -An attribute name may only occur once. - -Example: - -```nix -{ - x = 123; - text = "Hello"; - y = f { bla = 456; }; -} -``` - -This defines a set with attributes named `x`, `text`, `y`. - -Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection). - -Example: - -```nix -{ a = "Foo"; b = "Bar"; }.a -``` - -This evaluates to `"Foo"`. - -It is possible to provide a default value in an attribute selection using the `or` keyword. - -Example: - -```nix -{ a = "Foo"; b = "Bar"; }.c or "Xyzzy" -``` - -```nix -{ a = "Foo"; b = "Bar"; }.c.d.e.f.g or "Xyzzy" -``` - -will both evaluate to `"Xyzzy"` because there is no `c` attribute in the set. - -You can use arbitrary double-quoted strings as attribute names: - -```nix -{ "$!@#?" = 123; }."$!@#?" -``` - -```nix -let bar = "bar"; in -{ "foo ${bar}" = 123; }."foo ${bar}" -``` - -Both will evaluate to `123`. - -Attribute names support [string interpolation]: - -```nix -let bar = "foo"; in -{ foo = 123; }.${bar} -``` - -```nix -let bar = "foo"; in -{ ${bar} = 123; }.foo -``` - -Both will evaluate to `123`. - -In the special case where an attribute name inside of a set declaration -evaluates to `null` (which is normally an error, as `null` cannot be coerced to -a string), that attribute is simply not added to the set: - -```nix -{ ${if foo then "bar" else null} = true; } -``` - -This will evaluate to `{}` if `foo` evaluates to `false`. - -A set that has a `__functor` attribute whose value is callable (i.e. is -itself a function or a set with a `__functor` attribute whose value is -callable) can be applied as if it were a function, with the set itself -passed in first , e.g., - -```nix -let add = { __functor = self: x: x + self.x; }; - inc = add // { x = 1; }; -in inc 1 -``` - -evaluates to `2`. This can be used to attach metadata to a function -without the caller needing to treat it specially, or to implement a form -of object-oriented programming, for example. diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 393fed532..01546f9a0 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -74,7 +74,7 @@ MixEvalArgs::MixEvalArgs() .description = R"( Add *path* to the Nix search path. The Nix search path is initialized from the colon-separated [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) environment - variable, and is used to look up the location of Nix expressions using [paths](@docroot@/language/values.md#type-path) enclosed in angle + variable, and is used to look up the location of Nix expressions using [paths](@docroot@/language/types.md#type-path) enclosed in angle brackets (i.e., ``). For instance, passing diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 08f719f0f..7a946bdaa 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -732,11 +732,12 @@ static RegisterPrimOp primop_genericClosure(PrimOp { Each attribute set in the list `startSet` and the list returned by `operator` must have an attribute `key`, which must support equality comparison. The value of `key` can be one of the following types: - - [Number](@docroot@/language/values.md#type-number) - - [Boolean](@docroot@/language/values.md#type-boolean) - - [String](@docroot@/language/values.md#type-string) - - [Path](@docroot@/language/values.md#type-path) - - [List](@docroot@/language/values.md#list) + - [Int](@docroot@/language/types.md#type-int) + - [Float](@docroot@/language/types.md#type-float) + - [Boolean](@docroot@/language/types.md#type-boolean) + - [String](@docroot@/language/types.md#type-string) + - [Path](@docroot@/language/types.md#type-path) + - [List](@docroot@/language/types.md#list) The result is produced by calling the `operator` on each `item` that has not been called yet, including newly added items, until no new items are added. Items are compared by their `key` attribute. @@ -1709,7 +1710,7 @@ static RegisterPrimOp primop_baseNameOf({ .name = "baseNameOf", .args = {"x"}, .doc = R"( - Return the *base name* of either a [path value](@docroot@/language/values.md#type-path) *x* or a string *x*, depending on which type is passed, and according to the following rules. + Return the *base name* of either a [path value](@docroot@/language/types.md#type-path) *x* or a string *x*, depending on which type is passed, and according to the following rules. For a path value, the *base name* is considered to be the part of the path after the last directory separator, including any file extensions. This is the simple case, as path values don't have trailing slashes. @@ -1843,7 +1844,7 @@ static RegisterPrimOp primop_findFile(PrimOp { .doc = R"( Find *lookup-path* in *search-path*. - A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes: + A search path is represented list of [attribute sets](./types.md#attribute-set) with two attributes: - `prefix` is a relative path. - `path` denotes a file system location The exact syntax depends on the command line interface. @@ -1864,7 +1865,7 @@ static RegisterPrimOp primop_findFile(PrimOp { } ``` - The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match: + The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/types.md#type-path) of the match: - If *lookup-path* matches `prefix`, then the remainder of *lookup-path* (the "suffix") is searched for within the directory denoted by `path`. Note that the `path` may need to be downloaded at this point to look inside. @@ -2292,7 +2293,7 @@ static RegisterPrimOp primop_toFile({ ``` Note that `${configFile}` is a - [string interpolation](@docroot@/language/values.md#type-string), so the result of the + [string interpolation](@docroot@/language/types.md#type-string), so the result of the expression `configFile` (i.e., a path like `/nix/store/m7p7jfny445k...-foo.conf`) will be spliced into the resulting string. @@ -4538,7 +4539,7 @@ void EvalState::createBaseEnv() It can be returned by [comparison operators](@docroot@/language/operators.md#Comparison) and used in - [conditional expressions](@docroot@/language/constructs.md#Conditionals). + [conditional expressions](@docroot@/language/syntax.md#Conditionals). The name `true` is not special, and can be shadowed: @@ -4558,7 +4559,7 @@ void EvalState::createBaseEnv() It can be returned by [comparison operators](@docroot@/language/operators.md#Comparison) and used in - [conditional expressions](@docroot@/language/constructs.md#Conditionals). + [conditional expressions](@docroot@/language/syntax.md#Conditionals). The name `false` is not special, and can be shadowed: From 2cf24a2df0f07479be57afe2391da2ae3d25be10 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 3 Jul 2024 17:34:02 +0530 Subject: [PATCH 471/910] fix tests and minor changes - use the iterator in `CanonPath` to count `level` - use the `CanonPath::basename` method - use `CanonPath::root` instead of `CanonPath{""}` - remove `Path` and `PathView`, use `std::filesystem::path` directly --- src/libstore/nar-accessor.cc | 6 ++++-- src/libutil/archive.cc | 4 ++-- src/libutil/archive.hh | 2 +- src/libutil/file-system.cc | 2 +- src/libutil/fs-sink.cc | 12 +++++++++--- src/libutil/git.cc | 2 +- tests/unit/libutil/git.cc | 6 +++--- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index 33dde48e5..b1079b027 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -73,7 +73,9 @@ struct NarAccessor : public SourceAccessor NarMember & createMember(const CanonPath & path, NarMember member) { - size_t level = std::count(path.rel().begin(), path.rel().end(), '/'); + size_t level = 0; + for (auto _ : path) ++level; + while (parents.size() > level) parents.pop(); if (parents.empty()) { @@ -83,7 +85,7 @@ struct NarAccessor : public SourceAccessor } else { if (parents.top()->stat.type != Type::tDirectory) throw Error("NAR file missing parent directory of path '%s'", path); - auto result = parents.top()->children.emplace(baseNameOf(path.rel()), std::move(member)); + auto result = parents.top()->children.emplace(*path.baseName(), std::move(member)); auto & ref = result.first->second; parents.push(&ref); return ref; diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 3693a1ffd..e2ebcda0c 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -290,11 +290,11 @@ void parseDump(FileSystemObjectSink & sink, Source & source) } if (version != narVersionMagic1) throw badArchive("input doesn't look like a Nix archive"); - parse(sink, source, CanonPath{""}); + parse(sink, source, CanonPath::root); } -void restorePath(const Path & path, Source & source) +void restorePath(const std::filesystem::path & path, Source & source) { RestoreSink sink; sink.dstPath = path; diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index bd70072ce..2da8f0cb1 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -75,7 +75,7 @@ void dumpString(std::string_view s, Sink & sink); void parseDump(FileSystemObjectSink & sink, Source & source); -void restorePath(const Path & path, Source & source); +void restorePath(const std::filesystem::path & path, Source & source); /** * Read a NAR from 'source' and write it to 'sink'. diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9307a0b53..f75851bbd 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -127,7 +127,7 @@ Path dirOf(const PathView path) } -std::string_view baseNameOf(PathView path) +std::string_view baseNameOf(std::string_view path) { if (path.empty()) return ""; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index b4900cf8b..597272ec9 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -84,8 +84,12 @@ struct RestoreRegularFile : CreateRegularFileSink { void RestoreSink::createRegularFile(const CanonPath & path, std::function func) { - std::cout << "SCREAM!!!====== " << dstPath / path.rel() << std::endl; - std::filesystem::path p = dstPath / path.rel(); + auto p = dstPath; + + if (!path.rel().empty()) { + p = p / path.rel(); + } + RestoreRegularFile crf; crf.fd = #ifdef _WIN32 @@ -136,7 +140,9 @@ void RestoreRegularFile::operator () (std::string_view data) void RestoreSink::createSymlink(const CanonPath & path, const std::string & target) { - std::filesystem::path p = dstPath / path.rel(); + auto p = dstPath; + if (!path.rel().empty()) + p = dstPath / path.rel(); nix::createSymlink(target, p); } diff --git a/src/libutil/git.cc b/src/libutil/git.cc index f23df566a..a6968a43e 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -208,7 +208,7 @@ std::optional convertMode(SourceAccessor::Type type) void restore(FileSystemObjectSink & sink, Source & source, std::function hook) { - parse(sink, CanonPath{""}, source, BlobMode::Regular, [&](CanonPath name, TreeEntry entry) { + parse(sink, CanonPath::root, source, BlobMode::Regular, [&](CanonPath name, TreeEntry entry) { auto [accessor, from] = hook(entry.hash); auto stat = accessor->lstat(from); auto gotOpt = convertMode(stat.type); diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index 9454bb675..a0125d023 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -67,7 +67,7 @@ TEST_F(GitTest, blob_read) { StringSink out; RegularFileSink out2 { out }; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Blob); - parseBlob(out2, CanonPath{""}, in, BlobMode::Regular, mockXpSettings); + parseBlob(out2, CanonPath::root, in, BlobMode::Regular, mockXpSettings); auto expected = readFile(goldenMaster("hello-world.bin")); @@ -132,7 +132,7 @@ TEST_F(GitTest, tree_read) { NullFileSystemObjectSink out; Tree got; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Tree); - parseTree(out, CanonPath{""}, in, [&](auto & name, auto entry) { + parseTree(out, CanonPath::root, in, [&](auto & name, auto entry) { auto name2 = std::string{name.rel()}; if (entry.mode == Mode::Directory) name2 += '/'; @@ -227,7 +227,7 @@ TEST_F(GitTest, both_roundrip) { mockXpSettings); }; - mkSinkHook(CanonPath{""}, root.hash, BlobMode::Regular); + mkSinkHook(CanonPath::root, root.hash, BlobMode::Regular); ASSERT_EQ(*files, *files2); } From 79ed3df8f84adf87b1db708d431e768b8fcc4c05 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 14:14:20 +0200 Subject: [PATCH 472/910] Tarball fetcher: Fix handling of cached tarballs Fixes a regression introduced in 5a9e1c0d20e2332c79fb0fd7570315a5d93041f2 where downloading a cached file causes the error "Failed to open archive (Unrecognized archive format)". --- src/libutil/tarfile.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index f0e24e937..445968b57 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -81,6 +81,7 @@ TarArchive::TarArchive(Source & source, bool raw, std::optional com if (!raw) { archive_read_support_format_tar(archive); archive_read_support_format_zip(archive); + archive_read_support_format_empty(archive); } else { archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); @@ -99,6 +100,7 @@ TarArchive::TarArchive(const Path & path) archive_read_support_filter_all(archive); archive_read_support_format_tar(archive); archive_read_support_format_zip(archive); + archive_read_support_format_empty(archive); archive_read_set_option(archive, NULL, "mac-ext", NULL); check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s"); } From 8bdd0ecd80cbe85a03abb3d8eaf67bc771e64703 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 15:52:49 +0200 Subject: [PATCH 473/910] Add a test --- tests/nixos/tarball-flakes.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index 2e7f98b5e..e20b853f7 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -74,8 +74,10 @@ in assert info["revision"] == "${nixpkgs.rev}" assert info["revCount"] == 1234 - # Check that fetching with rev/revCount/narHash succeeds. + # Check that a 0-byte cached (304) result works. + machine.succeed("nix flake metadata --refresh --json http://localhost/tags/latest.tar.gz") + # Check that fetching with rev/revCount/narHash succeeds. machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?rev=" + info["revision"]) machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?revCount=" + str(info["revCount"])) machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?narHash=" + info["locked"]["narHash"]) From 9d95c228eeb2750d37f86228905d11eea5fb1e05 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 16:28:24 +0200 Subject: [PATCH 474/910] Tarball fetcher: Fix fetchToStore() and eval caching --- src/libfetchers/fetchers.cc | 1 + src/libfetchers/tarball.cc | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 170a8910c..087880ebe 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -260,6 +260,7 @@ std::pair, Input> Input::getAccessorUnchecked(ref sto auto [accessor, final] = scheme->getAccessor(store, *this); + assert(!accessor->fingerprint); accessor->fingerprint = scheme->getFingerprint(store, final); return {accessor, std::move(final)}; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 5de367052..aa8ff652f 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -365,6 +365,16 @@ struct TarballInputScheme : CurlInputScheme return {result.accessor, input}; } + + std::optional getFingerprint(ref store, const Input & input) const override + { + if (auto narHash = input.getNarHash()) + return narHash->to_string(HashFormat::SRI, true); + else if (auto rev = input.getRev()) + return rev->gitRev(); + else + return std::nullopt; + } }; static auto rTarballInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); From 1ff186fc6e99e57501fee9291e7013ea88d8720a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 16:37:26 +0200 Subject: [PATCH 475/910] nix flake metadata: Show flake fingerprint This is useful for testing/debugging and maybe for sharing eval caches (since it tells you what file in ~/.cache/nix/eval-cache-v5 to copy). --- src/nix/flake.cc | 6 ++++++ tests/functional/flakes/flakes.sh | 1 + tests/nixos/tarball-flakes.nix | 3 +++ 3 files changed, 10 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index fb7ea6211..84c659023 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -233,6 +233,8 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["lastModified"] = *lastModified; j["path"] = storePath; j["locks"] = lockedFlake.lockFile.toJSON().first; + if (auto fingerprint = lockedFlake.getFingerprint(store)) + j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false); logger->cout("%s", j.dump()); } else { logger->cout( @@ -265,6 +267,10 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON logger->cout( ANSI_BOLD "Last modified:" ANSI_NORMAL " %s", std::put_time(std::localtime(&*lastModified), "%F %T")); + if (auto fingerprint = lockedFlake.getFingerprint(store)) + logger->cout( + ANSI_BOLD "Fingerprint:" ANSI_NORMAL " %s", + fingerprint->to_string(HashFormat::Base16, false)); if (!lockedFlake.lockFile.root->inputs.empty()) logger->cout(ANSI_BOLD "Inputs:" ANSI_NORMAL); diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index c3cb2c661..26b91eda7 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -195,6 +195,7 @@ json=$(nix flake metadata flake1 --json | jq .) [[ -d $(echo "$json" | jq -r .path) ]] [[ $(echo "$json" | jq -r .lastModified) = $(git -C "$flake1Dir" log -n1 --format=%ct) ]] hash1=$(echo "$json" | jq -r .revision) +[[ -n $(echo "$json" | jq -r .fingerprint) ]] echo foo > "$flake1Dir/foo" git -C "$flake1Dir" add $flake1Dir/foo diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index 2e7f98b5e..bc0ddc3f6 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -70,6 +70,9 @@ in # Check that we got redirected to the immutable URL. assert info["locked"]["url"] == "http://localhost/stable/${nixpkgs.rev}.tar.gz" + # Check that we got a fingerprint for caching. + assert info["fingerprint"] + # Check that we got the rev and revCount attributes. assert info["revision"] == "${nixpkgs.rev}" assert info["revCount"] == 1234 From a09360400bc0f43e865273ba1ccb2b513d8a962e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 3 Jul 2024 11:15:56 -0400 Subject: [PATCH 476/910] Ident some CPP in nix daemon Makes it easier for me to read. --- src/nix/unix/daemon.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index 41ea1f5a4..c7be77067 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -211,9 +211,9 @@ static PeerInfo getPeerInfo(int remote) #elif defined(LOCAL_PEERCRED) -#if !defined(SOL_LOCAL) -#define SOL_LOCAL 0 -#endif +# if !defined(SOL_LOCAL) +# define SOL_LOCAL 0 +# endif xucred cred; socklen_t credLen = sizeof(cred); From 10ccdb7a415aec754dc7f834c5f9944c13241473 Mon Sep 17 00:00:00 2001 From: kn Date: Sat, 11 Mar 2023 19:43:04 +0000 Subject: [PATCH 477/910] Use proper struct sockpeercred for SO_PEERCRED for OpenBSD getsockopt(2) documents this; ucred is wrong ("cr_" member prefix, no pid). --- src/nix/unix/daemon.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index c7be77067..4a7997b1f 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -203,7 +203,11 @@ static PeerInfo getPeerInfo(int remote) #if defined(SO_PEERCRED) - ucred cred; +# if defined(__OpenBSD__) + struct sockpeercred cred; +# else + ucred cred; +# endif socklen_t credLen = sizeof(cred); if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) throw SysError("getting peer credentials"); From 5b4102c3b25350872d8fefffac3547a63863f0c1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 21:54:54 +0200 Subject: [PATCH 478/910] Tarball fetcher: Include revCount/lastModified in the fingerprint This can influence the evaluation result so they should be included in the fingerprint. --- src/libfetchers/fetchers.cc | 2 +- src/libflake/flake/flake.cc | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 087880ebe..294960678 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -419,7 +419,7 @@ namespace nlohmann { using namespace nix; fetchers::PublicKey adl_serializer::from_json(const json & json) { - fetchers::PublicKey res = { }; + fetchers::PublicKey res = { }; if (auto type = optionalValueAt(json, "type")) res.type = getString(*type); diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index 93d528d61..6f47b5992 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -950,10 +950,20 @@ std::optional LockedFlake::getFingerprint(ref store) const auto fingerprint = flake.lockedRef.input.getFingerprint(store); if (!fingerprint) return std::nullopt; + *fingerprint += fmt(";%s;%s", flake.lockedRef.subdir, lockFile); + + /* Include revCount and lastModified because they're not + necessarily implied by the content fingerprint (e.g. for + tarball flakes) but can influence the evaluation result. */ + if (auto revCount = flake.lockedRef.input.getRevCount()) + *fingerprint += fmt(";revCount=%d", *revCount); + if (auto lastModified = flake.lockedRef.input.getLastModified()) + *fingerprint += fmt(";lastModified=%d", *lastModified); + // FIXME: as an optimization, if the flake contains a lock file // and we haven't changed it, then it's sufficient to use // flake.sourceInfo.storePath for the fingerprint. - return hashString(HashAlgorithm::SHA256, fmt("%s;%s;%s", *fingerprint, flake.lockedRef.subdir, lockFile)); + return hashString(HashAlgorithm::SHA256, *fingerprint); } Flake::~Flake() { } From 976c05879f48e73eabe6818231da4bc7886c6c8c Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Thu, 4 Jul 2024 11:09:23 +0530 Subject: [PATCH 479/910] factor duplicate code into util function `append` --- src/libutil/fs-sink.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 597272ec9..194e86fdd 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -82,13 +82,17 @@ struct RestoreRegularFile : CreateRegularFileSink { void preallocateContents(uint64_t size) override; }; +static std::filesystem::path append(const std::filesystem::path & src, const CanonPath & path) +{ + auto dst = src; + if (!path.rel().empty()) + dst /= path.rel(); + return dst; +} + void RestoreSink::createRegularFile(const CanonPath & path, std::function func) { - auto p = dstPath; - - if (!path.rel().empty()) { - p = p / path.rel(); - } + auto p = append(dstPath, path); RestoreRegularFile crf; crf.fd = @@ -140,9 +144,7 @@ void RestoreRegularFile::operator () (std::string_view data) void RestoreSink::createSymlink(const CanonPath & path, const std::string & target) { - auto p = dstPath; - if (!path.rel().empty()) - p = dstPath / path.rel(); + auto p = append(dstPath, path); nix::createSymlink(target, p); } From c66079f1e875b8d7c75abc9b553c8b02d2746216 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 4 Jul 2024 10:36:48 +0200 Subject: [PATCH 480/910] use self-descriptive name for config file parser, document Co-authored-by: Robert Hensing --- src/libutil/config.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 192a4ecb9..907ca7fc1 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -91,7 +91,14 @@ void Config::getSettings(std::map & res, bool overridd } -static void applyConfigInner(const std::string & contents, const std::string & path, std::vector> & parsedContents) { +/** + * Parse configuration in `contents`, and also the configuration files included from there, with their location specified relative to `path`. + * + * `contents` and `path` represent the file that is being parsed. + * The result is only an intermediate list of key-value pairs of strings. + * More parsing according to the settings-specific semantics is being done by `loadConfFile` in `libstore/globals.cc`. +*/ +static void parseConfigFiles(const std::string & contents, const std::string & path, std::vector> & parsedContents) { unsigned int pos = 0; while (pos < contents.size()) { @@ -125,7 +132,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p if (pathExists(p)) { try { std::string includedContents = readFile(p); - applyConfigInner(includedContents, p, parsedContents); + parseConfigFiles(includedContents, p, parsedContents); } catch (SystemError &) { // TODO: Do we actually want to ignore this? Or is it better to fail? } @@ -153,7 +160,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p void AbstractConfig::applyConfig(const std::string & contents, const std::string & path) { std::vector> parsedContents; - applyConfigInner(contents, path, parsedContents); + parseConfigFiles(contents, path, parsedContents); // First apply experimental-feature related settings for (const auto & [name, value] : parsedContents) From 76e4adfaac3083056e79b518ccc197a7645a0f2d Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 4 Jul 2024 16:19:51 +0100 Subject: [PATCH 481/910] libstore: clean up the build directory properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the fix for CVE-2024-38531, this was only removing the nested build directory, rather than the top‐level temporary directory. Fixes: 1d3696f0fb88d610abc234a60e0d6d424feafdf1 --- src/libstore/unix/build/local-derivation-goal.cc | 9 +++++---- src/libstore/unix/build/local-derivation-goal.hh | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index a20ed5300..b20bd2d8c 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -503,12 +503,12 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ - tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); + topTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); if (useChroot) { /* If sandboxing is enabled, put the actual TMPDIR underneath an inaccessible root-owned directory, to prevent outside access. */ - tmpDir = tmpDir + "/build"; + tmpDir = topTmpDir + "/build"; createDir(tmpDir, 0700); } chownToBuilder(tmpDir); @@ -2980,7 +2980,7 @@ void LocalDerivationGoal::checkOutputs(const std::mapisBuiltin()) { @@ -2988,7 +2988,8 @@ void LocalDerivationGoal::deleteTmpDir(bool force) chmod(tmpDir.c_str(), 0755); } else - deletePath(tmpDir); + deletePath(topTmpDir); + topTmpDir = ""; tmpDir = ""; } } diff --git a/src/libstore/unix/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh index 77d07de98..4bcf5c9d4 100644 --- a/src/libstore/unix/build/local-derivation-goal.hh +++ b/src/libstore/unix/build/local-derivation-goal.hh @@ -27,10 +27,16 @@ struct LocalDerivationGoal : public DerivationGoal std::optional cgroup; /** - * The temporary directory. + * The temporary directory used for the build. */ Path tmpDir; + /** + * The top-level temporary directory. `tmpDir` is either equal to + * or a child of this directory. + */ + Path topTmpDir; + /** * The path of the temporary directory in the sandbox. */ From af2e1142b17dadf24a0b66d8973033dce6efa32b Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 4 Jul 2024 16:24:59 +0100 Subject: [PATCH 482/910] libstore: fix sandboxed builds on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent fix for CVE-2024-38531 broke the sandbox on macOS completely. As it’s not practical to use `chroot(2)` on macOS, the build takes place in the main filesystem tree, and the world‐unreadable wrapper directory prevents the build from accessing its `$TMPDIR` at all. The macOS sandbox probably shouldn’t be treated as any kind of a security boundary in its current state, but this specific vulnerability wasn’t possible to exploit on macOS anyway, as creating `set{u,g}id` binaries is blocked by sandbox policy. Locking down the build sandbox further may be a good idea in future, but it already has significant compatibility issues. For now, restore the previous status quo on macOS. Thanks to @alois31 for helping me come to a better understanding of the vulnerability. Fixes: 1d3696f0fb88d610abc234a60e0d6d424feafdf1 Closes: #11002 --- src/libstore/unix/build/local-derivation-goal.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index b20bd2d8c..d5a3e0034 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -504,12 +504,22 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ topTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); +#if __APPLE__ + if (false) { +#else if (useChroot) { +#endif /* If sandboxing is enabled, put the actual TMPDIR underneath an inaccessible root-owned directory, to prevent outside - access. */ + access. + + On macOS, we don't use an actual chroot, so this isn't + possible. Any mitigation along these lines would have to be + done directly in the sandbox profile. */ tmpDir = topTmpDir + "/build"; createDir(tmpDir, 0700); + } else { + tmpDir = topTmpDir; } chownToBuilder(tmpDir); From e4056b9afdf732068a309e9cac959190a640f055 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 4 Jul 2024 17:48:27 -0400 Subject: [PATCH 483/910] Apply suggestions from code review Co-authored-by: Robert Hensing --- build-utils-meson/deps-lists/meson.build | 2 +- build-utils-meson/diagnostics/meson.build | 3 --- src/libexpr-c/meson.build | 2 +- src/libexpr-c/package.nix | 1 - src/libstore-c/meson.build | 2 +- src/libstore/meson.build | 3 ++- tests/unit/libexpr/meson.build | 3 +-- 7 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build-utils-meson/deps-lists/meson.build b/build-utils-meson/deps-lists/meson.build index 89fbfdb36..237eac545 100644 --- a/build-utils-meson/deps-lists/meson.build +++ b/build-utils-meson/deps-lists/meson.build @@ -20,7 +20,7 @@ deps_private = [ ] # C library, whose public interface --- including public but not private # dependencies --- will also likewise soon be stable. # -# N.B. For distributions that care about "ABI" stablity and not just +# N.B. For distributions that care about "ABI" stability and not just # "API" stability, the private dependencies also matter as they can # potentially affect the public ABI. deps_public = [ ] diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index eb0c636c0..2b79f6566 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -9,8 +9,5 @@ add_project_arguments( # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked # at ~1% overhead in `nix search`. # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index fb9ade28d..2a2669b3e 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -69,7 +69,7 @@ headers = [config_h] + files( 'nix_api_value.h', ) -# TODO don't install this once tests don't use it. +# TODO move this header to libexpr, maybe don't use it in tests? headers += files('nix_api_expr_internal.h') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 81e42cf6a..b445a8187 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , mkMesonDerivation -, releaseTools , meson , ninja diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 426f07a34..917de4cda 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -61,7 +61,7 @@ headers = [config_h] + files( 'nix_api_store.h', ) -# TODO don't install this once tests don't use it. +# TODO don't install this once tests don't use it and/or move the header into `libstore`, non-`c` headers += files('nix_api_store_internal.h') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libstore/meson.build b/src/libstore/meson.build index f94a454da..7444cba20 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -118,7 +118,8 @@ busybox = find_program(get_option('sandbox-shell'), required : false) if get_option('embedded-sandbox-shell') # This one goes in config.h # The path to busybox is passed as a -D flag when compiling this_library. - # Idk why, ask the old buildsystem. + # This solution is inherited from the old make buildsystem + # TODO: do this differently? configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) hexdump = find_program('hexdump', native : true) embedded_sandbox_shell_gen = custom_target( diff --git a/tests/unit/libexpr/meson.build b/tests/unit/libexpr/meson.build index 71865b59f..a7b22f7f1 100644 --- a/tests/unit/libexpr/meson.build +++ b/tests/unit/libexpr/meson.build @@ -30,7 +30,7 @@ subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck -gtest = dependency('gtest', main : true) +gtest = dependency('gtest') deps_private += gtest gtest = dependency('gmock') @@ -77,7 +77,6 @@ this_exe = executable( include_directories : include_dirs, # TODO: -lrapidcheck, see ../libutil-support/build.meson link_args: linker_export_flags + ['-lrapidcheck'], - # get main from gtest install : true, ) From 09763c7cad66938e1675a4081194ae9e8054c22b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Jul 2024 15:28:41 +0200 Subject: [PATCH 484/910] getDerivations: add attributes to trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the error message of nix-env -qa, among others, which is crucial for understanding some ofborg eval error reports, such as https://gist.github.com/GrahamcOfBorg/89101ca9c2c855d288178f1d3c78efef After this change, it will report the same trace, but also start with ``` error: … while evaluating the attribute 'devShellTools' … while evaluating the attribute 'nixos' … while evaluating the attribute 'docker-tools-nix-shell' … while evaluating the attribute 'aarch64-darwin' … from call site at /home/user/h/nixpkgs/outpaths.nix:48:6: 47| tweak = lib.mapAttrs 48| (name: val: | ^ 49| if name == "recurseForDerivations" then true ``` --- src/libexpr/get-drvs.cc | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 0d2aecc58..896733423 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -374,21 +374,26 @@ static void getDerivations(EvalState & state, Value & vIn, bound to the attribute with the "lower" name should take precedence). */ for (auto & i : v.attrs()->lexicographicOrder(state.symbols)) { - debug("evaluating attribute '%1%'", state.symbols[i->name]); - if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) - continue; - std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]); - if (combineChannels) - getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); - else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { - /* If the value of this attribute is itself a set, - should we recurse into it? => Only if it has a - `recurseForDerivations = true' attribute. */ - if (i->value->type() == nAttrs) { - auto j = i->value->attrs()->get(state.sRecurseForDerivations); - if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) - getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + try { + debug("evaluating attribute '%1%'", state.symbols[i->name]); + if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) + continue; + std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]); + if (combineChannels) + getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { + /* If the value of this attribute is itself a set, + should we recurse into it? => Only if it has a + `recurseForDerivations = true' attribute. */ + if (i->value->type() == nAttrs) { + auto j = i->value->attrs()->get(state.sRecurseForDerivations); + if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) + getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + } } + } catch (Error & e) { + e.addTrace(state.positions[i->pos], "while evaluating the attribute '%s'", state.symbols[i->name]); + throw; } } } From e7e070d36b8f58cb48c95ae3ca6dabc0df7b79b9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 Jul 2024 16:29:16 +0200 Subject: [PATCH 485/910] Document --- src/libutil/tarfile.cc | 23 +++++++++++++++-------- tests/nixos/tarball-flakes.nix | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 445968b57..d985e5c2a 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -67,6 +67,17 @@ int getArchiveFilterCodeByName(const std::string & method) return code; } +static void enableSupportedFormats(struct archive * archive) +{ + archive_read_support_format_tar(archive); + archive_read_support_format_zip(archive); + + /* Enable support for empty files so we don't throw an exception + for empty HTTP 304 "Not modified" responses. See + downloadTarball(). */ + archive_read_support_format_empty(archive); +} + TarArchive::TarArchive(Source & source, bool raw, std::optional compression_method) : archive{archive_read_new()} , source{&source} @@ -78,11 +89,9 @@ TarArchive::TarArchive(Source & source, bool raw, std::optional com archive_read_support_filter_by_code(archive, getArchiveFilterCodeByName(*compression_method)); } - if (!raw) { - archive_read_support_format_tar(archive); - archive_read_support_format_zip(archive); - archive_read_support_format_empty(archive); - } else { + if (!raw) + enableSupportedFormats(archive); + else { archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); } @@ -98,9 +107,7 @@ TarArchive::TarArchive(const Path & path) , buffer(defaultBufferSize) { archive_read_support_filter_all(archive); - archive_read_support_format_tar(archive); - archive_read_support_format_zip(archive); - archive_read_support_format_empty(archive); + enableSupportedFormats(archive); archive_read_set_option(archive, NULL, "mac-ext", NULL); check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s"); } diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index e20b853f7..3945bd8b0 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -74,7 +74,7 @@ in assert info["revision"] == "${nixpkgs.rev}" assert info["revCount"] == 1234 - # Check that a 0-byte cached (304) result works. + # Check that a 0-byte HTTP 304 "Not modified" result works. machine.succeed("nix flake metadata --refresh --json http://localhost/tags/latest.tar.gz") # Check that fetching with rev/revCount/narHash succeeds. From d63bd8295e05b31dd8dbf85c91a8a782e471a383 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Jul 2024 16:43:48 +0200 Subject: [PATCH 486/910] assert: Report why values aren't equal --- src/libexpr/eval.cc | 232 +++++++++++++++++- src/libexpr/eval.hh | 9 + ...al-fail-assert-equal-attrs-names-2.err.exp | 8 + .../eval-fail-assert-equal-attrs-names-2.nix | 2 + ...eval-fail-assert-equal-attrs-names.err.exp | 8 + .../eval-fail-assert-equal-attrs-names.nix | 2 + ...ail-assert-equal-derivations-extra.err.exp | 26 ++ ...al-fail-assert-equal-derivations-extra.nix | 5 + ...eval-fail-assert-equal-derivations.err.exp | 26 ++ .../eval-fail-assert-equal-derivations.nix | 5 + .../eval-fail-assert-equal-floats.err.exp | 22 ++ .../lang/eval-fail-assert-equal-floats.nix | 2 + ...-fail-assert-equal-function-direct.err.exp | 9 + ...eval-fail-assert-equal-function-direct.nix | 7 + .../eval-fail-assert-equal-int-float.err.exp | 8 + .../lang/eval-fail-assert-equal-int-float.nix | 2 + .../lang/eval-fail-assert-equal-ints.err.exp | 22 ++ .../lang/eval-fail-assert-equal-ints.nix | 2 + ...eval-fail-assert-equal-list-length.err.exp | 8 + .../eval-fail-assert-equal-list-length.nix | 2 + .../lang/eval-fail-assert-equal-paths.err.exp | 8 + .../lang/eval-fail-assert-equal-paths.nix | 2 + ...eval-fail-assert-equal-type-nested.err.exp | 22 ++ .../eval-fail-assert-equal-type-nested.nix | 2 + .../lang/eval-fail-assert-equal-type.err.exp | 8 + .../lang/eval-fail-assert-equal-type.nix | 2 + .../lang/eval-fail-assert-nested-bool.err.exp | 74 ++++++ .../lang/eval-fail-assert-nested-bool.nix | 6 + .../functional/lang/eval-fail-assert.err.exp | 6 +- 29 files changed, 532 insertions(+), 5 deletions(-) create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-floats.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-floats.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-function-direct.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-int-float.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-int-float.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-ints.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-ints.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-list-length.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-list-length.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-paths.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-paths.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-type-nested.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-type.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-type.nix create mode 100644 tests/functional/lang/eval-fail-assert-nested-bool.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-nested-bool.nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d2be00e55..fb6050e50 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1759,9 +1759,24 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v) void ExprAssert::eval(EvalState & state, Env & env, Value & v) { if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) { - std::ostringstream out; - cond->show(state.symbols, out); - state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow(); + auto exprStr = ({ + std::ostringstream out; + cond->show(state.symbols, out); + out.str(); + }); + + if (auto eq = dynamic_cast(cond)) { + try { + Value v1; eq->e1->eval(state, env, v1); + Value v2; eq->e2->eval(state, env, v2); + state.assertEqValues(v1, v2, eq->pos, "in an equality assertion"); + } catch (AssertionError & e) { + e.addTrace(state.positions[pos], "while evaluating the condition of the assertion '%s'", exprStr); + throw; + } + } + + state.error("assertion '%1%' failed", exprStr).atPos(pos).withFrame(env, *this).debugThrow(); } body->eval(state, env, v); } @@ -2418,6 +2433,216 @@ SingleDerivedPath EvalState::coerceToSingleDerivedPath(const PosIdx pos, Value & } + +// NOTE: This implementation must match eqValues! +// We accept this burden because informative error messages for +// `assert a == b; x` are critical for our users' testing UX. +void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx) +{ + // This implementation must match eqValues. + forceValue(v1, pos); + forceValue(v2, pos); + + if (&v1 == &v2) + return; + + // Special case type-compatibility between float and int + if ((v1.type() == nInt || v1.type() == nFloat) && (v2.type() == nInt || v2.type() == nFloat)) { + if (eqValues(v1, v2, pos, errorCtx)) { + return; + } else { + error( + "%s with value '%s' is not equal to %s with value '%s'", + showType(v1), + ValuePrinter(*this, v1, errorPrintOptions), + showType(v2), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + } + + if (v1.type() != v2.type()) { + error( + "%s of value '%s' is not equal to %s of value '%s'", + showType(v1), + ValuePrinter(*this, v1, errorPrintOptions), + showType(v2), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + + switch (v1.type()) { + case nInt: + if (v1.integer() != v2.integer()) { + error("integer '%d' is not equal to integer '%d'", v1.integer(), v2.integer()).debugThrow(); + } + return; + + case nBool: + if (v1.boolean() != v2.boolean()) { + error( + "boolean '%s' is not equal to boolean '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nString: + if (strcmp(v1.c_str(), v2.c_str()) != 0) { + error( + "string '%s' is not equal to string '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nPath: + if (v1.payload.path.accessor != v2.payload.path.accessor) { + error( + "path '%s' is not equal to path '%s' because their accessors are different", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + if (strcmp(v1.payload.path.path, v2.payload.path.path) != 0) { + error( + "path '%s' is not equal to path '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nNull: + return; + + case nList: + if (v1.listSize() != v2.listSize()) { + error( + "list of size '%d' is not equal to list of size '%d', left hand side is '%s', right hand side is '%s'", + v1.listSize(), + v2.listSize(), + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + for (size_t n = 0; n < v1.listSize(); ++n) { + try { + assertEqValues(*v1.listElems()[n], *v2.listElems()[n], pos, errorCtx); + } catch (Error & e) { + e.addTrace(positions[pos], "while comparing list element %d", n); + throw; + } + } + return; + + case nAttrs: { + if (isDerivation(v1) && isDerivation(v2)) { + auto i = v1.attrs()->get(sOutPath); + auto j = v2.attrs()->get(sOutPath); + if (i && j) { + try { + assertEqValues(*i->value, *j->value, pos, errorCtx); + return; + } catch (Error & e) { + e.addTrace(positions[pos], "while comparing a derivation by its '%s' attribute", "outPath"); + throw; + } + assert(false); + } + } + + if (v1.attrs()->size() != v2.attrs()->size()) { + error( + "attribute names of attribute set '%s' differs from attribute set '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + + // Like normal comparison, we compare the attributes in non-deterministic Symbol index order. + // This function is called when eqValues has found a difference, so to reliably + // report about its result, we should follow in its literal footsteps and not + // try anything fancy that could lead to an error. + Bindings::const_iterator i, j; + for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j) { + if (i->name != j->name) { + // A difference in a sorted list means that one attribute is not contained in the other, but we don't + // know which. Let's find out. Could use <, but this is more clear. + if (!v2.attrs()->get(i->name)) { + error( + "attribute name '%s' is contained in '%s', but not in '%s'", + symbols[i->name], + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + if (!v1.attrs()->get(j->name)) { + error( + "attribute name '%s' is missing in '%s', but is contained in '%s'", + symbols[j->name], + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + assert(false); + } + try { + assertEqValues(*i->value, *j->value, pos, errorCtx); + } catch (Error & e) { + // The order of traces is reversed, so this presents as + // where left hand side is + // at + // where right hand side is + // at + // while comparing attribute '' + if (j->pos != noPos) + e.addTrace(positions[j->pos], "where right hand side is"); + if (i->pos != noPos) + e.addTrace(positions[i->pos], "where left hand side is"); + e.addTrace(positions[pos], "while comparing attribute '%s'", symbols[i->name]); + throw; + } + } + return; + } + + case nFunction: + error("distinct functions and immediate comparisons of identical functions compare as unequal") + .debugThrow(); + + case nExternal: + if (!(*v1.external() == *v2.external())) { + error( + "external value '%s' is not equal to external value '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nFloat: + // !!! + if (!(v1.fpoint() == v2.fpoint())) { + error("float '%f' is not equal to float '%f'", v1.fpoint(), v2.fpoint()).debugThrow(); + } + return; + + case nThunk: // Must not be left by forceValue + default: + // This should never happen, because eqValues already throws an + // error for this, and this function should only be called when + // eqValues has found a difference, and it should match + // its behavior. + error( + "cannot compare %1% with %2%; is assertEqValues out of sync with eqValues?", showType(v1), showType(v2)) + .debugThrow(); + } +} + +// This implementation must match assertEqValues bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx) { forceValue(v1, pos); @@ -2491,6 +2716,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return *v1.external() == *v2.external(); case nFloat: + // !!! return v1.fpoint() == v2.fpoint(); case nThunk: // Must not be left by forceValue diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..df44bed70 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -644,6 +644,15 @@ public: */ bool eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx); + /** + * Like `eqValues`, but throws an `AssertionError` if not equal. + * + * WARNING: + * Callers should call `eqValues` first and report if `assertEqValues` behaves + * incorrectly. (e.g. if it doesn't throw if eqValues returns false or vice versa) + */ + void assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx); + bool isFunctor(Value & fun); // FIXME: use std::span diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp new file mode 100644 index 000000000..4b68d97c2 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '({ a = true; } == { a = true; b = true; })' + at /pwd/lang/eval-fail-assert-equal-attrs-names-2.nix:1:1: + 1| assert { a = true; } == { a = true; b = true; }; + | ^ + 2| throw "unreachable" + + error: attribute names of attribute set '{ a = true; }' differs from attribute set '{ a = true; b = true; }' diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix new file mode 100644 index 000000000..8e7ac9cf2 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix @@ -0,0 +1,2 @@ +assert { a = true; } == { a = true; b = true; }; +throw "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp b/tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp new file mode 100644 index 000000000..bc61ca63a --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '({ a = true; b = true; } == { a = true; })' + at /pwd/lang/eval-fail-assert-equal-attrs-names.nix:1:1: + 1| assert { a = true; b = true; } == { a = true; }; + | ^ + 2| throw "unreachable" + + error: attribute names of attribute set '{ a = true; b = true; }' differs from attribute set '{ a = true; }' diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names.nix b/tests/functional/lang/eval-fail-assert-equal-attrs-names.nix new file mode 100644 index 000000000..e2f53a85a --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names.nix @@ -0,0 +1,2 @@ +assert { a = true; b = true; } == { a = true; }; +throw "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp new file mode 100644 index 000000000..7f4924074 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp @@ -0,0 +1,26 @@ +error: + … while evaluating the condition of the assertion '({ foo = { outPath = "/nix/store/0"; type = "derivation"; }; } == { foo = { devious = true; outPath = "/nix/store/1"; type = "derivation"; }; })' + at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:1:1: + 1| assert + | ^ + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; }; } + + … while comparing attribute 'foo' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:2:5: + 1| assert + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; }; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:4:5: + 3| == + 4| { foo = { type = "derivation"; outPath = "/nix/store/1"; devious = true; }; }; + | ^ + 5| throw "unreachable" + + … while comparing a derivation by its 'outPath' attribute + + error: string '"/nix/store/0"' is not equal to string '"/nix/store/1"' diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix new file mode 100644 index 000000000..fd8bc3f26 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix @@ -0,0 +1,5 @@ +assert + { foo = { type = "derivation"; outPath = "/nix/store/0"; }; } + == + { foo = { type = "derivation"; outPath = "/nix/store/1"; devious = true; }; }; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations.err.exp b/tests/functional/lang/eval-fail-assert-equal-derivations.err.exp new file mode 100644 index 000000000..d7f0face0 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations.err.exp @@ -0,0 +1,26 @@ +error: + … while evaluating the condition of the assertion '({ foo = { ignored = (abort "not ignored"); outPath = "/nix/store/0"; type = "derivation"; }; } == { foo = { ignored = (abort "not ignored"); outPath = "/nix/store/1"; type = "derivation"; }; })' + at /pwd/lang/eval-fail-assert-equal-derivations.nix:1:1: + 1| assert + | ^ + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; ignored = abort "not ignored"; }; } + + … while comparing attribute 'foo' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-derivations.nix:2:5: + 1| assert + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; ignored = abort "not ignored"; }; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-derivations.nix:4:5: + 3| == + 4| { foo = { type = "derivation"; outPath = "/nix/store/1"; ignored = abort "not ignored"; }; }; + | ^ + 5| throw "unreachable" + + … while comparing a derivation by its 'outPath' attribute + + error: string '"/nix/store/0"' is not equal to string '"/nix/store/1"' diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations.nix b/tests/functional/lang/eval-fail-assert-equal-derivations.nix new file mode 100644 index 000000000..c648eae37 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations.nix @@ -0,0 +1,5 @@ +assert + { foo = { type = "derivation"; outPath = "/nix/store/0"; ignored = abort "not ignored"; }; } + == + { foo = { type = "derivation"; outPath = "/nix/store/1"; ignored = abort "not ignored"; }; }; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-floats.err.exp b/tests/functional/lang/eval-fail-assert-equal-floats.err.exp new file mode 100644 index 000000000..d8545e2db --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-floats.err.exp @@ -0,0 +1,22 @@ +error: + … while evaluating the condition of the assertion '({ b = 1; } == { b = 1.01; })' + at /pwd/lang/eval-fail-assert-equal-floats.nix:1:1: + 1| assert { b = 1.0; } == { b = 1.01; }; + | ^ + 2| abort "unreachable" + + … while comparing attribute 'b' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-floats.nix:1:10: + 1| assert { b = 1.0; } == { b = 1.01; }; + | ^ + 2| abort "unreachable" + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-floats.nix:1:26: + 1| assert { b = 1.0; } == { b = 1.01; }; + | ^ + 2| abort "unreachable" + + error: a float with value '1' is not equal to a float with value '1.01' diff --git a/tests/functional/lang/eval-fail-assert-equal-floats.nix b/tests/functional/lang/eval-fail-assert-equal-floats.nix new file mode 100644 index 000000000..438e85abf --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-floats.nix @@ -0,0 +1,2 @@ +assert { b = 1.0; } == { b = 1.01; }; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp b/tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp new file mode 100644 index 000000000..f06d79698 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp @@ -0,0 +1,9 @@ +error: + … while evaluating the condition of the assertion '((x: x) == (x: x))' + at /pwd/lang/eval-fail-assert-equal-function-direct.nix:3:1: + 2| # This only compares a direct comparison and makes no claims about functions in nested structures. + 3| assert + | ^ + 4| (x: x) + + error: distinct functions and immediate comparisons of identical functions compare as unequal diff --git a/tests/functional/lang/eval-fail-assert-equal-function-direct.nix b/tests/functional/lang/eval-fail-assert-equal-function-direct.nix new file mode 100644 index 000000000..68e5e3908 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-function-direct.nix @@ -0,0 +1,7 @@ +# Note: functions in nested structures, e.g. attributes, may be optimized away by pointer identity optimization. +# This only compares a direct comparison and makes no claims about functions in nested structures. +assert + (x: x) + == + (x: x); +abort "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-int-float.err.exp b/tests/functional/lang/eval-fail-assert-equal-int-float.err.exp new file mode 100644 index 000000000..c927e38d6 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-int-float.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '(1 == 1.1)' + at /pwd/lang/eval-fail-assert-equal-int-float.nix:1:1: + 1| assert 1 == 1.1; + | ^ + 2| throw "unreachable" + + error: an integer with value '1' is not equal to a float with value '1.1' diff --git a/tests/functional/lang/eval-fail-assert-equal-int-float.nix b/tests/functional/lang/eval-fail-assert-equal-int-float.nix new file mode 100644 index 000000000..1dfdf2bda --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-int-float.nix @@ -0,0 +1,2 @@ +assert 1 == 1.1; +throw "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-ints.err.exp b/tests/functional/lang/eval-fail-assert-equal-ints.err.exp new file mode 100644 index 000000000..d6219e200 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-ints.err.exp @@ -0,0 +1,22 @@ +error: + … while evaluating the condition of the assertion '({ b = 1; } == { b = 2; })' + at /pwd/lang/eval-fail-assert-equal-ints.nix:1:1: + 1| assert { b = 1; } == { b = 2; }; + | ^ + 2| abort "unreachable" + + … while comparing attribute 'b' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-ints.nix:1:10: + 1| assert { b = 1; } == { b = 2; }; + | ^ + 2| abort "unreachable" + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-ints.nix:1:24: + 1| assert { b = 1; } == { b = 2; }; + | ^ + 2| abort "unreachable" + + error: an integer with value '1' is not equal to an integer with value '2' diff --git a/tests/functional/lang/eval-fail-assert-equal-ints.nix b/tests/functional/lang/eval-fail-assert-equal-ints.nix new file mode 100644 index 000000000..645258ea6 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-ints.nix @@ -0,0 +1,2 @@ +assert { b = 1; } == { b = 2; }; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-list-length.err.exp b/tests/functional/lang/eval-fail-assert-equal-list-length.err.exp new file mode 100644 index 000000000..90108552c --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-list-length.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '([ (1) (0) ] == [ (10) ])' + at /pwd/lang/eval-fail-assert-equal-list-length.nix:1:1: + 1| assert [ 1 0 ] == [ 10 ]; + | ^ + 2| throw "unreachable" + + error: list of size '2' is not equal to list of size '1', left hand side is '[ 1 0 ]', right hand side is '[ 10 ]' diff --git a/tests/functional/lang/eval-fail-assert-equal-list-length.nix b/tests/functional/lang/eval-fail-assert-equal-list-length.nix new file mode 100644 index 000000000..6d40f4d8e --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-list-length.nix @@ -0,0 +1,2 @@ +assert [ 1 0 ] == [ 10 ]; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-paths.err.exp b/tests/functional/lang/eval-fail-assert-equal-paths.err.exp new file mode 100644 index 000000000..66c34e971 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-paths.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '(/pwd/lang/foo == /pwd/lang/bar)' + at /pwd/lang/eval-fail-assert-equal-paths.nix:1:1: + 1| assert ./foo == ./bar; + | ^ + 2| throw "unreachable" + + error: path '/pwd/lang/foo' is not equal to path '/pwd/lang/bar' diff --git a/tests/functional/lang/eval-fail-assert-equal-paths.nix b/tests/functional/lang/eval-fail-assert-equal-paths.nix new file mode 100644 index 000000000..ef0b67024 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-paths.nix @@ -0,0 +1,2 @@ +assert ./foo == ./bar; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp b/tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp new file mode 100644 index 000000000..f78badd25 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp @@ -0,0 +1,22 @@ +error: + … while evaluating the condition of the assertion '({ ding = false; } == { ding = null; })' + at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:1: + 1| assert { ding = false; } == { ding = null; }; + | ^ + 2| abort "unreachable" + + … while comparing attribute 'ding' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:10: + 1| assert { ding = false; } == { ding = null; }; + | ^ + 2| abort "unreachable" + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:31: + 1| assert { ding = false; } == { ding = null; }; + | ^ + 2| abort "unreachable" + + error: a Boolean of value 'false' is not equal to null of value 'null' diff --git a/tests/functional/lang/eval-fail-assert-equal-type-nested.nix b/tests/functional/lang/eval-fail-assert-equal-type-nested.nix new file mode 100644 index 000000000..3fbd14ce6 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type-nested.nix @@ -0,0 +1,2 @@ +assert { ding = false; } == { ding = null; }; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-type.err.exp b/tests/functional/lang/eval-fail-assert-equal-type.err.exp new file mode 100644 index 000000000..4dc3f2ece --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '(false == null)' + at /pwd/lang/eval-fail-assert-equal-type.nix:1:1: + 1| assert false == null; + | ^ + 2| abort "unreachable" + + error: a Boolean of value 'false' is not equal to null of value 'null' diff --git a/tests/functional/lang/eval-fail-assert-equal-type.nix b/tests/functional/lang/eval-fail-assert-equal-type.nix new file mode 100644 index 000000000..7023ea007 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type.nix @@ -0,0 +1,2 @@ +assert false == null; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-nested-bool.err.exp b/tests/functional/lang/eval-fail-assert-nested-bool.err.exp new file mode 100644 index 000000000..1debb668c --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-nested-bool.err.exp @@ -0,0 +1,74 @@ +error: + … while evaluating the condition of the assertion '({ a = { b = [ ({ c = { d = true; }; }) ]; }; } == { a = { b = [ ({ c = { d = false; }; }) ]; }; })' + at /pwd/lang/eval-fail-assert-nested-bool.nix:1:1: + 1| assert + | ^ + 2| { a.b = [ { c.d = true; } ]; } + + … while comparing attribute 'a' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:5: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:5: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + … while comparing attribute 'b' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:5: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:5: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + … while comparing list element 0 + + … while comparing attribute 'c' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:15: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:15: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + … while comparing attribute 'd' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:15: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:15: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + error: boolean 'true' is not equal to boolean 'false' diff --git a/tests/functional/lang/eval-fail-assert-nested-bool.nix b/tests/functional/lang/eval-fail-assert-nested-bool.nix new file mode 100644 index 000000000..228576983 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-nested-bool.nix @@ -0,0 +1,6 @@ +assert + { a.b = [ { c.d = true; } ]; } + == + { a.b = [ { c.d = false; } ]; }; + +abort "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert.err.exp b/tests/functional/lang/eval-fail-assert.err.exp index 0656ec81c..7be9e2387 100644 --- a/tests/functional/lang/eval-fail-assert.err.exp +++ b/tests/functional/lang/eval-fail-assert.err.exp @@ -20,9 +20,11 @@ error: | ^ 3| - error: assertion '(arg == "y")' failed - at /pwd/lang/eval-fail-assert.nix:2:12: + … while evaluating the condition of the assertion '(arg == "y")' + at /pwd/lang/eval-fail-assert.nix:2:12: 1| let { 2| x = arg: assert arg == "y"; 123; | ^ 3| + + error: string '"x"' is not equal to string '"y"' From 6ef00a503a62dc5554139804d22968b25ba4bf3f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Jul 2024 18:32:34 +0200 Subject: [PATCH 487/910] Explain when man is missing Have you seen this man? Fixes #10677 --- src/libmain/shared.cc | 4 ++++ tests/functional/misc.sh | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index c1c936248..fc55fe3f1 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -320,6 +320,10 @@ void showManPage(const std::string & name) restoreProcessContext(); setEnv("MANPATH", settings.nixManDir.c_str()); execlp("man", "man", name.c_str(), nullptr); + if (errno == ENOENT) { + // Not SysError because we don't want to suffix the errno, aka No such file or directory. + throw Error("The '%1%' command was not found, but it is needed for '%2%' and some other '%3%' commands' help text. Perhaps you could install the '%1%' command?", "man", name.c_str(), "nix-*"); + } throw SysError("command 'man %1%' failed", name.c_str()); } diff --git a/tests/functional/misc.sh b/tests/functional/misc.sh index 9eb80ad22..7d63756b7 100755 --- a/tests/functional/misc.sh +++ b/tests/functional/misc.sh @@ -13,6 +13,9 @@ source common.sh # Can we ask for the version number? nix-env --version | grep "$version" +nix_env=$(type -P nix-env) +(PATH=""; ! $nix_env --help 2>&1 ) | grepQuiet -F "The 'man' command was not found, but it is needed for 'nix-env' and some other 'nix-*' commands' help text. Perhaps you could install the 'man' command?" + # Usage errors. expect 1 nix-env --foo 2>&1 | grep "no operation" expect 1 nix-env -q --foo 2>&1 | grep "unknown flag" From 8cea1fbd97903e58b4de780bbf428b981f196e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 5 Jul 2024 17:26:58 +0200 Subject: [PATCH 488/910] src/nix/prefetch: fix prefetch containing current directory instead of tarball When --unpack was used the nix would add the current directory to the nix store instead of the content of unpacked. The reason for this is that std::distance already consumes the iterator. To fix this we re-instantiate the directory iterator in case the directory only contains a single entry. --- src/nix/prefetch.cc | 11 ++++++----- tests/functional/tarball.sh | 12 ++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 5e890f3c8..55b8498ef 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -114,14 +114,15 @@ std::tuple prefetchFile( createDirs(unpacked); unpackTarfile(tmpFile.string(), unpacked); + auto entries = std::filesystem::directory_iterator{unpacked}; /* If the archive unpacks to a single file/directory, then use that as the top-level. */ - auto entries = std::filesystem::directory_iterator{unpacked}; - auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); - if (file_count == 1) - tmpFile = entries->path(); - else + tmpFile = entries->path(); + unsigned fileCount = std::distance(entries, std::filesystem::directory_iterator{}); + if (fileCount != 1) { + /* otherwise, use the directory itself */ tmpFile = unpacked; + } } Activity act(*logger, lvlChatty, actUnknown, diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index a2824cd12..f999b7a10 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -54,6 +54,18 @@ test_tarball() { # with the content-addressing (! nix-instantiate --eval -E "fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; name = \"foo\"; }") + store_path=$(nix store prefetch-file --json "file://$tarball" | jq -r .storePath) + if ! cmp -s "$store_path" "$tarball"; then + echo "prefetched tarball differs from original: $store_path vs $tarball" >&2 + exit 1 + fi + store_path2=$(nix store prefetch-file --json --unpack "file://$tarball" | jq -r .storePath) + diff_output=$(diff -r "$store_path2" "$tarroot") + if [ -n "$diff_output" ]; then + echo "prefetched tarball differs from original: $store_path2 vs $tarroot" >&2 + echo "$diff_output" + exit 1 + fi } test_tarball '' cat From 05381c0b3093a0b5e091946ce2f6fa9f02b981ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 5 Jul 2024 19:45:03 +0200 Subject: [PATCH 489/910] Update src/nix/prefetch.cc Co-authored-by: Eelco Dolstra --- src/nix/prefetch.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 55b8498ef..c6e60a53b 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -118,7 +118,7 @@ std::tuple prefetchFile( /* If the archive unpacks to a single file/directory, then use that as the top-level. */ tmpFile = entries->path(); - unsigned fileCount = std::distance(entries, std::filesystem::directory_iterator{}); + auto fileCount = std::distance(entries, std::filesystem::directory_iterator{}); if (fileCount != 1) { /* otherwise, use the directory itself */ tmpFile = unpacked; From 3acf3fc7465ed2f3e872f290fb9ca39a428b6379 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 3 Jul 2024 14:47:23 -0400 Subject: [PATCH 490/910] Package `libnixmain` and `libnixcmd` with Meson Co-authored-by: Robert Hensing --- meson.build | 2 + packaging/components.nix | 4 ++ packaging/hydra.nix | 2 + src/libcmd/.version | 1 + src/libcmd/meson.build | 126 ++++++++++++++++++++++++++++++++++ src/libcmd/meson.options | 15 ++++ src/libcmd/package.nix | 112 ++++++++++++++++++++++++++++++ src/libcmd/repl-interacter.cc | 2 +- src/libcmd/repl.cc | 2 +- src/libmain/.version | 1 + src/libmain/meson.build | 98 ++++++++++++++++++++++++++ src/libmain/package.nix | 78 +++++++++++++++++++++ 12 files changed, 441 insertions(+), 2 deletions(-) create mode 120000 src/libcmd/.version create mode 100644 src/libcmd/meson.build create mode 100644 src/libcmd/meson.options create mode 100644 src/libcmd/package.nix create mode 120000 src/libmain/.version create mode 100644 src/libmain/meson.build create mode 100644 src/libmain/package.nix diff --git a/meson.build b/meson.build index 1690bb50a..356d978dc 100644 --- a/meson.build +++ b/meson.build @@ -11,6 +11,8 @@ subproject('libstore') subproject('libfetchers') subproject('libexpr') subproject('libflake') +subproject('libmain') +subproject('libcmd') # Docs subproject('internal-api-docs') diff --git a/packaging/components.nix b/packaging/components.nix index e9e95c028..f1cd3b9c6 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -28,6 +28,10 @@ in nix-flake = callPackage ../src/libflake/package.nix { }; nix-flake-tests = callPackage ../tests/unit/libflake/package.nix { }; + nix-main = callPackage ../src/libmain/package.nix { }; + + nix-cmd = callPackage ../src/libcmd/package.nix { }; + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 97f2c59b7..0bbbc31f7 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -51,6 +51,8 @@ let "nix-expr-tests" "nix-flake" "nix-flake-tests" + "nix-main" + "nix-cmd" ]; in { diff --git a/src/libcmd/.version b/src/libcmd/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libcmd/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build new file mode 100644 index 000000000..d9a90508a --- /dev/null +++ b/src/libcmd/meson.build @@ -0,0 +1,126 @@ +project('nix-cmd', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +configdata = configuration_data() + +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-fetchers'), + dependency('nix-expr'), + dependency('nix-flake'), + dependency('nix-main'), +] +subdir('build-utils-meson/subprojects') + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +lowdown = dependency('lowdown', version : '>= 0.9.0', required : get_option('markdown')) +deps_private += lowdown +configdata.set('HAVE_LOWDOWN', lowdown.found().to_int()) + +readline_flavor = get_option('readline-flavor') +if readline_flavor == 'editline' + editline = dependency('libeditline', 'editline', version : '>=1.14') + deps_private += editline +elif readline_flavor == 'readline' + readline = dependency('readline') + deps_private += readline + configdata.set( + 'USE_READLINE', + 1, + description: 'Use readline instead of editline', + ) +else + error('illegal editline flavor', readline_flavor) +endif + +config_h = configure_file( + configuration : configdata, + output : 'config-cmd.hh', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + # '-include', 'config-fetchers.h', + '-include', 'config-main.hh', + '-include', 'config-cmd.hh', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') + +sources = files( + 'built-path.cc', + 'command-installable-value.cc', + 'command.cc', + 'common-eval-args.cc', + 'editor-for.cc', + 'installable-attr-path.cc', + 'installable-derived-path.cc', + 'installable-flake.cc', + 'installable-value.cc', + 'installables.cc', + 'legacy.cc', + 'markdown.cc', + 'misc-store-flags.cc', + 'network-proxy.cc', + 'repl-interacter.cc', + 'repl.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'built-path.hh', + 'command-installable-value.hh', + 'command.hh', + 'common-eval-args.hh', + 'editor-for.hh', + 'installable-attr-path.hh', + 'installable-derived-path.hh', + 'installable-flake.hh', + 'installable-value.hh', + 'installables.hh', + 'legacy.hh', + 'markdown.hh', + 'misc-store-flags.hh', + 'network-proxy.hh', + 'repl-interacter.hh', + 'repl.hh', +) + +this_library = library( + 'nixcmd', + sources, + dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +subdir('build-utils-meson/export') diff --git a/src/libcmd/meson.options b/src/libcmd/meson.options new file mode 100644 index 000000000..79ae4fa55 --- /dev/null +++ b/src/libcmd/meson.options @@ -0,0 +1,15 @@ +# vim: filetype=meson + +option( + 'markdown', + type: 'feature', + description: 'Enable Markdown rendering in the Nix binary (requires lowdown)', +) + +option( + 'readline-flavor', + type : 'combo', + choices : ['editline', 'readline'], + value : 'editline', + description : 'Which library to use for nice line editing with the Nix language REPL', +) diff --git a/src/libcmd/package.nix b/src/libcmd/package.nix new file mode 100644 index 000000000..5e6381a17 --- /dev/null +++ b/src/libcmd/package.nix @@ -0,0 +1,112 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nix-fetchers +, nix-expr +, nix-flake +, nix-main +, editline +, readline +, lowdown +, nlohmann_json + +# Configuration Options + +, versionSuffix ? "" + +# Whether to enable Markdown rendering in the Nix binary. +, enableMarkdown ? !stdenv.hostPlatform.isWindows + +# Which interactive line editor library to use for Nix's repl. +# +# Currently supported choices are: +# +# - editline (default) +# - readline +, readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix-cmd"; + inherit version; + + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + ({ inherit editline readline; }.${readlineFlavor}) + ] ++ lib.optional enableMarkdown lowdown; + + propagatedBuildInputs = [ + nix-util + nix-store + nix-fetchers + nix-expr + nix-flake + nix-main + nlohmann_json + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../.version + ''; + + mesonFlags = [ + (lib.mesonEnable "markdown" enableMarkdown) + (lib.mesonOption "readline-flavor" readlineFlavor) + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index eb4361e25..420cce1db 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -18,7 +18,7 @@ extern "C" { #include "finally.hh" #include "repl-interacter.hh" #include "file-system.hh" -#include "libcmd/repl.hh" +#include "repl.hh" namespace nix { diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 53dd94c7a..ce1c5af69 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -3,7 +3,7 @@ #include #include -#include "libcmd/repl-interacter.hh" +#include "repl-interacter.hh" #include "repl.hh" #include "ansicolor.hh" diff --git a/src/libmain/.version b/src/libmain/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libmain/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libmain/meson.build b/src/libmain/meson.build new file mode 100644 index 000000000..859ce22f8 --- /dev/null +++ b/src/libmain/meson.build @@ -0,0 +1,98 @@ +project('nix-main', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +configdata = configuration_data() + +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), +] +subdir('build-utils-meson/subprojects') + + +pubsetbuf_test = ''' +#include + +using namespace std; + +char buf[1024]; + +int main() { + cerr.rdbuf()->pubsetbuf(buf, sizeof(buf)); +} +''' + +configdata.set( + 'HAVE_PUBSETBUF', + cxx.compiles(pubsetbuf_test).to_int(), + description: 'Optionally used for buffering on standard error' +) + +config_h = configure_file( + configuration : configdata, + output : 'config-main.hh', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-main.hh', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') + +sources = files( + 'common-args.cc', + 'loggers.cc', + 'progress-bar.cc', + 'shared.cc', +) + +if host_machine.system() != 'windows' + sources += files( + 'unix/stack.cc', + ) +endif + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'common-args.hh', + 'loggers.hh', + 'progress-bar.hh', + 'shared.hh', +) + +this_library = library( + 'nixmain', + sources, + dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +subdir('build-utils-meson/export') diff --git a/src/libmain/package.nix b/src/libmain/package.nix new file mode 100644 index 000000000..8c06fd004 --- /dev/null +++ b/src/libmain/package.nix @@ -0,0 +1,78 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store + +# Configuration Options + +, versionSuffix ? "" +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix-main"; + inherit version; + + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util + nix-store + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../.version + ''; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) From b7e5446b812f44dbdce19314c84d9384fa51b5e9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:33:06 +0200 Subject: [PATCH 491/910] flake.nix: Remove unused binding --- flake.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/flake.nix b/flake.nix index cfea7d386..d83c2ecad 100644 --- a/flake.nix +++ b/flake.nix @@ -25,7 +25,6 @@ let inherit (nixpkgs) lib; - inherit (lib) fileset; officialRelease = false; From 4d0c55ae55c0c36b8f8cf5561b10e35da543de62 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:41:05 +0200 Subject: [PATCH 492/910] api docs: Use mkMesonDerivation --- src/external-api-docs/package.nix | 46 ++++++++++++++----------------- src/internal-api-docs/package.nix | 36 +++++++++++------------- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index 352698360..7fd6a988a 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -1,5 +1,5 @@ { lib -, stdenv +, mkMesonDerivation , meson , ninja @@ -14,27 +14,27 @@ let inherit (lib) fileset; in -stdenv.mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-external-api-docs"; version = lib.fileContents ./.version + versionSuffix; - src = fileset.toSource { - root = ../..; - fileset = - let - cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "h"); - in - fileset.unions [ - ./meson.build - ./doxygen.cfg.in - ./README.md - # Source is not compiled, but still must be available for Doxygen - # to gather comments. - (cpp ../libexpr-c) - (cpp ../libstore-c) - (cpp ../libutil-c) - ]; - }; + workDir = ./.; + fileset = + let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "h"); + in + fileset.unions [ + ./.version + ../../.version + ./meson.build + ./doxygen.cfg.in + ./README.md + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../libexpr-c) + (cpp ../libstore-c) + (cpp ../libutil-c) + ]; nativeBuildInputs = [ meson @@ -42,14 +42,10 @@ stdenv.mkDerivation (finalAttrs: { doxygen ]; - postUnpack = '' - sourceRoot=$sourceRoot/src/external-api-docs - ''; - preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix '' - echo ${finalAttrs.version} > .version + chmod u+w ./.version + echo ${finalAttrs.version} > ./.version ''; postInstall = '' diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index 6a3bc0501..44ddb00ab 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -1,5 +1,5 @@ { lib -, stdenv +, mkMesonDerivation , meson , ninja @@ -14,22 +14,22 @@ let inherit (lib) fileset; in -stdenv.mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-internal-api-docs"; version = lib.fileContents ./.version + versionSuffix; - src = fileset.toSource { - root = ../..; - fileset = let - cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "hh"); - in fileset.unions [ - ./meson.build - ./doxygen.cfg.in - # Source is not compiled, but still must be available for Doxygen - # to gather comments. - (cpp ../.) - ]; - }; + workDir = ./.; + fileset = let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "hh"); + in fileset.unions [ + ./.version + ../../.version + ./meson.build + ./doxygen.cfg.in + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../.) + ]; nativeBuildInputs = [ meson @@ -37,14 +37,10 @@ stdenv.mkDerivation (finalAttrs: { doxygen ]; - postUnpack = '' - sourceRoot=$sourceRoot/src/internal-api-docs - ''; - preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix '' - echo ${finalAttrs.version} > .version + chmod u+w ./.version + echo ${finalAttrs.version} > ./.version ''; postInstall = '' From 4c014e238b9dd9d52c6406b8adfd5bb9b77bb0c2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:43:48 +0200 Subject: [PATCH 493/910] nix-main: Add openssl --- src/libmain/package.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libmain/package.nix b/src/libmain/package.nix index 8c06fd004..f6c92bac6 100644 --- a/src/libmain/package.nix +++ b/src/libmain/package.nix @@ -7,6 +7,8 @@ , ninja , pkg-config +, openssl + , nix-util , nix-store @@ -47,6 +49,7 @@ mkMesonDerivation (finalAttrs: { propagatedBuildInputs = [ nix-util nix-store + openssl ]; preConfigure = From efd5f50f5e6ef74e1db77101d9d73a3d3ebf3e84 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:47:11 +0200 Subject: [PATCH 494/910] nix-perl: Add deps, use mkMesonDerivation --- src/perl/package.nix | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/perl/package.nix b/src/perl/package.nix index 6b8487148..08ceaa33e 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , perl , perlPackages , meson @@ -8,38 +9,44 @@ , nix-store , darwin , versionSuffix ? "" +, curl +, bzip2 +, libsodium }: let inherit (lib) fileset; in -perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { +perl.pkgs.toPerlModule (mkMesonDerivation (finalAttrs: { pname = "nix-perl"; version = lib.fileContents ./.version + versionSuffix; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions ([ - ./MANIFEST - ./lib - ./meson.build - ./meson.options - ] ++ lib.optionals finalAttrs.doCheck [ - ./.yath.rc.in - ./t - ]); - }; + workDir = ./.; + fileset = fileset.unions ([ + ./.version + ../../.version + ./MANIFEST + ./lib + ./meson.build + ./meson.options + ] ++ lib.optionals finalAttrs.doCheck [ + ./.yath.rc.in + ./t + ]); nativeBuildInputs = [ meson ninja pkg-config perl + curl ]; buildInputs = [ nix-store + bzip2 + libsodium ]; # `perlPackages.Test2Harness` is marked broken for Darwin @@ -52,6 +59,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { preConfigure = # "Inline" .version so its not a symlink, and includes the suffix '' + chmod u+w .version echo ${finalAttrs.version} > .version ''; From 0729f0a113cb28369f492f16bb9f92c1d628e58f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:36:31 +0200 Subject: [PATCH 495/910] packaging: Pass version directly --- packaging/dependencies.nix | 1 + src/external-api-docs/package.nix | 4 ++-- src/internal-api-docs/package.nix | 4 ++-- src/libcmd/package.nix | 4 +--- src/libexpr-c/package.nix | 4 +--- src/libexpr/package.nix | 4 +--- src/libfetchers/package.nix | 4 +--- src/libflake/package.nix | 4 +--- src/libmain/package.nix | 4 +--- src/libstore-c/package.nix | 4 +--- src/libstore/package.nix | 4 +--- src/libutil-c/package.nix | 4 +--- src/libutil/package.nix | 4 +--- src/perl/package.nix | 4 ++-- tests/unit/libexpr-support/package.nix | 4 +--- tests/unit/libexpr/package.nix | 4 +--- tests/unit/libfetchers/package.nix | 4 +--- tests/unit/libflake/package.nix | 4 +--- tests/unit/libstore-support/package.nix | 4 +--- tests/unit/libstore/package.nix | 4 +--- tests/unit/libutil-support/package.nix | 4 +--- tests/unit/libutil/package.nix | 4 +--- 22 files changed, 25 insertions(+), 60 deletions(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 909a0a38e..78a857d85 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -40,6 +40,7 @@ let in scope: { inherit stdenv versionSuffix; + version = lib.fileContents ../.version + versionSuffix; libseccomp = pkgs.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index 7fd6a988a..da136bbe1 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -7,7 +7,7 @@ # Configuration Options -, versionSuffix ? "" +, version }: let @@ -16,7 +16,7 @@ in mkMesonDerivation (finalAttrs: { pname = "nix-external-api-docs"; - version = lib.fileContents ./.version + versionSuffix; + inherit version; workDir = ./.; fileset = diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index 44ddb00ab..f2077dcaf 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -7,7 +7,7 @@ # Configuration Options -, versionSuffix ? "" +, version }: let @@ -16,7 +16,7 @@ in mkMesonDerivation (finalAttrs: { pname = "nix-internal-api-docs"; - version = lib.fileContents ./.version + versionSuffix; + inherit version; workDir = ./.; fileset = let diff --git a/src/libcmd/package.nix b/src/libcmd/package.nix index 5e6381a17..ec3aa4660 100644 --- a/src/libcmd/package.nix +++ b/src/libcmd/package.nix @@ -20,7 +20,7 @@ # Configuration Options -, versionSuffix ? "" +, version # Whether to enable Markdown rendering in the Nix binary. , enableMarkdown ? !stdenv.hostPlatform.isWindows @@ -36,8 +36,6 @@ let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index b445a8187..0b895437b 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -11,13 +11,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index d4296bc07..704456c96 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -20,7 +20,7 @@ # Configuration Options -, versionSuffix ? "" +, version # Whether to use garbage collection for the Nix language evaluator. # @@ -36,8 +36,6 @@ let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 7786a4f35..b4abb144b 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -15,13 +15,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libflake/package.nix b/src/libflake/package.nix index f0609d5d5..af6f5da94 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libmain/package.nix b/src/libmain/package.nix index f6c92bac6..bbd97ec3e 100644 --- a/src/libmain/package.nix +++ b/src/libmain/package.nix @@ -14,13 +14,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index c14cf955d..fc34c1bda 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -12,13 +12,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libstore/package.nix b/src/libstore/package.nix index df92b5b28..0a2ace91e 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -20,15 +20,13 @@ # Configuration Options -, versionSuffix ? "" +, version , embeddedSandboxShell ? stdenv.hostPlatform.isStatic }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index f92cb036c..53451998d 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -11,13 +11,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 74d4d7853..28d7d8f0e 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/perl/package.nix b/src/perl/package.nix index 08ceaa33e..26856e631 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -8,7 +8,7 @@ , pkg-config , nix-store , darwin -, versionSuffix ? "" +, version , curl , bzip2 , libsodium @@ -20,7 +20,7 @@ in perl.pkgs.toPerlModule (mkMesonDerivation (finalAttrs: { pname = "nix-perl"; - version = lib.fileContents ./.version + versionSuffix; + inherit version; workDir = ./.; fileset = fileset.unions ([ diff --git a/tests/unit/libexpr-support/package.nix b/tests/unit/libexpr-support/package.nix index f32cf2615..0c966c55a 100644 --- a/tests/unit/libexpr-support/package.nix +++ b/tests/unit/libexpr-support/package.nix @@ -14,13 +14,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 1667dc77e..6394b595d 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index e9daacaeb..563481cc5 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -16,13 +16,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index c2bcc8eb8..15bf44907 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -16,13 +16,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libstore-support/package.nix b/tests/unit/libstore-support/package.nix index f3a5bfc82..cb15cdd5f 100644 --- a/tests/unit/libstore-support/package.nix +++ b/tests/unit/libstore-support/package.nix @@ -14,13 +14,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index 663e8ba43..2de8af103 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -18,13 +18,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix index 431fe91c6..fdecdec72 100644 --- a/tests/unit/libutil-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -13,13 +13,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index 6bfa571d8..ad5ff7d1e 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { From da4c55995b9fd355c72cca2f47bdbe1d163de454 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 19:15:53 +0200 Subject: [PATCH 496/910] ci.yml: Build non unit-tested components in meson_build --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4939dd11..ca94ff956 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -205,4 +205,6 @@ jobs: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix build -L .#hydraJobs.build.{nix-fetchers,nix-store,nix-util}.$(nix-instantiate --eval --expr builtins.currentSystem | sed -e 's/"//g') + # Only meson packages that don't have a tests.run derivation. + # Those that have it are already built and tested as part of nix flake check. + - run: nix build -L .#hydraJobs.build.{nix-cmd,nix-main}.$(nix-instantiate --eval --expr builtins.currentSystem | sed -e 's/"//g') From bea54d116e572366b21bcbdeb85d567df00ef4a4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:49:58 +0200 Subject: [PATCH 497/910] Add resolvePath, filesetToSource indirections for Nixpkgs --- packaging/dependencies.nix | 11 ++++++++++- tests/unit/libexpr/package.nix | 3 ++- tests/unit/libfetchers/package.nix | 3 ++- tests/unit/libflake/package.nix | 3 ++- tests/unit/libstore/package.nix | 3 ++- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 78a857d85..34b344971 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -14,9 +14,16 @@ let inherit (pkgs) lib; + root = ../.; + + # Nixpkgs implements this by returning a subpath into the fetched Nix sources. + resolvePath = p: p; + + # Indirection for Nixpkgs to override when package.nix files are vendored + filesetToSource = lib.fileset.toSource; + localSourceLayer = finalAttrs: prevAttrs: let - root = ../.; workDirPath = # Ideally we'd pick finalAttrs.workDir, but for now `mkDerivation` has # the requirement that everything except passthru and meta must be @@ -104,5 +111,7 @@ scope: { meta.platforms = lib.platforms.all; }); + inherit resolvePath filesetToSource; + mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); } diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 6394b595d..6b7e12c4a 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -18,6 +18,7 @@ # Configuration Options , version +, resolvePath }: let @@ -84,7 +85,7 @@ mkMesonDerivation (finalAttrs: { run = runCommand "${finalAttrs.pname}-run" { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${resolvePath ./data} nix-expr-tests touch $out ''; diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index 563481cc5..9522f9639 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -17,6 +17,7 @@ # Configuration Options , version +, resolvePath }: let @@ -82,7 +83,7 @@ mkMesonDerivation (finalAttrs: { run = runCommand "${finalAttrs.pname}-run" { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${resolvePath ./data} nix-fetchers-tests touch $out ''; diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index 15bf44907..859bc49d0 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -17,6 +17,7 @@ # Configuration Options , version +, resolvePath }: let @@ -82,7 +83,7 @@ mkMesonDerivation (finalAttrs: { run = runCommand "${finalAttrs.pname}-run" { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${resolvePath ./data} nix-flake-tests touch $out ''; diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index 2de8af103..efffd0063 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -19,6 +19,7 @@ # Configuration Options , version +, filesetToSource }: let @@ -86,7 +87,7 @@ mkMesonDerivation (finalAttrs: { run = let # Some data is shared with the functional tests: they create it, # we consume it. - data = lib.fileset.toSource { + data = filesetToSource { root = ../..; fileset = lib.fileset.unions [ ./data From 13181356fc6fc3fa7e5f8ac98da6a2f28cb50003 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:01:46 +0200 Subject: [PATCH 498/910] Refactor: rename runEnv -> isNixShell --- src/nix-build/nix-build.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 57630c8c3..d0b3b4f9f 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -93,7 +93,7 @@ static std::vector shellwords(const std::string & s) static void main_nix_build(int argc, char * * argv) { auto dryRun = false; - auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$")); + auto isNixShell = std::regex_search(argv[0], std::regex("nix-shell$")); auto pure = false; auto fromArgs = false; auto packages = false; @@ -107,7 +107,7 @@ static void main_nix_build(int argc, char * * argv) std::string envCommand; // interactive shell Strings envExclude; - auto myName = runEnv ? "nix-shell" : "nix-build"; + auto myName = isNixShell ? "nix-shell" : "nix-build"; auto inShebang = false; std::string script; @@ -132,7 +132,7 @@ static void main_nix_build(int argc, char * * argv) // Heuristic to see if we're invoked as a shebang script, namely, // if we have at least one argument, it's the name of an // executable file, and it starts with "#!". - if (runEnv && argc > 1) { + if (isNixShell && argc > 1) { script = argv[1]; try { auto lines = tokenizeString(readFile(script), "\n"); @@ -186,9 +186,9 @@ static void main_nix_build(int argc, char * * argv) dryRun = true; else if (*arg == "--run-env") // obsolete - runEnv = true; + isNixShell = true; - else if (runEnv && (*arg == "--command" || *arg == "--run")) { + else if (isNixShell && (*arg == "--command" || *arg == "--run")) { if (*arg == "--run") interactive = false; envCommand = getArg(*arg, arg, end) + "\nexit"; @@ -206,7 +206,7 @@ static void main_nix_build(int argc, char * * argv) else if (*arg == "--pure") pure = true; else if (*arg == "--impure") pure = false; - else if (runEnv && (*arg == "--packages" || *arg == "-p")) + else if (isNixShell && (*arg == "--packages" || *arg == "-p")) packages = true; else if (inShebang && *arg == "-i") { @@ -266,7 +266,7 @@ static void main_nix_build(int argc, char * * argv) auto autoArgs = myArgs.getAutoArgs(*state); auto autoArgsWithInNixShell = autoArgs; - if (runEnv) { + if (isNixShell) { auto newArgs = state->buildBindings(autoArgsWithInNixShell->size() + 1); newArgs.alloc("inNixShell").mkBool(true); for (auto & i : *autoArgs) newArgs.insert(i); @@ -282,13 +282,13 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; left = {joined.str()}; } else if (!fromArgs) { - if (left.empty() && runEnv && pathExists("shell.nix")) + if (left.empty() && isNixShell && pathExists("shell.nix")) left = {"shell.nix"}; if (left.empty()) left = {"default.nix"}; } - if (runEnv) + if (isNixShell) setEnv("IN_NIX_SHELL", pure ? "pure" : "impure"); PackageInfos drvs; @@ -330,7 +330,7 @@ static void main_nix_build(int argc, char * * argv) std::function takesNixShellAttr; takesNixShellAttr = [&](const Value & v) { - if (!runEnv) { + if (!isNixShell) { return false; } bool add = false; @@ -381,7 +381,7 @@ static void main_nix_build(int argc, char * * argv) store->buildPaths(paths, buildMode, evalStore); }; - if (runEnv) { + if (isNixShell) { if (drvs.size() != 1) throw UsageError("nix-shell requires a single derivation"); From 5c367ece895601a90ef4f38547e7cd84ce5d83d5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:03:30 +0200 Subject: [PATCH 499/910] Refactor: rename left -> remainingArgs --- src/nix-build/nix-build.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index d0b3b4f9f..faa9e5fae 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -100,7 +100,7 @@ static void main_nix_build(int argc, char * * argv) // Same condition as bash uses for interactive shells auto interactive = isatty(STDIN_FILENO) && isatty(STDERR_FILENO); Strings attrPaths; - Strings left; + Strings remainingArgs; BuildMode buildMode = bmNormal; bool readStdin = false; @@ -246,7 +246,7 @@ static void main_nix_build(int argc, char * * argv) return false; else - left.push_back(*arg); + remainingArgs.push_back(*arg); return true; }); @@ -276,16 +276,16 @@ static void main_nix_build(int argc, char * * argv) if (packages) { std::ostringstream joined; joined << "{...}@args: with import args; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ "; - for (const auto & i : left) + for (const auto & i : remainingArgs) joined << '(' << i << ") "; joined << "]; } \"\""; fromArgs = true; - left = {joined.str()}; + remainingArgs = {joined.str()}; } else if (!fromArgs) { - if (left.empty() && isNixShell && pathExists("shell.nix")) - left = {"shell.nix"}; - if (left.empty()) - left = {"default.nix"}; + if (remainingArgs.empty() && isNixShell && pathExists("shell.nix")) + remainingArgs = {"shell.nix"}; + if (remainingArgs.empty()) + remainingArgs = {"default.nix"}; } if (isNixShell) @@ -299,7 +299,7 @@ static void main_nix_build(int argc, char * * argv) if (readStdin) exprs = {state->parseStdin()}; else - for (auto i : left) { + for (auto i : remainingArgs) { if (fromArgs) exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath("."))); else { From e9479b272faaf00068348e7df8de7f50dce58113 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:51:45 +0200 Subject: [PATCH 500/910] nix-build.cc: Refactor: extract baseDir variable --- src/nix-build/nix-build.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index faa9e5fae..648917444 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -310,14 +310,17 @@ static void main_nix_build(int argc, char * * argv) auto [path, outputNames] = parsePathWithOutputs(absolute); if (evalStore->isStorePath(path) && hasSuffix(path, ".drv")) drvs.push_back(PackageInfo(*state, evalStore, absolute)); - else + else { /* If we're in a #! script, interpret filenames relative to the script. */ + auto baseDir = inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i; + exprs.push_back( state->parseExprFromFile( resolveExprPath( lookupFileArg(*state, - inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))); + baseDir)))); + } } } From 76245ffbebc8466ac17d241fceda6dcb9ec7c23e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:55:27 +0200 Subject: [PATCH 501/910] nix-build.cc: Refactor: extract sourcePath, resolvedPath variables --- src/nix-build/nix-build.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 648917444..e873f712b 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -315,11 +315,11 @@ static void main_nix_build(int argc, char * * argv) relative to the script. */ auto baseDir = inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i; - exprs.push_back( - state->parseExprFromFile( - resolveExprPath( - lookupFileArg(*state, - baseDir)))); + auto sourcePath = lookupFileArg(*state, + baseDir); + auto resolvedPath = resolveExprPath(sourcePath); + + exprs.push_back(state->parseExprFromFile(resolvedPath)); } } } From 514062c227f16af1410c9b2b12ede6c9f2069223 Mon Sep 17 00:00:00 2001 From: Romain NEIL Date: Sat, 6 Jul 2024 21:46:58 +0200 Subject: [PATCH 502/910] feat: configure aws s3 lib to use system defined proxy, if existent --- src/libstore/s3-binary-cache-store.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index e9850dce6..27013c0f1 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -132,6 +132,7 @@ ref S3Helper::makeConfig( { initAWS(); auto res = make_ref(); + res->allowSystemProxy = true; res->region = region; if (!scheme.empty()) { res->scheme = Aws::Http::SchemeMapper::FromString(scheme.c_str()); From 32fb127b9cbaf833027e646de5ee5198a62b6995 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 22:58:15 +0200 Subject: [PATCH 503/910] Add legacy setting: nix-shell-always-looks-for-shell-nix --- src/libcmd/common-eval-args.cc | 7 +++++++ src/libcmd/common-eval-args.hh | 6 ++++++ src/libcmd/meson.build | 1 + src/nix-build/nix-build.cc | 23 ++++++++++++++++++----- tests/functional/nix-shell.sh | 9 +++++++++ 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30cc86456..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -100,7 +101,13 @@ static SourcePath resolveShellExprPath(SourcePath path) auto resolvedOrDir = resolveExprPath(path, false); if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { if ((resolvedOrDir / "shell.nix").pathExists()) { - return resolvedOrDir / "shell.nix"; + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } } if ((resolvedOrDir / "default.nix").pathExists()) { return resolvedOrDir / "default.nix"; @@ -302,11 +309,17 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; remainingArgs = {joined.str()}; } else if (!fromArgs && remainingArgs.empty()) { - remainingArgs = {"."}; + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; - // Instead of letting it throw later, we throw here to give a more relevant error message - if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) - throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } if (isNixShell) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index b7a7db27c..2a1d556dd 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -101,6 +105,11 @@ nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet # https://github.com/NixOS/nix/issues/4529 nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + ( cd $TEST_ROOT/empty; expectStderr 1 nix-shell | \ From a22f8b5276162a87072eee7f0febc0a216f4fa9b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 23:02:32 +0200 Subject: [PATCH 504/910] rl-next: Add note about shell.nix lookups --- .../rl-next/nix-shell-looks-for-shell-nix.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..1f44ba33c --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,26 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. From b865625a8eab8382e1643605594e9db23271c2e5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 21:31:24 +0200 Subject: [PATCH 505/910] nix-shell: Look for shell.nix when directory is specified --- src/libexpr/eval.cc | 4 ++-- src/libexpr/eval.hh | 4 +++- src/nix-build/nix-build.cc | 34 ++++++++++++++++++++++----- tests/functional/nix-shell.sh | 44 +++++++++++++++++++++++++++++++++++ tests/functional/shell.nix | 11 +++++++++ 5 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 48ed66883..2a0862123 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2650,7 +2650,7 @@ void EvalState::printStatistics() } -SourcePath resolveExprPath(SourcePath path) +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix) { unsigned int followCount = 0, maxFollow = 1024; @@ -2666,7 +2666,7 @@ SourcePath resolveExprPath(SourcePath path) } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) + if (addDefaultNix && path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..e45358055 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -850,8 +850,10 @@ std::string showType(const Value & v); /** * If `path` refers to a directory, then append "/default.nix". + * + * @param addDefaultNix Whether to append "/default.nix" after resolving symlinks. */ -SourcePath resolveExprPath(SourcePath path); +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true); /** * Whether a URI is allowed, assuming restrictEval is enabled diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index e873f712b..30cc86456 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -90,6 +90,26 @@ static std::vector shellwords(const std::string & s) return res; } +/** + * Like `resolveExprPath`, but prefers `shell.nix` instead of `default.nix`, + * and if `path` was a directory, it checks eagerly whether `shell.nix` or + * `default.nix` exist, throwing an error if they don't. + */ +static SourcePath resolveShellExprPath(SourcePath path) +{ + auto resolvedOrDir = resolveExprPath(path, false); + if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { + if ((resolvedOrDir / "shell.nix").pathExists()) { + return resolvedOrDir / "shell.nix"; + } + if ((resolvedOrDir / "default.nix").pathExists()) { + return resolvedOrDir / "default.nix"; + } + throw Error("neither '%s' nor '%s' found in '%s'", "shell.nix", "default.nix", resolvedOrDir); + } + return resolvedOrDir; +} + static void main_nix_build(int argc, char * * argv) { auto dryRun = false; @@ -281,11 +301,12 @@ static void main_nix_build(int argc, char * * argv) joined << "]; } \"\""; fromArgs = true; remainingArgs = {joined.str()}; - } else if (!fromArgs) { - if (remainingArgs.empty() && isNixShell && pathExists("shell.nix")) - remainingArgs = {"shell.nix"}; - if (remainingArgs.empty()) - remainingArgs = {"default.nix"}; + } else if (!fromArgs && remainingArgs.empty()) { + remainingArgs = {"."}; + + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); } if (isNixShell) @@ -317,7 +338,8 @@ static void main_nix_build(int argc, char * * argv) auto sourcePath = lookupFileArg(*state, baseDir); - auto resolvedPath = resolveExprPath(sourcePath); + auto resolvedPath = + isNixShell ? resolveShellExprPath(sourcePath) : resolveExprPath(sourcePath); exprs.push_back(state->parseExprFromFile(resolvedPath)); } diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 2c94705de..b7a7db27c 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -91,6 +91,50 @@ sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.nix > $TEST_ROOT/shell.sheba chmod a+rx $TEST_ROOT/shell.shebang.nix $TEST_ROOT/shell.shebang.nix +mkdir $TEST_ROOT/lookup-test $TEST_ROOT/empty + +cp $shellDotNix $TEST_ROOT/lookup-test/shell.nix +cp config.nix $TEST_ROOT/lookup-test/ +echo 'abort "do not load default.nix!"' > $TEST_ROOT/lookup-test/default.nix + +nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +# https://github.com/NixOS/nix/issues/4529 +nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" + +( + cd $TEST_ROOT/empty; + expectStderr 1 nix-shell | \ + grepQuiet "error.*no argument specified and no .*shell\.nix.* or .*default\.nix.* file found in the working directory" +) + +expectStderr 1 nix-shell -I "testRoot=$TEST_ROOT" '' | + grepQuiet "error.*neither .*shell\.nix.* nor .*default\.nix.* found in .*/empty" + +cat >$TEST_ROOT/lookup-test/shebangscript <<"EOF" +#!/usr/bin/env nix-shell +#!nix-shell -A shellDrv -i bash +[[ $VAR_FROM_NIX == bar ]] +echo "script works" +EOF +chmod +x $TEST_ROOT/lookup-test/shebangscript + +$TEST_ROOT/lookup-test/shebangscript | grepQuiet "script works" + +# https://github.com/NixOS/nix/issues/5431 +mkdir $TEST_ROOT/marco{,/polo} +echo 'abort "marco/shell.nix must not be used, but its mere existence used to cause #5431"' > $TEST_ROOT/marco/shell.nix +cat >$TEST_ROOT/marco/polo/default.nix < Date: Sat, 6 Jul 2024 23:15:01 +0200 Subject: [PATCH 506/910] rl-next: Enter PR --- doc/manual/rl-next/nix-shell-looks-for-shell-nix.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md index 1f44ba33c..99be4148b 100644 --- a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -7,6 +7,8 @@ issues: - 4529 - 5431 - 11053 +prs: +- 11057 --- `nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. From d5854f33e2872b583f00d35321405c022858b9ce Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 00:18:26 +0200 Subject: [PATCH 507/910] rl-next: Typo --- doc/manual/rl-next/shebang-relative.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index dbda0db4c..e6ab9346f 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -3,6 +3,6 @@ prs: #5088 description: { `nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. -The new behavior is that evalutations are performed relative to the script. +The new behavior is that evaluations are performed relative to the script. } From f5b59fbc6478ab944e75213e5ec711c0066ad43d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 00:22:21 +0200 Subject: [PATCH 508/910] Fix and extend nix-shell baseDir test --- src/libcmd/common-eval-args.cc | 2 +- src/libutil/args/root.hh | 1 + src/nix-build/nix-build.cc | 6 ++++++ tests/functional/nix-shell.sh | 3 ++- tests/functional/shell.nix | 1 + tests/functional/shell.shebang.expr | 9 +++++++++ 6 files changed, 20 insertions(+), 2 deletions(-) create mode 100755 tests/functional/shell.shebang.expr diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 62745b681..ffc1ebd59 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -202,7 +202,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) auto v = state.allocValue(); std::visit(overloaded { [&](const AutoArgExpr & arg) { - state.mkThunk_(*v, state.parseExprFromString(arg.expr, state.rootPath("."))); + state.mkThunk_(*v, state.parseExprFromString(arg.expr, true ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); }, [&](const AutoArgString & arg) { v->mkString(arg.s); diff --git a/src/libutil/args/root.hh b/src/libutil/args/root.hh index 5c55c37a5..34a43b538 100644 --- a/src/libutil/args/root.hh +++ b/src/libutil/args/root.hh @@ -29,6 +29,7 @@ struct Completions final : AddCompletions */ class RootArgs : virtual public Args { +protected: /** * @brief The command's "working directory", but only set when top level. * diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 872295045..cfe183888 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -183,6 +183,9 @@ static void main_nix_build(int argc, char * * argv) struct MyArgs : LegacyArgs, MixEvalArgs { using LegacyArgs::LegacyArgs; + void setBaseDir(Path baseDir) { + commandBaseDir = baseDir; + } }; MyArgs myArgs(myName, [&](Strings::iterator & arg, const Strings::iterator & end) { @@ -290,6 +293,9 @@ static void main_nix_build(int argc, char * * argv) state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; + if (inShebang) { + myArgs.setBaseDir(absPath(dirOf(script))); + } auto autoArgs = myArgs.getAutoArgs(*state); auto autoArgsWithInNixShell = autoArgs; diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index f881acd03..596ac5951 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -72,8 +72,9 @@ chmod a+rx $TEST_ROOT/shell.shebang.expr ! $TEST_ROOT/shell.shebang.expr bar cp shell.nix config.nix $TEST_ROOT # Should succeed +echo "cwd: $PWD" output=$($TEST_ROOT/shell.shebang.expr bar) -[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/shell.shebang.expr bar' ] +[ "$output" = foo ] # Test nix-shell shebang mode again with metacharacters in the filename. # First word of filename is chosen to not match any file in the test root. diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix index 75e3845ea..a7577ff63 100644 --- a/tests/functional/shell.nix +++ b/tests/functional/shell.nix @@ -43,6 +43,7 @@ let pkgs = rec { ASCII_PERCENT = "%"; ASCII_AT = "@"; TEST_inNixShell = if inNixShell then "true" else "false"; + FOO = fooContents; inherit stdenv; outputs = ["dev" "out"]; } // { diff --git a/tests/functional/shell.shebang.expr b/tests/functional/shell.shebang.expr new file mode 100755 index 000000000..c602dedbf --- /dev/null +++ b/tests/functional/shell.shebang.expr @@ -0,0 +1,9 @@ +#! @ENV_PROG@ nix-shell +#! nix-shell "{ script, path, ... }: assert path == ./shell.nix; script { }" +#! nix-shell --no-substitute +#! nix-shell --expr +#! nix-shell --arg script "import ./shell.nix" +#! nix-shell --arg path "./shell.nix" +#! nix-shell -A shellDrv +#! nix-shell -i bash +echo "$FOO" From 6c6d5263e26bc463aee97e49a5e9b8d867a4731a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 22:58:15 +0200 Subject: [PATCH 509/910] Add legacy setting: nix-shell-always-looks-for-shell-nix --- src/libcmd/common-eval-args.cc | 7 +++++++ src/libcmd/common-eval-args.hh | 6 ++++++ src/libcmd/compatibility-settings.hh | 19 +++++++++++++++++++ src/libcmd/meson.build | 1 + src/nix-build/nix-build.cc | 23 ++++++++++++++++++----- tests/functional/nix-shell.sh | 9 +++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/libcmd/compatibility-settings.hh diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh new file mode 100644 index 000000000..5dc0eaf2b --- /dev/null +++ b/src/libcmd/compatibility-settings.hh @@ -0,0 +1,19 @@ +#pragma once +#include "config.hh" + +namespace nix { +struct CompatibilitySettings : public Config +{ + + CompatibilitySettings() = default; + + Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( + Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. + + Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; +}; + +}; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30cc86456..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -100,7 +101,13 @@ static SourcePath resolveShellExprPath(SourcePath path) auto resolvedOrDir = resolveExprPath(path, false); if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { if ((resolvedOrDir / "shell.nix").pathExists()) { - return resolvedOrDir / "shell.nix"; + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } } if ((resolvedOrDir / "default.nix").pathExists()) { return resolvedOrDir / "default.nix"; @@ -302,11 +309,17 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; remainingArgs = {joined.str()}; } else if (!fromArgs && remainingArgs.empty()) { - remainingArgs = {"."}; + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; - // Instead of letting it throw later, we throw here to give a more relevant error message - if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) - throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } if (isNixShell) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index b7a7db27c..2a1d556dd 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -101,6 +105,11 @@ nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet # https://github.com/NixOS/nix/issues/4529 nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + ( cd $TEST_ROOT/empty; expectStderr 1 nix-shell | \ From 6959ac157bf4e9ff8cbd30033cf8de07f5849ab7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 23:02:32 +0200 Subject: [PATCH 510/910] rl-next: Add note about shell.nix lookups --- .../rl-next/nix-shell-looks-for-shell-nix.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..99be4148b --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,28 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +prs: +- 11057 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. From 63262e78c7fd281b813e858640adbaa6f6b3d826 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 00:55:33 +0200 Subject: [PATCH 511/910] Add opt-out: nix-shell-shebang-arguments-relative-to-script --- doc/manual/rl-next/shebang-relative.md | 2 ++ src/libcmd/common-eval-args.cc | 2 +- src/libcmd/compatibility-settings.hh | 9 +++++++++ src/nix-build/nix-build.cc | 4 ++-- tests/functional/nix-shell.sh | 8 ++++++++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index e6ab9346f..ab39a359c 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -5,4 +5,6 @@ description: { `nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. The new behavior is that evaluations are performed relative to the script. +The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. + } diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index ffc1ebd59..a243b8c49 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -202,7 +202,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) auto v = state.allocValue(); std::visit(overloaded { [&](const AutoArgExpr & arg) { - state.mkThunk_(*v, state.parseExprFromString(arg.expr, true ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); + state.mkThunk_(*v, state.parseExprFromString(arg.expr, compatibilitySettings.nixShellShebangArgumentsRelativeToScript ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); }, [&](const AutoArgString & arg) { v->mkString(arg.s); diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh index 5dc0eaf2b..961001080 100644 --- a/src/libcmd/compatibility-settings.hh +++ b/src/libcmd/compatibility-settings.hh @@ -14,6 +14,15 @@ struct CompatibilitySettings : public Config You may set this to `false` to revert to the Nix 2.3 behavior. )"}; + + Setting nixShellShebangArgumentsRelativeToScript{ + this, true, "nix-shell-shebang-arguments-relative-to-script", R"( + Before Nix 2.24, the arguments in a `nix-shell` shebang - as well as `--arg` - were relative to working directory. + + Since Nix 2.24, the arguments are relative to the [base directory](@docroot@/glossary.md#gloss-base-directory) defined as the script's directory. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; }; }; diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index cfe183888..f4af3fd04 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -293,7 +293,7 @@ static void main_nix_build(int argc, char * * argv) state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; - if (inShebang) { + if (inShebang && compatibilitySettings.nixShellShebangArgumentsRelativeToScript) { myArgs.setBaseDir(absPath(dirOf(script))); } auto autoArgs = myArgs.getAutoArgs(*state); @@ -345,7 +345,7 @@ static void main_nix_build(int argc, char * * argv) if (fromArgs) exprs.push_back(state->parseExprFromString( std::move(i), - inShebang ? lookupFileArg(*state, baseDir) : state->rootPath(".") + (inShebang && compatibilitySettings.nixShellShebangArgumentsRelativeToScript) ? lookupFileArg(*state, baseDir) : state->rootPath(".") )); else { auto absolute = i; diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 596ac5951..fd3edf81a 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -76,6 +76,14 @@ echo "cwd: $PWD" output=$($TEST_ROOT/shell.shebang.expr bar) [ "$output" = foo ] +# Test nix-shell shebang mode with an alternate working directory +sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.legacy.expr > $TEST_ROOT/shell.shebang.legacy.expr +chmod a+rx $TEST_ROOT/shell.shebang.legacy.expr +# Should fail due to expressions using relative path +mkdir -p "$TEST_ROOT/somewhere-unrelated" +output="$(cd "$TEST_ROOT/somewhere-unrelated"; $TEST_ROOT/shell.shebang.legacy.expr bar;)" +[[ $(realpath "$output") = $(realpath "$TEST_ROOT/somewhere-unrelated") ]] + # Test nix-shell shebang mode again with metacharacters in the filename. # First word of filename is chosen to not match any file in the test root. sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh From 73602a7c6f4dc6f4f4bea8368a8564403b7b5604 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 21:31:24 +0200 Subject: [PATCH 512/910] nix-shell: Look for shell.nix when directory is specified --- src/libexpr/eval.cc | 4 ++-- src/libexpr/eval.hh | 4 +++- src/nix-build/nix-build.cc | 34 ++++++++++++++++++++++----- tests/functional/nix-shell.sh | 44 +++++++++++++++++++++++++++++++++++ tests/functional/shell.nix | 11 +++++++++ 5 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 48ed66883..2a0862123 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2650,7 +2650,7 @@ void EvalState::printStatistics() } -SourcePath resolveExprPath(SourcePath path) +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix) { unsigned int followCount = 0, maxFollow = 1024; @@ -2666,7 +2666,7 @@ SourcePath resolveExprPath(SourcePath path) } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) + if (addDefaultNix && path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..e45358055 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -850,8 +850,10 @@ std::string showType(const Value & v); /** * If `path` refers to a directory, then append "/default.nix". + * + * @param addDefaultNix Whether to append "/default.nix" after resolving symlinks. */ -SourcePath resolveExprPath(SourcePath path); +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true); /** * Whether a URI is allowed, assuming restrictEval is enabled diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index e873f712b..30cc86456 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -90,6 +90,26 @@ static std::vector shellwords(const std::string & s) return res; } +/** + * Like `resolveExprPath`, but prefers `shell.nix` instead of `default.nix`, + * and if `path` was a directory, it checks eagerly whether `shell.nix` or + * `default.nix` exist, throwing an error if they don't. + */ +static SourcePath resolveShellExprPath(SourcePath path) +{ + auto resolvedOrDir = resolveExprPath(path, false); + if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { + if ((resolvedOrDir / "shell.nix").pathExists()) { + return resolvedOrDir / "shell.nix"; + } + if ((resolvedOrDir / "default.nix").pathExists()) { + return resolvedOrDir / "default.nix"; + } + throw Error("neither '%s' nor '%s' found in '%s'", "shell.nix", "default.nix", resolvedOrDir); + } + return resolvedOrDir; +} + static void main_nix_build(int argc, char * * argv) { auto dryRun = false; @@ -281,11 +301,12 @@ static void main_nix_build(int argc, char * * argv) joined << "]; } \"\""; fromArgs = true; remainingArgs = {joined.str()}; - } else if (!fromArgs) { - if (remainingArgs.empty() && isNixShell && pathExists("shell.nix")) - remainingArgs = {"shell.nix"}; - if (remainingArgs.empty()) - remainingArgs = {"default.nix"}; + } else if (!fromArgs && remainingArgs.empty()) { + remainingArgs = {"."}; + + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); } if (isNixShell) @@ -317,7 +338,8 @@ static void main_nix_build(int argc, char * * argv) auto sourcePath = lookupFileArg(*state, baseDir); - auto resolvedPath = resolveExprPath(sourcePath); + auto resolvedPath = + isNixShell ? resolveShellExprPath(sourcePath) : resolveExprPath(sourcePath); exprs.push_back(state->parseExprFromFile(resolvedPath)); } diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 2c94705de..f54e3621c 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -91,6 +91,50 @@ sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.nix > $TEST_ROOT/shell.sheba chmod a+rx $TEST_ROOT/shell.shebang.nix $TEST_ROOT/shell.shebang.nix +mkdir $TEST_ROOT/lookup-test $TEST_ROOT/empty + +echo "import $shellDotNix" > $TEST_ROOT/lookup-test/shell.nix +cp config.nix $TEST_ROOT/lookup-test/ +echo 'abort "do not load default.nix!"' > $TEST_ROOT/lookup-test/default.nix + +nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +# https://github.com/NixOS/nix/issues/4529 +nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" + +( + cd $TEST_ROOT/empty; + expectStderr 1 nix-shell | \ + grepQuiet "error.*no argument specified and no .*shell\.nix.* or .*default\.nix.* file found in the working directory" +) + +expectStderr 1 nix-shell -I "testRoot=$TEST_ROOT" '' | + grepQuiet "error.*neither .*shell\.nix.* nor .*default\.nix.* found in .*/empty" + +cat >$TEST_ROOT/lookup-test/shebangscript < $TEST_ROOT/marco/shell.nix +cat >$TEST_ROOT/marco/polo/default.nix < Date: Sat, 6 Jul 2024 22:58:15 +0200 Subject: [PATCH 513/910] Add legacy setting: nix-shell-always-looks-for-shell-nix --- src/libcmd/common-eval-args.cc | 7 +++++++ src/libcmd/common-eval-args.hh | 6 ++++++ src/libcmd/compatibility-settings.hh | 19 +++++++++++++++++++ src/libcmd/meson.build | 1 + src/nix-build/nix-build.cc | 23 ++++++++++++++++++----- tests/functional/nix-shell.sh | 9 +++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/libcmd/compatibility-settings.hh diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh new file mode 100644 index 000000000..5dc0eaf2b --- /dev/null +++ b/src/libcmd/compatibility-settings.hh @@ -0,0 +1,19 @@ +#pragma once +#include "config.hh" + +namespace nix { +struct CompatibilitySettings : public Config +{ + + CompatibilitySettings() = default; + + Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( + Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. + + Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; +}; + +}; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30cc86456..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -100,7 +101,13 @@ static SourcePath resolveShellExprPath(SourcePath path) auto resolvedOrDir = resolveExprPath(path, false); if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { if ((resolvedOrDir / "shell.nix").pathExists()) { - return resolvedOrDir / "shell.nix"; + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } } if ((resolvedOrDir / "default.nix").pathExists()) { return resolvedOrDir / "default.nix"; @@ -302,11 +309,17 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; remainingArgs = {joined.str()}; } else if (!fromArgs && remainingArgs.empty()) { - remainingArgs = {"."}; + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; - // Instead of letting it throw later, we throw here to give a more relevant error message - if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) - throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } if (isNixShell) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index f54e3621c..65ff279f8 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -101,6 +105,11 @@ nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet # https://github.com/NixOS/nix/issues/4529 nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + ( cd $TEST_ROOT/empty; expectStderr 1 nix-shell | \ From c4a20a41019ef3cd806059102cbe1d45fcbdd2b8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 23:02:32 +0200 Subject: [PATCH 514/910] rl-next: Add note about shell.nix lookups --- .../rl-next/nix-shell-looks-for-shell-nix.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..99be4148b --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,28 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +prs: +- 11057 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. From 0f8a655023be204499c6360e072b36f58f6f194c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 13:02:21 +0200 Subject: [PATCH 515/910] tests/functional/shell.nix: Implement runHook for dummy stdenv --- tests/functional/shell.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix index 75e3845ea..1fb00c5a3 100644 --- a/tests/functional/shell.nix +++ b/tests/functional/shell.nix @@ -26,6 +26,9 @@ let pkgs = rec { fun() { echo blabla } + runHook() { + eval "''${!1}" + } ''; stdenv = mkDerivation { From e1106b45a31228c6f5fe8be0bd5fbde08e7c3255 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 13:03:19 +0200 Subject: [PATCH 516/910] tests/functional/nix-shell.sh: Fix Polo test for VM test It is unclear to me why this worked when not in a VM test, but the explanation would be in the part of nix-shell we're getting rid of with the devShell attribute. --- tests/functional/shell.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix index 1fb00c5a3..750cdf0bc 100644 --- a/tests/functional/shell.nix +++ b/tests/functional/shell.nix @@ -56,6 +56,7 @@ let pkgs = rec { # See nix-shell.sh polo = mkDerivation { name = "polo"; + inherit stdenv; shellHook = '' echo Polo ''; From 193dd5d9342e4ee7892b391d32240bbc431f16c8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 14:49:52 +0200 Subject: [PATCH 517/910] Fixup: add missing test file --- tests/functional/shell.shebang.legacy.expr | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 tests/functional/shell.shebang.legacy.expr diff --git a/tests/functional/shell.shebang.legacy.expr b/tests/functional/shell.shebang.legacy.expr new file mode 100755 index 000000000..490542f43 --- /dev/null +++ b/tests/functional/shell.shebang.legacy.expr @@ -0,0 +1,10 @@ +#! @ENV_PROG@ nix-shell +#! nix-shell "{ script, path, ... }: assert path == ./shell.nix; script { fooContents = toString ./.; }" +#! nix-shell --no-substitute +#! nix-shell --expr +#! nix-shell --arg script "import ((builtins.getEnv ''TEST_ROOT'')+''/shell.nix'')" +#! nix-shell --arg path "./shell.nix" +#! nix-shell -A shellDrv +#! nix-shell -i bash +#! nix-shell --option nix-shell-shebang-arguments-relative-to-script false +echo "$FOO" From 95890b3e1d1aa0cf3d4df672287bc1b15686d671 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Sun, 7 Jul 2024 15:57:23 -0400 Subject: [PATCH 518/910] docs: merge builtin-constants into builtins --- doc/manual/generate-builtin-constants.nix | 31 ------------ doc/manual/generate-builtins.nix | 16 ++++-- doc/manual/local.mk | 10 +--- doc/manual/src/SUMMARY.md.in | 9 ++-- doc/manual/src/_redirects | 1 + .../src/language/builtin-constants-prefix.md | 5 -- .../src/language/builtin-constants-suffix.md | 1 - doc/manual/src/language/builtins-prefix.md | 10 ++-- .../src/language/constructs/lookup-path.md | 2 +- doc/manual/src/language/derivations.md | 2 +- doc/manual/src/language/types.md | 4 +- src/libexpr/eval-settings.hh | 14 +++--- src/libexpr/primops.cc | 4 +- src/libstore/globals.hh | 2 +- src/libutil/experimental-features.cc | 2 +- src/nix/main.cc | 49 ++++++++----------- 16 files changed, 62 insertions(+), 100 deletions(-) delete mode 100644 doc/manual/generate-builtin-constants.nix delete mode 100644 doc/manual/src/language/builtin-constants-prefix.md delete mode 100644 doc/manual/src/language/builtin-constants-suffix.md diff --git a/doc/manual/generate-builtin-constants.nix b/doc/manual/generate-builtin-constants.nix deleted file mode 100644 index cccd1e279..000000000 --- a/doc/manual/generate-builtin-constants.nix +++ /dev/null @@ -1,31 +0,0 @@ -let - inherit (builtins) concatStringsSep attrValues mapAttrs; - inherit (import ) optionalString squash; -in - -builtinsInfo: -let - showBuiltin = name: { doc, type, impure-only }: - let - type' = optionalString (type != null) " (${type})"; - - impureNotice = optionalString impure-only '' - > **Note** - > - > Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). - ''; - in - squash '' -
- ${name}${type'} -
-
- - ${doc} - - ${impureNotice} - -
- ''; -in -concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo)) diff --git a/doc/manual/generate-builtins.nix b/doc/manual/generate-builtins.nix index 007b698f1..13de6c397 100644 --- a/doc/manual/generate-builtins.nix +++ b/doc/manual/generate-builtins.nix @@ -5,8 +5,10 @@ in builtinsInfo: let - showBuiltin = name: { doc, args, arity, experimental-feature }: + showBuiltin = name: { doc, type ? null, args ? [ ], experimental-feature ? null, impure-only ? false }: let + type' = optionalString (type != null) " (${type})"; + experimentalNotice = optionalString (experimental-feature != null) '' > **Note** > @@ -18,18 +20,26 @@ let > extra-experimental-features = ${experimental-feature} > ``` ''; + + impureNotice = optionalString impure-only '' + > **Note** + > + > Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). + ''; in squash ''
- ${name} ${listArgs args} + ${name}${listArgs args}${type'}
${experimentalNotice} ${doc} + + ${impureNotice}
''; - listArgs = args: concatStringsSep " " (map (s: "${s}") args); + listArgs = args: concatStringsSep "" (map (s: " ${s}") args); in concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo)) diff --git a/doc/manual/local.mk b/doc/manual/local.mk index 71ad5c8e6..0cec52885 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -140,16 +140,10 @@ $(d)/xp-features.json: $(doc_nix) $(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(doc_nix) @cat doc/manual/src/language/builtins-prefix.md > $@.tmp - $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<)).builtins' >> $@.tmp; + $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp; @cat doc/manual/src/language/builtins-suffix.md >> $@.tmp @mv $@.tmp $@ -$(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin-constants.nix $(d)/src/language/builtin-constants-prefix.md $(doc_nix) - @cat doc/manual/src/language/builtin-constants-prefix.md > $@.tmp - $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtin-constants.nix (builtins.fromJSON (builtins.readFile $<)).constants' >> $@.tmp; - @cat doc/manual/src/language/builtin-constants-suffix.md >> $@.tmp - @mv $@.tmp $@ - $(d)/language.json: $(doc_nix) $(trace-gen) $(dummy-env) $(doc_nix) __dump-language > $@.tmp @mv $@.tmp $@ @@ -217,7 +211,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli # `@docroot@` is to be preserved for documenting the mechanism # FIXME: maybe contributing guides should live right next to the code # instead of in the manual -$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg +$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg $(trace-gen) \ tmp="$$(mktemp -d)"; \ cp -r doc/manual "$$tmp"; \ diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 6e5c1aee1..a6a2101e9 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -32,11 +32,10 @@ - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) - [Operators](language/operators.md) - - [Derivations](language/derivations.md) - - [Advanced Attributes](language/advanced-attributes.md) - - [Import From Derivation](language/import-from-derivation.md) - - [Built-in Constants](language/builtin-constants.md) - - [Built-in Functions](language/builtins.md) + - [Built-ins](language/builtins.md) + - [Derivations](language/derivations.md) + - [Advanced Attributes](language/advanced-attributes.md) + - [Import From Derivation](language/import-from-derivation.md) - [Package Management](package-management/index.md) - [Profiles](package-management/profiles.md) - [Garbage Collection](package-management/garbage-collection.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index c52ca0ddd..578c48f06 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -29,6 +29,7 @@ /expressions/* /language/:splat 301! /language/values /language/types 301! /language/constructs /language/syntax 301! +/language/builtin-constants /language/builtins 301! /installation/installation /installation 301! diff --git a/doc/manual/src/language/builtin-constants-prefix.md b/doc/manual/src/language/builtin-constants-prefix.md deleted file mode 100644 index 50f43006d..000000000 --- a/doc/manual/src/language/builtin-constants-prefix.md +++ /dev/null @@ -1,5 +0,0 @@ -# Built-in Constants - -These constants are built into the Nix language evaluator: - -
diff --git a/doc/manual/src/language/builtin-constants-suffix.md b/doc/manual/src/language/builtin-constants-suffix.md deleted file mode 100644 index a74db2857..000000000 --- a/doc/manual/src/language/builtin-constants-suffix.md +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/doc/manual/src/language/builtins-prefix.md b/doc/manual/src/language/builtins-prefix.md index 7b2321466..fb983bb7f 100644 --- a/doc/manual/src/language/builtins-prefix.md +++ b/doc/manual/src/language/builtins-prefix.md @@ -1,9 +1,11 @@ -# Built-in Functions +# Built-ins -This section lists the functions built into the Nix language evaluator. -All built-in functions are available through the global [`builtins`](./builtin-constants.md#builtins-builtins) constant. +This section lists the values and functions built into the Nix language evaluator. +All built-ins are available through the global [`builtins`](#builtins-builtins) constant. -For convenience, some built-ins can be accessed directly: +Some built-ins are also exposed directly in the global scope: + + - [`derivation`](#builtins-derivation) - [`import`](#builtins-import) diff --git a/doc/manual/src/language/constructs/lookup-path.md b/doc/manual/src/language/constructs/lookup-path.md index 11278f3a8..11b9fe88c 100644 --- a/doc/manual/src/language/constructs/lookup-path.md +++ b/doc/manual/src/language/constructs/lookup-path.md @@ -6,7 +6,7 @@ A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/types.md#type-path) if the identifier matches a search path entry. -The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). +The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath). See [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile) for details on lookup path resolution. diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 8879fe706..8e3f0f791 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -64,7 +64,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > } > ``` > - > [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation. + > [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation. - [`builder`]{#attr-builder} ([Path](@docroot@/language/types.md#type-path) | [String](@docroot@/language/types.md#type-string)) diff --git a/doc/manual/src/language/types.md b/doc/manual/src/language/types.md index 1b3e6b247..c6cfb3c69 100644 --- a/doc/manual/src/language/types.md +++ b/doc/manual/src/language/types.md @@ -37,7 +37,7 @@ A _boolean_ in the Nix language is one of _true_ or _false_. -These values are available as attributes of [`builtins`](builtin-constants.md#builtins-builtins) as [`builtins.true`](builtin-constants.md#builtins-true) and [`builtins.false`](builtin-constants.md#builtins-false). +These values are available as attributes of [`builtins`](builtins.md#builtins-builtins) as [`builtins.true`](builtins.md#builtins-true) and [`builtins.false`](builtins.md#builtins-false). The function [`builtins.isBool`](builtins.md#builtins-isBool) can be used to determine if a value is a boolean. ### String {#type-string} @@ -60,7 +60,7 @@ There is a single value of type _null_ in the Nix language. -This value is available as an attribute on the [`builtins`](builtin-constants.md#builtins-builtins) attribute set as [`builtins.null`](builtin-constants.md#builtins-null). +This value is available as an attribute on the [`builtins`](builtins.md#builtins-builtins) attribute set as [`builtins.null`](builtins.md#builtins-null). ## Compound values diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 5eae708a2..191dde21a 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -74,7 +74,7 @@ struct EvalSettings : Config R"( List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution. This setting determines the value of - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) and can be used with [`builtins.findFile`](@docroot@/language/builtin-constants.md#builtins-findFile). + [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath) and can be used with [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile). The default value is @@ -95,7 +95,7 @@ struct EvalSettings : Config this, "", "eval-system", R"( This option defines - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) in the Nix language if it is set as a non-empty string. Otherwise, if it is defined as the empty string (the default), the value of the [`system` ](#conf-system) @@ -116,7 +116,7 @@ struct EvalSettings : Config R"( If set to `true`, the Nix evaluator will not allow access to any files outside of - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath), + [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath), or to URIs outside of [`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris). )"}; @@ -127,10 +127,10 @@ struct EvalSettings : Config - Restrict file system and network access to files specified by cryptographic hash - Disable impure constants: - - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) - - [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) - - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) - - [`builtins.storePath`](@docroot@/language/builtin-constants.md#builtins-storePath) + - [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) + - [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime) + - [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath) + - [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath) )" }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7a946bdaa..134363e1a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1872,7 +1872,7 @@ static RegisterPrimOp primop_findFile(PrimOp { - If the suffix is found inside that directory, then the entry is a match. The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. - [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath): + [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](#builtins-nixPath): ```nix @@ -4519,7 +4519,7 @@ void EvalState::createBaseEnv() addConstant("builtins", v, { .type = nAttrs, .doc = R"( - Contains all the [built-in functions](@docroot@/language/builtins.md) and values. + Contains all the built-in functions and values. Since built-in functions were added over time, [testing for attributes](./operators.md#has-attribute) in `builtins` can be used for graceful fallback on older Nix installations: diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 439e9f4fc..dfe25f317 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -228,7 +228,7 @@ public: While you can force Nix to run a Darwin-specific `builder` executable on a Linux machine, the result would obviously be wrong. This value is available in the Nix language as - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) if the [`eval-system`](#conf-eval-system) configuration option is set as the empty string. diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 9b7000f9f..1c080e372 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -66,7 +66,7 @@ constexpr std::array xpFeatureDetails an impure derivation cannot also be [content-addressed](#xp-feature-ca-derivations). - This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime). + This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime). )", .trackingUrl = "https://github.com/NixOS/nix/milestone/42", }, diff --git a/src/nix/main.cc b/src/nix/main.cc index e95558781..de6d89bd3 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -419,35 +419,28 @@ void mainWrapped(int argc, char * * argv) }; evalSettings.pureEval = false; EvalState state({}, openStore("dummy://"), evalSettings); - auto res = nlohmann::json::object(); - res["builtins"] = ({ - auto builtinsJson = nlohmann::json::object(); - for (auto & builtin : *state.baseEnv.values[0]->attrs()) { - auto b = nlohmann::json::object(); - if (!builtin.value->isPrimOp()) continue; - auto primOp = builtin.value->primOp(); - if (!primOp->doc) continue; - b["arity"] = primOp->arity; - b["args"] = primOp->args; - b["doc"] = trim(stripIndentation(primOp->doc)); + auto builtinsJson = nlohmann::json::object(); + for (auto & builtin : *state.baseEnv.values[0]->attrs()) { + auto b = nlohmann::json::object(); + if (!builtin.value->isPrimOp()) continue; + auto primOp = builtin.value->primOp(); + if (!primOp->doc) continue; + b["args"] = primOp->args; + b["doc"] = trim(stripIndentation(primOp->doc)); + if (primOp->experimentalFeature) b["experimental-feature"] = primOp->experimentalFeature; - builtinsJson[state.symbols[builtin.name]] = std::move(b); - } - std::move(builtinsJson); - }); - res["constants"] = ({ - auto constantsJson = nlohmann::json::object(); - for (auto & [name, info] : state.constantInfos) { - auto c = nlohmann::json::object(); - if (!info.doc) continue; - c["doc"] = trim(stripIndentation(info.doc)); - c["type"] = showType(info.type, false); - c["impure-only"] = info.impureOnly; - constantsJson[name] = std::move(c); - } - std::move(constantsJson); - }); - logger->cout("%s", res); + builtinsJson[state.symbols[builtin.name]] = std::move(b); + } + for (auto & [name, info] : state.constantInfos) { + auto b = nlohmann::json::object(); + if (!info.doc) continue; + b["doc"] = trim(stripIndentation(info.doc)); + b["type"] = showType(info.type, false); + if (info.impureOnly) + b["impure-only"] = true; + builtinsJson[name] = std::move(b); + } + logger->cout("%s", builtinsJson); return; } From 48804cffbf0663ac8cecd603973032377a0cab07 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Mon, 8 Jul 2024 00:41:19 -0400 Subject: [PATCH 519/910] docs: fill out language/types.md#type-path --- doc/manual/src/language/syntax.md | 18 ++---------------- doc/manual/src/language/types.md | 31 ++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md index 238c502f9..b0779ea95 100644 --- a/doc/manual/src/language/syntax.md +++ b/doc/manual/src/language/syntax.md @@ -190,18 +190,13 @@ This section covers syntax and semantics of the Nix language. ### Path {#path-literal} - *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - - Paths are suitable for referring to local files, and are often preferable over strings. - - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. - - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). - - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. + *Paths* can be expressed by path literals such as `./builder.sh`. A path literal must contain at least one slash to be recognised as such. For instance, `builder.sh` is not a path: it's parsed as an expression that selects the attribute `sh` from the variable `builder`. + Path literals are resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). Path literals may also refer to absolute paths by starting with a slash. > **Note** @@ -215,15 +210,6 @@ This section covers syntax and semantics of the Nix language. For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. - Paths can be used in [string interpolation] and string concatenation. - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. - - Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. - For example, assume you used a file path in an interpolated string during a `nix repl` session. - Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. - - [store path]: @docroot@/store/store-path.md - Path literals can also include [string interpolation], besides being [interpolated into other expressions]. [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions diff --git a/doc/manual/src/language/types.md b/doc/manual/src/language/types.md index c6cfb3c69..229756e6b 100644 --- a/doc/manual/src/language/types.md +++ b/doc/manual/src/language/types.md @@ -50,8 +50,37 @@ The function [`builtins.isString`](builtins.md#builtins-isString) can be used to ### Path {#type-path} - +A _path_ in the Nix language is an immutable, finite-length sequence of bytes starting with `/`, representing a POSIX-style, canonical file system path. +Path values are distinct from string values, even if they contain the same sequence of bytes. +Operations that produce paths will simplify the result as the standard C function [`realpath`] would, except that there is no symbolic link resolution. +[`realpath`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html + +Paths are suitable for referring to local files, and are often preferable over strings. +- Path values do not contain trailing or duplicate slashes, `.`, or `..`. +- Relative path literals are automatically resolved relative to their [base directory]. +- Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. + +[base directory]: @docroot@/glossary.md#gloss-base-directory + +A file is not required to exist at a given path in order for that path value to be valid, but a path that is converted to a string with [string interpolation] or [string-and-path concatenation] must resolve to a readable file or directory which will be copied into the Nix store. +For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. +Operations such as [`import`] can also expect a path to resolve to a readable file or directory. + +[string interpolation]: string-interpolation.md#interpolated-expression +[string-and-path concatenation]: operators.md#string-and-path-concatenation +[`import`]: builtins.md#builtins-import + +> **Note** +> +> The Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. +> For example, assume you used a file path in an interpolated string during a `nix repl` session. +> Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. +> Use `:r` to reset the repl as needed. + +[store path]: @docroot@/store/store-path.md + +Path values can be expressed as [path literals](syntax.md#path-literal). The function [`builtins.isPath`](builtins.md#builtins-isPath) can be used to determine if a value is a path. ### Null {#type-null} From cfe3ee3de84458a8962a6c714e602e6791666101 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Jul 2024 14:36:36 +0200 Subject: [PATCH 520/910] `nix-shell`: look up `shell.nix` when argument is a directory (#11057) * Refactor: rename runEnv -> isNixShell * Refactor: rename left -> remainingArgs * nix-build.cc: Refactor: extract baseDir variable * nix-build.cc: Refactor: extract sourcePath, resolvedPath variables * nix-shell: Look for shell.nix when directory is specified * Add legacy setting: nix-shell-always-looks-for-shell-nix * rl-next: Add note about shell.nix lookups * tests/functional/shell.nix: Implement runHook for dummy stdenv --- .../rl-next/nix-shell-looks-for-shell-nix.md | 28 ++++++ src/libcmd/common-eval-args.cc | 7 ++ src/libcmd/common-eval-args.hh | 6 ++ src/libcmd/compatibility-settings.hh | 19 ++++ src/libcmd/meson.build | 1 + src/libexpr/eval.cc | 4 +- src/libexpr/eval.hh | 4 +- src/nix-build/nix-build.cc | 90 +++++++++++++------ tests/functional/nix-shell.sh | 53 +++++++++++ tests/functional/shell.nix | 15 ++++ 10 files changed, 198 insertions(+), 29 deletions(-) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md create mode 100644 src/libcmd/compatibility-settings.hh diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..99be4148b --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,28 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +prs: +- 11057 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh new file mode 100644 index 000000000..5dc0eaf2b --- /dev/null +++ b/src/libcmd/compatibility-settings.hh @@ -0,0 +1,19 @@ +#pragma once +#include "config.hh" + +namespace nix { +struct CompatibilitySettings : public Config +{ + + CompatibilitySettings() = default; + + Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( + Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. + + Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; +}; + +}; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 48ed66883..2a0862123 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2650,7 +2650,7 @@ void EvalState::printStatistics() } -SourcePath resolveExprPath(SourcePath path) +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix) { unsigned int followCount = 0, maxFollow = 1024; @@ -2666,7 +2666,7 @@ SourcePath resolveExprPath(SourcePath path) } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) + if (addDefaultNix && path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..e45358055 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -850,8 +850,10 @@ std::string showType(const Value & v); /** * If `path` refers to a directory, then append "/default.nix". + * + * @param addDefaultNix Whether to append "/default.nix" after resolving symlinks. */ -SourcePath resolveExprPath(SourcePath path); +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true); /** * Whether a URI is allowed, assuming restrictEval is enabled diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 57630c8c3..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -90,24 +91,50 @@ static std::vector shellwords(const std::string & s) return res; } +/** + * Like `resolveExprPath`, but prefers `shell.nix` instead of `default.nix`, + * and if `path` was a directory, it checks eagerly whether `shell.nix` or + * `default.nix` exist, throwing an error if they don't. + */ +static SourcePath resolveShellExprPath(SourcePath path) +{ + auto resolvedOrDir = resolveExprPath(path, false); + if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { + if ((resolvedOrDir / "shell.nix").pathExists()) { + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } + } + if ((resolvedOrDir / "default.nix").pathExists()) { + return resolvedOrDir / "default.nix"; + } + throw Error("neither '%s' nor '%s' found in '%s'", "shell.nix", "default.nix", resolvedOrDir); + } + return resolvedOrDir; +} + static void main_nix_build(int argc, char * * argv) { auto dryRun = false; - auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$")); + auto isNixShell = std::regex_search(argv[0], std::regex("nix-shell$")); auto pure = false; auto fromArgs = false; auto packages = false; // Same condition as bash uses for interactive shells auto interactive = isatty(STDIN_FILENO) && isatty(STDERR_FILENO); Strings attrPaths; - Strings left; + Strings remainingArgs; BuildMode buildMode = bmNormal; bool readStdin = false; std::string envCommand; // interactive shell Strings envExclude; - auto myName = runEnv ? "nix-shell" : "nix-build"; + auto myName = isNixShell ? "nix-shell" : "nix-build"; auto inShebang = false; std::string script; @@ -132,7 +159,7 @@ static void main_nix_build(int argc, char * * argv) // Heuristic to see if we're invoked as a shebang script, namely, // if we have at least one argument, it's the name of an // executable file, and it starts with "#!". - if (runEnv && argc > 1) { + if (isNixShell && argc > 1) { script = argv[1]; try { auto lines = tokenizeString(readFile(script), "\n"); @@ -186,9 +213,9 @@ static void main_nix_build(int argc, char * * argv) dryRun = true; else if (*arg == "--run-env") // obsolete - runEnv = true; + isNixShell = true; - else if (runEnv && (*arg == "--command" || *arg == "--run")) { + else if (isNixShell && (*arg == "--command" || *arg == "--run")) { if (*arg == "--run") interactive = false; envCommand = getArg(*arg, arg, end) + "\nexit"; @@ -206,7 +233,7 @@ static void main_nix_build(int argc, char * * argv) else if (*arg == "--pure") pure = true; else if (*arg == "--impure") pure = false; - else if (runEnv && (*arg == "--packages" || *arg == "-p")) + else if (isNixShell && (*arg == "--packages" || *arg == "-p")) packages = true; else if (inShebang && *arg == "-i") { @@ -246,7 +273,7 @@ static void main_nix_build(int argc, char * * argv) return false; else - left.push_back(*arg); + remainingArgs.push_back(*arg); return true; }); @@ -266,7 +293,7 @@ static void main_nix_build(int argc, char * * argv) auto autoArgs = myArgs.getAutoArgs(*state); auto autoArgsWithInNixShell = autoArgs; - if (runEnv) { + if (isNixShell) { auto newArgs = state->buildBindings(autoArgsWithInNixShell->size() + 1); newArgs.alloc("inNixShell").mkBool(true); for (auto & i : *autoArgs) newArgs.insert(i); @@ -276,19 +303,26 @@ static void main_nix_build(int argc, char * * argv) if (packages) { std::ostringstream joined; joined << "{...}@args: with import args; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ "; - for (const auto & i : left) + for (const auto & i : remainingArgs) joined << '(' << i << ") "; joined << "]; } \"\""; fromArgs = true; - left = {joined.str()}; - } else if (!fromArgs) { - if (left.empty() && runEnv && pathExists("shell.nix")) - left = {"shell.nix"}; - if (left.empty()) - left = {"default.nix"}; + remainingArgs = {joined.str()}; + } else if (!fromArgs && remainingArgs.empty()) { + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; + + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } - if (runEnv) + if (isNixShell) setEnv("IN_NIX_SHELL", pure ? "pure" : "impure"); PackageInfos drvs; @@ -299,7 +333,7 @@ static void main_nix_build(int argc, char * * argv) if (readStdin) exprs = {state->parseStdin()}; else - for (auto i : left) { + for (auto i : remainingArgs) { if (fromArgs) exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath("."))); else { @@ -310,14 +344,18 @@ static void main_nix_build(int argc, char * * argv) auto [path, outputNames] = parsePathWithOutputs(absolute); if (evalStore->isStorePath(path) && hasSuffix(path, ".drv")) drvs.push_back(PackageInfo(*state, evalStore, absolute)); - else + else { /* If we're in a #! script, interpret filenames relative to the script. */ - exprs.push_back( - state->parseExprFromFile( - resolveExprPath( - lookupFileArg(*state, - inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))); + auto baseDir = inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i; + + auto sourcePath = lookupFileArg(*state, + baseDir); + auto resolvedPath = + isNixShell ? resolveShellExprPath(sourcePath) : resolveExprPath(sourcePath); + + exprs.push_back(state->parseExprFromFile(resolvedPath)); + } } } @@ -330,7 +368,7 @@ static void main_nix_build(int argc, char * * argv) std::function takesNixShellAttr; takesNixShellAttr = [&](const Value & v) { - if (!runEnv) { + if (!isNixShell) { return false; } bool add = false; @@ -381,7 +419,7 @@ static void main_nix_build(int argc, char * * argv) store->buildPaths(paths, buildMode, evalStore); }; - if (runEnv) { + if (isNixShell) { if (drvs.size() != 1) throw UsageError("nix-shell requires a single derivation"); diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 2c94705de..65ff279f8 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -91,6 +95,55 @@ sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.nix > $TEST_ROOT/shell.sheba chmod a+rx $TEST_ROOT/shell.shebang.nix $TEST_ROOT/shell.shebang.nix +mkdir $TEST_ROOT/lookup-test $TEST_ROOT/empty + +echo "import $shellDotNix" > $TEST_ROOT/lookup-test/shell.nix +cp config.nix $TEST_ROOT/lookup-test/ +echo 'abort "do not load default.nix!"' > $TEST_ROOT/lookup-test/default.nix + +nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +# https://github.com/NixOS/nix/issues/4529 +nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" + +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + +( + cd $TEST_ROOT/empty; + expectStderr 1 nix-shell | \ + grepQuiet "error.*no argument specified and no .*shell\.nix.* or .*default\.nix.* file found in the working directory" +) + +expectStderr 1 nix-shell -I "testRoot=$TEST_ROOT" '' | + grepQuiet "error.*neither .*shell\.nix.* nor .*default\.nix.* found in .*/empty" + +cat >$TEST_ROOT/lookup-test/shebangscript < $TEST_ROOT/marco/shell.nix +cat >$TEST_ROOT/marco/polo/default.nix < Date: Mon, 8 Jul 2024 14:38:57 +0200 Subject: [PATCH 521/910] Remove the Hydra status check workflow I'm sick of receiving an email about this every 30 minutes. --- .github/workflows/hydra_status.yml | 20 ------------------ scripts/check-hydra-status.sh | 33 ------------------------------ 2 files changed, 53 deletions(-) delete mode 100644 .github/workflows/hydra_status.yml delete mode 100644 scripts/check-hydra-status.sh diff --git a/.github/workflows/hydra_status.yml b/.github/workflows/hydra_status.yml deleted file mode 100644 index 2a7574747..000000000 --- a/.github/workflows/hydra_status.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Hydra status - -permissions: read-all - -on: - schedule: - - cron: "12,42 * * * *" - workflow_dispatch: - -jobs: - check_hydra_status: - name: Check Hydra status - if: github.repository_owner == 'NixOS' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - run: bash scripts/check-hydra-status.sh - diff --git a/scripts/check-hydra-status.sh b/scripts/check-hydra-status.sh deleted file mode 100644 index e62705e94..000000000 --- a/scripts/check-hydra-status.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail -# set -x - - -# mapfile BUILDS_FOR_LATEST_EVAL < <( -# curl -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \ -# jq -r '.evals[0].builds[] | @sh') -BUILDS_FOR_LATEST_EVAL=$( -curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \ - jq -r '.evals[0].builds[]') - -someBuildFailed=0 - -for buildId in $BUILDS_FOR_LATEST_EVAL; do - buildInfo=$(curl --fail -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId") - - finished=$(echo "$buildInfo" | jq -r '.finished') - - if [[ $finished = 0 ]]; then - continue - fi - - buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus') - - if [[ $buildStatus != 0 ]]; then - someBuildFailed=1 - echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo" - fi -done - -exit "$someBuildFailed" From c5284a84f3535547aea02b296e0e10e6a7113ca4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 7 Jul 2024 14:29:28 -0400 Subject: [PATCH 522/910] Forgot to include `config-expr.hh` in some places --- src/libcmd/meson.build | 1 + src/libexpr/meson.build | 1 + tests/unit/libexpr/meson.build | 2 +- tests/unit/libfetchers/meson.build | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..8548bea70 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -64,6 +64,7 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', + '-include', 'config-expr.hh', '-include', 'config-main.hh', '-include', 'config-cmd.hh', language : 'cpp', diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 9fe7c17c4..3025b6da1 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -69,6 +69,7 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', + '-include', 'config-expr.hh', language : 'cpp', ) diff --git a/tests/unit/libexpr/meson.build b/tests/unit/libexpr/meson.build index a7b22f7f1..ee35258cf 100644 --- a/tests/unit/libexpr/meson.build +++ b/tests/unit/libexpr/meson.build @@ -41,7 +41,7 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-include', 'config-store.hh', + '-include', 'config-expr.hh', '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', diff --git a/tests/unit/libfetchers/meson.build b/tests/unit/libfetchers/meson.build index b4bc77a97..d2de93829 100644 --- a/tests/unit/libfetchers/meson.build +++ b/tests/unit/libfetchers/meson.build @@ -37,7 +37,7 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-include', 'config-store.hh', + # '-include', 'config-fetchers.h', language : 'cpp', ) From 6e5cec292b56814541f71052ec566ebf3129ea1c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 Jul 2024 09:47:25 -0400 Subject: [PATCH 523/910] Use a meson "generator" to deduplicate `.gen.hh` creation --- build-utils-meson/generate-header/meson.build | 7 +++++++ meson.build | 3 ++- src/libexpr/meson.build | 9 +++------ src/libexpr/primops/meson.build | 11 +++-------- src/libstore/meson.build | 10 +++------- 5 files changed, 18 insertions(+), 22 deletions(-) create mode 100644 build-utils-meson/generate-header/meson.build diff --git a/build-utils-meson/generate-header/meson.build b/build-utils-meson/generate-header/meson.build new file mode 100644 index 000000000..dfbe1375f --- /dev/null +++ b/build-utils-meson/generate-header/meson.build @@ -0,0 +1,7 @@ +bash = find_program('bash', native: true) + +gen_header = generator( + bash, + arguments : [ '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + output : '@PLAINNAME@.gen.hh', +) diff --git a/meson.build b/meson.build index 356d978dc..f09998ab6 100644 --- a/meson.build +++ b/meson.build @@ -6,6 +6,7 @@ project('nix-dev-shell', 'cpp', subproject_dir : 'src', ) +# Internal Libraries subproject('libutil') subproject('libstore') subproject('libfetchers') @@ -18,7 +19,7 @@ subproject('libcmd') subproject('internal-api-docs') subproject('external-api-docs') -# C wrappers +# External C wrapper libraries subproject('libutil-c') subproject('libstore-c') subproject('libexpr-c') diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 9fe7c17c4..05097c286 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -116,20 +116,17 @@ lexer_tab = custom_target( install_dir : get_option('includedir') / 'nix', ) +subdir('build-utils-meson/generate-header') + generated_headers = [] foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix', 'call-flake.nix', ] - generated_headers += custom_target( - command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], - input : header, - output : '@PLAINNAME@.gen.hh', - ) + generated_headers += gen_header.process(header) endforeach - sources = files( 'attr-path.cc', 'attr-set.cc', diff --git a/src/libexpr/primops/meson.build b/src/libexpr/primops/meson.build index 96a1dd07e..f910fe237 100644 --- a/src/libexpr/primops/meson.build +++ b/src/libexpr/primops/meson.build @@ -1,12 +1,7 @@ -foreach header : [ +generated_headers += gen_header.process( 'derivation.nix', -] - generated_headers += custom_target( - command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], - input : header, - output : '@PLAINNAME@.gen.hh', - ) -endforeach + preserve_path_from: meson.project_source_root(), +) sources += files( 'context.cc', diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 7444cba20..5324b2a1f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -99,18 +99,14 @@ deps_public += nlohmann_json sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') deps_private += sqlite +subdir('build-utils-meson/generate-header') + generated_headers = [] foreach header : [ 'schema.sql', 'ca-specific-schema.sql', ] - generated_headers += custom_target( - command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], - input : header, - output : '@PLAINNAME@.gen.hh', - install : true, - install_dir : get_option('includedir') / 'nix', - ) + generated_headers += gen_header.process(header) endforeach busybox = find_program(get_option('sandbox-shell'), required : false) From 7a6269ba7b37a31bdc4b7ec539d45adf860f5623 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 5 Jul 2024 16:21:20 -0400 Subject: [PATCH 524/910] Package the Nix CLI with Meson Co-Authored-By: Qyriad --- meson.build | 3 + packaging/components.nix | 3 + packaging/hydra.nix | 1 + src/nix/.version | 1 + src/nix/build-remote | 1 + src/nix/build-utils-meson | 1 + src/nix/doc | 1 + src/nix/help-stores.md | 1 + src/nix/local.mk | 21 ++--- src/nix/main.cc | 2 +- src/nix/meson.build | 170 ++++++++++++++++++++++++++++++++++++ src/nix/nix-build | 1 + src/nix/nix-channel | 1 + src/nix/nix-collect-garbage | 1 + src/nix/nix-copy-closure | 1 + src/nix/nix-env | 1 + src/nix/nix-instantiate | 1 + src/nix/nix-store | 1 + src/nix/package.nix | 129 +++++++++++++++++++++++++++ src/nix/profile.md | 2 +- src/nix/profiles.md | 1 + 21 files changed, 326 insertions(+), 18 deletions(-) create mode 120000 src/nix/.version create mode 120000 src/nix/build-remote create mode 120000 src/nix/build-utils-meson create mode 120000 src/nix/doc create mode 120000 src/nix/help-stores.md create mode 100644 src/nix/meson.build create mode 120000 src/nix/nix-build create mode 120000 src/nix/nix-channel create mode 120000 src/nix/nix-collect-garbage create mode 120000 src/nix/nix-copy-closure create mode 120000 src/nix/nix-env create mode 120000 src/nix/nix-instantiate create mode 120000 src/nix/nix-store create mode 100644 src/nix/package.nix create mode 120000 src/nix/profiles.md diff --git a/meson.build b/meson.build index f09998ab6..e6bdc2eac 100644 --- a/meson.build +++ b/meson.build @@ -15,6 +15,9 @@ subproject('libflake') subproject('libmain') subproject('libcmd') +# Executables +subproject('nix') + # Docs subproject('internal-api-docs') subproject('external-api-docs') diff --git a/packaging/components.nix b/packaging/components.nix index f1cd3b9c6..0e369a055 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -32,6 +32,9 @@ in nix-cmd = callPackage ../src/libcmd/package.nix { }; + # Will replace `nix` once the old build system is gone. + nix-ng = callPackage ../src/nix/package.nix { }; + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 0bbbc31f7..4dfaf9bbf 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -53,6 +53,7 @@ let "nix-flake-tests" "nix-main" "nix-cmd" + "nix-ng" ]; in { diff --git a/src/nix/.version b/src/nix/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/nix/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/nix/build-remote b/src/nix/build-remote new file mode 120000 index 000000000..2cea44d46 --- /dev/null +++ b/src/nix/build-remote @@ -0,0 +1 @@ +../build-remote \ No newline at end of file diff --git a/src/nix/build-utils-meson b/src/nix/build-utils-meson new file mode 120000 index 000000000..91937f183 --- /dev/null +++ b/src/nix/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix/doc b/src/nix/doc new file mode 120000 index 000000000..7e57b0f58 --- /dev/null +++ b/src/nix/doc @@ -0,0 +1 @@ +../../doc \ No newline at end of file diff --git a/src/nix/help-stores.md b/src/nix/help-stores.md new file mode 120000 index 000000000..5c5624f5e --- /dev/null +++ b/src/nix/help-stores.md @@ -0,0 +1 @@ +../../doc/manual/src/store/types/index.md.in \ No newline at end of file diff --git a/src/nix/local.mk b/src/nix/local.mk index 4b6117330..28b30b586 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -42,27 +42,16 @@ $(eval $(call install-symlink, $(bindir)/nix, $(libexecdir)/nix/build-remote)) src/nix-env/user-env.cc: src/nix-env/buildenv.nix.gen.hh -src/nix/develop.cc: src/nix/get-env.sh.gen.hh +$(d)/develop.cc: $(d)/get-env.sh.gen.hh src/nix-channel/nix-channel.cc: src/nix-channel/unpack-channel.nix.gen.hh -src/nix/main.cc: \ +$(d)/main.cc: \ doc/manual/generate-manpage.nix.gen.hh \ doc/manual/utils.nix.gen.hh doc/manual/generate-settings.nix.gen.hh \ doc/manual/generate-store-info.nix.gen.hh \ - src/nix/generated-doc/help-stores.md + $(d)/help-stores.md.gen.hh -src/nix/generated-doc/files/%.md: doc/manual/src/command-ref/files/%.md - @mkdir -p $$(dirname $@) - @cp $< $@ +$(d)/profile.cc: $(d)/profile.md -src/nix/profile.cc: src/nix/profile.md src/nix/generated-doc/files/profiles.md.gen.hh - -src/nix/generated-doc/help-stores.md: doc/manual/src/store/types/index.md.in - @mkdir -p $$(dirname $@) - @echo 'R"(' >> $@.tmp - @echo >> $@.tmp - @cat $^ >> $@.tmp - @echo >> $@.tmp - @echo ')"' >> $@.tmp - @mv $@.tmp $@ +$(d)/profile.md: $(d)/profiles.md.gen.hh diff --git a/src/nix/main.cc b/src/nix/main.cc index de6d89bd3..c90bb25a7 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -333,7 +333,7 @@ struct CmdHelpStores : Command std::string doc() override { return - #include "generated-doc/help-stores.md" + #include "help-stores.md.gen.hh" ; } diff --git a/src/nix/meson.build b/src/nix/meson.build new file mode 100644 index 000000000..dd21c4b1b --- /dev/null +++ b/src/nix/meson.build @@ -0,0 +1,170 @@ +project('nix', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +deps_private_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-expr'), + dependency('nix-fetchers'), + dependency('nix-main'), + dependency('nix-cmd'), +] +deps_public_maybe_subproject = [ +] +subdir('build-utils-meson/subprojects') + +subdir('build-utils-meson/export-all-symbols') + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + #'-include', 'config-fetchers.hh', + '-include', 'config-main.hh', + '-include', 'config-cmd.hh', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') +subdir('build-utils-meson/generate-header') + +nix_sources = files( + 'add-to-store.cc', + 'app.cc', + 'build.cc', + 'bundle.cc', + 'cat.cc', + 'config-check.cc', + 'config.cc', + 'copy.cc', + 'derivation-add.cc', + 'derivation-show.cc', + 'derivation.cc', + 'develop.cc', + 'diff-closures.cc', + 'dump-path.cc', + 'edit.cc', + 'env.cc', + 'eval.cc', + 'flake.cc', + 'fmt.cc', + 'hash.cc', + 'log.cc', + 'ls.cc', + 'main.cc', + 'make-content-addressed.cc', + 'nar.cc', + 'optimise-store.cc', + 'path-from-hash-part.cc', + 'path-info.cc', + 'prefetch.cc', + 'profile.cc', + 'realisation.cc', + 'registry.cc', + 'repl.cc', + 'run.cc', + 'search.cc', + 'sigs.cc', + 'store-copy-log.cc', + 'store-delete.cc', + 'store-gc.cc', + 'store-info.cc', + 'store-repair.cc', + 'store.cc', + 'unix/daemon.cc', + 'upgrade-nix.cc', + 'verify.cc', + 'why-depends.cc', +) + +nix_sources += [ + gen_header.process('doc/manual/generate-manpage.nix'), + gen_header.process('doc/manual/generate-settings.nix'), + gen_header.process('doc/manual/generate-store-info.nix'), + gen_header.process('doc/manual/utils.nix'), + gen_header.process('get-env.sh'), + gen_header.process('profiles.md'), + gen_header.process('help-stores.md'), +] + +if host_machine.system() != 'windows' + nix_sources += files( + 'unix/daemon.cc', + ) +endif + +# The rest of the subdirectories aren't separate components, +# just source files in another directory, so we process them here. + +build_remote_sources = files( + 'build-remote/build-remote.cc', +) +nix_build_sources = files( + 'nix-build/nix-build.cc', +) +nix_channel_sources = files( + 'nix-channel/nix-channel.cc', +) +unpack_channel_gen = gen_header.process('nix-channel/unpack-channel.nix') +nix_collect_garbage_sources = files( + 'nix-collect-garbage/nix-collect-garbage.cc', +) +nix_copy_closure_sources = files( + 'nix-copy-closure/nix-copy-closure.cc', +) +nix_env_buildenv_gen = gen_header.process('nix-env/buildenv.nix') +nix_env_sources = files( + 'nix-env/nix-env.cc', + 'nix-env/user-env.cc', +) +nix_instantiate_sources = files( + 'nix-instantiate/nix-instantiate.cc', +) +nix_store_sources = files( + 'nix-store/dotgraph.cc', + 'nix-store/graphml.cc', + 'nix-store/nix-store.cc', +) + +# Hurray for Meson list flattening! +sources = [ + nix_sources, + build_remote_sources, + nix_build_sources, + nix_channel_sources, + unpack_channel_gen, + nix_collect_garbage_sources, + nix_copy_closure_sources, + nix_env_buildenv_gen, + nix_env_sources, + nix_instantiate_sources, + nix_store_sources, +] + +include_dirs = [include_directories('.')] + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) diff --git a/src/nix/nix-build b/src/nix/nix-build new file mode 120000 index 000000000..2954d8ac7 --- /dev/null +++ b/src/nix/nix-build @@ -0,0 +1 @@ +../nix-build \ No newline at end of file diff --git a/src/nix/nix-channel b/src/nix/nix-channel new file mode 120000 index 000000000..29b759473 --- /dev/null +++ b/src/nix/nix-channel @@ -0,0 +1 @@ +../nix-channel \ No newline at end of file diff --git a/src/nix/nix-collect-garbage b/src/nix/nix-collect-garbage new file mode 120000 index 000000000..b037fc1b0 --- /dev/null +++ b/src/nix/nix-collect-garbage @@ -0,0 +1 @@ +../nix-collect-garbage \ No newline at end of file diff --git a/src/nix/nix-copy-closure b/src/nix/nix-copy-closure new file mode 120000 index 000000000..9063c583a --- /dev/null +++ b/src/nix/nix-copy-closure @@ -0,0 +1 @@ +../nix-copy-closure \ No newline at end of file diff --git a/src/nix/nix-env b/src/nix/nix-env new file mode 120000 index 000000000..f2f19f580 --- /dev/null +++ b/src/nix/nix-env @@ -0,0 +1 @@ +../nix-env \ No newline at end of file diff --git a/src/nix/nix-instantiate b/src/nix/nix-instantiate new file mode 120000 index 000000000..2d7502ffa --- /dev/null +++ b/src/nix/nix-instantiate @@ -0,0 +1 @@ +../nix-instantiate \ No newline at end of file diff --git a/src/nix/nix-store b/src/nix/nix-store new file mode 120000 index 000000000..e6efcac42 --- /dev/null +++ b/src/nix/nix-store @@ -0,0 +1 @@ +../nix-store/ \ No newline at end of file diff --git a/src/nix/package.nix b/src/nix/package.nix new file mode 100644 index 000000000..fe83c6969 --- /dev/null +++ b/src/nix/package.nix @@ -0,0 +1,129 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store +, nix-expr +, nix-main +, nix-cmd + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, version +}: + +let + inherit (lib) fileset; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix"; + inherit version; + + workDir = ./.; + fileset = fileset.unions ([ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + + # Symbolic links to other dirs + ./build-remote + ./doc + ./nix-build + ./nix-channel + ./nix-collect-garbage + ./nix-copy-closure + ./nix-env + ./nix-instantiate + ./nix-store + + # Doc nix files for --help + ../../doc/manual/generate-manpage.nix + ../../doc/manual/utils.nix + ../../doc/manual/generate-settings.nix + ../../doc/manual/generate-store-info.nix + + # Other files to be included as string literals + ../nix-channel/unpack-channel.nix + ../nix-env/buildenv.nix + ./get-env.sh + ./help-stores.md + ../../doc/manual/src/store/types/index.md.in + ./profiles.md + ../../doc/manual/src/command-ref/files/profiles.md + + # Files + ] ++ lib.concatMap + (dir: [ + (fileset.fileFilter (file: file.hasExt "cc") dir) + (fileset.fileFilter (file: file.hasExt "hh") dir) + (fileset.fileFilter (file: file.hasExt "md") dir) + ]) + [ + ./. + ../build-remote + ../nix-build + ../nix-channel + ../nix-collect-garbage + ../nix-copy-closure + ../nix-env + ../nix-instantiate + ../nix-store + ] + ); + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-store + nix-expr + nix-main + nix-cmd + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../../.version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) diff --git a/src/nix/profile.md b/src/nix/profile.md index 9b2f86f4a..83a0b5f29 100644 --- a/src/nix/profile.md +++ b/src/nix/profile.md @@ -11,7 +11,7 @@ them to be rolled back easily. )"" -#include "generated-doc/files/profiles.md.gen.hh" +#include "profiles.md.gen.hh" R""( diff --git a/src/nix/profiles.md b/src/nix/profiles.md new file mode 120000 index 000000000..c67a86194 --- /dev/null +++ b/src/nix/profiles.md @@ -0,0 +1 @@ +../../doc/manual/src/command-ref/files/profiles.md \ No newline at end of file From 4c788504fa8a3153e79d5080cf23fdc5204035bc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 9 Jul 2024 16:44:01 +0200 Subject: [PATCH 525/910] Remove reference to check-hydra-status --- maintainers/flake-module.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 8f95e788b..46b3e1363 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -499,7 +499,6 @@ ''^misc/bash/completion\.sh$'' ''^misc/fish/completion\.fish$'' ''^misc/zsh/completion\.zsh$'' - ''^scripts/check-hydra-status\.sh$'' ''^scripts/create-darwin-volume\.sh$'' ''^scripts/install-darwin-multi-user\.sh$'' ''^scripts/install-multi-user\.sh$'' From 13522229a9efc83b4a3d90c66445355c6bc7c815 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 16:08:10 +0200 Subject: [PATCH 526/910] assertEqValues: clarify potential bug error message --- src/libexpr/eval.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fb6050e50..6f1a7d618 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2636,8 +2636,11 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st // error for this, and this function should only be called when // eqValues has found a difference, and it should match // its behavior. + // Note that as of writing, we make the compiler require that all enum + // values are handled explicitly with `case`s, _despite_ having a + // `default:`. error( - "cannot compare %1% with %2%; is assertEqValues out of sync with eqValues?", showType(v1), showType(v2)) + "BUG: cannot compare %1% with %2%; did forceValue leave a thunk, or might assertEqValues be out of sync with eqValues?", showType(v1), showType(v2)) .debugThrow(); } } From c4e3e2dc27da93cae22cd35a11ee1ef87e23eb57 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 16:24:31 +0200 Subject: [PATCH 527/910] Soft-deprecate the compatibility settings --- src/libcmd/compatibility-settings.hh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh index 961001080..0506743f3 100644 --- a/src/libcmd/compatibility-settings.hh +++ b/src/libcmd/compatibility-settings.hh @@ -7,14 +7,18 @@ struct CompatibilitySettings : public Config CompatibilitySettings() = default; + // Added in Nix 2.24, July 2024. Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. You may set this to `false` to revert to the Nix 2.3 behavior. + + This setting is not recommended, and will be deprecated and later removed in the future. )"}; + // Added in Nix 2.24, July 2024. Setting nixShellShebangArgumentsRelativeToScript{ this, true, "nix-shell-shebang-arguments-relative-to-script", R"( Before Nix 2.24, the arguments in a `nix-shell` shebang - as well as `--arg` - were relative to working directory. @@ -22,6 +26,8 @@ struct CompatibilitySettings : public Config Since Nix 2.24, the arguments are relative to the [base directory](@docroot@/glossary.md#gloss-base-directory) defined as the script's directory. You may set this to `false` to revert to the Nix 2.3 behavior. + + This setting is not recommended, and will be deprecated and later removed in the future. )"}; }; From 61577402ba331451a10051b11cf77bdc80f83fa8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 11:35:58 +0200 Subject: [PATCH 528/910] Add EvalErrorBuilder::panic() An nicer alternative to printError + abort, or assert(false /* foo */) --- src/libexpr/eval-error.cc | 8 ++++++++ src/libexpr/eval-error.hh | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index bd84e0428..cdb0b4772 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -92,6 +92,14 @@ void EvalErrorBuilder::debugThrow() throw error; } +template +void EvalErrorBuilder::panic() +{ + logError(error.info()); + printError("This is a bug! An unexpected condition occurred, causing the Nix evaluator to have to stop. If you could share a reproducible example or a core dump, please open an issue at https://github.com/NixOS/nix/issues"); + abort(); +} + template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index fe48e054b..6409dc68a 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -112,6 +112,12 @@ public: * Delete the `EvalErrorBuilder` and throw the underlying exception. */ [[gnu::noinline, gnu::noreturn]] void debugThrow(); + + /** + * A programming error or fatal condition occurred. Abort the process for core dump and debugging. + * This does not print a proper backtrace, because unwinding the stack is destructive. + */ + [[gnu::noinline, gnu::noreturn]] void panic(); }; } From 4fd8f19ecfd8ced21c0f43bb3f3e3567d1d38bcd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 12:14:48 +0200 Subject: [PATCH 529/910] Fix build to use CanonPath in new FSO sinks --- src/libfetchers/git-utils.cc | 7 +++-- src/libutil/fs-sink.hh | 2 +- src/libutil/tarfile.cc | 2 +- tests/unit/libfetchers/git-utils.cc | 26 ++++++++++--------- .../tests/tracing-file-system-object-sink.cc | 9 ++++--- .../tests/tracing-file-system-object-sink.hh | 8 +++--- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index a88bdc8b6..ecc71ae47 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -909,9 +909,12 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink addToTree(*pathComponents.rbegin(), oid, GIT_FILEMODE_LINK); } - void createHardlink(const Path & path, const CanonPath & target) override + void createHardlink(const CanonPath & path, const CanonPath & target) override { - auto pathComponents = tokenizeString>(path, "/"); + std::vector pathComponents; + for (auto & c : path) + pathComponents.emplace_back(c); + if (!prepareDirs(pathComponents, false)) return; // We can't just look up the path from the start of the root, since diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index e5e240073..774c0d942 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -51,7 +51,7 @@ struct ExtendedFileSystemObjectSink : virtual FileSystemObjectSink * Create a hard link. The target must be the path of a previously * encountered file relative to the root of the FSO. */ - virtual void createHardlink(const Path & path, const CanonPath & target) = 0; + virtual void createHardlink(const CanonPath & path, const CanonPath & target) = 0; }; /** diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index f3b2f55b5..2e3236295 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -196,7 +196,7 @@ time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & lastModified = std::max(lastModified, archive_entry_mtime(entry)); if (auto target = archive_entry_hardlink(entry)) { - parseSink.createHardlink(path, CanonPath(target)); + parseSink.createHardlink(cpath, CanonPath(target)); continue; } diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index 3c06b593a..d3547ec6a 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -62,17 +62,18 @@ TEST_F(GitUtilsTest, sink_basic) // general, and I can't imagine that "non-conventional" archives or any other source to be handled by // this sink. - sink->createDirectory("foo-1.1"); + sink->createDirectory(CanonPath("foo-1.1")); - sink->createRegularFile( - "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); - sink->createRegularFile("foo-1.1/bye", [](CreateRegularFileSink & fileSink) { + sink->createRegularFile(CanonPath("foo-1.1/hello"), [](CreateRegularFileSink & fileSink) { + writeString(fileSink, "hello world", false); + }); + sink->createRegularFile(CanonPath("foo-1.1/bye"), [](CreateRegularFileSink & fileSink) { writeString(fileSink, "thanks for all the fish", false); }); - sink->createSymlink("foo-1.1/bye-link", "bye"); - sink->createDirectory("foo-1.1/empty"); - sink->createDirectory("foo-1.1/links"); - sink->createHardlink("foo-1.1/links/foo", CanonPath("foo-1.1/hello")); + sink->createSymlink(CanonPath("foo-1.1/bye-link"), "bye"); + sink->createDirectory(CanonPath("foo-1.1/empty")); + sink->createDirectory(CanonPath("foo-1.1/links")); + sink->createHardlink(CanonPath("foo-1.1/links/foo"), CanonPath("foo-1.1/hello")); // sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello")); @@ -92,13 +93,14 @@ TEST_F(GitUtilsTest, sink_hardlink) auto repo = openRepo(); auto sink = repo->getFileSystemObjectSink(); - sink->createDirectory("foo-1.1"); + sink->createDirectory(CanonPath("foo-1.1")); - sink->createRegularFile( - "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); + sink->createRegularFile(CanonPath("foo-1.1/hello"), [](CreateRegularFileSink & fileSink) { + writeString(fileSink, "hello world", false); + }); try { - sink->createHardlink("foo-1.1/link", CanonPath("hello")); + sink->createHardlink(CanonPath("foo-1.1/link"), CanonPath("hello")); FAIL() << "Expected an exception"; } catch (const nix::Error & e) { ASSERT_THAT(e.msg(), testing::HasSubstr("invalid hard link target")); diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc index 737e02213..122a09dcb 100644 --- a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc @@ -3,13 +3,14 @@ namespace nix::test { -void TracingFileSystemObjectSink::createDirectory(const Path & path) +void TracingFileSystemObjectSink::createDirectory(const CanonPath & path) { std::cerr << "createDirectory(" << path << ")\n"; sink.createDirectory(path); } -void TracingFileSystemObjectSink::createRegularFile(const Path & path, std::function fn) +void TracingFileSystemObjectSink::createRegularFile( + const CanonPath & path, std::function fn) { std::cerr << "createRegularFile(" << path << ")\n"; sink.createRegularFile(path, [&](CreateRegularFileSink & crf) { @@ -18,13 +19,13 @@ void TracingFileSystemObjectSink::createRegularFile(const Path & path, std::func }); } -void TracingFileSystemObjectSink::createSymlink(const Path & path, const std::string & target) +void TracingFileSystemObjectSink::createSymlink(const CanonPath & path, const std::string & target) { std::cerr << "createSymlink(" << path << ", target: " << target << ")\n"; sink.createSymlink(path, target); } -void TracingExtendedFileSystemObjectSink::createHardlink(const Path & path, const CanonPath & target) +void TracingExtendedFileSystemObjectSink::createHardlink(const CanonPath & path, const CanonPath & target) { std::cerr << "createHardlink(" << path << ", target: " << target << ")\n"; sink.createHardlink(path, target); diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh index 9527b0be3..895ac3664 100644 --- a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh @@ -15,11 +15,11 @@ public: { } - void createDirectory(const Path & path) override; + void createDirectory(const CanonPath & path) override; - void createRegularFile(const Path & path, std::function fn); + void createRegularFile(const CanonPath & path, std::function fn) override; - void createSymlink(const Path & path, const std::string & target); + void createSymlink(const CanonPath & path, const std::string & target) override; }; /** @@ -35,7 +35,7 @@ public: { } - void createHardlink(const Path & path, const CanonPath & target); + void createHardlink(const CanonPath & path, const CanonPath & target) override; }; } From 56bf39e9056ae7a15ec9c0347fd0043782e2b8cd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 11:37:45 +0200 Subject: [PATCH 530/910] eqValues/assertEqValues: Clean up assertions It's still paranoid, and probably a waste of words, but at least now it's consistent and readily identifyable from a log. --- src/libexpr/eval.cc | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6f1a7d618..31d0c635a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2631,17 +2631,12 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st return; case nThunk: // Must not be left by forceValue - default: - // This should never happen, because eqValues already throws an - // error for this, and this function should only be called when - // eqValues has found a difference, and it should match - // its behavior. - // Note that as of writing, we make the compiler require that all enum - // values are handled explicitly with `case`s, _despite_ having a - // `default:`. - error( - "BUG: cannot compare %1% with %2%; did forceValue leave a thunk, or might assertEqValues be out of sync with eqValues?", showType(v1), showType(v2)) - .debugThrow(); + assert(false); + default: // Note that we pass compiler flags that should make `default:` unreachable. + // Also note that this probably ran after `eqValues`, which implements + // the same logic more efficiently (without having to unwind stacks), + // so maybe `assertEqValues` and `eqValues` are out of sync. Check it for solutions. + error("assertEqValues: cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).panic(); } } @@ -2723,8 +2718,9 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return v1.fpoint() == v2.fpoint(); case nThunk: // Must not be left by forceValue - default: - error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow(); + assert(false); + default: // Note that we pass compiler flags that should make `default:` unreachable. + error("eqValues: cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).panic(); } } From 6f5f741157ff14e8a67608be9bee2bfc8d5778a8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 13:51:03 +0200 Subject: [PATCH 531/910] doc/rl-next/shebang-relative: Update with example --- doc/manual/rl-next/shebang-relative.md | 64 +++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index ab39a359c..c887a598a 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -1,10 +1,62 @@ -synopsis: ensure nix-shell shebang uses relative path -prs: #5088 -description: { +--- +synopsis: "`nix-shell` shebang uses relative path" +prs: +- 5088 +- 11058 +issues: +- 4232 +--- -`nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. -The new behavior is that evaluations are performed relative to the script. + +Relative [path](@docroot@/language/values.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary?highlight=base%20directory#gloss-base-directory). +Previously they were resolved relative to the current working directory. +For example, consider the following script in `~/myproject/say-hi`: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell --expr 'import ./shell.nix' +#!nix-shell --arg toolset './greeting-tools.nix' +#!nix-shell -i bash +hello +``` + +Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: + +```console +[hostname:~]$ ./myproject/say-hi +error: + … while calling the 'import' builtin + at «string»:1:2: + 1| (import ./shell.nix) + | ^ + + error: path '/home/user/shell.nix' does not exist +``` + +Since this release, `nix-shell` resolves `shell.nix` relative to the script's location, and `~/myproject/shell.nix` is used. + +```console +$ ./myproject/say-hi +Hello, world! +``` + +**Opt-out** + +This is technically a breaking change, so we have added an option so you can adapt independently of your Nix update. The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. +This option will be removed in a future release. -} +**`nix` command shebang** + +The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. + +Example: + +```shell +#!/usr/bin/env nix +#!nix develop +#!nix --expr ``import ./shell.nix`` +#!nix -c bash +hello +``` From bb312a717451fc88f1220e1ce56700eaaf15e3de Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 13:53:03 +0200 Subject: [PATCH 532/910] Edit CompatibilitySettings --- src/libcmd/compatibility-settings.hh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh index 0506743f3..a129a957a 100644 --- a/src/libcmd/compatibility-settings.hh +++ b/src/libcmd/compatibility-settings.hh @@ -13,21 +13,23 @@ struct CompatibilitySettings : public Config Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. - You may set this to `false` to revert to the Nix 2.3 behavior. + You may set this to `false` to temporarily revert to the behavior of Nix 2.23 and older. - This setting is not recommended, and will be deprecated and later removed in the future. + Using this setting is not recommended. + It will be deprecated and removed. )"}; // Added in Nix 2.24, July 2024. Setting nixShellShebangArgumentsRelativeToScript{ this, true, "nix-shell-shebang-arguments-relative-to-script", R"( - Before Nix 2.24, the arguments in a `nix-shell` shebang - as well as `--arg` - were relative to working directory. + Before Nix 2.24, relative file path expressions in arguments in a `nix-shell` shebang were resolved relative to the working directory. - Since Nix 2.24, the arguments are relative to the [base directory](@docroot@/glossary.md#gloss-base-directory) defined as the script's directory. + Since Nix 2.24, `nix-shell` resolves these paths in a manner that is relative to the [base directory](@docroot@/glossary.md#gloss-base-directory), defined as the script's directory. - You may set this to `false` to revert to the Nix 2.3 behavior. + You may set this to `false` to temporarily revert to the behavior of Nix 2.23 and older. - This setting is not recommended, and will be deprecated and later removed in the future. + Using this setting is not recommended. + It will be deprecated and removed. )"}; }; From 0395ff9bd39fb966b69abc76ae9e1f0f2dd1c7ca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 15:01:38 +0200 Subject: [PATCH 533/910] packaging: Set darwinMinVersion to fix x86_64-darwin build Ported from https://github.com/NixOS/nixpkgs/pull/326172 Co-authored-by: Emily --- packaging/dependencies.nix | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 34b344971..73ba9cd58 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -11,11 +11,28 @@ versionSuffix, }: +let + prevStdenv = stdenv; +in + let inherit (pkgs) lib; root = ../.; + stdenv = if prevStdenv.isDarwin && prevStdenv.isx86_64 + then darwinStdenv + else prevStdenv; + + # Fix the following error with the default x86_64-darwin SDK: + # + # error: aligned allocation function of type 'void *(std::size_t, std::align_val_t)' is only available on macOS 10.13 or newer + # + # Despite the use of the 10.13 deployment target here, the aligned + # allocation function Clang uses with this setting actually works + # all the way back to 10.6. + darwinStdenv = pkgs.overrideSDK prevStdenv { darwinMinVersion = "10.13"; }; + # Nixpkgs implements this by returning a subpath into the fetched Nix sources. resolvePath = p: p; From 87323a5689f4789d9fc25271a16ba57c57f76392 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Jul 2024 16:21:27 +0200 Subject: [PATCH 534/910] Remove unused InstallableFlake::getFlakeOutputs() --- src/libcmd/installable-flake.cc | 14 -------------- src/libcmd/installable-flake.hh | 2 -- 2 files changed, 16 deletions(-) diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index d42fa7aac..899919550 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -43,20 +43,6 @@ std::vector InstallableFlake::getActualAttrPaths() return res; } -Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake) -{ - auto vFlake = state.allocValue(); - - callFlake(state, lockedFlake, *vFlake); - - auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs")); - assert(aOutputs); - - state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos)); - - return aOutputs->value; -} - static std::string showAttrPaths(const std::vector & paths) { std::string s; diff --git a/src/libcmd/installable-flake.hh b/src/libcmd/installable-flake.hh index 314918c14..30240a35a 100644 --- a/src/libcmd/installable-flake.hh +++ b/src/libcmd/installable-flake.hh @@ -52,8 +52,6 @@ struct InstallableFlake : InstallableValue std::vector getActualAttrPaths(); - Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); - DerivedPathsWithInfo toDerivedPaths() override; std::pair toValue(EvalState & state) override; From 61080554ab03201b2c70c127f6d97dcfd76d6058 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 Jun 2024 16:33:41 +0200 Subject: [PATCH 535/910] SymbolStr: Remove std::string conversion This refactoring allows the symbol table to be stored as something other than std::strings. --- src/libcmd/installables.cc | 4 ++-- src/libexpr-c/nix_api_value.cc | 4 ++-- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval.cc | 8 ++++---- src/libexpr/get-drvs.cc | 4 ++-- src/libexpr/primops.cc | 4 ++-- src/libexpr/symbol-table.hh | 4 ++-- src/libexpr/value-to-json.cc | 2 +- src/libexpr/value-to-xml.cc | 2 +- src/libflake/flake/flake.cc | 6 +++--- src/libutil/suggestions.cc | 4 ++-- src/libutil/suggestions.hh | 4 ++-- src/nix/flake.cc | 22 +++++++++++----------- src/nix/main.cc | 2 +- 15 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 6835c512c..0c9e69fe8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -289,10 +289,10 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s if (v2.type() == nAttrs) { for (auto & i : *v2.attrs()) { - std::string name = state->symbols[i.name]; + std::string_view name = state->symbols[i.name]; if (name.find(searchWord) == 0) { if (prefix_ == "") - completions.add(name); + completions.add(std::string(name)); else completions.add(prefix_ + "." + name); } diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 2f2f99617..cb5d9ee89 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -383,7 +383,7 @@ nix_value * nix_get_attr_byidx( try { auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; - *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); + *name = state->state.symbols[a.name].c_str(); nix_gc_incref(nullptr, a.value); state->state.forceValue(*a.value, nix::noPos); return as_nix_value_ptr(a.value); @@ -399,7 +399,7 @@ nix_get_attr_name_byidx(nix_c_context * context, const nix_value * value, EvalSt try { auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; - return ((const std::string &) (state->state.symbols[a.name])).c_str(); + return state->state.symbols[a.name].c_str(); } NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 9ad201b63..d61d93630 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -76,7 +76,7 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin if (!a) { std::set attrNames; for (auto & attr : *v->attrs()) - attrNames.insert(state.symbols[attr.name]); + attrNames.insert(std::string(state.symbols[attr.name])); auto suggestions = Suggestions::bestMatches(attrNames, attr); throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath); diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 2630c34d5..46dd3691c 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -484,7 +484,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) auto attrNames = getAttrs(); std::set strAttrNames; for (auto & name : attrNames) - strAttrNames.insert(root->state.symbols[name]); + strAttrNames.insert(std::string(root->state.symbols[name])); return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2a0862123..efca9dd2f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -633,11 +633,11 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En if (se.isWith && !env.values[0]->isThunk()) { // add 'with' bindings. for (auto & j : *env.values[0]->attrs()) - vm[st[j.name]] = j.value; + vm.insert_or_assign(std::string(st[j.name]), j.value); } else { // iterate through staticenv bindings and add them. for (auto & i : se.vars) - vm[st[i.first]] = env.values[i.second]; + vm.insert_or_assign(std::string(st[i.first]), env.values[i.second]); } } } @@ -1338,7 +1338,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) if (!(j = vAttrs->attrs()->get(name))) { std::set allAttrNames; for (auto & attr : *vAttrs->attrs()) - allAttrNames.insert(state.symbols[attr.name]); + allAttrNames.insert(std::string(state.symbols[attr.name])); auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); state.error("attribute '%1%' missing", state.symbols[name]) .atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow(); @@ -1496,7 +1496,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (!lambda.formals->has(i.name)) { std::set formalNames; for (auto & formal : lambda.formals->formals) - formalNames.insert(symbols[formal.name]); + formalNames.insert(std::string(symbols[formal.name])); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); error("function '%1%' called with unexpected argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 896733423..7041a3932 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -342,9 +342,9 @@ std::optional getDerivation(EvalState & state, Value & v, } -static std::string addToPath(const std::string & s1, const std::string & s2) +static std::string addToPath(const std::string & s1, std::string_view s2) { - return s1.empty() ? s2 : s1 + "." + s2; + return s1.empty() ? std::string(s2) : s1 + "." + s2; } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 134363e1a..bcde0bb12 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1225,7 +1225,7 @@ static void derivationStrictInternal( for (auto & i : attrs->lexicographicOrder(state.symbols)) { if (i->name == state.sIgnoreNulls) continue; - const std::string & key = state.symbols[i->name]; + auto key = state.symbols[i->name]; vomit("processing attribute '%1%'", key); auto handleHashMode = [&](const std::string_view s) { @@ -1309,7 +1309,7 @@ static void derivationStrictInternal( if (i->name == state.sStructuredAttrs) continue; - (*jsonObject)[key] = printValueAsJSON(state, true, *i->value, pos, context); + jsonObject->emplace(key, printValueAsJSON(state, true, *i->value, pos, context)); if (i->name == state.sBuilder) drv.builder = state.forceString(*i->value, context, pos, context_below); diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index 967a186dd..5c2821492 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -30,9 +30,9 @@ public: return *s == s2; } - operator const std::string & () const + const char * c_str() const { - return *s; + return s->c_str(); } operator const std::string_view () const diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 936ecf078..f8cc05616 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -58,7 +58,7 @@ json printValueAsJSON(EvalState & state, bool strict, out = json::object(); for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) { try { - out[state.symbols[a->name]] = printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore); + out.emplace(state.symbols[a->name], printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore)); } catch (Error & e) { e.addTrace(state.positions[a->pos], HintFmt("while evaluating attribute '%1%'", state.symbols[a->name])); diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 1de8cdf84..9734ebec4 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -9,7 +9,7 @@ namespace nix { -static XMLAttrs singletonAttrs(const std::string & name, const std::string & value) +static XMLAttrs singletonAttrs(const std::string & name, std::string_view value) { XMLAttrs attrs; attrs[name] = value; diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index 6f47b5992..eb083fcee 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -98,7 +98,7 @@ static std::map parseFlakeInputs( const std::optional & baseDir, InputPath lockRootPath); static FlakeInput parseFlakeInput(EvalState & state, - const std::string & inputName, Value * value, const PosIdx pos, + std::string_view inputName, Value * value, const PosIdx pos, const std::optional & baseDir, InputPath lockRootPath) { expectType(state, nAttrs, *value, pos); @@ -178,7 +178,7 @@ static FlakeInput parseFlakeInput(EvalState & state, } if (!input.follows && !input.ref) - input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", inputName}}); + input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", std::string(inputName)}}); return input; } @@ -244,7 +244,7 @@ static Flake readFlake( for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) { if (formal.name != state.sSelf) flake.inputs.emplace(state.symbols[formal.name], FlakeInput { - .ref = parseFlakeRef(state.symbols[formal.name]) + .ref = parseFlakeRef(std::string(state.symbols[formal.name])) }); } } diff --git a/src/libutil/suggestions.cc b/src/libutil/suggestions.cc index e67e986fb..84c8e296f 100644 --- a/src/libutil/suggestions.cc +++ b/src/libutil/suggestions.cc @@ -38,8 +38,8 @@ int levenshteinDistance(std::string_view first, std::string_view second) } Suggestions Suggestions::bestMatches ( - std::set allMatches, - std::string query) + const std::set & allMatches, + std::string_view query) { std::set res; for (const auto & possibleMatch : allMatches) { diff --git a/src/libutil/suggestions.hh b/src/libutil/suggestions.hh index 9abf5ee5f..17d1d69c1 100644 --- a/src/libutil/suggestions.hh +++ b/src/libutil/suggestions.hh @@ -35,8 +35,8 @@ public: ) const; static Suggestions bestMatches ( - std::set allMatches, - std::string query + const std::set & allMatches, + std::string_view query ); Suggestions& operator+=(const Suggestions & other); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 84c659023..b65c7f59d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -165,7 +165,7 @@ struct CmdFlakeLock : FlakeCommand }; static void enumerateOutputs(EvalState & state, Value & vFlake, - std::function callback) + std::function callback) { auto pos = vFlake.determinePos(noPos); state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs"); @@ -393,15 +393,15 @@ struct CmdFlakeCheck : FlakeCommand || (hasPrefix(name, "_") && name.substr(1) == expected); }; - auto checkSystemName = [&](const std::string & system, const PosIdx pos) { + auto checkSystemName = [&](std::string_view system, const PosIdx pos) { // FIXME: what's the format of "system"? if (system.find('-') == std::string::npos) reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos))); }; - auto checkSystemType = [&](const std::string & system, const PosIdx pos) { + auto checkSystemType = [&](std::string_view system, const PosIdx pos) { if (!checkAllSystems && system != localSystem) { - omittedSystems.insert(system); + omittedSystems.insert(std::string(system)); return false; } else { return true; @@ -450,7 +450,7 @@ struct CmdFlakeCheck : FlakeCommand } }; - auto checkOverlay = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkOverlay = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking overlay '%s'", attrPath)); @@ -469,7 +469,7 @@ struct CmdFlakeCheck : FlakeCommand } }; - auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkModule = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking NixOS module '%s'", attrPath)); @@ -480,9 +480,9 @@ struct CmdFlakeCheck : FlakeCommand } }; - std::function checkHydraJobs; + std::function checkHydraJobs; - checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + checkHydraJobs = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking Hydra job '%s'", attrPath)); @@ -523,7 +523,7 @@ struct CmdFlakeCheck : FlakeCommand } }; - auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkTemplate = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking template '%s'", attrPath)); @@ -579,7 +579,7 @@ struct CmdFlakeCheck : FlakeCommand enumerateOutputs(*state, *vFlake, - [&](const std::string & name, Value & vOutput, const PosIdx pos) { + [&](std::string_view name, Value & vOutput, const PosIdx pos) { Activity act(*logger, lvlInfo, actUnknown, fmt("checking flake output '%s'", name)); @@ -603,7 +603,7 @@ struct CmdFlakeCheck : FlakeCommand if (name == "checks") { state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs()) { - const auto & attr_name = state->symbols[attr.name]; + std::string_view attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); diff --git a/src/nix/main.cc b/src/nix/main.cc index c90bb25a7..775052351 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -429,7 +429,7 @@ void mainWrapped(int argc, char * * argv) b["doc"] = trim(stripIndentation(primOp->doc)); if (primOp->experimentalFeature) b["experimental-feature"] = primOp->experimentalFeature; - builtinsJson[state.symbols[builtin.name]] = std::move(b); + builtinsJson.emplace(state.symbols[builtin.name], std::move(b)); } for (auto & [name, info] : state.constantInfos) { auto b = nlohmann::json::object(); From f070d68c32463fab9972361e1874c9c270ec672a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 14:25:16 +0200 Subject: [PATCH 536/910] Add BaseError assignment operators The move assignment was implicitly generated and used in src/libstore/build/goal.cc:90:22: 90 | this->ex = std::move(*ex); Clang warns about this generated method being deprecated, so making them explicit fixes the warning. --- src/libutil/error.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 4b08a045e..1fe98077e 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -127,6 +127,8 @@ protected: public: BaseError(const BaseError &) = default; + BaseError& operator=(const BaseError &) = default; + BaseError& operator=(BaseError &&) = default; template BaseError(unsigned int status, const Args & ... args) From 3fc77f281ef3def1f997c959687cba04660dd27d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 13:37:30 -0400 Subject: [PATCH 537/910] No global settings in `libnixfetchers` and `libnixflake` Progress on #5638 There are still a global fetcher and eval settings, but they are pushed down into `libnixcmd`, which is a lot less bad a place for this sort of thing. Continuing process pioneered in 52bfccf8d8112ba738e6fc9e4891f85b6b864566. --- src/libcmd/command.cc | 2 +- src/libcmd/common-eval-args.cc | 20 +++- src/libcmd/common-eval-args.hh | 15 +++ src/libcmd/installable-flake.cc | 3 +- src/libcmd/installable-flake.hh | 3 +- src/libcmd/installables.cc | 19 +-- src/libcmd/repl.cc | 4 +- src/libexpr-c/local.mk | 2 +- src/libexpr-c/nix_api_expr.cc | 2 + src/libexpr-c/nix_api_expr_internal.h | 2 + src/libexpr/eval-settings.cc | 1 - src/libexpr/eval.cc | 4 +- src/libexpr/eval.hh | 7 +- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 8 +- src/libfetchers/fetch-settings.cc | 9 +- src/libfetchers/fetch-settings.hh | 17 ++- src/libfetchers/fetchers.cc | 18 +-- src/libfetchers/fetchers.hh | 28 ++++- src/libfetchers/git.cc | 24 ++-- src/libfetchers/github.cc | 42 ++++--- src/libfetchers/indirect.cc | 12 +- src/libfetchers/mercurial.cc | 16 ++- src/libfetchers/path.cc | 12 +- src/libfetchers/registry.cc | 67 +++++------ src/libfetchers/registry.hh | 14 ++- src/libfetchers/tarball.cc | 14 ++- src/libflake/flake-settings.cc | 12 -- src/libflake/flake/config.cc | 6 +- src/libflake/flake/flake.cc | 109 ++++++++++-------- src/libflake/flake/flake.hh | 13 ++- src/libflake/flake/flakeref.cc | 43 ++++--- src/libflake/flake/flakeref.hh | 15 ++- src/libflake/flake/lockfile.cc | 19 +-- src/libflake/flake/lockfile.hh | 8 +- src/libflake/flake/settings.cc | 7 ++ .../{flake-settings.hh => flake/settings.hh} | 9 +- src/libflake/meson.build | 6 +- src/nix-build/nix-build.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix/bundle.cc | 4 +- src/nix/flake.cc | 9 +- src/nix/main.cc | 6 +- src/nix/prefetch.cc | 2 +- src/nix/profile.cc | 4 +- src/nix/registry.cc | 16 +-- src/nix/upgrade-nix.cc | 2 +- tests/unit/libexpr-support/tests/libexpr.hh | 5 +- tests/unit/libflake/flakeref.cc | 4 +- 50 files changed, 401 insertions(+), 271 deletions(-) delete mode 100644 src/libflake/flake-settings.cc create mode 100644 src/libflake/flake/settings.cc rename src/libflake/{flake-settings.hh => flake/settings.hh} (86%) diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 74d146c66..e0e5f0890 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -132,7 +132,7 @@ ref EvalCommand::getEvalState() #else std::make_shared( #endif - lookupPath, getEvalStore(), evalSettings, getStore()) + lookupPath, getEvalStore(), fetchSettings, evalSettings, getStore()) ; evalState->repair = repair; diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 62745b681..470a25c4e 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -1,3 +1,4 @@ +#include "fetch-settings.hh" #include "eval-settings.hh" #include "common-eval-args.hh" #include "shared.hh" @@ -7,6 +8,7 @@ #include "fetchers.hh" #include "registry.hh" #include "flake/flakeref.hh" +#include "flake/settings.hh" #include "store-api.hh" #include "command.hh" #include "tarball.hh" @@ -16,6 +18,10 @@ namespace nix { +fetchers::Settings fetchSettings; + +static GlobalConfig::Register rFetchSettings(&fetchSettings); + EvalSettings evalSettings { settings.readOnlyMode, { @@ -24,7 +30,7 @@ EvalSettings evalSettings { [](ref store, std::string_view rest) { experimentalFeatureSettings.require(Xp::Flakes); // FIXME `parseFlakeRef` should take a `std::string_view`. - auto flakeRef = parseFlakeRef(std::string { rest }, {}, true, false); + auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false); debug("fetching flake search path element '%s''", rest); auto storePath = flakeRef.resolve(store).fetchTree(store).first; return store->toRealPath(storePath); @@ -35,6 +41,12 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); + +flake::Settings flakeSettings; + +static GlobalConfig::Register rFlakeSettings(&flakeSettings); + + CompatibilitySettings compatibilitySettings {}; static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); @@ -171,8 +183,8 @@ MixEvalArgs::MixEvalArgs() .category = category, .labels = {"original-ref", "resolved-ref"}, .handler = {[&](std::string _from, std::string _to) { - auto from = parseFlakeRef(_from, absPath(".")); - auto to = parseFlakeRef(_to, absPath(".")); + auto from = parseFlakeRef(fetchSettings, _from, absPath(".")); + auto to = parseFlakeRef(fetchSettings, _to, absPath(".")); fetchers::Attrs extraAttrs; if (to.subdir != "") extraAttrs["dir"] = to.subdir; fetchers::overrideRegistry(from.input, to.input, extraAttrs); @@ -230,7 +242,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas else if (hasPrefix(s, "flake:")) { experimentalFeatureSettings.require(Xp::Flakes); - auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false); + auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false); auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first; return state.rootPath(CanonPath(state.store->toRealPath(storePath))); } diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 8d303ee7c..c62365b32 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -11,17 +11,32 @@ namespace nix { class Store; + +namespace fetchers { struct Settings; } + class EvalState; struct EvalSettings; struct CompatibilitySettings; class Bindings; struct SourcePath; +namespace flake { struct Settings; } + +/** + * @todo Get rid of global setttings variables + */ +extern fetchers::Settings fetchSettings; + /** * @todo Get rid of global setttings variables */ extern EvalSettings evalSettings; +/** + * @todo Get rid of global setttings variables + */ +extern flake::Settings flakeSettings; + /** * Settings that control behaviors that have changed since Nix 2.3. */ diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 899919550..8796ad5ba 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -196,7 +196,8 @@ std::shared_ptr InstallableFlake::getLockedFlake() const flake::LockFlags lockFlagsApplyConfig = lockFlags; // FIXME why this side effect? lockFlagsApplyConfig.applyNixConfig = true; - _lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlagsApplyConfig)); + _lockedFlake = std::make_shared(lockFlake( + flakeSettings, *state, flakeRef, lockFlagsApplyConfig)); } return _lockedFlake; } diff --git a/src/libcmd/installable-flake.hh b/src/libcmd/installable-flake.hh index 30240a35a..8e0a232ef 100644 --- a/src/libcmd/installable-flake.hh +++ b/src/libcmd/installable-flake.hh @@ -1,6 +1,7 @@ #pragma once ///@file +#include "common-eval-args.hh" #include "installable-value.hh" namespace nix { @@ -78,7 +79,7 @@ struct InstallableFlake : InstallableValue */ static inline FlakeRef defaultNixpkgsFlakeRef() { - return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); + return FlakeRef::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", "nixpkgs"}}); } ref openEvalCache( diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 0c9e69fe8..417e15094 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -129,7 +129,7 @@ MixFlakeOptions::MixFlakeOptions() lockFlags.writeLockFile = false; lockFlags.inputOverrides.insert_or_assign( flake::parseInputPath(inputPath), - parseFlakeRef(flakeRef, absPath(getCommandBaseDir()), true)); + parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()), true)); }}, .completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) { if (n == 0) { @@ -170,14 +170,15 @@ MixFlakeOptions::MixFlakeOptions() .handler = {[&](std::string flakeRef) { auto evalState = getEvalState(); auto flake = flake::lockFlake( + flakeSettings, *evalState, - parseFlakeRef(flakeRef, absPath(getCommandBaseDir())), + parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())), { .writeLockFile = false }); for (auto & [inputName, input] : flake.lockFile.root->inputs) { auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes if (auto input3 = std::dynamic_pointer_cast(input2)) { overrideRegistry( - fetchers::Input::fromAttrs({{"type","indirect"}, {"id", inputName}}), + fetchers::Input::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", inputName}}), input3->lockedRef.input, {}); } @@ -338,10 +339,11 @@ void completeFlakeRefWithFragment( auto flakeRefS = std::string(prefix.substr(0, hash)); // TODO: ideally this would use the command base directory instead of assuming ".". - auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath(".")); + auto flakeRef = parseFlakeRef(fetchSettings, expandTilde(flakeRefS), absPath(".")); auto evalCache = openEvalCache(*evalState, - std::make_shared(lockFlake(*evalState, flakeRef, lockFlags))); + std::make_shared(lockFlake( + flakeSettings, *evalState, flakeRef, lockFlags))); auto root = evalCache->getRoot(); @@ -403,7 +405,7 @@ void completeFlakeRef(AddCompletions & completions, ref store, std::strin Args::completeDir(completions, 0, prefix); /* Look for registry entries that match the prefix. */ - for (auto & registry : fetchers::getRegistries(store)) { + for (auto & registry : fetchers::getRegistries(fetchSettings, store)) { for (auto & entry : registry->entries) { auto from = entry.from.to_string(); if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { @@ -534,7 +536,8 @@ Installables SourceExprCommand::parseInstallables( } try { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(getCommandBaseDir())); + auto [flakeRef, fragment] = parseFlakeRefWithFragment( + fetchSettings, std::string { prefix }, absPath(getCommandBaseDir())); result.push_back(make_ref( this, getEvalState(), @@ -851,6 +854,7 @@ std::vector RawInstallablesCommand::getFlakeRefsForCompletion() std::vector res; for (auto i : rawInstallables) res.push_back(parseFlakeRefWithFragment( + fetchSettings, expandTilde(i), absPath(getCommandBaseDir())).first); return res; @@ -873,6 +877,7 @@ std::vector InstallableCommand::getFlakeRefsForCompletion() { return { parseFlakeRefWithFragment( + fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir())).first }; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index ce1c5af69..661785335 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -690,14 +690,14 @@ void NixRepl::loadFlake(const std::string & flakeRefS) if (flakeRefS.empty()) throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)"); - auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true); + auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, absPath("."), true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); Value v; flake::callFlake(*state, - flake::lockFlake(*state, flakeRef, + flake::lockFlake(flakeSettings, *state, flakeRef, flake::LockFlags { .updateLockFile = false, .useRegistries = !evalSettings.pureEval, diff --git a/src/libexpr-c/local.mk b/src/libexpr-c/local.mk index 51b02562e..227a4095b 100644 --- a/src/libexpr-c/local.mk +++ b/src/libexpr-c/local.mk @@ -15,7 +15,7 @@ libexprc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) \ $(INCLUDE_libstore) $(INCLUDE_libstorec) \ $(INCLUDE_libexpr) $(INCLUDE_libexprc) -libexprc_LIBS = libutil libutilc libstore libstorec libexpr +libexprc_LIBS = libutil libutilc libstore libstorec libfetchers libexpr libexprc_LDFLAGS += $(THREAD_LDFLAGS) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 13e0f5f3a..34e9b2744 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -112,12 +112,14 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c static_cast(alignof(EvalState))); auto * p2 = static_cast(p); new (p) EvalState { + .fetchSettings = nix::fetchers::Settings{}, .settings = nix::EvalSettings{ nix::settings.readOnlyMode, }, .state = nix::EvalState( nix::LookupPath::parse(lookupPath), store->ptr, + p2->fetchSettings, p2->settings), }; loadConfFile(p2->settings); diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index d4ccffd29..12f24b6eb 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -1,6 +1,7 @@ #ifndef NIX_API_EXPR_INTERNAL_H #define NIX_API_EXPR_INTERNAL_H +#include "fetch-settings.hh" #include "eval.hh" #include "eval-settings.hh" #include "attr-set.hh" @@ -8,6 +9,7 @@ struct EvalState { + nix::fetchers::Settings fetchSettings; nix::EvalSettings settings; nix::EvalState state; }; diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 6b7b52cef..e2151aa7f 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -1,5 +1,4 @@ #include "users.hh" -#include "config-global.hh" #include "globals.hh" #include "profiles.hh" #include "eval.hh" diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index efca9dd2f..9eb3d972c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -217,9 +217,11 @@ static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( const LookupPath & _lookupPath, ref store, + const fetchers::Settings & fetchSettings, const EvalSettings & settings, std::shared_ptr buildStore) - : settings{settings} + : fetchSettings{fetchSettings} + , settings{settings} , sWith(symbols.create("")) , sOutPath(symbols.create("outPath")) , sDrvPath(symbols.create("drvPath")) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index e45358055..5df3e92be 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -30,6 +30,7 @@ namespace nix { constexpr size_t maxPrimOpArity = 8; class Store; +namespace fetchers { struct Settings; } struct EvalSettings; class EvalState; class StorePath; @@ -43,7 +44,7 @@ namespace eval_cache { /** * Function that implements a primop. */ -typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v); +using PrimOpFun = void(EvalState & state, const PosIdx pos, Value * * args, Value & v); /** * Info about a primitive operation, and its implementation @@ -84,7 +85,7 @@ struct PrimOp /** * Implementation of the primop. */ - std::function::type> fun; + std::function fun; /** * Optional experimental for this to be gated on. @@ -162,6 +163,7 @@ struct DebugTrace { class EvalState : public std::enable_shared_from_this { public: + const fetchers::Settings & fetchSettings; const EvalSettings & settings; SymbolTable symbols; PosTable positions; @@ -353,6 +355,7 @@ public: EvalState( const LookupPath & _lookupPath, ref store, + const fetchers::Settings & fetchSettings, const EvalSettings & settings, std::shared_ptr buildStore = nullptr); ~EvalState(); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 7b5f4193a..64e3abf2d 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a attrs.insert_or_assign("name", std::string(name)); if (ref) attrs.insert_or_assign("ref", *ref); if (rev) attrs.insert_or_assign("rev", rev->gitRev()); - auto input = fetchers::Input::fromAttrs(std::move(attrs)); + auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); auto [storePath, input2] = input.fetchToStore(state.store); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 567b73f9a..6a7accad7 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -85,7 +85,7 @@ static void fetchTree( Value & v, const FetchTreeParams & params = FetchTreeParams{} ) { - fetchers::Input input; + fetchers::Input input { state.fetchSettings }; NixStringContext context; std::optional type; if (params.isFetchGit) type = "git"; @@ -148,7 +148,7 @@ static void fetchTree( "attribute 'name' isn’t supported in call to 'fetchTree'" ).atPos(pos).debugThrow(); - input = fetchers::Input::fromAttrs(std::move(attrs)); + input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); } else { auto url = state.coerceToString(pos, *args[0], context, "while evaluating the first argument passed to the fetcher", @@ -161,13 +161,13 @@ static void fetchTree( if (!attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) { attrs.emplace("exportIgnore", Explicit{true}); } - input = fetchers::Input::fromAttrs(std::move(attrs)); + input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); } else { if (!experimentalFeatureSettings.isEnabled(Xp::Flakes)) state.error( "passing a string argument to 'fetchTree' requires the 'flakes' experimental feature" ).atPos(pos).debugThrow(); - input = fetchers::Input::fromURL(url); + input = fetchers::Input::fromURL(state.fetchSettings, url); } } diff --git a/src/libfetchers/fetch-settings.cc b/src/libfetchers/fetch-settings.cc index 21c42567c..c7ed4c7af 100644 --- a/src/libfetchers/fetch-settings.cc +++ b/src/libfetchers/fetch-settings.cc @@ -1,14 +1,9 @@ #include "fetch-settings.hh" -#include "config-global.hh" -namespace nix { +namespace nix::fetchers { -FetchSettings::FetchSettings() +Settings::Settings() { } -FetchSettings fetchSettings; - -static GlobalConfig::Register rFetchSettings(&fetchSettings); - } diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 629967697..f7cb34a02 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -9,11 +9,11 @@ #include -namespace nix { +namespace nix::fetchers { -struct FetchSettings : public Config +struct Settings : public Config { - FetchSettings(); + Settings(); Setting accessTokens{this, {}, "access-tokens", R"( @@ -84,9 +84,14 @@ struct FetchSettings : public Config `narHash` attribute is specified, e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f?narHash=sha256-PPXqKY2hJng4DBVE0I4xshv/vGLUskL7jl53roB8UdU%3D`. )"}; + + Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", + R"( + Path or URI of the global flake registry. + + When empty, disables the global flake registry. + )", + {}, true, Xp::Flakes}; }; -// FIXME: don't use a global variable. -extern FetchSettings fetchSettings; - } diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 294960678..59e77621c 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -35,9 +35,11 @@ nlohmann::json dumpRegisterInputSchemeInfo() { return res; } -Input Input::fromURL(const std::string & url, bool requireTree) +Input Input::fromURL( + const Settings & settings, + const std::string & url, bool requireTree) { - return fromURL(parseURL(url), requireTree); + return fromURL(settings, parseURL(url), requireTree); } static void fixupInput(Input & input) @@ -49,10 +51,12 @@ static void fixupInput(Input & input) input.getLastModified(); } -Input Input::fromURL(const ParsedURL & url, bool requireTree) +Input Input::fromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) { for (auto & [_, inputScheme] : *inputSchemes) { - auto res = inputScheme->inputFromURL(url, requireTree); + auto res = inputScheme->inputFromURL(settings, url, requireTree); if (res) { experimentalFeatureSettings.require(inputScheme->experimentalFeature()); res->scheme = inputScheme; @@ -64,7 +68,7 @@ Input Input::fromURL(const ParsedURL & url, bool requireTree) throw Error("input '%s' is unsupported", url.url); } -Input Input::fromAttrs(Attrs && attrs) +Input Input::fromAttrs(const Settings & settings, Attrs && attrs) { auto schemeName = ({ auto schemeNameOpt = maybeGetStrAttr(attrs, "type"); @@ -78,7 +82,7 @@ Input Input::fromAttrs(Attrs && attrs) // but not all of them. Doing this is to support those other // operations which are supposed to be robust on // unknown/uninterpretable inputs. - Input input; + Input input { settings }; input.attrs = attrs; fixupInput(input); return input; @@ -99,7 +103,7 @@ Input Input::fromAttrs(Attrs && attrs) if (name != "type" && allowedAttrs.count(name) == 0) throw Error("input attribute '%s' not supported by scheme '%s'", name, schemeName); - auto res = inputScheme->inputFromAttrs(attrs); + auto res = inputScheme->inputFromAttrs(settings, attrs); if (!res) return raw(); res->scheme = inputScheme; fixupInput(*res); diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 551be9a1f..34d3bafac 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -17,6 +17,8 @@ namespace nix::fetchers { struct InputScheme; +struct Settings; + /** * The `Input` object is generated by a specific fetcher, based on * user-supplied information, and contains @@ -28,6 +30,12 @@ struct Input { friend struct InputScheme; + const Settings * settings; + + Input(const Settings & settings) + : settings{&settings} + { } + std::shared_ptr scheme; // note: can be null Attrs attrs; @@ -42,16 +50,22 @@ public: * * The URL indicate which sort of fetcher, and provides information to that fetcher. */ - static Input fromURL(const std::string & url, bool requireTree = true); + static Input fromURL( + const Settings & settings, + const std::string & url, bool requireTree = true); - static Input fromURL(const ParsedURL & url, bool requireTree = true); + static Input fromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree = true); /** * Create an `Input` from a an `Attrs`. * * The URL indicate which sort of fetcher, and provides information to that fetcher. */ - static Input fromAttrs(Attrs && attrs); + static Input fromAttrs( + const Settings & settings, + Attrs && attrs); ParsedURL toURL() const; @@ -146,9 +160,13 @@ struct InputScheme virtual ~InputScheme() { } - virtual std::optional inputFromURL(const ParsedURL & url, bool requireTree) const = 0; + virtual std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const = 0; - virtual std::optional inputFromAttrs(const Attrs & attrs) const = 0; + virtual std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const = 0; /** * What is the name of the scheme? diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 184c1383e..076c757c5 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -164,7 +164,9 @@ static const Hash nullRev{HashAlgorithm::SHA1}; struct GitInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "git" && url.scheme != "git+http" && @@ -190,7 +192,7 @@ struct GitInputScheme : InputScheme attrs.emplace("url", url2.to_string()); - return inputFromAttrs(attrs); + return inputFromAttrs(settings, attrs); } @@ -222,7 +224,9 @@ struct GitInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { for (auto & [name, _] : attrs) if (name == "verifyCommit" @@ -238,7 +242,7 @@ struct GitInputScheme : InputScheme throw BadURL("invalid Git branch/tag name '%s'", *ref); } - Input input; + Input input{settings}; input.attrs = attrs; auto url = fixGitURL(getStrAttr(attrs, "url")); parseURL(url); @@ -366,13 +370,13 @@ struct GitInputScheme : InputScheme /* URL of the repo, or its path if isLocal. Never a `file` URL. */ std::string url; - void warnDirty() const + void warnDirty(const Settings & settings) const { if (workdirInfo.isDirty) { - if (!fetchSettings.allowDirty) + if (!settings.allowDirty) throw Error("Git tree '%s' is dirty", url); - if (fetchSettings.warnDirty) + if (settings.warnDirty) warn("Git tree '%s' is dirty", url); } } @@ -653,7 +657,7 @@ struct GitInputScheme : InputScheme attrs.insert_or_assign("exportIgnore", Explicit{ exportIgnore }); attrs.insert_or_assign("submodules", Explicit{ true }); attrs.insert_or_assign("allRefs", Explicit{ true }); - auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); + auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); @@ -711,7 +715,7 @@ struct GitInputScheme : InputScheme // TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out // attrs.insert_or_assign("allRefs", Explicit{ true }); - auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); + auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); @@ -743,7 +747,7 @@ struct GitInputScheme : InputScheme verifyCommit(input, repo); } else { - repoInfo.warnDirty(); + repoInfo.warnDirty(*input.settings); if (repoInfo.workdirInfo.headRev) { input.attrs.insert_or_assign("dirtyRev", diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index ddb41e63f..2968d2df2 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -31,7 +31,9 @@ struct GitArchiveInputScheme : InputScheme { virtual std::optional> accessHeaderFromToken(const std::string & token) const = 0; - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const fetchers::Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != schemeName()) return {}; @@ -90,7 +92,7 @@ struct GitArchiveInputScheme : InputScheme if (ref && rev) throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev()); - Input input; + Input input{settings}; input.attrs.insert_or_assign("type", std::string { schemeName() }); input.attrs.insert_or_assign("owner", path[0]); input.attrs.insert_or_assign("repo", path[1]); @@ -119,12 +121,14 @@ struct GitArchiveInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const fetchers::Settings & settings, + const Attrs & attrs) const override { getStrAttr(attrs, "owner"); getStrAttr(attrs, "repo"); - Input input; + Input input{settings}; input.attrs = attrs; return input; } @@ -168,18 +172,20 @@ struct GitArchiveInputScheme : InputScheme return input; } - std::optional getAccessToken(const std::string & host) const + std::optional getAccessToken(const fetchers::Settings & settings, const std::string & host) const { - auto tokens = fetchSettings.accessTokens.get(); + auto tokens = settings.accessTokens.get(); if (auto token = get(tokens, host)) return *token; return {}; } - Headers makeHeadersWithAuthTokens(const std::string & host) const + Headers makeHeadersWithAuthTokens( + const fetchers::Settings & settings, + const std::string & host) const { Headers headers; - auto accessToken = getAccessToken(host); + auto accessToken = getAccessToken(settings, host); if (accessToken) { auto hdr = accessHeaderFromToken(*accessToken); if (hdr) @@ -295,7 +301,7 @@ struct GitArchiveInputScheme : InputScheme locking. FIXME: in the future, we may want to require a Git tree hash instead of a NAR hash. */ return input.getRev().has_value() - && (fetchSettings.trustTarballsFromGitForges || + && (input.settings->trustTarballsFromGitForges || input.getNarHash().has_value()); } @@ -352,7 +358,7 @@ struct GitHubInputScheme : GitArchiveInputScheme : "https://%s/api/v3/repos/%s/%s/commits/%s", host, getOwner(input), getRepo(input), *input.getRef()); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); auto json = nlohmann::json::parse( readFile( @@ -369,7 +375,7 @@ struct GitHubInputScheme : GitArchiveInputScheme { auto host = getHost(input); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); // If we have no auth headers then we default to the public archive // urls so we do not run into rate limits. @@ -389,7 +395,7 @@ struct GitHubInputScheme : GitArchiveInputScheme void clone(const Input & input, const Path & destDir) const override { auto host = getHost(input); - Input::fromURL(fmt("git+https://%s/%s/%s.git", + Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); @@ -426,7 +432,7 @@ struct GitLabInputScheme : GitArchiveInputScheme auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef()); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); auto json = nlohmann::json::parse( readFile( @@ -456,7 +462,7 @@ struct GitLabInputScheme : GitArchiveInputScheme host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), input.getRev()->to_string(HashFormat::Base16, false)); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); return DownloadUrl { url, headers }; } @@ -464,7 +470,7 @@ struct GitLabInputScheme : GitArchiveInputScheme { auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); // FIXME: get username somewhere - Input::fromURL(fmt("git+https://%s/%s/%s.git", + Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); @@ -496,7 +502,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme auto base_url = fmt("https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); std::string refUri; if (ref == "HEAD") { @@ -543,14 +549,14 @@ struct SourceHutInputScheme : GitArchiveInputScheme host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), input.getRev()->to_string(HashFormat::Base16, false)); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); return DownloadUrl { url, headers }; } void clone(const Input & input, const Path & destDir) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); - Input::fromURL(fmt("git+https://%s/%s/%s", + Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index ba5078631..2e5cd82c7 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -8,7 +8,9 @@ std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript); struct IndirectInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "flake") return {}; @@ -41,7 +43,7 @@ struct IndirectInputScheme : InputScheme // FIXME: forbid query params? - Input input; + Input input{settings}; input.attrs.insert_or_assign("type", "indirect"); input.attrs.insert_or_assign("id", id); if (rev) input.attrs.insert_or_assign("rev", rev->gitRev()); @@ -65,13 +67,15 @@ struct IndirectInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { auto id = getStrAttr(attrs, "id"); if (!std::regex_match(id, flakeRegex)) throw BadURL("'%s' is not a valid flake ID", id); - Input input; + Input input{settings}; input.attrs = attrs; return input; } diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 198795caa..3feb3cb19 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -45,7 +45,9 @@ static std::string runHg(const Strings & args, const std::optional struct MercurialInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "hg+http" && url.scheme != "hg+https" && @@ -68,7 +70,7 @@ struct MercurialInputScheme : InputScheme attrs.emplace("url", url2.to_string()); - return inputFromAttrs(attrs); + return inputFromAttrs(settings, attrs); } std::string_view schemeName() const override @@ -88,7 +90,9 @@ struct MercurialInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { parseURL(getStrAttr(attrs, "url")); @@ -97,7 +101,7 @@ struct MercurialInputScheme : InputScheme throw BadURL("invalid Mercurial branch/tag name '%s'", *ref); } - Input input; + Input input{settings}; input.attrs = attrs; return input; } @@ -182,10 +186,10 @@ struct MercurialInputScheme : InputScheme /* This is an unclean working tree. So copy all tracked files. */ - if (!fetchSettings.allowDirty) + if (!input.settings->allowDirty) throw Error("Mercurial tree '%s' is unclean", actualUrl); - if (fetchSettings.warnDirty) + if (input.settings->warnDirty) warn("Mercurial tree '%s' is unclean", actualUrl); input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl }))); diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 68958d559..fca0df84b 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -7,14 +7,16 @@ namespace nix::fetchers { struct PathInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "path") return {}; if (url.authority && *url.authority != "") throw Error("path URL '%s' should not have an authority ('%s')", url.url, *url.authority); - Input input; + Input input{settings}; input.attrs.insert_or_assign("type", "path"); input.attrs.insert_or_assign("path", url.path); @@ -54,11 +56,13 @@ struct PathInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { getStrAttr(attrs, "path"); - Input input; + Input input{settings}; input.attrs = attrs; return input; } diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index 52cbac5e0..3c893c8ea 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -1,7 +1,7 @@ +#include "fetch-settings.hh" #include "registry.hh" #include "tarball.hh" #include "users.hh" -#include "config-global.hh" #include "globals.hh" #include "store-api.hh" #include "local-fs-store.hh" @@ -11,12 +11,13 @@ namespace nix::fetchers { std::shared_ptr Registry::read( + const Settings & settings, const Path & path, RegistryType type) { - auto registry = std::make_shared(type); + auto registry = std::make_shared(settings, type); if (!pathExists(path)) - return std::make_shared(type); + return std::make_shared(settings, type); try { @@ -36,8 +37,8 @@ std::shared_ptr Registry::read( auto exact = i.find("exact"); registry->entries.push_back( Entry { - .from = Input::fromAttrs(jsonToAttrs(i["from"])), - .to = Input::fromAttrs(std::move(toAttrs)), + .from = Input::fromAttrs(settings, jsonToAttrs(i["from"])), + .to = Input::fromAttrs(settings, std::move(toAttrs)), .extraAttrs = extraAttrs, .exact = exact != i.end() && exact.value() }); @@ -106,10 +107,10 @@ static Path getSystemRegistryPath() return settings.nixConfDir + "/registry.json"; } -static std::shared_ptr getSystemRegistry() +static std::shared_ptr getSystemRegistry(const Settings & settings) { static auto systemRegistry = - Registry::read(getSystemRegistryPath(), Registry::System); + Registry::read(settings, getSystemRegistryPath(), Registry::System); return systemRegistry; } @@ -118,25 +119,24 @@ Path getUserRegistryPath() return getConfigDir() + "/nix/registry.json"; } -std::shared_ptr getUserRegistry() +std::shared_ptr getUserRegistry(const Settings & settings) { static auto userRegistry = - Registry::read(getUserRegistryPath(), Registry::User); + Registry::read(settings, getUserRegistryPath(), Registry::User); return userRegistry; } -std::shared_ptr getCustomRegistry(const Path & p) +std::shared_ptr getCustomRegistry(const Settings & settings, const Path & p) { static auto customRegistry = - Registry::read(p, Registry::Custom); + Registry::read(settings, p, Registry::Custom); return customRegistry; } -static std::shared_ptr flagRegistry = - std::make_shared(Registry::Flag); - -std::shared_ptr getFlagRegistry() +std::shared_ptr getFlagRegistry(const Settings & settings) { + static auto flagRegistry = + std::make_shared(settings, Registry::Flag); return flagRegistry; } @@ -145,30 +145,15 @@ void overrideRegistry( const Input & to, const Attrs & extraAttrs) { - flagRegistry->add(from, to, extraAttrs); + getFlagRegistry(*from.settings)->add(from, to, extraAttrs); } -struct RegistrySettings : Config -{ - Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", - R"( - Path or URI of the global flake registry. - - When empty, disables the global flake registry. - )", - {}, true, Xp::Flakes}; -}; - -RegistrySettings registrySettings; - -static GlobalConfig::Register rRegistrySettings(®istrySettings); - -static std::shared_ptr getGlobalRegistry(ref store) +static std::shared_ptr getGlobalRegistry(const Settings & settings, ref store) { static auto reg = [&]() { - auto path = registrySettings.flakeRegistry.get(); + auto path = settings.flakeRegistry.get(); if (path == "") { - return std::make_shared(Registry::Global); // empty registry + return std::make_shared(settings, Registry::Global); // empty registry } if (!hasPrefix(path, "/")) { @@ -178,19 +163,19 @@ static std::shared_ptr getGlobalRegistry(ref store) path = store->toRealPath(storePath); } - return Registry::read(path, Registry::Global); + return Registry::read(settings, path, Registry::Global); }(); return reg; } -Registries getRegistries(ref store) +Registries getRegistries(const Settings & settings, ref store) { Registries registries; - registries.push_back(getFlagRegistry()); - registries.push_back(getUserRegistry()); - registries.push_back(getSystemRegistry()); - registries.push_back(getGlobalRegistry(store)); + registries.push_back(getFlagRegistry(settings)); + registries.push_back(getUserRegistry(settings)); + registries.push_back(getSystemRegistry(settings)); + registries.push_back(getGlobalRegistry(settings, store)); return registries; } @@ -207,7 +192,7 @@ std::pair lookupInRegistries( n++; if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string()); - for (auto & registry : getRegistries(store)) { + for (auto & registry : getRegistries(*input.settings, store)) { // FIXME: O(n) for (auto & entry : registry->entries) { if (entry.exact) { diff --git a/src/libfetchers/registry.hh b/src/libfetchers/registry.hh index f57ab1e6b..0d68ac395 100644 --- a/src/libfetchers/registry.hh +++ b/src/libfetchers/registry.hh @@ -10,6 +10,8 @@ namespace nix::fetchers { struct Registry { + const Settings & settings; + enum RegistryType { Flag = 0, User = 1, @@ -29,11 +31,13 @@ struct Registry std::vector entries; - Registry(RegistryType type) - : type(type) + Registry(const Settings & settings, RegistryType type) + : settings{settings} + , type{type} { } static std::shared_ptr read( + const Settings & settings, const Path & path, RegistryType type); void write(const Path & path); @@ -48,13 +52,13 @@ struct Registry typedef std::vector> Registries; -std::shared_ptr getUserRegistry(); +std::shared_ptr getUserRegistry(const Settings & settings); -std::shared_ptr getCustomRegistry(const Path & p); +std::shared_ptr getCustomRegistry(const Settings & settings, const Path & p); Path getUserRegistryPath(); -Registries getRegistries(ref store); +Registries getRegistries(const Settings & settings, ref store); void overrideRegistry( const Input & from, diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index aa8ff652f..e049dffeb 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -214,12 +214,14 @@ struct CurlInputScheme : InputScheme static const std::set specialParams; - std::optional inputFromURL(const ParsedURL & _url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & _url, bool requireTree) const override { if (!isValidURL(_url, requireTree)) return std::nullopt; - Input input; + Input input{settings}; auto url = _url; @@ -267,9 +269,11 @@ struct CurlInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { - Input input; + Input input{settings}; input.attrs = attrs; //input.locked = (bool) maybeGetStrAttr(input.attrs, "hash"); @@ -349,7 +353,7 @@ struct TarballInputScheme : CurlInputScheme result.accessor->setPathDisplay("«" + input.to_string() + "»"); if (result.immutableUrl) { - auto immutableInput = Input::fromURL(*result.immutableUrl); + auto immutableInput = Input::fromURL(*input.settings, *result.immutableUrl); // FIXME: would be nice to support arbitrary flakerefs // here, e.g. git flakes. if (immutableInput.getType() != "tarball") diff --git a/src/libflake/flake-settings.cc b/src/libflake/flake-settings.cc deleted file mode 100644 index ba97e0ce7..000000000 --- a/src/libflake/flake-settings.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include "flake-settings.hh" -#include "config-global.hh" - -namespace nix { - -FlakeSettings::FlakeSettings() {} - -FlakeSettings flakeSettings; - -static GlobalConfig::Register rFlakeSettings(&flakeSettings); - -} diff --git a/src/libflake/flake/config.cc b/src/libflake/flake/config.cc index 498595359..4e00d5c93 100644 --- a/src/libflake/flake/config.cc +++ b/src/libflake/flake/config.cc @@ -1,6 +1,6 @@ #include "users.hh" #include "config-global.hh" -#include "flake-settings.hh" +#include "flake/settings.hh" #include "flake.hh" #include @@ -30,7 +30,7 @@ static void writeTrustedList(const TrustedList & trustedList) writeFile(path, nlohmann::json(trustedList).dump()); } -void ConfigFile::apply() +void ConfigFile::apply(const Settings & flakeSettings) { std::set whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lock-file-summary", "commit-lockfile-summary"}; @@ -51,7 +51,7 @@ void ConfigFile::apply() else assert(false); - if (!whitelist.count(baseName) && !nix::flakeSettings.acceptFlakeConfig) { + if (!whitelist.count(baseName) && !flakeSettings.acceptFlakeConfig) { bool trusted = false; auto trustedList = readTrustedList(); auto tlname = get(trustedList, name); diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index eb083fcee..627dfe830 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -9,7 +9,7 @@ #include "fetchers.hh" #include "finally.hh" #include "fetch-settings.hh" -#include "flake-settings.hh" +#include "flake/settings.hh" #include "value-to-json.hh" #include "local-fs-store.hh" @@ -164,7 +164,7 @@ static FlakeInput parseFlakeInput(EvalState & state, if (attrs.count("type")) try { - input.ref = FlakeRef::fromAttrs(attrs); + input.ref = FlakeRef::fromAttrs(state.fetchSettings, attrs); } catch (Error & e) { e.addTrace(state.positions[pos], HintFmt("while evaluating flake input")); throw; @@ -174,11 +174,11 @@ static FlakeInput parseFlakeInput(EvalState & state, if (!attrs.empty()) throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]); if (url) - input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); + input.ref = parseFlakeRef(state.fetchSettings, *url, baseDir, true, input.isFlake); } if (!input.follows && !input.ref) - input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", std::string(inputName)}}); + input.ref = FlakeRef::fromAttrs(state.fetchSettings, {{"type", "indirect"}, {"id", std::string(inputName)}}); return input; } @@ -244,7 +244,7 @@ static Flake readFlake( for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) { if (formal.name != state.sSelf) flake.inputs.emplace(state.symbols[formal.name], FlakeInput { - .ref = parseFlakeRef(std::string(state.symbols[formal.name])) + .ref = parseFlakeRef(state.fetchSettings, std::string(state.symbols[formal.name])) }); } } @@ -329,16 +329,19 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup return getFlake(state, originalRef, allowLookup, flakeCache); } -static LockFile readLockFile(const SourcePath & lockFilePath) +static LockFile readLockFile( + const fetchers::Settings & fetchSettings, + const SourcePath & lockFilePath) { return lockFilePath.pathExists() - ? LockFile(lockFilePath.readFile(), fmt("%s", lockFilePath)) + ? LockFile(fetchSettings, lockFilePath.readFile(), fmt("%s", lockFilePath)) : LockFile(); } /* Compute an in-memory lock file for the specified top-level flake, and optionally write it to file, if the flake is writable. */ LockedFlake lockFlake( + const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags) @@ -347,21 +350,22 @@ LockedFlake lockFlake( FlakeCache flakeCache; - auto useRegistries = lockFlags.useRegistries.value_or(flakeSettings.useRegistries); + auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries); auto flake = getFlake(state, topRef, useRegistries, flakeCache); if (lockFlags.applyNixConfig) { - flake.config.apply(); + flake.config.apply(settings); state.store->setOptions(); } try { - if (!fetchSettings.allowDirty && lockFlags.referenceLockFilePath) { + if (!state.fetchSettings.allowDirty && lockFlags.referenceLockFilePath) { throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false"); } auto oldLockFile = readLockFile( + state.fetchSettings, lockFlags.referenceLockFilePath.value_or( flake.lockFilePath())); @@ -597,7 +601,7 @@ LockedFlake lockFlake( inputFlake.inputs, childNode, inputPath, oldLock ? std::dynamic_pointer_cast(oldLock) - : readLockFile(inputFlake.lockFilePath()).root.get_ptr(), + : readLockFile(state.fetchSettings, inputFlake.lockFilePath()).root.get_ptr(), oldLock ? lockRootPath : inputPath, localPath, false); @@ -660,7 +664,7 @@ LockedFlake lockFlake( if (lockFlags.writeLockFile) { if (sourcePath || lockFlags.outputLockFilePath) { if (auto unlockedInput = newLockFile.isUnlocked()) { - if (fetchSettings.warnDirty) + if (state.fetchSettings.warnDirty) warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput); } else { if (!lockFlags.updateLockFile) @@ -692,7 +696,7 @@ LockedFlake lockFlake( if (lockFlags.commitLockFile) { std::string cm; - cm = flakeSettings.commitLockFileSummary.get(); + cm = settings.commitLockFileSummary.get(); if (cm == "") { cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); @@ -800,46 +804,49 @@ void callFlake(EvalState & state, state.callFunction(*vTmp1, vOverrides, vRes, noPos); } -static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void initLib(const Settings & settings) { - std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); - auto flakeRef = parseFlakeRef(flakeRefS, {}, true); - if (state.settings.pureEval && !flakeRef.input.isLocked()) - throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); + auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value * * args, Value & v) + { + std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); + auto flakeRef = parseFlakeRef(state.fetchSettings, flakeRefS, {}, true); + if (state.settings.pureEval && !flakeRef.input.isLocked()) + throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); - callFlake(state, - lockFlake(state, flakeRef, - LockFlags { - .updateLockFile = false, - .writeLockFile = false, - .useRegistries = !state.settings.pureEval && flakeSettings.useRegistries, - .allowUnlocked = !state.settings.pureEval, - }), - v); + callFlake(state, + lockFlake(settings, state, flakeRef, + LockFlags { + .updateLockFile = false, + .writeLockFile = false, + .useRegistries = !state.settings.pureEval && settings.useRegistries, + .allowUnlocked = !state.settings.pureEval, + }), + v); + }; + + RegisterPrimOp::primOps->push_back({ + .name = "__getFlake", + .args = {"args"}, + .doc = R"( + Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: + + ```nix + (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix + ``` + + Unless impure evaluation is allowed (`--impure`), the flake reference + must be "locked", e.g. contain a Git revision or content hash. An + example of an unlocked usage is: + + ```nix + (builtins.getFlake "github:edolstra/dwarffs").rev + ``` + )", + .fun = prim_getFlake, + .experimentalFeature = Xp::Flakes, + }); } -static RegisterPrimOp r2({ - .name = "__getFlake", - .args = {"args"}, - .doc = R"( - Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: - - ```nix - (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix - ``` - - Unless impure evaluation is allowed (`--impure`), the flake reference - must be "locked", e.g. contain a Git revision or content hash. An - example of an unlocked usage is: - - ```nix - (builtins.getFlake "github:edolstra/dwarffs").rev - ``` - )", - .fun = prim_getFlake, - .experimentalFeature = Xp::Flakes, -}); - static void prim_parseFlakeRef( EvalState & state, const PosIdx pos, @@ -848,7 +855,7 @@ static void prim_parseFlakeRef( { std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.parseFlakeRef")); - auto attrs = parseFlakeRef(flakeRefS, {}, true).toAttrs(); + auto attrs = parseFlakeRef(state.fetchSettings, flakeRefS, {}, true).toAttrs(); auto binds = state.buildBindings(attrs.size()); for (const auto & [key, value] : attrs) { auto s = state.symbols.create(key); @@ -913,7 +920,7 @@ static void prim_flakeRefToString( showType(*attr.value)).debugThrow(); } } - auto flakeRef = FlakeRef::fromAttrs(attrs); + auto flakeRef = FlakeRef::fromAttrs(state.fetchSettings, attrs); v.mkString(flakeRef.to_string()); } diff --git a/src/libflake/flake/flake.hh b/src/libflake/flake/flake.hh index 1ba085f0f..cce17009c 100644 --- a/src/libflake/flake/flake.hh +++ b/src/libflake/flake/flake.hh @@ -12,6 +12,16 @@ class EvalState; namespace flake { +struct Settings; + +/** + * Initialize `libnixflake` + * + * So far, this registers the `builtins.getFlake` primop, which depends + * on the choice of `flake:Settings`. + */ +void initLib(const Settings & settings); + struct FlakeInput; typedef std::map FlakeInputs; @@ -57,7 +67,7 @@ struct ConfigFile std::map settings; - void apply(); + void apply(const Settings & settings); }; /** @@ -194,6 +204,7 @@ struct LockFlags }; LockedFlake lockFlake( + const Settings & settings, EvalState & state, const FlakeRef & flakeRef, const LockFlags & lockFlags); diff --git a/src/libflake/flake/flakeref.cc b/src/libflake/flake/flakeref.cc index 6e4aad64d..941790e0c 100644 --- a/src/libflake/flake/flakeref.cc +++ b/src/libflake/flake/flakeref.cc @@ -48,28 +48,32 @@ FlakeRef FlakeRef::resolve(ref store) const } FlakeRef parseFlakeRef( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, bool isFlake) { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); + auto [flakeRef, fragment] = parseFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake); if (fragment != "") throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url); return flakeRef; } std::optional maybeParseFlakeRef( - const std::string & url, const std::optional & baseDir) + const fetchers::Settings & fetchSettings, + const std::string & url, + const std::optional & baseDir) { try { - return parseFlakeRef(url, baseDir); + return parseFlakeRef(fetchSettings, url, baseDir); } catch (Error &) { return {}; } } std::pair parsePathFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, @@ -166,7 +170,7 @@ std::pair parsePathFlakeRefWithFragment( parsedURL.query.insert_or_assign("shallow", "1"); return std::make_pair( - FlakeRef(fetchers::Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")), + FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL), getOr(parsedURL.query, "dir", "")), fragment); } @@ -185,13 +189,14 @@ std::pair parsePathFlakeRefWithFragment( attrs.insert_or_assign("type", "path"); attrs.insert_or_assign("path", path); - return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(std::move(attrs)), ""), fragment); + return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(fetchSettings, std::move(attrs)), ""), fragment); }; /* Check if 'url' is a flake ID. This is an abbreviated syntax for 'flake:?ref=&rev='. */ -std::optional> parseFlakeIdRef( +static std::optional> parseFlakeIdRef( + const fetchers::Settings & fetchSettings, const std::string & url, bool isFlake ) @@ -213,7 +218,7 @@ std::optional> parseFlakeIdRef( }; return std::make_pair( - FlakeRef(fetchers::Input::fromURL(parsedURL, isFlake), ""), + FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake), ""), percentDecode(match.str(6))); } @@ -221,6 +226,7 @@ std::optional> parseFlakeIdRef( } std::optional> parseURLFlakeRef( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool isFlake @@ -236,7 +242,7 @@ std::optional> parseURLFlakeRef( std::string fragment; std::swap(fragment, parsedURL.fragment); - auto input = fetchers::Input::fromURL(parsedURL, isFlake); + auto input = fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake); input.parent = baseDir; return std::make_pair( @@ -245,6 +251,7 @@ std::optional> parseURLFlakeRef( } std::pair parseFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, @@ -254,31 +261,34 @@ std::pair parseFlakeRefWithFragment( std::smatch match; - if (auto res = parseFlakeIdRef(url, isFlake)) { + if (auto res = parseFlakeIdRef(fetchSettings, url, isFlake)) { return *res; - } else if (auto res = parseURLFlakeRef(url, baseDir, isFlake)) { + } else if (auto res = parseURLFlakeRef(fetchSettings, url, baseDir, isFlake)) { return *res; } else { - return parsePathFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); + return parsePathFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake); } } std::optional> maybeParseFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir) { try { - return parseFlakeRefWithFragment(url, baseDir); + return parseFlakeRefWithFragment(fetchSettings, url, baseDir); } catch (Error & e) { return {}; } } -FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs) +FlakeRef FlakeRef::fromAttrs( + const fetchers::Settings & fetchSettings, + const fetchers::Attrs & attrs) { auto attrs2(attrs); attrs2.erase("dir"); return FlakeRef( - fetchers::Input::fromAttrs(std::move(attrs2)), + fetchers::Input::fromAttrs(fetchSettings, std::move(attrs2)), fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); } @@ -289,13 +299,16 @@ std::pair FlakeRef::fetchTree(ref store) const } std::tuple parseFlakeRefWithFragmentAndExtendedOutputsSpec( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, bool isFlake) { auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(url); - auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, baseDir, allowMissing, isFlake); + auto [flakeRef, fragment] = parseFlakeRefWithFragment( + fetchSettings, + std::string { prefix }, baseDir, allowMissing, isFlake); return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)}; } diff --git a/src/libflake/flake/flakeref.hh b/src/libflake/flake/flakeref.hh index 04c812ed0..ad9b582c5 100644 --- a/src/libflake/flake/flakeref.hh +++ b/src/libflake/flake/flakeref.hh @@ -61,7 +61,9 @@ struct FlakeRef FlakeRef resolve(ref store) const; - static FlakeRef fromAttrs(const fetchers::Attrs & attrs); + static FlakeRef fromAttrs( + const fetchers::Settings & fetchSettings, + const fetchers::Attrs & attrs); std::pair fetchTree(ref store) const; }; @@ -72,6 +74,7 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ FlakeRef parseFlakeRef( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, @@ -81,12 +84,15 @@ FlakeRef parseFlakeRef( * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::optional maybeParseFlake( - const std::string & url, const std::optional & baseDir = {}); + const fetchers::Settings & fetchSettings, + const std::string & url, + const std::optional & baseDir = {}); /** * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::pair parseFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, @@ -96,12 +102,15 @@ std::pair parseFlakeRefWithFragment( * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::optional> maybeParseFlakeRefWithFragment( - const std::string & url, const std::optional & baseDir = {}); + const fetchers::Settings & fetchSettings, + const std::string & url, + const std::optional & baseDir = {}); /** * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::tuple parseFlakeRefWithFragmentAndExtendedOutputsSpec( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index d252214dd..c94692d6e 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -10,7 +10,8 @@ namespace nix::flake { -FlakeRef getFlakeRef( +static FlakeRef getFlakeRef( + const fetchers::Settings & fetchSettings, const nlohmann::json & json, const char * attr, const char * info) @@ -26,15 +27,17 @@ FlakeRef getFlakeRef( attrs.insert_or_assign(k.first, k.second); } } - return FlakeRef::fromAttrs(attrs); + return FlakeRef::fromAttrs(fetchSettings, attrs); } throw Error("attribute '%s' missing in lock file", attr); } -LockedNode::LockedNode(const nlohmann::json & json) - : lockedRef(getFlakeRef(json, "locked", "info")) // FIXME: remove "info" - , originalRef(getFlakeRef(json, "original", nullptr)) +LockedNode::LockedNode( + const fetchers::Settings & fetchSettings, + const nlohmann::json & json) + : lockedRef(getFlakeRef(fetchSettings, json, "locked", "info")) // FIXME: remove "info" + , originalRef(getFlakeRef(fetchSettings, json, "original", nullptr)) , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) { if (!lockedRef.input.isLocked()) @@ -84,7 +87,9 @@ std::shared_ptr LockFile::findInput(const InputPath & path) return doFind(root, path, visited); } -LockFile::LockFile(std::string_view contents, std::string_view path) +LockFile::LockFile( + const fetchers::Settings & fetchSettings, + std::string_view contents, std::string_view path) { auto json = nlohmann::json::parse(contents); @@ -113,7 +118,7 @@ LockFile::LockFile(std::string_view contents, std::string_view path) auto jsonNode2 = nodes.find(inputKey); if (jsonNode2 == nodes.end()) throw Error("lock file references missing node '%s'", inputKey); - auto input = make_ref(*jsonNode2); + auto input = make_ref(fetchSettings, *jsonNode2); k = nodeMap.insert_or_assign(inputKey, input).first; getInputs(*input, *jsonNode2); } diff --git a/src/libflake/flake/lockfile.hh b/src/libflake/flake/lockfile.hh index 7e62e6d09..e269b6c39 100644 --- a/src/libflake/flake/lockfile.hh +++ b/src/libflake/flake/lockfile.hh @@ -45,7 +45,9 @@ struct LockedNode : Node : lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake) { } - LockedNode(const nlohmann::json & json); + LockedNode( + const fetchers::Settings & fetchSettings, + const nlohmann::json & json); StorePath computeStorePath(Store & store) const; }; @@ -55,7 +57,9 @@ struct LockFile ref root = make_ref(); LockFile() {}; - LockFile(std::string_view contents, std::string_view path); + LockFile( + const fetchers::Settings & fetchSettings, + std::string_view contents, std::string_view path); typedef std::map, std::string> KeyMap; diff --git a/src/libflake/flake/settings.cc b/src/libflake/flake/settings.cc new file mode 100644 index 000000000..6a0294e62 --- /dev/null +++ b/src/libflake/flake/settings.cc @@ -0,0 +1,7 @@ +#include "flake/settings.hh" + +namespace nix::flake { + +Settings::Settings() {} + +} diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake/settings.hh similarity index 86% rename from src/libflake/flake-settings.hh rename to src/libflake/flake/settings.hh index f97c175e8..fee247a7d 100644 --- a/src/libflake/flake-settings.hh +++ b/src/libflake/flake/settings.hh @@ -10,11 +10,11 @@ #include -namespace nix { +namespace nix::flake { -struct FlakeSettings : public Config +struct Settings : public Config { - FlakeSettings(); + Settings(); Setting useRegistries{ this, @@ -47,7 +47,4 @@ struct FlakeSettings : public Config Xp::Flakes}; }; -// TODO: don't use a global variable. -extern FlakeSettings flakeSettings; - } diff --git a/src/libflake/meson.build b/src/libflake/meson.build index d3c3d3079..38d70c678 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -42,21 +42,21 @@ add_project_arguments( subdir('build-utils-meson/diagnostics') sources = files( - 'flake-settings.cc', 'flake/config.cc', 'flake/flake.cc', 'flake/flakeref.cc', - 'flake/url-name.cc', 'flake/lockfile.cc', + 'flake/settings.cc', + 'flake/url-name.cc', ) include_dirs = [include_directories('.')] headers = files( - 'flake-settings.hh', 'flake/flake.hh', 'flake/flakeref.hh', 'flake/lockfile.hh', + 'flake/settings.hh', 'flake/url-name.hh', ) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index d37b16bdc..4d373ef8d 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -286,7 +286,7 @@ static void main_nix_build(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, fetchSettings, evalSettings, store); state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index c72378cd5..5e170c99d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1525,7 +1525,7 @@ static int main_nix_env(int argc, char * * argv) auto store = openStore(); - globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store, evalSettings)); + globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store, fetchSettings, evalSettings)); globals.state->repair = myArgs.repair; globals.instSource.nixExprPath = std::make_shared( diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index a4bfeebbb..c48549511 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -157,7 +157,7 @@ static int main_nix_instantiate(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, fetchSettings, evalSettings, store); state->repair = myArgs.repair; Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 554c36540..7d9aa7711 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -76,7 +76,9 @@ struct CmdBundle : InstallableValueCommand auto val = installable->toValue(*evalState).first; - auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] = parseFlakeRefWithFragmentAndExtendedOutputsSpec(bundler, absPath(".")); + auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] = + parseFlakeRefWithFragmentAndExtendedOutputsSpec( + fetchSettings, bundler, absPath(".")); const flake::LockFlags lockFlags{ .writeLockFile = false }; InstallableFlake bundler{this, evalState, std::move(bundlerFlakeRef), bundlerName, std::move(extendedOutputsSpec), diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b65c7f59d..cfbe58712 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -48,19 +48,19 @@ public: FlakeRef getFlakeRef() { - return parseFlakeRef(flakeUrl, absPath(".")); //FIXME + return parseFlakeRef(fetchSettings, flakeUrl, absPath(".")); //FIXME } LockedFlake lockFlake() { - return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags); + return flake::lockFlake(flakeSettings, *getEvalState(), getFlakeRef(), lockFlags); } std::vector getFlakeRefsForCompletion() override { return { // Like getFlakeRef but with expandTilde calld first - parseFlakeRef(expandTilde(flakeUrl), absPath(".")) + parseFlakeRef(fetchSettings, expandTilde(flakeUrl), absPath(".")) }; } }; @@ -848,7 +848,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand auto evalState = getEvalState(); - auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath(".")); + auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment( + fetchSettings, templateUrl, absPath(".")); auto installable = InstallableFlake(nullptr, evalState, std::move(templateFlakeRef), templateName, ExtendedOutputsSpec::Default(), diff --git a/src/nix/main.cc b/src/nix/main.cc index 775052351..44e69f588 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -19,6 +19,7 @@ #include "users.hh" #include "network-proxy.hh" #include "eval-cache.hh" +#include "flake/flake.hh" #include #include @@ -242,7 +243,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) evalSettings.restrictEval = false; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://"), evalSettings); + EvalState state({}, openStore("dummy://"), fetchSettings, evalSettings); auto vGenerateManpage = state.allocValue(); state.eval(state.parseExprFromString( @@ -362,6 +363,7 @@ void mainWrapped(int argc, char * * argv) initNix(); initGC(); + flake::initLib(flakeSettings); #if __linux__ if (isRootUser()) { @@ -418,7 +420,7 @@ void mainWrapped(int argc, char * * argv) Xp::FetchTree, }; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://"), evalSettings); + EvalState state({}, openStore("dummy://"), fetchSettings, evalSettings); auto builtinsJson = nlohmann::json::object(); for (auto & builtin : *state.baseEnv.values[0]->attrs()) { auto b = nlohmann::json::object(); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index c6e60a53b..db7d9e4ef 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -195,7 +195,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) startProgressBar(); auto store = openStore(); - auto state = std::make_unique(myArgs.lookupPath, store, evalSettings); + auto state = std::make_unique(myArgs.lookupPath, store, fetchSettings, evalSettings); Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c89b8c9bd..bb6424c4f 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -154,8 +154,8 @@ struct ProfileManifest } if (e.value(sUrl, "") != "") { element.source = ProfileElementSource { - parseFlakeRef(e[sOriginalUrl]), - parseFlakeRef(e[sUrl]), + parseFlakeRef(fetchSettings, e[sOriginalUrl]), + parseFlakeRef(fetchSettings, e[sUrl]), e["attrPath"], e["outputs"].get() }; diff --git a/src/nix/registry.cc b/src/nix/registry.cc index 812429240..ee4516230 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -33,9 +33,9 @@ public: { if (registry) return registry; if (registry_path.empty()) { - registry = fetchers::getUserRegistry(); + registry = fetchers::getUserRegistry(fetchSettings); } else { - registry = fetchers::getCustomRegistry(registry_path); + registry = fetchers::getCustomRegistry(fetchSettings, registry_path); } return registry; } @@ -68,7 +68,7 @@ struct CmdRegistryList : StoreCommand { using namespace fetchers; - auto registries = getRegistries(store); + auto registries = getRegistries(fetchSettings, store); for (auto & registry : registries) { for (auto & entry : registry->entries) { @@ -109,8 +109,8 @@ struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand void run() override { - auto fromRef = parseFlakeRef(fromUrl); - auto toRef = parseFlakeRef(toUrl); + auto fromRef = parseFlakeRef(fetchSettings, fromUrl); + auto toRef = parseFlakeRef(fetchSettings, toUrl); auto registry = getRegistry(); fetchers::Attrs extraAttrs; if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; @@ -144,7 +144,7 @@ struct CmdRegistryRemove : RegistryCommand, Command void run() override { auto registry = getRegistry(); - registry->remove(parseFlakeRef(url).input); + registry->remove(parseFlakeRef(fetchSettings, url).input); registry->write(getRegistryPath()); } }; @@ -185,8 +185,8 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand { if (locked.empty()) locked = url; auto registry = getRegistry(); - auto ref = parseFlakeRef(url); - auto lockedRef = parseFlakeRef(locked); + auto ref = parseFlakeRef(fetchSettings, url); + auto lockedRef = parseFlakeRef(fetchSettings, locked); registry->remove(ref.input); auto resolved = lockedRef.resolve(store).input.getAccessor(store).second; if (!resolved.isLocked()) diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 29297253b..7b3357700 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -147,7 +147,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto req = FileTransferRequest((std::string&) settings.upgradeNixStorePathUrl); auto res = getFileTransfer()->download(req); - auto state = std::make_unique(LookupPath{}, store, evalSettings); + auto state = std::make_unique(LookupPath{}, store, fetchSettings, evalSettings); auto v = state->allocValue(); state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v); Bindings & bindings(*state->allocBindings(0)); diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index eacbf0d5c..c85545e6c 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -4,8 +4,10 @@ #include #include +#include "fetch-settings.hh" #include "value.hh" #include "nixexpr.hh" +#include "nixexpr.hh" #include "eval.hh" #include "eval-inline.hh" #include "eval-settings.hh" @@ -24,7 +26,7 @@ namespace nix { protected: LibExprTest() : LibStoreTest() - , state({}, store, evalSettings, nullptr) + , state({}, store, fetchSettings, evalSettings, nullptr) { evalSettings.nixPath = {}; } @@ -43,6 +45,7 @@ namespace nix { } bool readOnlyMode = true; + fetchers::Settings fetchSettings{}; EvalSettings evalSettings{readOnlyMode}; EvalState state; }; diff --git a/tests/unit/libflake/flakeref.cc b/tests/unit/libflake/flakeref.cc index 2b7809b93..d704a26d3 100644 --- a/tests/unit/libflake/flakeref.cc +++ b/tests/unit/libflake/flakeref.cc @@ -1,5 +1,6 @@ #include +#include "fetch-settings.hh" #include "flake/flakeref.hh" namespace nix { @@ -11,8 +12,9 @@ namespace nix { * --------------------------------------------------------------------------*/ TEST(to_string, doesntReencodeUrl) { + fetchers::Settings fetchSettings; auto s = "http://localhost:8181/test/+3d.tar.gz"; - auto flakeref = parseFlakeRef(s); + auto flakeref = parseFlakeRef(fetchSettings, s); auto parsed = flakeref.to_string(); auto expected = "http://localhost:8181/test/%2B3d.tar.gz"; From 8df041cbc6686eebda7efced8dab256838cff900 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 15:37:54 +0200 Subject: [PATCH 538/910] Solve unused header warnings reported by clangd --- src/libcmd/repl.cc | 4 +--- src/libexpr-c/nix_api_expr.cc | 4 +--- src/libexpr/attr-set.hh | 1 - src/libexpr/eval-error.hh | 2 -- src/libexpr/eval-settings.hh | 1 + src/libexpr/eval.cc | 4 +--- src/libexpr/eval.hh | 6 +++--- src/libexpr/nixexpr.cc | 1 - src/libexpr/nixexpr.hh | 3 --- src/libexpr/pos-table.hh | 4 +--- src/libexpr/primops.cc | 1 - src/libexpr/value.hh | 1 - src/libfetchers/fetchers.hh | 2 ++ src/libfetchers/tarball.cc | 2 -- src/libfetchers/tarball.hh | 9 +++++---- src/libflake/flake/flakeref.hh | 6 ++---- src/libflake/flake/lockfile.cc | 3 ++- src/libmain/shared.cc | 2 +- src/libmain/shared.hh | 4 ---- src/libstore/derivations.hh | 1 - src/libstore/derived-path.hh | 1 + src/libstore/filetransfer.hh | 10 ++++++---- src/libstore/gc-store.hh | 3 ++- src/libstore/machines.hh | 2 +- src/libstore/misc.cc | 2 ++ src/libstore/nar-accessor.cc | 1 - src/libstore/serve-protocol-impl.hh | 1 - src/libstore/store-api.hh | 4 ---- src/libstore/unix/user-lock.cc | 7 ++++--- src/libstore/unix/user-lock.hh | 6 ++---- src/libutil/args.hh | 2 +- src/libutil/error.hh | 4 ---- src/libutil/file-content-address.hh | 2 -- src/libutil/fs-sink.hh | 1 - src/libutil/logging.hh | 1 - src/libutil/ref.hh | 2 -- src/libutil/source-accessor.hh | 1 + src/libutil/terminal.hh | 3 ++- src/libutil/types.hh | 2 -- src/nix-store/nix-store.cc | 6 +++--- src/nix/config-check.cc | 1 + src/nix/env.cc | 4 +++- src/nix/flake.cc | 1 - src/nix/main.cc | 3 +-- src/nix/verify.cc | 4 ++-- tests/unit/libexpr-support/tests/libexpr.hh | 2 +- 46 files changed, 53 insertions(+), 84 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index ce1c5af69..dc5a311e7 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include "repl-interacter.hh" #include "repl.hh" @@ -10,8 +9,6 @@ #include "shared.hh" #include "config-global.hh" #include "eval.hh" -#include "eval-cache.hh" -#include "eval-inline.hh" #include "eval-settings.hh" #include "attr-path.hh" #include "signals.hh" @@ -29,6 +26,7 @@ #include "markdown.hh" #include "local-fs-store.hh" #include "print.hh" +#include "ref.hh" #if HAVE_BOEHMGC #define GC_INCLUDE_NEW diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 13e0f5f3a..2494c653c 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -1,12 +1,10 @@ #include -#include #include #include -#include "config.hh" #include "eval.hh" +#include "eval-gc.hh" #include "globals.hh" -#include "util.hh" #include "eval-settings.hh" #include "nix_api_expr.h" diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index ba798196d..9d10783d9 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -5,7 +5,6 @@ #include "symbol-table.hh" #include -#include namespace nix { diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index fe48e054b..2f6046473 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -1,7 +1,5 @@ #pragma once -#include - #include "error.hh" #include "pos-idx.hh" diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 191dde21a..b915ba530 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -2,6 +2,7 @@ ///@file #include "config.hh" +#include "ref.hh" namespace nix { diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index efca9dd2f..1da934693 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1,6 +1,6 @@ #include "eval.hh" +#include "eval-gc.hh" #include "eval-settings.hh" -#include "hash.hh" #include "primops.hh" #include "print-options.hh" #include "exit.hh" @@ -16,7 +16,6 @@ #include "print.hh" #include "filtering-source-accessor.hh" #include "memory-source-accessor.hh" -#include "signals.hh" #include "gc-small-vector.hh" #include "url.hh" #include "fetch-to-store.hh" @@ -24,7 +23,6 @@ #include "parser-tab.hh" #include -#include #include #include #include diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index e45358055..e5818a501 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -3,21 +3,21 @@ #include "attr-set.hh" #include "eval-error.hh" -#include "eval-gc.hh" #include "types.hh" #include "value.hh" #include "nixexpr.hh" #include "symbol-table.hh" #include "config.hh" #include "experimental-features.hh" +#include "position.hh" +#include "pos-table.hh" #include "source-accessor.hh" #include "search-path.hh" #include "repl-exit-status.hh" +#include "ref.hh" #include #include -#include -#include #include namespace nix { diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 44198a252..816389165 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -1,5 +1,4 @@ #include "nixexpr.hh" -#include "derivations.hh" #include "eval.hh" #include "symbol-table.hh" #include "util.hh" diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index e37e3bdd1..5152e3119 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -6,11 +6,8 @@ #include "value.hh" #include "symbol-table.hh" -#include "error.hh" -#include "position.hh" #include "eval-error.hh" #include "pos-idx.hh" -#include "pos-table.hh" namespace nix { diff --git a/src/libexpr/pos-table.hh b/src/libexpr/pos-table.hh index 8a0a3ba86..ba2b91cf3 100644 --- a/src/libexpr/pos-table.hh +++ b/src/libexpr/pos-table.hh @@ -1,10 +1,8 @@ #pragma once -#include -#include +#include #include -#include "chunked-vector.hh" #include "pos-idx.hh" #include "position.hh" #include "sync.hh" diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index bcde0bb12..127823d49 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1,4 +1,3 @@ -#include "archive.hh" #include "derivations.hh" #include "downstream-placeholder.hh" #include "eval-inline.hh" diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 208cab21d..1f7b75f6e 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -2,7 +2,6 @@ ///@file #include -#include #include #include "symbol-table.hh" diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 551be9a1f..6126b263c 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -11,6 +11,8 @@ #include #include +#include "ref.hh" + namespace nix { class Store; class StorePath; struct SourceAccessor; } namespace nix::fetchers { diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index aa8ff652f..4425575bd 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -2,12 +2,10 @@ #include "fetchers.hh" #include "cache.hh" #include "filetransfer.hh" -#include "globals.hh" #include "store-api.hh" #include "archive.hh" #include "tarfile.hh" #include "types.hh" -#include "split.hh" #include "store-path-accessor.hh" #include "store-api.hh" #include "git-utils.hh" diff --git a/src/libfetchers/tarball.hh b/src/libfetchers/tarball.hh index ba0dfd623..d9bdd123d 100644 --- a/src/libfetchers/tarball.hh +++ b/src/libfetchers/tarball.hh @@ -1,11 +1,12 @@ #pragma once -#include "types.hh" -#include "path.hh" -#include "hash.hh" - #include +#include "hash.hh" +#include "path.hh" +#include "ref.hh" +#include "types.hh" + namespace nix { class Store; struct SourceAccessor; diff --git a/src/libflake/flake/flakeref.hh b/src/libflake/flake/flakeref.hh index 04c812ed0..54794ccab 100644 --- a/src/libflake/flake/flakeref.hh +++ b/src/libflake/flake/flakeref.hh @@ -1,14 +1,12 @@ #pragma once ///@file +#include + #include "types.hh" -#include "hash.hh" #include "fetchers.hh" #include "outputs-spec.hh" -#include -#include - namespace nix { class Store; diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index d252214dd..83ca2c678 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -1,6 +1,7 @@ +#include + #include "lockfile.hh" #include "store-api.hh" -#include "url-parts.hh" #include #include diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index fc55fe3f1..fee4d0c1e 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -8,7 +8,6 @@ #include "signals.hh" #include -#include #include #include @@ -23,6 +22,7 @@ #include +#include "exit.hh" namespace nix { diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index aa44e1321..712b404d3 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -8,13 +8,9 @@ #include "common-args.hh" #include "path.hh" #include "derived-path.hh" -#include "exit.hh" #include -#include - - namespace nix { int handleExceptions(const std::string & programName, std::function fun); diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 522523e45..9b82e5d12 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -14,7 +14,6 @@ #include #include - namespace nix { struct StoreDirConfig; diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index b238f844e..af66ed4f0 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -5,6 +5,7 @@ #include "outputs-spec.hh" #include "comparator.hh" #include "config.hh" +#include "ref.hh" #include diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh index 1c271cbec..1f5b4ab93 100644 --- a/src/libstore/filetransfer.hh +++ b/src/libstore/filetransfer.hh @@ -1,13 +1,15 @@ #pragma once ///@file -#include "types.hh" -#include "hash.hh" -#include "config.hh" - #include #include +#include "logging.hh" +#include "types.hh" +#include "ref.hh" +#include "config.hh" +#include "serialise.hh" + namespace nix { struct FileTransferSettings : Config diff --git a/src/libstore/gc-store.hh b/src/libstore/gc-store.hh index ab1059fb1..020f770b0 100644 --- a/src/libstore/gc-store.hh +++ b/src/libstore/gc-store.hh @@ -1,8 +1,9 @@ #pragma once ///@file -#include "store-api.hh" +#include +#include "store-api.hh" namespace nix { diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh index 2a55c4456..983652d5f 100644 --- a/src/libstore/machines.hh +++ b/src/libstore/machines.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "types.hh" +#include "ref.hh" #include "store-reference.hh" namespace nix { diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index cc3f4884f..dd0efbe19 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,3 +1,5 @@ +#include + #include "derivations.hh" #include "parsed-derivations.hh" #include "globals.hh" diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index b1079b027..6376efbf4 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -3,7 +3,6 @@ #include #include -#include #include diff --git a/src/libstore/serve-protocol-impl.hh b/src/libstore/serve-protocol-impl.hh index 67bc5dc6e..6f3b177ac 100644 --- a/src/libstore/serve-protocol-impl.hh +++ b/src/libstore/serve-protocol-impl.hh @@ -10,7 +10,6 @@ #include "serve-protocol.hh" #include "length-prefixed-protocol-helper.hh" -#include "store-api.hh" namespace nix { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index a5effb4c1..749d7ea09 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -18,14 +18,10 @@ #include #include -#include #include -#include -#include #include #include #include -#include namespace nix { diff --git a/src/libstore/unix/user-lock.cc b/src/libstore/unix/user-lock.cc index 8057aa13e..29f4b2cb3 100644 --- a/src/libstore/unix/user-lock.cc +++ b/src/libstore/unix/user-lock.cc @@ -1,12 +1,13 @@ +#include +#include +#include + #include "user-lock.hh" #include "file-system.hh" #include "globals.hh" #include "pathlocks.hh" #include "users.hh" -#include -#include - namespace nix { #if __linux__ diff --git a/src/libstore/unix/user-lock.hh b/src/libstore/unix/user-lock.hh index 1c268e1fb..a7caf8518 100644 --- a/src/libstore/unix/user-lock.hh +++ b/src/libstore/unix/user-lock.hh @@ -1,10 +1,8 @@ #pragma once ///@file -#include "types.hh" - -#include - +#include +#include #include namespace nix { diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 7759b74a9..f6e74e67e 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include #include #include #include @@ -11,6 +10,7 @@ #include "types.hh" #include "experimental-features.hh" +#include "ref.hh" namespace nix { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 1fe98077e..8cc8fb303 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -16,16 +16,12 @@ */ #include "suggestions.hh" -#include "ref.hh" -#include "types.hh" #include "fmt.hh" #include #include #include -#include #include -#include #include #include diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index 4c7218f19..ec42d3d34 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -2,8 +2,6 @@ ///@file #include "source-accessor.hh" -#include "fs-sink.hh" -#include "util.hh" namespace nix { diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index 774c0d942..de165fb7d 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "types.hh" #include "serialise.hh" #include "source-accessor.hh" #include "file-system.hh" diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 9e81132e3..250f92099 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "types.hh" #include "error.hh" #include "config.hh" diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 03aa64273..8318251bd 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -1,9 +1,7 @@ #pragma once ///@file -#include #include -#include #include namespace nix { diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index cc8db01f5..32ab3685d 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -4,6 +4,7 @@ #include "canon-path.hh" #include "hash.hh" +#include "ref.hh" namespace nix { diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 902e75945..7ff05a487 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -1,7 +1,8 @@ #pragma once ///@file -#include "types.hh" +#include +#include namespace nix { /** diff --git a/src/libutil/types.hh b/src/libutil/types.hh index c86f52175..325e3ea73 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -1,12 +1,10 @@ #pragma once ///@file -#include "ref.hh" #include #include #include -#include #include #include #include diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index d0840a02e..f073074e8 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -2,13 +2,11 @@ #include "derivations.hh" #include "dotgraph.hh" #include "globals.hh" -#include "build-result.hh" #include "store-cast.hh" #include "local-fs-store.hh" #include "log-store.hh" #include "serve-protocol.hh" #include "serve-protocol-connection.hh" -#include "serve-protocol-impl.hh" #include "shared.hh" #include "graphml.hh" #include "legacy.hh" @@ -23,12 +21,14 @@ #include #include -#include #include #include #include +#include "build-result.hh" +#include "exit.hh" +#include "serve-protocol-impl.hh" namespace nix_store { diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index 9575bf338..09d140733 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -1,6 +1,7 @@ #include #include "command.hh" +#include "exit.hh" #include "logging.hh" #include "serve-protocol.hh" #include "shared.hh" diff --git a/src/nix/env.cc b/src/nix/env.cc index 021c47cbb..bc9cd91ad 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -1,6 +1,8 @@ +#include +#include + #include "command.hh" #include "run.hh" -#include using namespace nix; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b65c7f59d..896425c5a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -19,7 +19,6 @@ #include "users.hh" #include -#include #include using namespace nix; diff --git a/src/nix/main.cc b/src/nix/main.cc index 775052351..ec3e2a406 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -1,9 +1,8 @@ -#include - #include "args/root.hh" #include "current-process.hh" #include "command.hh" #include "common-args.hh" +#include "eval-gc.hh" #include "eval.hh" #include "eval-settings.hh" #include "globals.hh" diff --git a/src/nix/verify.cc b/src/nix/verify.cc index 2a0cbd19f..124a05bed 100644 --- a/src/nix/verify.cc +++ b/src/nix/verify.cc @@ -1,14 +1,14 @@ #include "command.hh" #include "shared.hh" #include "store-api.hh" -#include "sync.hh" #include "thread-pool.hh" -#include "references.hh" #include "signals.hh" #include "keys.hh" #include +#include "exit.hh" + using namespace nix; struct CmdVerify : StorePathsCommand diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index eacbf0d5c..bfb425bff 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -7,9 +7,9 @@ #include "value.hh" #include "nixexpr.hh" #include "eval.hh" +#include "eval-gc.hh" #include "eval-inline.hh" #include "eval-settings.hh" -#include "store-api.hh" #include "tests/libstore.hh" From 27eaeebc4164341afdbbede8bf33ec71ce866a5b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 15:38:17 +0200 Subject: [PATCH 539/910] nar-accessor.cc: Silence unused variable warning --- src/libstore/nar-accessor.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index 6376efbf4..9a541bb77 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -73,7 +73,10 @@ struct NarAccessor : public SourceAccessor NarMember & createMember(const CanonPath & path, NarMember member) { size_t level = 0; - for (auto _ : path) ++level; + for (auto _ : path) { + (void)_; + ++level; + } while (parents.size() > level) parents.pop(); From 51a12b38bda241623aa12a400c066f0b3a95606a Mon Sep 17 00:00:00 2001 From: Andrew Marshall Date: Fri, 12 Jul 2024 09:17:31 -0400 Subject: [PATCH 540/910] Fix stackoverflow during doc generation On some systems, previous usage of `match` may cause a stackoverflow (presumably due to the large size of the match result). Avoid this by (ab)using `replaceStrings` to test for containment without using regexes, thereby avoiding the issue. The causal configuration seems to be the stack size hard limit, which e.g. Amazon Linux sets, whereas most Linux distros leave unlimited. Match the fn name to similar fn in nixpkgs.lib, but different implementation that does not use `match`. This impl gives perhaps unexpected results when the needle is `""`, but the scope of this is narrow and that case is a bit odd anyway. This makes for some duplication-of-work as we do a different `replaceStrings` if this one is true, but this only runs during doc generation at build time so has no runtime impact. See https://github.com/NixOS/nix/issues/11085 for details. --- doc/manual/generate-manpage.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index ba5667a43..90eaa1a73 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -116,9 +116,12 @@ let storeInfo = commandInfo.stores; inherit inlineHTML; }; + hasInfix = infix: content: + builtins.stringLength content != builtins.stringLength (replaceStrings [ infix ] [ "" ] content); in optionalString (details ? doc) ( - if match ".*@store-types@.*" details.doc != null + # An alternate implementation with builtins.match stack overflowed on some systems. + if hasInfix "@store-types@" details.doc then help-stores else details.doc ); From 11a6db5993426940f9746623005114e735658c9d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 17:37:27 +0200 Subject: [PATCH 541/910] Remove unused operator<=>'s that darwin can't generate It was complaining *a lot*, with dozens of MB of logs. --- src/libstore/content-address.hh | 8 -------- src/libstore/store-reference.hh | 1 - 2 files changed, 9 deletions(-) diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index 6cc3b7cd9..270e04789 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -217,8 +217,6 @@ struct StoreReferences * iff self is true. */ size_t size() const; - - auto operator <=>(const StoreReferences &) const = default; }; // This matches the additional info that we need for makeTextPath @@ -234,8 +232,6 @@ struct TextInfo * disallowed */ StorePathSet references; - - auto operator <=>(const TextInfo &) const = default; }; struct FixedOutputInfo @@ -254,8 +250,6 @@ struct FixedOutputInfo * References to other store objects or this one. */ StoreReferences references; - - auto operator <=>(const FixedOutputInfo &) const = default; }; /** @@ -272,8 +266,6 @@ struct ContentAddressWithReferences Raw raw; - auto operator <=>(const ContentAddressWithReferences &) const = default; - MAKE_WRAPPER_CONSTRUCTOR(ContentAddressWithReferences); /** diff --git a/src/libstore/store-reference.hh b/src/libstore/store-reference.hh index e99335c0d..459cea9c2 100644 --- a/src/libstore/store-reference.hh +++ b/src/libstore/store-reference.hh @@ -71,7 +71,6 @@ struct StoreReference Params params; bool operator==(const StoreReference & rhs) const = default; - auto operator<=>(const StoreReference & rhs) const = default; /** * Render the whole store reference as a URI, including parameters. From cdc23b67a66ff3daf495b8d1e5b199095e01bac9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 Jun 2024 19:12:36 +0200 Subject: [PATCH 542/910] Provide std::hash --- src/libexpr/eval.cc | 6 +++--- src/libexpr/eval.hh | 10 +++++----- src/libutil/source-path.hh | 11 +++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 72ae159cb..584828a09 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1011,7 +1011,7 @@ void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial) if (!e) e = parseExprFromFile(resolvedPath); - fileParseCache[resolvedPath] = e; + fileParseCache.emplace(resolvedPath, e); try { auto dts = debugRepl @@ -1034,8 +1034,8 @@ void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial) throw; } - fileEvalCache[resolvedPath] = v; - if (path != resolvedPath) fileEvalCache[path] = v; + fileEvalCache.emplace(resolvedPath, v); + if (path != resolvedPath) fileEvalCache.emplace(path, v); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7dbf61c5d..d368bc049 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -307,15 +307,15 @@ private: /* Cache for calls to addToStore(); maps source paths to the store paths. */ - Sync> srcToStore; + Sync> srcToStore; /** * A cache from path names to parse trees. */ #if HAVE_BOEHMGC - typedef std::map, traceable_allocator>> FileParseCache; + typedef std::unordered_map, std::equal_to, traceable_allocator>> FileParseCache; #else - typedef std::map FileParseCache; + typedef std::unordered_map FileParseCache; #endif FileParseCache fileParseCache; @@ -323,9 +323,9 @@ private: * A cache from path names to values. */ #if HAVE_BOEHMGC - typedef std::map, traceable_allocator>> FileEvalCache; + typedef std::unordered_map, std::equal_to, traceable_allocator>> FileEvalCache; #else - typedef std::map FileEvalCache; + typedef std::unordered_map FileEvalCache; #endif FileEvalCache fileEvalCache; diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 83ec6295d..941744127 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -115,8 +115,19 @@ struct SourcePath { return {accessor, accessor->resolveSymlinks(path, mode)}; } + + friend class std::hash; }; std::ostream & operator << (std::ostream & str, const SourcePath & path); } + +template<> +struct std::hash +{ + std::size_t operator()(const nix::SourcePath & s) const noexcept + { + return std::hash{}(s.path); + } +}; From e764ed31f61308690a9a8a5dc6f20e570b7de262 Mon Sep 17 00:00:00 2001 From: Lexi Mattick Date: Fri, 12 Jul 2024 09:45:35 -0700 Subject: [PATCH 543/910] Eval cache: fix cache regressions - Fix eval cache not being persisted in `nix develop` (since #10570) - Don't attempt to commit cache transaction if there is no active transaction, which will spew errors in edge cases - Drive-by: trivial typo fix --- src/libcmd/installable-value.hh | 2 +- src/libexpr/eval-cache.cc | 2 +- src/nix/develop.cc | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libcmd/installable-value.hh b/src/libcmd/installable-value.hh index f300d392b..798cb5e1a 100644 --- a/src/libcmd/installable-value.hh +++ b/src/libcmd/installable-value.hh @@ -66,7 +66,7 @@ struct ExtraPathInfoValue : ExtraPathInfo }; /** - * An Installable which corresponds a Nix langauge value, in addition to + * An Installable which corresponds a Nix language value, in addition to * a collection of \ref DerivedPath "derived paths". */ struct InstallableValue : Installable diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 2630c34d5..5085fedc2 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -95,7 +95,7 @@ struct AttrDb { try { auto state(_state->lock()); - if (!failed) + if (!failed && state->txn->active) state->txn->commit(); state->txn.reset(); } catch (...) { diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 6bd3dc9ef..b89de5d5c 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -697,6 +697,10 @@ struct CmdDevelop : Common, MixEnvironment } } + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + getEvalState()->evalCaches.clear(); + runProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem()); #endif } From 6c4470ec2a495fc201447aa8fca63c71f9849fe6 Mon Sep 17 00:00:00 2001 From: Lexi Mattick Date: Fri, 12 Jul 2024 11:54:12 -0700 Subject: [PATCH 544/910] Clean up cache for all commands --- src/nix/develop.cc | 2 +- src/nix/env.cc | 7 ++++++- src/nix/fmt.cc | 7 ++++++- src/nix/run.cc | 8 ++++++-- src/nix/run.hh | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/nix/develop.cc b/src/nix/develop.cc index b89de5d5c..84237be85 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -701,7 +701,7 @@ struct CmdDevelop : Common, MixEnvironment // we are about to exec out of this process without running C++ destructors. getEvalState()->evalCaches.clear(); - runProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem()); + execProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem()); #endif } }; diff --git a/src/nix/env.cc b/src/nix/env.cc index 021c47cbb..84a87eaee 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -1,4 +1,5 @@ #include "command.hh" +#include "eval.hh" #include "run.hh" #include @@ -99,7 +100,11 @@ struct CmdShell : InstallablesCommand, MixEnvironment for (auto & arg : command) args.push_back(arg); - runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + getEvalState()->evalCaches.clear(); + + execProgramInStore(store, UseLookupPath::Use, *command.begin(), args); } }; diff --git a/src/nix/fmt.cc b/src/nix/fmt.cc index 4b0fbb89d..d65834495 100644 --- a/src/nix/fmt.cc +++ b/src/nix/fmt.cc @@ -1,5 +1,6 @@ #include "command.hh" #include "installable-value.hh" +#include "eval.hh" #include "run.hh" using namespace nix; @@ -49,7 +50,11 @@ struct CmdFmt : SourceExprCommand { } } - runProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs); + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + evalState->evalCaches.clear(); + + execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs); }; }; diff --git a/src/nix/run.cc b/src/nix/run.cc index c1aae1685..ec6a4d1e8 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -25,7 +25,7 @@ std::string chrootHelperName = "__run_in_chroot"; namespace nix { -void runProgramInStore(ref store, +void execProgramInStore(ref store, UseLookupPath useLookupPath, const std::string & program, const Strings & args, @@ -128,7 +128,11 @@ struct CmdRun : InstallableValueCommand Strings allArgs{app.program}; for (auto & i : args) allArgs.push_back(i); - runProgramInStore(store, UseLookupPath::DontUse, app.program, allArgs); + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + state->evalCaches.clear(); + + execProgramInStore(store, UseLookupPath::DontUse, app.program, allArgs); } }; diff --git a/src/nix/run.hh b/src/nix/run.hh index 2fe6ed86a..51517fdc9 100644 --- a/src/nix/run.hh +++ b/src/nix/run.hh @@ -10,7 +10,7 @@ enum struct UseLookupPath { DontUse }; -void runProgramInStore(ref store, +void execProgramInStore(ref store, UseLookupPath useLookupPath, const std::string & program, const Strings & args, From bc83b9dc1fbf52ca7aabee275264a8fb850bbb9b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 18:46:38 -0400 Subject: [PATCH 545/910] Remove `comparator.hh` and switch to `<=>` in a bunch of places Known behavior changes: - `MemorySourceAccessor`'s comparison operators no longer forget to compare the `SourceAccessor` base class. Progress on #10832 What remains for that issue is hopefully much easier! --- src/libcmd/built-path.cc | 39 ++++++++---------- src/libcmd/built-path.hh | 16 ++++++-- src/libexpr-c/nix_api_external.cc | 2 +- src/libexpr/attr-set.hh | 4 +- src/libexpr/eval.cc | 2 +- src/libexpr/pos-idx.hh | 9 +---- src/libexpr/symbol-table.hh | 3 +- src/libexpr/value.hh | 2 +- src/libfetchers/fetchers.cc | 2 +- src/libfetchers/fetchers.hh | 2 +- src/libflake/flake/flakeref.cc | 5 --- src/libflake/flake/flakeref.hh | 2 +- src/libflake/flake/lockfile.cc | 5 --- src/libflake/flake/lockfile.hh | 3 -- src/libstore/build-result.cc | 14 +------ src/libstore/build-result.hh | 4 +- src/libstore/content-address.hh | 18 +++++++++ src/libstore/derivations.hh | 48 +++++++++++----------- src/libstore/derived-path-map.cc | 19 ++++----- src/libstore/derived-path-map.hh | 34 +++++++++++----- src/libstore/derived-path.cc | 43 +++++++++++--------- src/libstore/derived-path.hh | 18 +++++++-- src/libstore/nar-info.cc | 9 ----- src/libstore/nar-info.hh | 4 +- src/libstore/outputs-spec.hh | 12 ++++-- src/libstore/path-info.cc | 8 +--- src/libstore/path-info.hh | 44 +++++++++++++++++++-- src/libutil/args.cc | 3 +- src/libutil/args.hh | 2 +- src/libutil/canon-path.hh | 7 ++-- src/libutil/comparator.hh | 57 +++++++-------------------- src/libutil/error.cc | 17 +++----- src/libutil/error.hh | 5 +-- src/libutil/experimental-features.hh | 1 - src/libutil/git.hh | 3 +- src/libutil/hash.cc | 4 +- src/libutil/hash.hh | 4 +- src/libutil/memory-source-accessor.hh | 34 +++++++++++++--- src/libutil/position.cc | 6 --- src/libutil/position.hh | 19 ++++----- src/libutil/ref.hh | 4 +- src/libutil/source-accessor.hh | 4 +- src/libutil/source-path.cc | 11 ++---- src/libutil/source-path.hh | 5 +-- src/libutil/suggestions.hh | 4 +- src/libutil/url.cc | 2 +- src/libutil/url.hh | 2 +- src/nix/profile.cc | 4 +- tests/unit/libutil/git.cc | 2 +- 49 files changed, 300 insertions(+), 271 deletions(-) diff --git a/src/libcmd/built-path.cc b/src/libcmd/built-path.cc index c5eb93c5d..905e70f32 100644 --- a/src/libcmd/built-path.cc +++ b/src/libcmd/built-path.cc @@ -1,6 +1,7 @@ #include "built-path.hh" #include "derivations.hh" #include "store-api.hh" +#include "comparator.hh" #include @@ -8,30 +9,24 @@ namespace nix { -#define CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, COMPARATOR) \ - bool MY_TYPE ::operator COMPARATOR (const MY_TYPE & other) const \ - { \ - const MY_TYPE* me = this; \ - auto fields1 = std::tie(*me->drvPath, me->FIELD); \ - me = &other; \ - auto fields2 = std::tie(*me->drvPath, me->FIELD); \ - return fields1 COMPARATOR fields2; \ - } -#define CMP(CHILD_TYPE, MY_TYPE, FIELD) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, ==) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, !=) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, <) +// Custom implementation to avoid `ref` ptr equality +GENERATE_CMP_EXT( + , + std::strong_ordering, + SingleBuiltPathBuilt, + *me->drvPath, + me->output); -#define FIELD_TYPE std::pair -CMP(SingleBuiltPath, SingleBuiltPathBuilt, output) -#undef FIELD_TYPE +// Custom implementation to avoid `ref` ptr equality -#define FIELD_TYPE std::map -CMP(SingleBuiltPath, BuiltPathBuilt, outputs) -#undef FIELD_TYPE - -#undef CMP -#undef CMP_ONE +// TODO no `GENERATE_CMP_EXT` because no `std::set::operator<=>` on +// Darwin, per header. +GENERATE_EQUAL( + , + BuiltPathBuilt ::, + BuiltPathBuilt, + *me->drvPath, + me->outputs); StorePath SingleBuiltPath::outPath() const { diff --git a/src/libcmd/built-path.hh b/src/libcmd/built-path.hh index 99917e0ee..dc78d3e59 100644 --- a/src/libcmd/built-path.hh +++ b/src/libcmd/built-path.hh @@ -18,7 +18,8 @@ struct SingleBuiltPathBuilt { static SingleBuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view); nlohmann::json toJSON(const StoreDirConfig & store) const; - DECLARE_CMP(SingleBuiltPathBuilt); + bool operator ==(const SingleBuiltPathBuilt &) const noexcept; + std::strong_ordering operator <=>(const SingleBuiltPathBuilt &) const noexcept; }; using _SingleBuiltPathRaw = std::variant< @@ -33,6 +34,9 @@ struct SingleBuiltPath : _SingleBuiltPathRaw { using Opaque = DerivedPathOpaque; using Built = SingleBuiltPathBuilt; + bool operator == (const SingleBuiltPath &) const = default; + auto operator <=> (const SingleBuiltPath &) const = default; + inline const Raw & raw() const { return static_cast(*this); } @@ -59,11 +63,13 @@ struct BuiltPathBuilt { ref drvPath; std::map outputs; + bool operator == (const BuiltPathBuilt &) const noexcept; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //std::strong_ordering operator <=> (const BuiltPathBuilt &) const noexcept; + std::string to_string(const StoreDirConfig & store) const; static BuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view); nlohmann::json toJSON(const StoreDirConfig & store) const; - - DECLARE_CMP(BuiltPathBuilt); }; using _BuiltPathRaw = std::variant< @@ -82,6 +88,10 @@ struct BuiltPath : _BuiltPathRaw { using Opaque = DerivedPathOpaque; using Built = BuiltPathBuilt; + bool operator == (const BuiltPath &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=> (const BuiltPath &) const = default; + inline const Raw & raw() const { return static_cast(*this); } diff --git a/src/libexpr-c/nix_api_external.cc b/src/libexpr-c/nix_api_external.cc index 3c3dd6ca9..3565092a4 100644 --- a/src/libexpr-c/nix_api_external.cc +++ b/src/libexpr-c/nix_api_external.cc @@ -115,7 +115,7 @@ public: /** * Compare to another value of the same type. */ - virtual bool operator==(const ExternalValueBase & b) const override + virtual bool operator==(const ExternalValueBase & b) const noexcept override { if (!desc.equal) { return false; diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 9d10783d9..4df9a1acd 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -27,9 +27,9 @@ struct Attr Attr(Symbol name, Value * value, PosIdx pos = noPos) : name(name), pos(pos), value(value) { }; Attr() { }; - bool operator < (const Attr & a) const + auto operator <=> (const Attr & a) const { - return name < a.name; + return name <=> a.name; } }; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 72ae159cb..2ede55de7 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2842,7 +2842,7 @@ std::string ExternalValueBase::coerceToString(EvalState & state, const PosIdx & } -bool ExternalValueBase::operator==(const ExternalValueBase & b) const +bool ExternalValueBase::operator==(const ExternalValueBase & b) const noexcept { return false; } diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index e94fd85c6..e13491560 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -28,20 +28,15 @@ public: return id > 0; } - bool operator<(const PosIdx other) const + auto operator<=>(const PosIdx other) const { - return id < other.id; + return id <=> other.id; } bool operator==(const PosIdx other) const { return id == other.id; } - - bool operator!=(const PosIdx other) const - { - return id != other.id; - } }; inline PosIdx noPos = {}; diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index 5c2821492..b85725e12 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -62,9 +62,8 @@ public: explicit operator bool() const { return id > 0; } - bool operator<(const Symbol other) const { return id < other.id; } + auto operator<=>(const Symbol other) const { return id <=> other.id; } bool operator==(const Symbol other) const { return id == other.id; } - bool operator!=(const Symbol other) const { return id != other.id; } }; /** diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 1f7b75f6e..1f4d72d39 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -111,7 +111,7 @@ class ExternalValueBase * Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. */ - virtual bool operator ==(const ExternalValueBase & b) const; + virtual bool operator ==(const ExternalValueBase & b) const noexcept; /** * Print the value as JSON. Defaults to unconvertable, i.e. throws an error diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 59e77621c..dee1f687b 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -150,7 +150,7 @@ Attrs Input::toAttrs() const return attrs; } -bool Input::operator ==(const Input & other) const +bool Input::operator ==(const Input & other) const noexcept { return attrs == other.attrs; } diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 185987fec..a5f9bdcc6 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -89,7 +89,7 @@ public: */ bool isLocked() const; - bool operator ==(const Input & other) const; + bool operator ==(const Input & other) const noexcept; bool contains(const Input & other) const; diff --git a/src/libflake/flake/flakeref.cc b/src/libflake/flake/flakeref.cc index 941790e0c..a57fce9f3 100644 --- a/src/libflake/flake/flakeref.cc +++ b/src/libflake/flake/flakeref.cc @@ -36,11 +36,6 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef) return str; } -bool FlakeRef::operator ==(const FlakeRef & other) const -{ - return input == other.input && subdir == other.subdir; -} - FlakeRef FlakeRef::resolve(ref store) const { auto [input2, extraAttrs] = lookupInRegistries(store, input); diff --git a/src/libflake/flake/flakeref.hh b/src/libflake/flake/flakeref.hh index 974cf55a4..1064538a7 100644 --- a/src/libflake/flake/flakeref.hh +++ b/src/libflake/flake/flakeref.hh @@ -46,7 +46,7 @@ struct FlakeRef */ Path subdir; - bool operator==(const FlakeRef & other) const; + bool operator ==(const FlakeRef & other) const = default; FlakeRef(fetchers::Input && input, const Path & subdir) : input(std::move(input)), subdir(subdir) diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index 28afba5fe..792dda740 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -249,11 +249,6 @@ bool LockFile::operator ==(const LockFile & other) const return toJSON().first == other.toJSON().first; } -bool LockFile::operator !=(const LockFile & other) const -{ - return !(*this == other); -} - InputPath parseInputPath(std::string_view s) { InputPath path; diff --git a/src/libflake/flake/lockfile.hh b/src/libflake/flake/lockfile.hh index e269b6c39..841931c11 100644 --- a/src/libflake/flake/lockfile.hh +++ b/src/libflake/flake/lockfile.hh @@ -74,9 +74,6 @@ struct LockFile std::optional isUnlocked() const; bool operator ==(const LockFile & other) const; - // Needed for old gcc versions that don't synthesize it (like gcc 8.2.2 - // that is still the default on aarch64-linux) - bool operator !=(const LockFile & other) const; std::shared_ptr findInput(const InputPath & path); diff --git a/src/libstore/build-result.cc b/src/libstore/build-result.cc index 18f519c5c..96cbfd62f 100644 --- a/src/libstore/build-result.cc +++ b/src/libstore/build-result.cc @@ -2,17 +2,7 @@ namespace nix { -GENERATE_CMP_EXT( - , - BuildResult, - me->status, - me->errorMsg, - me->timesBuilt, - me->isNonDeterministic, - me->builtOutputs, - me->startTime, - me->stopTime, - me->cpuUser, - me->cpuSystem); +bool BuildResult::operator==(const BuildResult &) const noexcept = default; +std::strong_ordering BuildResult::operator<=>(const BuildResult &) const noexcept = default; } diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh index 3636ad3a4..8c66cfeb3 100644 --- a/src/libstore/build-result.hh +++ b/src/libstore/build-result.hh @@ -3,7 +3,6 @@ #include "realisation.hh" #include "derived-path.hh" -#include "comparator.hh" #include #include @@ -101,7 +100,8 @@ struct BuildResult */ std::optional cpuUser, cpuSystem; - DECLARE_CMP(BuildResult); + bool operator ==(const BuildResult &) const noexcept; + std::strong_ordering operator <=>(const BuildResult &) const noexcept; bool success() { diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index 270e04789..bb515013a 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -73,6 +73,7 @@ struct ContentAddressMethod Raw raw; + bool operator ==(const ContentAddressMethod &) const = default; auto operator <=>(const ContentAddressMethod &) const = default; MAKE_WRAPPER_CONSTRUCTOR(ContentAddressMethod); @@ -159,6 +160,7 @@ struct ContentAddress */ Hash hash; + bool operator ==(const ContentAddress &) const = default; auto operator <=>(const ContentAddress &) const = default; /** @@ -217,6 +219,10 @@ struct StoreReferences * iff self is true. */ size_t size() const; + + bool operator ==(const StoreReferences &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const StoreReferences &) const = default; }; // This matches the additional info that we need for makeTextPath @@ -232,6 +238,10 @@ struct TextInfo * disallowed */ StorePathSet references; + + bool operator ==(const TextInfo &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const TextInfo &) const = default; }; struct FixedOutputInfo @@ -250,6 +260,10 @@ struct FixedOutputInfo * References to other store objects or this one. */ StoreReferences references; + + bool operator ==(const FixedOutputInfo &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const FixedOutputInfo &) const = default; }; /** @@ -266,6 +280,10 @@ struct ContentAddressWithReferences Raw raw; + bool operator ==(const ContentAddressWithReferences &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const ContentAddressWithReferences &) const = default; + MAKE_WRAPPER_CONSTRUCTOR(ContentAddressWithReferences); /** diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 9b82e5d12..58e5328a5 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -8,7 +8,6 @@ #include "repair-flag.hh" #include "derived-path-map.hh" #include "sync.hh" -#include "comparator.hh" #include "variant-wrapper.hh" #include @@ -32,7 +31,8 @@ struct DerivationOutput { StorePath path; - GENERATE_CMP(InputAddressed, me->path); + bool operator == (const InputAddressed &) const = default; + auto operator <=> (const InputAddressed &) const = default; }; /** @@ -56,7 +56,8 @@ struct DerivationOutput */ StorePath path(const StoreDirConfig & store, std::string_view drvName, OutputNameView outputName) const; - GENERATE_CMP(CAFixed, me->ca); + bool operator == (const CAFixed &) const = default; + auto operator <=> (const CAFixed &) const = default; }; /** @@ -76,7 +77,8 @@ struct DerivationOutput */ HashAlgorithm hashAlgo; - GENERATE_CMP(CAFloating, me->method, me->hashAlgo); + bool operator == (const CAFloating &) const = default; + auto operator <=> (const CAFloating &) const = default; }; /** @@ -84,7 +86,8 @@ struct DerivationOutput * isn't known yet. */ struct Deferred { - GENERATE_CMP(Deferred); + bool operator == (const Deferred &) const = default; + auto operator <=> (const Deferred &) const = default; }; /** @@ -103,7 +106,8 @@ struct DerivationOutput */ HashAlgorithm hashAlgo; - GENERATE_CMP(Impure, me->method, me->hashAlgo); + bool operator == (const Impure &) const = default; + auto operator <=> (const Impure &) const = default; }; typedef std::variant< @@ -116,7 +120,8 @@ struct DerivationOutput Raw raw; - GENERATE_CMP(DerivationOutput, me->raw); + bool operator == (const DerivationOutput &) const = default; + auto operator <=> (const DerivationOutput &) const = default; MAKE_WRAPPER_CONSTRUCTOR(DerivationOutput); @@ -177,7 +182,8 @@ struct DerivationType { */ bool deferred; - GENERATE_CMP(InputAddressed, me->deferred); + bool operator == (const InputAddressed &) const = default; + auto operator <=> (const InputAddressed &) const = default; }; /** @@ -201,7 +207,8 @@ struct DerivationType { */ bool fixed; - GENERATE_CMP(ContentAddressed, me->sandboxed, me->fixed); + bool operator == (const ContentAddressed &) const = default; + auto operator <=> (const ContentAddressed &) const = default; }; /** @@ -211,7 +218,8 @@ struct DerivationType { * type, but has some restrictions on its usage. */ struct Impure { - GENERATE_CMP(Impure); + bool operator == (const Impure &) const = default; + auto operator <=> (const Impure &) const = default; }; typedef std::variant< @@ -222,7 +230,8 @@ struct DerivationType { Raw raw; - GENERATE_CMP(DerivationType, me->raw); + bool operator == (const DerivationType &) const = default; + auto operator <=> (const DerivationType &) const = default; MAKE_WRAPPER_CONSTRUCTOR(DerivationType); @@ -312,14 +321,9 @@ struct BasicDerivation static std::string_view nameFromPath(const StorePath & storePath); - GENERATE_CMP(BasicDerivation, - me->outputs, - me->inputSrcs, - me->platform, - me->builder, - me->args, - me->env, - me->name); + bool operator == (const BasicDerivation &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=> (const BasicDerivation &) const = default; }; class Store; @@ -377,9 +381,9 @@ struct Derivation : BasicDerivation const nlohmann::json & json, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); - GENERATE_CMP(Derivation, - static_cast(*me), - me->inputDrvs); + bool operator == (const Derivation &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=> (const Derivation &) const = default; }; diff --git a/src/libstore/derived-path-map.cc b/src/libstore/derived-path-map.cc index 4c1ea417a..c97d52773 100644 --- a/src/libstore/derived-path-map.cc +++ b/src/libstore/derived-path-map.cc @@ -54,17 +54,18 @@ typename DerivedPathMap::ChildNode * DerivedPathMap::findSlot(const Single namespace nix { -GENERATE_CMP_EXT( - template<>, - DerivedPathMap>::ChildNode, - me->value, - me->childMap); +template<> +bool DerivedPathMap>::ChildNode::operator == ( + const DerivedPathMap>::ChildNode &) const noexcept = default; -GENERATE_CMP_EXT( - template<>, - DerivedPathMap>, - me->map); +// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. +#if 0 +template<> +std::strong_ordering DerivedPathMap>::ChildNode::operator <=> ( + const DerivedPathMap>::ChildNode &) const noexcept = default; +#endif +template struct DerivedPathMap>::ChildNode; template struct DerivedPathMap>; }; diff --git a/src/libstore/derived-path-map.hh b/src/libstore/derived-path-map.hh index 393cdedf7..bd60fe887 100644 --- a/src/libstore/derived-path-map.hh +++ b/src/libstore/derived-path-map.hh @@ -47,7 +47,11 @@ struct DerivedPathMap { */ Map childMap; - DECLARE_CMP(ChildNode); + bool operator == (const ChildNode &) const noexcept; + + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + // decltype(std::declval() <=> std::declval()) + // operator <=> (const ChildNode &) const noexcept; }; /** @@ -60,7 +64,10 @@ struct DerivedPathMap { */ Map map; - DECLARE_CMP(DerivedPathMap); + bool operator == (const DerivedPathMap &) const = default; + + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + // auto operator <=> (const DerivedPathMap &) const noexcept; /** * Find the node for `k`, creating it if needed. @@ -83,14 +90,21 @@ struct DerivedPathMap { ChildNode * findSlot(const SingleDerivedPath & k); }; +template<> +bool DerivedPathMap>::ChildNode::operator == ( + const DerivedPathMap>::ChildNode &) const noexcept; -DECLARE_CMP_EXT( - template<>, - DerivedPathMap>::, - DerivedPathMap>); -DECLARE_CMP_EXT( - template<>, - DerivedPathMap>::ChildNode::, - DerivedPathMap>::ChildNode); +// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. +#if 0 +template<> +std::strong_ordering DerivedPathMap>::ChildNode::operator <=> ( + const DerivedPathMap>::ChildNode &) const noexcept; + +template<> +inline auto DerivedPathMap>::operator <=> (const DerivedPathMap> &) const noexcept = default; +#endif + +extern template struct DerivedPathMap>::ChildNode; +extern template struct DerivedPathMap>; } diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index a7b404321..1eef881de 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -1,6 +1,7 @@ #include "derived-path.hh" #include "derivations.hh" #include "store-api.hh" +#include "comparator.hh" #include @@ -8,26 +9,32 @@ namespace nix { -#define CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, COMPARATOR) \ - bool MY_TYPE ::operator COMPARATOR (const MY_TYPE & other) const \ - { \ - const MY_TYPE* me = this; \ - auto fields1 = std::tie(*me->drvPath, me->FIELD); \ - me = &other; \ - auto fields2 = std::tie(*me->drvPath, me->FIELD); \ - return fields1 COMPARATOR fields2; \ - } -#define CMP(CHILD_TYPE, MY_TYPE, FIELD) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, ==) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, !=) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, <) +// Custom implementation to avoid `ref` ptr equality +GENERATE_CMP_EXT( + , + std::strong_ordering, + SingleDerivedPathBuilt, + *me->drvPath, + me->output); -CMP(SingleDerivedPath, SingleDerivedPathBuilt, output) +// Custom implementation to avoid `ref` ptr equality -CMP(SingleDerivedPath, DerivedPathBuilt, outputs) - -#undef CMP -#undef CMP_ONE +// TODO no `GENERATE_CMP_EXT` because no `std::set::operator<=>` on +// Darwin, per header. +GENERATE_EQUAL( + , + DerivedPathBuilt ::, + DerivedPathBuilt, + *me->drvPath, + me->outputs); +GENERATE_ONE_CMP( + , + bool, + DerivedPathBuilt ::, + <, + DerivedPathBuilt, + *me->drvPath, + me->outputs); nlohmann::json DerivedPath::Opaque::toJSON(const StoreDirConfig & store) const { diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index af66ed4f0..4ba3fb37d 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -3,7 +3,6 @@ #include "path.hh" #include "outputs-spec.hh" -#include "comparator.hh" #include "config.hh" #include "ref.hh" @@ -32,7 +31,8 @@ struct DerivedPathOpaque { static DerivedPathOpaque parse(const StoreDirConfig & store, std::string_view); nlohmann::json toJSON(const StoreDirConfig & store) const; - GENERATE_CMP(DerivedPathOpaque, me->path); + bool operator == (const DerivedPathOpaque &) const = default; + auto operator <=> (const DerivedPathOpaque &) const = default; }; struct SingleDerivedPath; @@ -79,7 +79,8 @@ struct SingleDerivedPathBuilt { const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); nlohmann::json toJSON(Store & store) const; - DECLARE_CMP(SingleDerivedPathBuilt); + bool operator == (const SingleDerivedPathBuilt &) const noexcept; + std::strong_ordering operator <=> (const SingleDerivedPathBuilt &) const noexcept; }; using _SingleDerivedPathRaw = std::variant< @@ -109,6 +110,9 @@ struct SingleDerivedPath : _SingleDerivedPathRaw { return static_cast(*this); } + bool operator == (const SingleDerivedPath &) const = default; + auto operator <=> (const SingleDerivedPath &) const = default; + /** * Get the store path this is ultimately derived from (by realising * and projecting outputs). @@ -202,7 +206,9 @@ struct DerivedPathBuilt { const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); nlohmann::json toJSON(Store & store) const; - DECLARE_CMP(DerivedPathBuilt); + bool operator == (const DerivedPathBuilt &) const noexcept; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + bool operator < (const DerivedPathBuilt &) const noexcept; }; using _DerivedPathRaw = std::variant< @@ -231,6 +237,10 @@ struct DerivedPath : _DerivedPathRaw { return static_cast(*this); } + bool operator == (const DerivedPath &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + //auto operator <=> (const DerivedPath &) const = default; + /** * Get the store path this is ultimately derived from (by realising * and projecting outputs). diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 0d219a489..3e0a754f9 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -4,15 +4,6 @@ namespace nix { -GENERATE_CMP_EXT( - , - NarInfo, - me->url, - me->compression, - me->fileHash, - me->fileSize, - static_cast(*me)); - NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence) : ValidPathInfo(StorePath(StorePath::dummy), Hash(Hash::dummy)) // FIXME: hack { diff --git a/src/libstore/nar-info.hh b/src/libstore/nar-info.hh index fd538a7cd..561c9a863 100644 --- a/src/libstore/nar-info.hh +++ b/src/libstore/nar-info.hh @@ -24,7 +24,9 @@ struct NarInfo : ValidPathInfo NarInfo(const ValidPathInfo & info) : ValidPathInfo(info) { } NarInfo(const Store & store, const std::string & s, const std::string & whence); - DECLARE_CMP(NarInfo); + bool operator ==(const NarInfo &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::optional::operator <=>`, can't do yet + //auto operator <=>(const NarInfo &) const = default; std::string to_string(const Store & store) const; diff --git a/src/libstore/outputs-spec.hh b/src/libstore/outputs-spec.hh index 1ef99a5fc..30d15311d 100644 --- a/src/libstore/outputs-spec.hh +++ b/src/libstore/outputs-spec.hh @@ -6,9 +6,7 @@ #include #include -#include "comparator.hh" #include "json-impls.hh" -#include "comparator.hh" #include "variant-wrapper.hh" namespace nix { @@ -60,7 +58,11 @@ struct OutputsSpec { Raw raw; - GENERATE_CMP(OutputsSpec, me->raw); + bool operator == (const OutputsSpec &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + bool operator < (const OutputsSpec & other) const { + return raw < other.raw; + } MAKE_WRAPPER_CONSTRUCTOR(OutputsSpec); @@ -99,7 +101,9 @@ struct ExtendedOutputsSpec { Raw raw; - GENERATE_CMP(ExtendedOutputsSpec, me->raw); + bool operator == (const ExtendedOutputsSpec &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + bool operator < (const ExtendedOutputsSpec &) const; MAKE_WRAPPER_CONSTRUCTOR(ExtendedOutputsSpec); diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index 5c27182b7..51ed5fc62 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -3,11 +3,13 @@ #include "path-info.hh" #include "store-api.hh" #include "json-utils.hh" +#include "comparator.hh" namespace nix { GENERATE_CMP_EXT( , + std::weak_ordering, UnkeyedValidPathInfo, me->deriver, me->narHash, @@ -19,12 +21,6 @@ GENERATE_CMP_EXT( me->sigs, me->ca); -GENERATE_CMP_EXT( - , - ValidPathInfo, - me->path, - static_cast(*me)); - std::string ValidPathInfo::fingerprint(const Store & store) const { if (narSize == 0) diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh index b6dc0855d..caefa7975 100644 --- a/src/libstore/path-info.hh +++ b/src/libstore/path-info.hh @@ -32,17 +32,47 @@ struct SubstitutablePathInfo using SubstitutablePathInfos = std::map; +/** + * Information about a store object. + * + * See `store/store-object` and `protocols/json/store-object-info` in + * the Nix manual + */ struct UnkeyedValidPathInfo { + /** + * Path to derivation that produced this store object, if known. + */ std::optional deriver; + /** * \todo document this */ Hash narHash; + + /** + * Other store objects this store object referes to. + */ StorePathSet references; + + /** + * When this store object was registered in the store that contains + * it, if known. + */ time_t registrationTime = 0; - uint64_t narSize = 0; // 0 = unknown - uint64_t id = 0; // internal use only + + /** + * 0 = unknown + */ + uint64_t narSize = 0; + + /** + * internal use only: SQL primary key for on-disk store objects with + * `LocalStore`. + * + * @todo Remove, layer violation + */ + uint64_t id = 0; /** * Whether the path is ultimately trusted, that is, it's a @@ -75,7 +105,12 @@ struct UnkeyedValidPathInfo UnkeyedValidPathInfo(Hash narHash) : narHash(narHash) { }; - DECLARE_CMP(UnkeyedValidPathInfo); + bool operator == (const UnkeyedValidPathInfo &) const noexcept; + + /** + * @todo return `std::strong_ordering` once `id` is removed + */ + std::weak_ordering operator <=> (const UnkeyedValidPathInfo &) const noexcept; virtual ~UnkeyedValidPathInfo() { } @@ -95,7 +130,8 @@ struct UnkeyedValidPathInfo struct ValidPathInfo : UnkeyedValidPathInfo { StorePath path; - DECLARE_CMP(ValidPathInfo); + bool operator == (const ValidPathInfo &) const = default; + auto operator <=> (const ValidPathInfo &) const = default; /** * Return a fingerprint of the store path to be used in binary diff --git a/src/libutil/args.cc b/src/libutil/args.cc index c202facdf..d58f4b4ae 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -57,8 +57,7 @@ void Completions::add(std::string completion, std::string description) }); } -bool Completion::operator<(const Completion & other) const -{ return completion < other.completion || (completion == other.completion && description < other.description); } +auto Completion::operator<=>(const Completion & other) const noexcept = default; std::string completionMarker = "___COMPLETE___"; diff --git a/src/libutil/args.hh b/src/libutil/args.hh index f6e74e67e..c0236ee3d 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -380,7 +380,7 @@ struct Completion { std::string completion; std::string description; - bool operator<(const Completion & other) const; + auto operator<=>(const Completion & other) const noexcept; }; /** diff --git a/src/libutil/canon-path.hh b/src/libutil/canon-path.hh index 8f5a1c279..f84347dc4 100644 --- a/src/libutil/canon-path.hh +++ b/src/libutil/canon-path.hh @@ -169,7 +169,7 @@ public: * a directory is always followed directly by its children. For * instance, 'foo' < 'foo/bar' < 'foo!'. */ - bool operator < (const CanonPath & x) const + auto operator <=> (const CanonPath & x) const { auto i = path.begin(); auto j = x.path.begin(); @@ -178,10 +178,9 @@ public: if (c_i == '/') c_i = 0; auto c_j = *j; if (c_j == '/') c_j = 0; - if (c_i < c_j) return true; - if (c_i > c_j) return false; + if (auto cmp = c_i <=> c_j; cmp != 0) return cmp; } - return i == path.end() && j != x.path.end(); + return (i != path.end()) <=> (j != x.path.end()); } /** diff --git a/src/libutil/comparator.hh b/src/libutil/comparator.hh index cbc2bb4fd..34ba6f453 100644 --- a/src/libutil/comparator.hh +++ b/src/libutil/comparator.hh @@ -1,17 +1,8 @@ #pragma once ///@file -#define DECLARE_ONE_CMP(PRE, QUAL, COMPARATOR, MY_TYPE) \ - PRE bool QUAL operator COMPARATOR(const MY_TYPE & other) const; -#define DECLARE_EQUAL(prefix, qualification, my_type) \ - DECLARE_ONE_CMP(prefix, qualification, ==, my_type) -#define DECLARE_LEQ(prefix, qualification, my_type) \ - DECLARE_ONE_CMP(prefix, qualification, <, my_type) -#define DECLARE_NEQ(prefix, qualification, my_type) \ - DECLARE_ONE_CMP(prefix, qualification, !=, my_type) - -#define GENERATE_ONE_CMP(PRE, QUAL, COMPARATOR, MY_TYPE, ...) \ - PRE bool QUAL operator COMPARATOR(const MY_TYPE & other) const { \ +#define GENERATE_ONE_CMP(PRE, RET, QUAL, COMPARATOR, MY_TYPE, ...) \ + PRE RET QUAL operator COMPARATOR(const MY_TYPE & other) const noexcept { \ __VA_OPT__(const MY_TYPE * me = this;) \ auto fields1 = std::tie( __VA_ARGS__ ); \ __VA_OPT__(me = &other;) \ @@ -19,30 +10,9 @@ return fields1 COMPARATOR fields2; \ } #define GENERATE_EQUAL(prefix, qualification, my_type, args...) \ - GENERATE_ONE_CMP(prefix, qualification, ==, my_type, args) -#define GENERATE_LEQ(prefix, qualification, my_type, args...) \ - GENERATE_ONE_CMP(prefix, qualification, <, my_type, args) -#define GENERATE_NEQ(prefix, qualification, my_type, args...) \ - GENERATE_ONE_CMP(prefix, qualification, !=, my_type, args) - -/** - * Declare comparison methods without defining them. - */ -#define DECLARE_CMP(my_type) \ - DECLARE_EQUAL(,,my_type) \ - DECLARE_LEQ(,,my_type) \ - DECLARE_NEQ(,,my_type) - -/** - * @param prefix This is for something before each declaration like - * `template`. - * - * @param my_type the type are defining operators for. - */ -#define DECLARE_CMP_EXT(prefix, qualification, my_type) \ - DECLARE_EQUAL(prefix, qualification, my_type) \ - DECLARE_LEQ(prefix, qualification, my_type) \ - DECLARE_NEQ(prefix, qualification, my_type) + GENERATE_ONE_CMP(prefix, bool, qualification, ==, my_type, args) +#define GENERATE_SPACESHIP(prefix, ret, qualification, my_type, args...) \ + GENERATE_ONE_CMP(prefix, ret, qualification, <=>, my_type, args) /** * Awful hacky generation of the comparison operators by doing a lexicographic @@ -55,15 +25,19 @@ * will generate comparison operators semantically equivalent to: * * ``` - * bool operator<(const ClassName& other) { - * return field1 < other.field1 && field2 < other.field2 && ...; + * auto operator<=>(const ClassName& other) const noexcept { + * if (auto cmp = field1 <=> other.field1; cmp != 0) + * return cmp; + * if (auto cmp = field2 <=> other.field2; cmp != 0) + * return cmp; + * ... + * return 0; * } * ``` */ #define GENERATE_CMP(args...) \ GENERATE_EQUAL(,,args) \ - GENERATE_LEQ(,,args) \ - GENERATE_NEQ(,,args) + GENERATE_SPACESHIP(,auto,,args) /** * @param prefix This is for something before each declaration like @@ -71,7 +45,6 @@ * * @param my_type the type are defining operators for. */ -#define GENERATE_CMP_EXT(prefix, my_type, args...) \ +#define GENERATE_CMP_EXT(prefix, ret, my_type, args...) \ GENERATE_EQUAL(prefix, my_type ::, my_type, args) \ - GENERATE_LEQ(prefix, my_type ::, my_type, args) \ - GENERATE_NEQ(prefix, my_type ::, my_type, args) + GENERATE_SPACESHIP(prefix, ret, my_type ::, my_type, args) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index e01f06448..33c391963 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -46,27 +46,22 @@ std::ostream & operator <<(std::ostream & os, const HintFmt & hf) /** * An arbitrarily defined value comparison for the purpose of using traces in the key of a sorted container. */ -inline bool operator<(const Trace& lhs, const Trace& rhs) +inline std::strong_ordering operator<=>(const Trace& lhs, const Trace& rhs) { // `std::shared_ptr` does not have value semantics for its comparison // functions, so we need to check for nulls and compare the dereferenced // values here. if (lhs.pos != rhs.pos) { - if (!lhs.pos) - return true; - if (!rhs.pos) - return false; - if (*lhs.pos != *rhs.pos) - return *lhs.pos < *rhs.pos; + if (auto cmp = bool{lhs.pos} <=> bool{rhs.pos}; cmp != 0) + return cmp; + if (auto cmp = *lhs.pos <=> *rhs.pos; cmp != 0) + return cmp; } // This formats a freshly formatted hint string and then throws it away, which // shouldn't be much of a problem because it only runs when pos is equal, and this function is // used for trace printing, which is infrequent. - return lhs.hint.str() < rhs.hint.str(); + return lhs.hint.str() <=> rhs.hint.str(); } -inline bool operator> (const Trace& lhs, const Trace& rhs) { return rhs < lhs; } -inline bool operator<=(const Trace& lhs, const Trace& rhs) { return !(lhs > rhs); } -inline bool operator>=(const Trace& lhs, const Trace& rhs) { return !(lhs < rhs); } // print lines of code to the ostream, indicating the error column. void printCodeLines(std::ostream & out, diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 8cc8fb303..d7fe902d6 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -75,10 +75,7 @@ struct Trace { TracePrint print = TracePrint::Default; }; -inline bool operator<(const Trace& lhs, const Trace& rhs); -inline bool operator> (const Trace& lhs, const Trace& rhs); -inline bool operator<=(const Trace& lhs, const Trace& rhs); -inline bool operator>=(const Trace& lhs, const Trace& rhs); +inline std::strong_ordering operator<=>(const Trace& lhs, const Trace& rhs); struct ErrorInfo { Verbosity level; diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index 1da2a3ff5..6ffbc0c10 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "comparator.hh" #include "error.hh" #include "json-utils.hh" #include "types.hh" diff --git a/src/libutil/git.hh b/src/libutil/git.hh index 2190cc550..1dbdb7335 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -39,7 +39,8 @@ struct TreeEntry Mode mode; Hash hash; - GENERATE_CMP(TreeEntry, me->mode, me->hash); + bool operator ==(const TreeEntry &) const = default; + auto operator <=>(const TreeEntry &) const = default; }; /** diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 7064e96e6..35b913e42 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -41,7 +41,7 @@ Hash::Hash(HashAlgorithm algo) : algo(algo) } -bool Hash::operator == (const Hash & h2) const +bool Hash::operator == (const Hash & h2) const noexcept { if (hashSize != h2.hashSize) return false; for (unsigned int i = 0; i < hashSize; i++) @@ -50,7 +50,7 @@ bool Hash::operator == (const Hash & h2) const } -std::strong_ordering Hash::operator <=> (const Hash & h) const +std::strong_ordering Hash::operator <=> (const Hash & h) const noexcept { if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp; for (unsigned int i = 0; i < hashSize; i++) { diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index ef96d08c9..dc95b9f2f 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -88,12 +88,12 @@ public: /** * Check whether two hashes are equal. */ - bool operator == (const Hash & h2) const; + bool operator == (const Hash & h2) const noexcept; /** * Compare how two hashes are ordered. */ - std::strong_ordering operator <=> (const Hash & h2) const; + std::strong_ordering operator <=> (const Hash & h2) const noexcept; /** * Returns the length of a base-16 representation of this hash. diff --git a/src/libutil/memory-source-accessor.hh b/src/libutil/memory-source-accessor.hh index cd5146c89..012a388c0 100644 --- a/src/libutil/memory-source-accessor.hh +++ b/src/libutil/memory-source-accessor.hh @@ -15,11 +15,15 @@ struct MemorySourceAccessor : virtual SourceAccessor * defining what a "file system object" is in Nix. */ struct File { + bool operator == (const File &) const noexcept; + std::strong_ordering operator <=> (const File &) const noexcept; + struct Regular { bool executable = false; std::string contents; - GENERATE_CMP(Regular, me->executable, me->contents); + bool operator == (const Regular &) const = default; + auto operator <=> (const Regular &) const = default; }; struct Directory { @@ -27,13 +31,16 @@ struct MemorySourceAccessor : virtual SourceAccessor std::map> contents; - GENERATE_CMP(Directory, me->contents); + bool operator == (const Directory &) const noexcept; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + bool operator < (const Directory &) const noexcept; }; struct Symlink { std::string target; - GENERATE_CMP(Symlink, me->target); + bool operator == (const Symlink &) const = default; + auto operator <=> (const Symlink &) const = default; }; using Raw = std::variant; @@ -41,14 +48,15 @@ struct MemorySourceAccessor : virtual SourceAccessor MAKE_WRAPPER_CONSTRUCTOR(File); - GENERATE_CMP(File, me->raw); - Stat lstat() const; }; File root { File::Directory {} }; - GENERATE_CMP(MemorySourceAccessor, me->root); + bool operator == (const MemorySourceAccessor &) const noexcept = default; + bool operator < (const MemorySourceAccessor & other) const noexcept { + return root < other.root; + } std::string readFile(const CanonPath & path) override; bool pathExists(const CanonPath & path) override; @@ -72,6 +80,20 @@ struct MemorySourceAccessor : virtual SourceAccessor SourcePath addFile(CanonPath path, std::string && contents); }; + +inline bool MemorySourceAccessor::File::Directory::operator == ( + const MemorySourceAccessor::File::Directory &) const noexcept = default; +inline bool MemorySourceAccessor::File::Directory::operator < ( + const MemorySourceAccessor::File::Directory & other) const noexcept +{ + return contents < other.contents; +} + +inline bool MemorySourceAccessor::File::operator == ( + const MemorySourceAccessor::File &) const noexcept = default; +inline std::strong_ordering MemorySourceAccessor::File::operator <=> ( + const MemorySourceAccessor::File &) const noexcept = default; + /** * Write to a `MemorySourceAccessor` at the given path */ diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 724e560b7..573efeeb2 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -17,12 +17,6 @@ Pos::operator std::shared_ptr() const return std::make_shared(&*this); } -bool Pos::operator<(const Pos &rhs) const -{ - return std::forward_as_tuple(line, column, origin) - < std::forward_as_tuple(rhs.line, rhs.column, rhs.origin); -} - std::optional Pos::getCodeLines() const { if (line == 0) diff --git a/src/libutil/position.hh b/src/libutil/position.hh index 9bdf3b4b5..f8f34419b 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -22,21 +22,17 @@ struct Pos struct Stdin { ref source; - bool operator==(const Stdin & rhs) const + bool operator==(const Stdin & rhs) const noexcept { return *source == *rhs.source; } - bool operator!=(const Stdin & rhs) const - { return *source != *rhs.source; } - bool operator<(const Stdin & rhs) const - { return *source < *rhs.source; } + std::strong_ordering operator<=>(const Stdin & rhs) const noexcept + { return *source <=> *rhs.source; } }; struct String { ref source; - bool operator==(const String & rhs) const + bool operator==(const String & rhs) const noexcept { return *source == *rhs.source; } - bool operator!=(const String & rhs) const - { return *source != *rhs.source; } - bool operator<(const String & rhs) const - { return *source < *rhs.source; } + std::strong_ordering operator<=>(const String & rhs) const noexcept + { return *source <=> *rhs.source; } }; typedef std::variant Origin; @@ -65,8 +61,7 @@ struct Pos std::optional getCodeLines() const; bool operator==(const Pos & rhs) const = default; - bool operator!=(const Pos & rhs) const = default; - bool operator<(const Pos & rhs) const; + auto operator<=>(const Pos & rhs) const = default; struct LinesIterator { using difference_type = size_t; diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 8318251bd..016fdd74a 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -87,9 +87,9 @@ public: return p != other.p; } - bool operator < (const ref & other) const + auto operator <=> (const ref & other) const { - return p < other.p; + return p <=> other.p; } private: diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 32ab3685d..b16960d4a 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -152,9 +152,9 @@ struct SourceAccessor : std::enable_shared_from_this return number == x.number; } - bool operator < (const SourceAccessor & x) const + auto operator <=> (const SourceAccessor & x) const { - return number < x.number; + return number <=> x.number; } void setPathDisplay(std::string displayPrefix, std::string displaySuffix = ""); diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc index 023b5ed4b..759d3c355 100644 --- a/src/libutil/source-path.cc +++ b/src/libutil/source-path.cc @@ -47,19 +47,14 @@ SourcePath SourcePath::operator / (const CanonPath & x) const SourcePath SourcePath::operator / (std::string_view c) const { return {accessor, path / c}; } -bool SourcePath::operator==(const SourcePath & x) const +bool SourcePath::operator==(const SourcePath & x) const noexcept { return std::tie(*accessor, path) == std::tie(*x.accessor, x.path); } -bool SourcePath::operator!=(const SourcePath & x) const +std::strong_ordering SourcePath::operator<=>(const SourcePath & x) const noexcept { - return std::tie(*accessor, path) != std::tie(*x.accessor, x.path); -} - -bool SourcePath::operator<(const SourcePath & x) const -{ - return std::tie(*accessor, path) < std::tie(*x.accessor, x.path); + return std::tie(*accessor, path) <=> std::tie(*x.accessor, x.path); } std::ostream & operator<<(std::ostream & str, const SourcePath & path) diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 83ec6295d..faf0a5a31 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -103,9 +103,8 @@ struct SourcePath */ SourcePath operator / (std::string_view c) const; - bool operator==(const SourcePath & x) const; - bool operator!=(const SourcePath & x) const; - bool operator<(const SourcePath & x) const; + bool operator==(const SourcePath & x) const noexcept; + std::strong_ordering operator<=>(const SourcePath & x) const noexcept; /** * Convenience wrapper around `SourceAccessor::resolveSymlinks()`. diff --git a/src/libutil/suggestions.hh b/src/libutil/suggestions.hh index 17d1d69c1..e39ab400c 100644 --- a/src/libutil/suggestions.hh +++ b/src/libutil/suggestions.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "comparator.hh" #include "types.hh" #include @@ -20,7 +19,8 @@ public: std::string to_string() const; - GENERATE_CMP(Suggestion, me->distance, me->suggestion) + bool operator ==(const Suggestion &) const = default; + auto operator <=>(const Suggestion &) const = default; }; class Suggestions { diff --git a/src/libutil/url.cc b/src/libutil/url.cc index f4178f87f..bcbe9ea4e 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -132,7 +132,7 @@ std::string ParsedURL::to_string() const + (fragment.empty() ? "" : "#" + percentEncode(fragment)); } -bool ParsedURL::operator ==(const ParsedURL & other) const +bool ParsedURL::operator ==(const ParsedURL & other) const noexcept { return scheme == other.scheme diff --git a/src/libutil/url.hh b/src/libutil/url.hh index 6cd06e53d..738ee9f82 100644 --- a/src/libutil/url.hh +++ b/src/libutil/url.hh @@ -18,7 +18,7 @@ struct ParsedURL std::string to_string() const; - bool operator ==(const ParsedURL & other) const; + bool operator ==(const ParsedURL & other) const noexcept; /** * Remove `.` and `..` path elements. diff --git a/src/nix/profile.cc b/src/nix/profile.cc index bb6424c4f..78532a2ec 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -27,7 +27,9 @@ struct ProfileElementSource std::string attrPath; ExtendedOutputsSpec outputs; - bool operator < (const ProfileElementSource & other) const + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + //auto operator <=> (const ProfileElementSource & other) const + auto operator < (const ProfileElementSource & other) const { return std::tuple(originalRef.to_string(), attrPath, outputs) < diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index a0125d023..3d01d9806 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -229,7 +229,7 @@ TEST_F(GitTest, both_roundrip) { mkSinkHook(CanonPath::root, root.hash, BlobMode::Regular); - ASSERT_EQ(*files, *files2); + ASSERT_EQ(files->root, files2->root); } TEST(GitLsRemote, parseSymrefLineWithReference) { From 1a8defd06f6b9fb1867680a053dd23dfccd5df50 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 22:09:27 +0200 Subject: [PATCH 546/910] Refactor: rename C++ concatStringsSep -> dropEmptyInitThenConcatStringsSep --- src/build-remote/build-remote.cc | 8 ++++---- src/libcmd/command.cc | 2 +- src/libcmd/installables.cc | 4 ++-- src/libcmd/repl.cc | 2 +- src/libexpr/eval-cache.cc | 6 +++--- src/libflake/flake/config.cc | 2 +- src/libflake/flake/lockfile.cc | 4 ++-- src/libmain/shared.cc | 6 +++--- src/libstore/build/derivation-goal.cc | 4 ++-- src/libstore/build/entry-points.cc | 2 +- src/libstore/globals.cc | 2 +- src/libstore/local-store.cc | 8 ++++---- src/libstore/misc.cc | 2 +- src/libstore/nar-info-disk-cache.cc | 4 ++-- src/libstore/nar-info.cc | 2 +- src/libstore/outputs-spec.cc | 2 +- src/libstore/path-info.cc | 2 +- src/libstore/path-with-outputs.cc | 2 +- src/libstore/store-api.cc | 2 +- src/libstore/unix/build/hook-instance.cc | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 8 ++++---- src/libutil/config.cc | 8 ++++---- src/libutil/util.hh | 4 ++-- src/nix/develop.cc | 2 +- src/nix/diff-closures.cc | 4 ++-- src/nix/env.cc | 2 +- src/nix/flake.cc | 10 +++++----- src/nix/main.cc | 6 +++--- src/nix/path-info.cc | 2 +- src/nix/profile.cc | 6 +++--- src/nix/search.cc | 4 ++-- tests/unit/libutil/references.cc | 2 +- tests/unit/libutil/tests.cc | 14 +++++++------- 33 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 582e6d623..1c3ce930a 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -206,15 +206,15 @@ static int main_build_remote(int argc, char * * argv) error % drvstr % neededSystem - % concatStringsSep(", ", requiredFeatures) + % dropEmptyInitThenConcatStringsSep(", ", requiredFeatures) % machines.size(); for (auto & m : machines) error - % concatStringsSep(", ", m.systemTypes) + % dropEmptyInitThenConcatStringsSep(", ", m.systemTypes) % m.maxJobs - % concatStringsSep(", ", m.supportedFeatures) - % concatStringsSep(", ", m.mandatoryFeatures); + % dropEmptyInitThenConcatStringsSep(", ", m.supportedFeatures) + % dropEmptyInitThenConcatStringsSep(", ", m.mandatoryFeatures); printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str()); diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index e0e5f0890..891a01f91 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -42,7 +42,7 @@ void NixMultiCommand::run() for (auto & [name, _] : commands) subCommandTextLines.insert(fmt("- `%s`", name)); std::string markdownError = fmt("`nix %s` requires a sub-command. Available sub-commands:\n\n%s\n", - commandName, concatStringsSep("\n", subCommandTextLines)); + commandName, dropEmptyInitThenConcatStringsSep("\n", subCommandTextLines)); throw UsageError(renderMarkdownToTerminal(markdownError)); } command->second->run(); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 417e15094..1f6ee1e23 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -374,7 +374,7 @@ void completeFlakeRefWithFragment( auto attrPath2 = (*attr)->getAttrPath(attr2); /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); - completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); + completions.add(flakeRefS + "#" + prefixRoot + dropEmptyInitThenConcatStringsSep(".", evalState->symbols.resolve(attrPath2))); } } } @@ -630,7 +630,7 @@ static void throwBuildErrors( } failedPaths.insert(failedResult->path.to_string(store)); } - throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); + throw Error("build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failedPaths))); } } } diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 3dd19ce39..1d39ef167 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -625,7 +625,7 @@ ProcessLineResult NixRepl::processLine(std::string line) markdown += "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " - + concatStringsSep(" ", args) + "\n\n"; + + dropEmptyInitThenConcatStringsSep(" ", args) + "\n\n"; } markdown += stripIndentation(doc->doc); diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 46dd3691c..e573fe884 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -225,7 +225,7 @@ struct AttrDb (key.first) (symbols[key.second]) (AttrType::ListOfStrings) - (concatStringsSep("\t", l)).exec(); + (dropEmptyInitThenConcatStringsSep("\t", l)).exec(); return state->db.getLastInsertedRowId(); }); @@ -435,12 +435,12 @@ std::vector AttrCursor::getAttrPath(Symbol name) const std::string AttrCursor::getAttrPathStr() const { - return concatStringsSep(".", root->state.symbols.resolve(getAttrPath())); + return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath())); } std::string AttrCursor::getAttrPathStr(Symbol name) const { - return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name))); + return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath(name))); } Value & AttrCursor::forceValue() diff --git a/src/libflake/flake/config.cc b/src/libflake/flake/config.cc index 4e00d5c93..e526cdddf 100644 --- a/src/libflake/flake/config.cc +++ b/src/libflake/flake/config.cc @@ -47,7 +47,7 @@ void ConfigFile::apply(const Settings & flakeSettings) else if (auto* b = std::get_if>(&value)) valueS = b->t ? "true" : "false"; else if (auto ss = std::get_if>(&value)) - valueS = concatStringsSep(" ", *ss); // FIXME: evil + valueS = dropEmptyInitThenConcatStringsSep(" ", *ss); // FIXME: evil else assert(false); diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index 792dda740..f0e22a75a 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -61,7 +61,7 @@ static std::shared_ptr doFind(const ref& root, const InputPath & pat std::vector cycle; std::transform(found, visited.cend(), std::back_inserter(cycle), printInputPath); cycle.push_back(printInputPath(path)); - throw Error("follow cycle detected: [%s]", concatStringsSep(" -> ", cycle)); + throw Error("follow cycle detected: [%s]", dropEmptyInitThenConcatStringsSep(" -> ", cycle)); } visited.push_back(path); @@ -367,7 +367,7 @@ void check(); std::string printInputPath(const InputPath & path) { - return concatStringsSep("/", path); + return dropEmptyInitThenConcatStringsSep("/", path); } } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index fee4d0c1e..681c1039f 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -301,11 +301,11 @@ void printVersion(const std::string & programName) #endif cfg.push_back("signed-caches"); std::cout << "System type: " << settings.thisSystem << "\n"; - std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; - std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n"; + std::cout << "Additional system types: " << dropEmptyInitThenConcatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; + std::cout << "Features: " << dropEmptyInitThenConcatStringsSep(", ", cfg) << "\n"; std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n"; std::cout << "User configuration files: " << - concatStringsSep(":", settings.nixUserConfFiles) + dropEmptyInitThenConcatStringsSep(":", settings.nixUserConfFiles) << "\n"; std::cout << "Store directory: " << settings.nixStore << "\n"; std::cout << "State directory: " << settings.nixStateDir << "\n"; diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 64b8495e1..c0a784349 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -895,7 +895,7 @@ void runPostBuildHook( std::map hookEnvironment = getEnv(); hookEnvironment.emplace("DRV_PATH", store.printStorePath(drvPath)); - hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths)))); + hookEnvironment.emplace("OUT_PATHS", chomp(dropEmptyInitThenConcatStringsSep(" ", store.printStorePathSet(outputPaths)))); hookEnvironment.emplace("NIX_CONFIG", globalConfig.toKeyValue()); struct LogSink : Sink { @@ -1505,7 +1505,7 @@ std::pair DerivationGoal::checkPathValidity() if (!wantedOutputsLeft.empty()) throw Error("derivation '%s' does not have wanted outputs %s", worker.store.printStorePath(drvPath), - concatStringsSep(", ", quoteStrings(wantedOutputsLeft))); + dropEmptyInitThenConcatStringsSep(", ", quoteStrings(wantedOutputsLeft))); bool allValid = true; for (auto & [_, status] : initialOutputs) { diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index 784f618c1..8bf7ad35d 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -42,7 +42,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod throw std::move(*ex); } else if (!failed.empty()) { if (ex) logError(ex->info()); - throw Error(worker.failingExitStatus(), "build of %s failed", concatStringsSep(", ", quoteStrings(failed))); + throw Error(worker.failingExitStatus(), "build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failed))); } } diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 7e1d7ea6d..5f5b4f89b 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -82,7 +82,7 @@ Settings::Settings() Strings ss; for (auto & p : tokenizeString(*s, ":")) ss.push_back("@" + p); - builders = concatStringsSep(" ", ss); + builders = dropEmptyInitThenConcatStringsSep(" ", ss); } #if defined(__linux__) && defined(SANDBOX_SHELL) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 2b4e01eb3..8764b88b7 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -656,7 +656,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) combinedSignatures.insert(info.signatures.begin(), info.signatures.end()); state->stmts->UpdateRealisedOutput.use() - (concatStringsSep(" ", combinedSignatures)) + (dropEmptyInitThenConcatStringsSep(" ", combinedSignatures)) (info.id.strHash()) (info.id.outputName) .exec(); @@ -675,7 +675,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) (info.id.strHash()) (info.id.outputName) (printStorePath(info.outPath)) - (concatStringsSep(" ", info.signatures)) + (dropEmptyInitThenConcatStringsSep(" ", info.signatures)) .exec(); } for (auto & [outputId, depPath] : info.dependentRealisations) { @@ -729,7 +729,7 @@ uint64_t LocalStore::addValidPath(State & state, (info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver) (info.narSize, info.narSize != 0) (info.ultimate ? 1 : 0, info.ultimate) - (concatStringsSep(" ", info.sigs), !info.sigs.empty()) + (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) .exec(); uint64_t id = state.db.getLastInsertedRowId(); @@ -833,7 +833,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) (info.narSize, info.narSize != 0) (info.narHash.to_string(HashFormat::Base16, true)) (info.ultimate ? 1 : 0, info.ultimate) - (concatStringsSep(" ", info.sigs), !info.sigs.empty()) + (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) (printStorePath(info.path)) .exec(); diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index dd0efbe19..179e5478c 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -464,7 +464,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd) if (!outputsLeft.empty()) throw Error("derivation '%s' does not have an outputs %s", store.printStorePath(drvPath), - concatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); + dropEmptyInitThenConcatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); return outputMap; } diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 07beb8acb..42d172237 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -337,9 +337,9 @@ public: (narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize) (info->narHash.to_string(HashFormat::Nix32, true)) (info->narSize) - (concatStringsSep(" ", info->shortRefs())) + (dropEmptyInitThenConcatStringsSep(" ", info->shortRefs())) (info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver) - (concatStringsSep(" ", info->sigs)) + (dropEmptyInitThenConcatStringsSep(" ", info->sigs)) (renderContentAddress(info->ca)) (time(0)).exec(); diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 3e0a754f9..577466f55 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -111,7 +111,7 @@ std::string NarInfo::to_string(const Store & store) const res += "NarHash: " + narHash.to_string(HashFormat::Nix32, true) + "\n"; res += "NarSize: " + std::to_string(narSize) + "\n"; - res += "References: " + concatStringsSep(" ", shortRefs()) + "\n"; + res += "References: " + dropEmptyInitThenConcatStringsSep(" ", shortRefs()) + "\n"; if (deriver) res += "Deriver: " + std::string(deriver->to_string()) + "\n"; diff --git a/src/libstore/outputs-spec.cc b/src/libstore/outputs-spec.cc index 21c069223..4ed8f95ae 100644 --- a/src/libstore/outputs-spec.cc +++ b/src/libstore/outputs-spec.cc @@ -83,7 +83,7 @@ std::string OutputsSpec::to_string() const return "*"; }, [&](const OutputsSpec::Names & outputNames) -> std::string { - return concatStringsSep(",", outputNames); + return dropEmptyInitThenConcatStringsSep(",", outputNames); }, }, raw); } diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index 51ed5fc62..a13bb8bef 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -30,7 +30,7 @@ std::string ValidPathInfo::fingerprint(const Store & store) const "1;" + store.printStorePath(path) + ";" + narHash.to_string(HashFormat::Nix32, true) + ";" + std::to_string(narSize) + ";" - + concatStringsSep(",", store.printStorePathSet(references)); + + dropEmptyInitThenConcatStringsSep(",", store.printStorePathSet(references)); } diff --git a/src/libstore/path-with-outputs.cc b/src/libstore/path-with-outputs.cc index 026e37647..5fa38d5d9 100644 --- a/src/libstore/path-with-outputs.cc +++ b/src/libstore/path-with-outputs.cc @@ -9,7 +9,7 @@ std::string StorePathWithOutputs::to_string(const StoreDirConfig & store) const { return outputs.empty() ? store.printStorePath(path) - : store.printStorePath(path) + "!" + concatStringsSep(",", outputs); + : store.printStorePath(path) + "!" + dropEmptyInitThenConcatStringsSep(",", outputs); } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 05c4e1c5e..6904996b5 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1208,7 +1208,7 @@ std::string StoreDirConfig::showPaths(const StorePathSet & paths) std::string showPaths(const PathSet & paths) { - return concatStringsSep(", ", quoteStrings(paths)); + return dropEmptyInitThenConcatStringsSep(", ", quoteStrings(paths)); } diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index dfc208798..ba6c3a912 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -8,7 +8,7 @@ namespace nix { HookInstance::HookInstance() { - debug("starting build hook '%s'", concatStringsSep(" ", settings.buildHook.get())); + debug("starting build hook '%s'", dropEmptyInitThenConcatStringsSep(" ", settings.buildHook.get())); auto buildHookArgs = settings.buildHook.get(); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index d5a3e0034..2ab4334e9 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -496,10 +496,10 @@ void LocalDerivationGoal::startBuilder() if (!parsedDrv->canBuildLocally(worker.store)) throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", drv->platform, - concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), + dropEmptyInitThenConcatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), worker.store.printStorePath(drvPath), settings.thisSystem, - concatStringsSep(", ", worker.store.systemFeatures)); + dropEmptyInitThenConcatStringsSep(", ", worker.store.systemFeatures)); /* Create a temporary directory where the build will take place. */ @@ -840,7 +840,7 @@ void LocalDerivationGoal::startBuilder() /* Run the builder. */ printMsg(lvlChatty, "executing builder '%1%'", drv->builder); - printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args)); + printMsg(lvlChatty, "using builder args '%1%'", dropEmptyInitThenConcatStringsSep(" ", drv->args)); for (auto & i : drv->env) printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); @@ -1063,7 +1063,7 @@ void LocalDerivationGoal::startBuilder() e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)", worker.store.printStorePath(drvPath), statusToString(status), - concatStringsSep("|", msgs)); + dropEmptyInitThenConcatStringsSep("|", msgs)); throw; } }(); diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 907ca7fc1..81ec9a4c3 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -152,7 +152,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p parsedContents.push_back({ std::move(name), - concatStringsSep(" ", Strings(i, tokens.end())), + dropEmptyInitThenConcatStringsSep(" ", Strings(i, tokens.end())), }); }; } @@ -318,7 +318,7 @@ template<> void BaseSetting::appendOrSet(Strings newValue, bool append) template<> std::string BaseSetting::to_string() const { - return concatStringsSep(" ", value); + return dropEmptyInitThenConcatStringsSep(" ", value); } template<> StringSet BaseSetting::parse(const std::string & str) const @@ -334,7 +334,7 @@ template<> void BaseSetting::appendOrSet(StringSet newValue, bool app template<> std::string BaseSetting::to_string() const { - return concatStringsSep(" ", value); + return dropEmptyInitThenConcatStringsSep(" ", value); } template<> std::set BaseSetting>::parse(const std::string & str) const @@ -362,7 +362,7 @@ template<> std::string BaseSetting>::to_string() c StringSet stringifiedXpFeatures; for (const auto & feature : value) stringifiedXpFeatures.insert(std::string(showExperimentalFeature(feature))); - return concatStringsSep(" ", stringifiedXpFeatures); + return dropEmptyInitThenConcatStringsSep(" ", stringifiedXpFeatures); } template<> StringMap BaseSetting::parse(const std::string & str) const diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 23682ff7c..66cef62ed 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -37,7 +37,7 @@ template C tokenizeString(std::string_view s, std::string_view separato * elements. */ template -std::string concatStringsSep(const std::string_view sep, const C & ss) +std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) { size_t size = 0; // need a cast to string_view since this is also called with Symbols @@ -56,7 +56,7 @@ auto concatStrings(Parts && ... parts) -> std::enable_if_t<(... && std::is_convertible_v), std::string> { std::string_view views[sizeof...(parts)] = { parts... }; - return concatStringsSep({}, views); + return dropEmptyInitThenConcatStringsSep({}, views); } diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 6bd3dc9ef..807c75d4a 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -608,7 +608,7 @@ struct CmdDevelop : Common, MixEnvironment std::vector args; for (auto s : command) args.push_back(shellEscape(s)); - script += fmt("exec %s\n", concatStringsSep(" ", args)); + script += fmt("exec %s\n", dropEmptyInitThenConcatStringsSep(" ", args)); } else { diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index c7c37b66f..204213396 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -49,7 +49,7 @@ std::string showVersions(const std::set & versions) std::set versions2; for (auto & version : versions) versions2.insert(version.empty() ? "ε" : version); - return concatStringsSep(", ", versions2); + return dropEmptyInitThenConcatStringsSep(", ", versions2); } void printClosureDiff( @@ -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)); - logger->cout("%s%s: %s", indent, name, concatStringsSep(", ", items)); + logger->cout("%s%s: %s", indent, name, dropEmptyInitThenConcatStringsSep(", ", items)); } } } diff --git a/src/nix/env.cc b/src/nix/env.cc index bc9cd91ad..7cc019c1d 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -94,7 +94,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = concatStringsSep(":", unixPath); + auto unixPathString = dropEmptyInitThenConcatStringsSep(":", unixPath); setEnv("PATH", unixPathString.c_str()); Strings args; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index cb73778b3..3ee2b1838 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -805,7 +805,7 @@ struct CmdFlakeCheck : FlakeCommand warn( "The check omitted these incompatible systems: %s\n" "Use '--all-systems' to check all.", - concatStringsSep(", ", omittedSystems) + dropEmptyInitThenConcatStringsSep(", ", omittedSystems) ); }; }; @@ -1211,7 +1211,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); try { auto recurse = [&]() @@ -1291,7 +1291,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) @@ -1315,13 +1315,13 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--legacy' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); } } else if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) diff --git a/src/nix/main.cc b/src/nix/main.cc index e39f79f1f..aa4ced623 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -185,7 +185,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs auto & info = i->second; if (info.status == AliasStatus::Deprecated) { warn("'%s' is a deprecated alias for '%s'", - arg, concatStringsSep(" ", info.replacement)); + arg, dropEmptyInitThenConcatStringsSep(" ", info.replacement)); } pos = args.erase(pos); for (auto j = info.replacement.rbegin(); j != info.replacement.rend(); ++j) @@ -238,7 +238,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs lowdown. */ static void showHelp(std::vector subcommand, NixArgs & toplevel) { - auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); + auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", dropEmptyInitThenConcatStringsSep("-", subcommand)); evalSettings.restrictEval = false; evalSettings.pureEval = false; @@ -273,7 +273,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) auto attr = vRes->attrs()->get(state.symbols.create(mdName + ".md")); if (!attr) - throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); + throw UsageError("Nix has no subcommand '%s'", dropEmptyInitThenConcatStringsSep("", subcommand)); auto markdown = state.forceString(*attr->value, noPos, "while evaluating the lowdown help text"); diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 47f9baee5..2383fbe08 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -185,7 +185,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON if (info->ultimate) ss.push_back("ultimate"); if (info->ca) ss.push_back("ca:" + renderContentAddress(*info->ca)); for (auto & sig : info->sigs) ss.push_back(sig); - std::cout << concatStringsSep(" ", ss); + std::cout << dropEmptyInitThenConcatStringsSep(" ", ss); } std::cout << std::endl; diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 78532a2ec..c21eb3040 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -58,7 +58,7 @@ struct ProfileElement StringSet names; for (auto & path : storePaths) names.insert(DrvName(path.name()).name); - return concatStringsSep(", ", names); + return dropEmptyInitThenConcatStringsSep(", ", names); } /** @@ -472,7 +472,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile originalConflictingFilePath, newConflictingFilePath, originalEntryName, - concatStringsSep(" ", newConflictingRefs), + dropEmptyInitThenConcatStringsSep(" ", newConflictingRefs), conflictError.priority, conflictError.priority - 1, conflictError.priority + 1 @@ -813,7 +813,7 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro logger->cout("Original flake URL: %s", element.source->originalRef.to_string()); logger->cout("Locked flake URL: %s", element.source->lockedRef.to_string()); } - logger->cout("Store paths: %s", concatStringsSep(" ", store->printStorePathSet(element.storePaths))); + logger->cout("Store paths: %s", dropEmptyInitThenConcatStringsSep(" ", store->printStorePathSet(element.storePaths))); } } } diff --git a/src/nix/search.cc b/src/nix/search.cc index 97ef1375e..d709774ad 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -96,7 +96,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); try { auto recurse = [&]() { @@ -115,7 +115,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto aDescription = aMeta ? aMeta->maybeGetAttr(state->sDescription) : nullptr; auto description = aDescription ? aDescription->getString() : ""; std::replace(description.begin(), description.end(), '\n', ' '); - auto attrPath2 = concatStringsSep(".", attrPathS); + auto attrPath2 = dropEmptyInitThenConcatStringsSep(".", attrPathS); std::vector attrPathMatches; std::vector descriptionMatches; diff --git a/tests/unit/libutil/references.cc b/tests/unit/libutil/references.cc index a517d9aa1..c3efa6d51 100644 --- a/tests/unit/libutil/references.cc +++ b/tests/unit/libutil/references.cc @@ -15,7 +15,7 @@ struct RewriteParams { strRewrites.insert(from + "->" + to); return os << "OriginalString: " << bar.originalString << std::endl << - "Rewrites: " << concatStringsSep(",", strRewrites) << std::endl << + "Rewrites: " << dropEmptyInitThenConcatStringsSep(",", strRewrites) << std::endl << "Expected result: " << bar.finalString; } }; diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index 9be4a400d..8a3ca8561 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -227,32 +227,32 @@ namespace nix { } /* ---------------------------------------------------------------------------- - * concatStringsSep + * dropEmptyInitThenConcatStringsSep * --------------------------------------------------------------------------*/ - TEST(concatStringsSep, buildCommaSeparatedString) { + TEST(dropEmptyInitThenConcatStringsSep, buildCommaSeparatedString) { Strings strings; strings.push_back("this"); strings.push_back("is"); strings.push_back("great"); - ASSERT_EQ(concatStringsSep(",", strings), "this,is,great"); + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this,is,great"); } - TEST(concatStringsSep, buildStringWithEmptySeparator) { + TEST(dropEmptyInitThenConcatStringsSep, buildStringWithEmptySeparator) { Strings strings; strings.push_back("this"); strings.push_back("is"); strings.push_back("great"); - ASSERT_EQ(concatStringsSep("", strings), "thisisgreat"); + ASSERT_EQ(dropEmptyInitThenConcatStringsSep("", strings), "thisisgreat"); } - TEST(concatStringsSep, buildSingleString) { + TEST(dropEmptyInitThenConcatStringsSep, buildSingleString) { Strings strings; strings.push_back("this"); - ASSERT_EQ(concatStringsSep(",", strings), "this"); + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this"); } /* ---------------------------------------------------------------------------- From 79eb0adf9db942609b22d55ff764d1e759453543 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 22:33:14 +0200 Subject: [PATCH 547/910] dropEmptyInitThenConcatStringSep: Check that we don't drop... ... initial empty strings. The tests pass, which is encouraging. --- src/libexpr/symbol-table.hh | 5 +++++ src/libutil/util.hh | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index b85725e12..c7a3563b0 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -41,6 +41,11 @@ public: } friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol); + + bool empty() const + { + return s->empty(); + } }; /** diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 66cef62ed..c545afd9e 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -40,6 +40,13 @@ template std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) { size_t size = 0; + + for (auto & i : ss) { + // Make sure we don't rely on the empty item ignoring behavior + assert(!i.empty()); + break; + } + // need a cast to string_view since this is also called with Symbols for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); std::string s; From a681d354e717a549595e9b687567dc7d05eaf29d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 23:02:08 +0200 Subject: [PATCH 548/910] Add fresh concatStringsSep without bug The buggy version was previously renamed to dropEmptyInitThenConcatStringsSep --- src/libutil/meson.build | 3 ++ src/libutil/strings-inline.hh | 31 +++++++++++++ src/libutil/strings.cc | 12 +++++ src/libutil/strings.hh | 21 +++++++++ tests/unit/libutil/strings.cc | 83 +++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 src/libutil/strings-inline.hh create mode 100644 src/libutil/strings.cc create mode 100644 src/libutil/strings.hh create mode 100644 tests/unit/libutil/strings.cc diff --git a/src/libutil/meson.build b/src/libutil/meson.build index ac2b83536..fbfcbe67c 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -148,6 +148,7 @@ sources = files( 'signature/signer.cc', 'source-accessor.cc', 'source-path.cc', + 'strings.cc', 'suggestions.cc', 'tarfile.cc', 'terminal.cc', @@ -215,6 +216,8 @@ headers = [config_h] + files( 'source-accessor.hh', 'source-path.hh', 'split.hh', + 'strings.hh', + 'strings-inline.hh', 'suggestions.hh', 'sync.hh', 'tarfile.hh', diff --git a/src/libutil/strings-inline.hh b/src/libutil/strings-inline.hh new file mode 100644 index 000000000..10c1b19e6 --- /dev/null +++ b/src/libutil/strings-inline.hh @@ -0,0 +1,31 @@ +#pragma once + +#include "strings.hh" + +namespace nix { + +template +std::string concatStringsSep(const std::string_view sep, const C & ss) +{ + size_t size = 0; + bool tail = false; + // need a cast to string_view since this is also called with Symbols + for (const auto & s : ss) { + if (tail) + size += sep.size(); + size += std::string_view(s).size(); + tail = true; + } + std::string s; + s.reserve(size); + tail = false; + for (auto & i : ss) { + if (tail) + s += sep; + s += i; + tail = true; + } + return s; +} + +} // namespace nix diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc new file mode 100644 index 000000000..15937d415 --- /dev/null +++ b/src/libutil/strings.cc @@ -0,0 +1,12 @@ +#include + +#include "strings-inline.hh" +#include "util.hh" + +namespace nix { + +template std::string concatStringsSep(std::string_view, const Strings &); +template std::string concatStringsSep(std::string_view, const StringSet &); +template std::string concatStringsSep(std::string_view, const std::vector &); + +} // namespace nix diff --git a/src/libutil/strings.hh b/src/libutil/strings.hh new file mode 100644 index 000000000..3b112c409 --- /dev/null +++ b/src/libutil/strings.hh @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nix { + +/** + * Concatenate the given strings with a separator between the elements. + */ +template +std::string concatStringsSep(const std::string_view sep, const C & ss); + +extern template std::string concatStringsSep(std::string_view, const std::list &); +extern template std::string concatStringsSep(std::string_view, const std::set &); +extern template std::string concatStringsSep(std::string_view, const std::vector &); + +} diff --git a/tests/unit/libutil/strings.cc b/tests/unit/libutil/strings.cc new file mode 100644 index 000000000..47a20770e --- /dev/null +++ b/tests/unit/libutil/strings.cc @@ -0,0 +1,83 @@ +#include + +#include "strings.hh" + +namespace nix { + +using Strings = std::vector; + +/* ---------------------------------------------------------------------------- + * concatStringsSep + * --------------------------------------------------------------------------*/ + +TEST(concatStringsSep, empty) +{ + Strings strings; + + ASSERT_EQ(concatStringsSep(",", strings), ""); +} + +TEST(concatStringsSep, justOne) +{ + Strings strings; + strings.push_back("this"); + + ASSERT_EQ(concatStringsSep(",", strings), "this"); +} + +TEST(concatStringsSep, emptyString) +{ + Strings strings; + strings.push_back(""); + + ASSERT_EQ(concatStringsSep(",", strings), ""); +} + +TEST(concatStringsSep, emptyStrings) +{ + Strings strings; + strings.push_back(""); + strings.push_back(""); + + ASSERT_EQ(concatStringsSep(",", strings), ","); +} + +TEST(concatStringsSep, threeEmptyStrings) +{ + Strings strings; + strings.push_back(""); + strings.push_back(""); + strings.push_back(""); + + ASSERT_EQ(concatStringsSep(",", strings), ",,"); +} + +TEST(concatStringsSep, buildCommaSeparatedString) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); + + ASSERT_EQ(concatStringsSep(",", strings), "this,is,great"); +} + +TEST(concatStringsSep, buildStringWithEmptySeparator) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); + + ASSERT_EQ(concatStringsSep("", strings), "thisisgreat"); +} + +TEST(concatStringsSep, buildSingleString) +{ + Strings strings; + strings.push_back("this"); + + ASSERT_EQ(concatStringsSep(",", strings), "this"); +} + +} // namespace nix From ea966a70fcae93538f41ff413fb9cec852054b3c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 23:06:32 +0200 Subject: [PATCH 549/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: diagnostics and docs These are non-critical, so their behavior is ok to change. Dropping empty items is not needed and usually not expected. --- src/build-remote/build-remote.cc | 9 +++++---- src/libcmd/command.cc | 7 ++++--- src/libcmd/installables.cc | 4 +++- src/libcmd/repl.cc | 4 +++- src/libflake/flake/lockfile.cc | 6 ++++-- src/libmain/shared.cc | 7 ++++--- src/libstore/build/entry-points.cc | 3 ++- src/libstore/misc.cc | 1 + src/libstore/unix/build/hook-instance.cc | 3 ++- 9 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 1c3ce930a..600fc7ee2 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -16,6 +16,7 @@ #include "serialise.hh" #include "build-result.hh" #include "store-api.hh" +#include "strings.hh" #include "derivations.hh" #include "local-store.hh" #include "legacy.hh" @@ -206,15 +207,15 @@ static int main_build_remote(int argc, char * * argv) error % drvstr % neededSystem - % dropEmptyInitThenConcatStringsSep(", ", requiredFeatures) + % concatStringsSep(", ", requiredFeatures) % machines.size(); for (auto & m : machines) error - % dropEmptyInitThenConcatStringsSep(", ", m.systemTypes) + % concatStringsSep(", ", m.systemTypes) % m.maxJobs - % dropEmptyInitThenConcatStringsSep(", ", m.supportedFeatures) - % dropEmptyInitThenConcatStringsSep(", ", m.mandatoryFeatures); + % concatStringsSep(", ", m.supportedFeatures) + % concatStringsSep(", ", m.mandatoryFeatures); printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str()); diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 891a01f91..67fef1909 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -1,3 +1,5 @@ +#include + #include "command.hh" #include "markdown.hh" #include "store-api.hh" @@ -6,8 +8,7 @@ #include "nixexpr.hh" #include "profiles.hh" #include "repl.hh" - -#include +#include "strings.hh" extern char * * environ __attribute__((weak)); @@ -42,7 +43,7 @@ void NixMultiCommand::run() for (auto & [name, _] : commands) subCommandTextLines.insert(fmt("- `%s`", name)); std::string markdownError = fmt("`nix %s` requires a sub-command. Available sub-commands:\n\n%s\n", - commandName, dropEmptyInitThenConcatStringsSep("\n", subCommandTextLines)); + commandName, concatStringsSep("\n", subCommandTextLines)); throw UsageError(renderMarkdownToTerminal(markdownError)); } command->second->run(); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 1f6ee1e23..406e4bfd8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -27,6 +27,8 @@ #include +#include "strings-inline.hh" + namespace nix { void completeFlakeInputPath( @@ -630,7 +632,7 @@ static void throwBuildErrors( } failedPaths.insert(failedResult->path.to_string(store)); } - throw Error("build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failedPaths))); + throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); } } } diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 1d39ef167..37a34e3de 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -33,6 +33,8 @@ #include #endif +#include "strings.hh" + namespace nix { /** @@ -625,7 +627,7 @@ ProcessLineResult NixRepl::processLine(std::string line) markdown += "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " - + dropEmptyInitThenConcatStringsSep(" ", args) + "\n\n"; + + concatStringsSep(" ", args) + "\n\n"; } markdown += stripIndentation(doc->doc); diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index f0e22a75a..80f14ff6f 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -9,6 +9,8 @@ #include #include +#include "strings.hh" + namespace nix::flake { static FlakeRef getFlakeRef( @@ -61,7 +63,7 @@ static std::shared_ptr doFind(const ref& root, const InputPath & pat std::vector cycle; std::transform(found, visited.cend(), std::back_inserter(cycle), printInputPath); cycle.push_back(printInputPath(path)); - throw Error("follow cycle detected: [%s]", dropEmptyInitThenConcatStringsSep(" -> ", cycle)); + throw Error("follow cycle detected: [%s]", concatStringsSep(" -> ", cycle)); } visited.push_back(path); @@ -367,7 +369,7 @@ void check(); std::string printInputPath(const InputPath & path) { - return dropEmptyInitThenConcatStringsSep("/", path); + return concatStringsSep("/", path); } } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 681c1039f..a224f8d92 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -23,6 +23,7 @@ #include #include "exit.hh" +#include "strings.hh" namespace nix { @@ -301,11 +302,11 @@ void printVersion(const std::string & programName) #endif cfg.push_back("signed-caches"); std::cout << "System type: " << settings.thisSystem << "\n"; - std::cout << "Additional system types: " << dropEmptyInitThenConcatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; - std::cout << "Features: " << dropEmptyInitThenConcatStringsSep(", ", cfg) << "\n"; + std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; + std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n"; std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n"; std::cout << "User configuration files: " << - dropEmptyInitThenConcatStringsSep(":", settings.nixUserConfFiles) + concatStringsSep(":", settings.nixUserConfFiles) << "\n"; std::cout << "Store directory: " << settings.nixStore << "\n"; std::cout << "State directory: " << settings.nixStateDir << "\n"; diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index 8bf7ad35d..4c1373bfa 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -4,6 +4,7 @@ # include "derivation-goal.hh" #endif #include "local-store.hh" +#include "strings.hh" namespace nix { @@ -42,7 +43,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod throw std::move(*ex); } else if (!failed.empty()) { if (ex) logError(ex->info()); - throw Error(worker.failingExitStatus(), "build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failed))); + throw Error(worker.failingExitStatus(), "build of %s failed", concatStringsSep(", ", quoteStrings(failed))); } } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 179e5478c..fbfa15f51 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -10,6 +10,7 @@ #include "callback.hh" #include "closure.hh" #include "filetransfer.hh" +#include "strings.hh" namespace nix { diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index ba6c3a912..d73d86ff2 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -3,12 +3,13 @@ #include "hook-instance.hh" #include "file-system.hh" #include "child.hh" +#include "strings.hh" namespace nix { HookInstance::HookInstance() { - debug("starting build hook '%s'", dropEmptyInitThenConcatStringsSep(" ", settings.buildHook.get())); + debug("starting build hook '%s'", concatStringsSep(" ", settings.buildHook.get())); auto buildHookArgs = settings.buildHook.get(); From 39878c89798a95a423395ccfbc64d49f0fad92b9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 23:08:23 +0200 Subject: [PATCH 550/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: preserve empty attr The empty attribute name should not be dropped from attribute paths. Rendering attribute paths with concatStringsSep is lossy and wrong, but this is just a first improvement while dealing with the dropEmptyInitThenConcatStringsSep problem. --- src/libcmd/installables.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 406e4bfd8..0fe956ec0 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -376,7 +376,8 @@ void completeFlakeRefWithFragment( auto attrPath2 = (*attr)->getAttrPath(attr2); /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); - completions.add(flakeRefS + "#" + prefixRoot + dropEmptyInitThenConcatStringsSep(".", evalState->symbols.resolve(attrPath2))); + // FIXME: handle names with dots + completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); } } } From 3f37785afd002a36ecd5070b16c59902eba9cf88 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:07:42 +0200 Subject: [PATCH 551/910] NIX_REMOTE_SYSTEMS: actually support multiple :-separated entries Bug not reported in 6 years, but here you go. Also it is safe to switch to normal concatStringsSep behavior because tokenizeString does not produce empty items. --- src/libstore/globals.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 5f5b4f89b..4eabf6054 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -35,6 +35,8 @@ #include #endif +#include "strings.hh" + namespace nix { @@ -82,7 +84,7 @@ Settings::Settings() Strings ss; for (auto & p : tokenizeString(*s, ":")) ss.push_back("@" + p); - builders = dropEmptyInitThenConcatStringsSep(" ", ss); + builders = concatStringsSep("\n", ss); } #if defined(__linux__) && defined(SANDBOX_SHELL) From 75dde71ff97fcafeeefa08a5c9adba3414693ea2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:11:14 +0200 Subject: [PATCH 552/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: sigs are non-empty The sigs field is produced by tokenizeStrings, which does not return empty strings. --- src/libstore/local-store.cc | 10 ++++++---- src/libstore/nar-info-disk-cache.cc | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 8764b88b7..82b70ff21 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -51,6 +51,8 @@ #include +#include "strings.hh" + namespace nix { @@ -656,7 +658,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) combinedSignatures.insert(info.signatures.begin(), info.signatures.end()); state->stmts->UpdateRealisedOutput.use() - (dropEmptyInitThenConcatStringsSep(" ", combinedSignatures)) + (concatStringsSep(" ", combinedSignatures)) (info.id.strHash()) (info.id.outputName) .exec(); @@ -675,7 +677,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) (info.id.strHash()) (info.id.outputName) (printStorePath(info.outPath)) - (dropEmptyInitThenConcatStringsSep(" ", info.signatures)) + (concatStringsSep(" ", info.signatures)) .exec(); } for (auto & [outputId, depPath] : info.dependentRealisations) { @@ -729,7 +731,7 @@ uint64_t LocalStore::addValidPath(State & state, (info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver) (info.narSize, info.narSize != 0) (info.ultimate ? 1 : 0, info.ultimate) - (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) + (concatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) .exec(); uint64_t id = state.db.getLastInsertedRowId(); @@ -833,7 +835,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) (info.narSize, info.narSize != 0) (info.narHash.to_string(HashFormat::Base16, true)) (info.ultimate ? 1 : 0, info.ultimate) - (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) + (concatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) (printStorePath(info.path)) .exec(); diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 42d172237..c75043237 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -7,6 +7,8 @@ #include #include +#include "strings.hh" + namespace nix { static const char * schema = R"sql( @@ -339,7 +341,7 @@ public: (info->narSize) (dropEmptyInitThenConcatStringsSep(" ", info->shortRefs())) (info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver) - (dropEmptyInitThenConcatStringsSep(" ", info->sigs)) + (concatStringsSep(" ", info->sigs)) (renderContentAddress(info->ca)) (time(0)).exec(); From 608a425550b3c90de39d4a668a504debcc8c53de Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:13:58 +0200 Subject: [PATCH 553/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: diag --- src/libstore/misc.cc | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 6 ++++-- src/nix/diff-closures.cc | 2 +- src/nix/profile.cc | 4 +++- src/nix/search.cc | 4 +++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index fbfa15f51..bcc02206b 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -465,7 +465,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd) if (!outputsLeft.empty()) throw Error("derivation '%s' does not have an outputs %s", store.printStorePath(drvPath), - dropEmptyInitThenConcatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); + concatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); return outputMap; } diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 2ab4334e9..523fe07e7 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -64,6 +64,8 @@ #include #include +#include "strings.hh" + namespace nix { void handleDiffHook( @@ -840,7 +842,7 @@ void LocalDerivationGoal::startBuilder() /* Run the builder. */ printMsg(lvlChatty, "executing builder '%1%'", drv->builder); - printMsg(lvlChatty, "using builder args '%1%'", dropEmptyInitThenConcatStringsSep(" ", drv->args)); + printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args)); for (auto & i : drv->env) printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); @@ -1063,7 +1065,7 @@ void LocalDerivationGoal::startBuilder() e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)", worker.store.printStorePath(drvPath), statusToString(status), - dropEmptyInitThenConcatStringsSep("|", msgs)); + concatStringsSep("|", msgs)); throw; } }(); diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 204213396..9e5a7e4c3 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)); - logger->cout("%s%s: %s", indent, name, dropEmptyInitThenConcatStringsSep(", ", items)); + logger->cout("%s%s: %s", indent, name, concatStringsSep(", ", items)); } } } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c21eb3040..548a793d8 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -17,6 +17,8 @@ #include #include +#include "strings.hh" + using namespace nix; struct ProfileElementSource @@ -472,7 +474,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile originalConflictingFilePath, newConflictingFilePath, originalEntryName, - dropEmptyInitThenConcatStringsSep(" ", newConflictingRefs), + concatStringsSep(" ", newConflictingRefs), conflictError.priority, conflictError.priority - 1, conflictError.priority + 1 diff --git a/src/nix/search.cc b/src/nix/search.cc index d709774ad..7c46f9fec 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -15,6 +15,8 @@ #include #include +#include "strings.hh" + using namespace nix; using json = nlohmann::json; @@ -96,7 +98,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); try { auto recurse = [&]() { From d3e49ac881654529f8de3e2a3b39f5840d8ed669 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:50:39 +0200 Subject: [PATCH 554/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: shortRefs are not empty --- src/libstore/nar-info-disk-cache.cc | 2 +- src/libstore/nar-info.cc | 3 ++- src/libstore/path-info.hh | 3 +++ tests/unit/libstore/path-info.cc | 16 ++++++++++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index c75043237..288f618d5 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -339,7 +339,7 @@ public: (narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize) (info->narHash.to_string(HashFormat::Nix32, true)) (info->narSize) - (dropEmptyInitThenConcatStringsSep(" ", info->shortRefs())) + (concatStringsSep(" ", info->shortRefs())) (info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver) (concatStringsSep(" ", info->sigs)) (renderContentAddress(info->ca)) diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 577466f55..2442a7b09 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -1,6 +1,7 @@ #include "globals.hh" #include "nar-info.hh" #include "store-api.hh" +#include "strings.hh" namespace nix { @@ -111,7 +112,7 @@ std::string NarInfo::to_string(const Store & store) const res += "NarHash: " + narHash.to_string(HashFormat::Nix32, true) + "\n"; res += "NarSize: " + std::to_string(narSize) + "\n"; - res += "References: " + dropEmptyInitThenConcatStringsSep(" ", shortRefs()) + "\n"; + res += "References: " + concatStringsSep(" ", shortRefs()) + "\n"; if (deriver) res += "Deriver: " + std::string(deriver->to_string()) + "\n"; diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh index caefa7975..71f1476a6 100644 --- a/src/libstore/path-info.hh +++ b/src/libstore/path-info.hh @@ -171,6 +171,9 @@ struct ValidPathInfo : UnkeyedValidPathInfo { */ bool checkSignature(const Store & store, const PublicKeys & publicKeys, const std::string & sig) const; + /** + * References as store path basenames, including a self reference if it has one. + */ Strings shortRefs() const; ValidPathInfo(const ValidPathInfo & other) = default; diff --git a/tests/unit/libstore/path-info.cc b/tests/unit/libstore/path-info.cc index 7637cb366..9e9c6303d 100644 --- a/tests/unit/libstore/path-info.cc +++ b/tests/unit/libstore/path-info.cc @@ -26,9 +26,9 @@ static UnkeyedValidPathInfo makeEmpty() }; } -static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo) +static ValidPathInfo makeFullKeyed(const Store & store, bool includeImpureInfo) { - UnkeyedValidPathInfo info = ValidPathInfo { + ValidPathInfo info = ValidPathInfo { store, "foo", FixedOutputInfo { @@ -57,6 +57,9 @@ static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo } return info; } +static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo) { + return makeFullKeyed(store, includeImpureInfo); +} #define JSON_TEST(STEM, OBJ, PURE) \ TEST_F(PathInfoTest, PathInfo_ ## STEM ## _from_json) { \ @@ -86,4 +89,13 @@ JSON_TEST(empty_impure, makeEmpty(), true) JSON_TEST(pure, makeFull(*store, false), false) JSON_TEST(impure, makeFull(*store, true), true) +TEST_F(PathInfoTest, PathInfo_full_shortRefs) { + ValidPathInfo it = makeFullKeyed(*store, true); + // it.references = unkeyed.references; + auto refs = it.shortRefs(); + ASSERT_EQ(refs.size(), 2); + ASSERT_EQ(*refs.begin(), "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"); + ASSERT_EQ(*++refs.begin(), "n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"); } + +} // namespace nix From 49d100ba8b5d65c6f2df909e53ec92ba279cfc4d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:00:06 +0200 Subject: [PATCH 555/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: output name empty not feasible I don't think it's completely impossible, but I can't construct one easily as derivationStrict seems to (re)tokenize the outputs attribute, dropping the empty output. It's not a scenario we have to account for here. --- src/libstore/build/derivation-goal.cc | 2 +- src/libstore/outputs-spec.cc | 3 ++- src/libstore/path-with-outputs.cc | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index c0a784349..99d9cceda 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1505,7 +1505,7 @@ std::pair DerivationGoal::checkPathValidity() if (!wantedOutputsLeft.empty()) throw Error("derivation '%s' does not have wanted outputs %s", worker.store.printStorePath(drvPath), - dropEmptyInitThenConcatStringsSep(", ", quoteStrings(wantedOutputsLeft))); + concatStringsSep(", ", quoteStrings(wantedOutputsLeft))); bool allValid = true; for (auto & [_, status] : initialOutputs) { diff --git a/src/libstore/outputs-spec.cc b/src/libstore/outputs-spec.cc index 4ed8f95ae..86788a87e 100644 --- a/src/libstore/outputs-spec.cc +++ b/src/libstore/outputs-spec.cc @@ -5,6 +5,7 @@ #include "regex-combinators.hh" #include "outputs-spec.hh" #include "path-regex.hh" +#include "strings-inline.hh" namespace nix { @@ -83,7 +84,7 @@ std::string OutputsSpec::to_string() const return "*"; }, [&](const OutputsSpec::Names & outputNames) -> std::string { - return dropEmptyInitThenConcatStringsSep(",", outputNames); + return concatStringsSep(",", outputNames); }, }, raw); } diff --git a/src/libstore/path-with-outputs.cc b/src/libstore/path-with-outputs.cc index 5fa38d5d9..161d023d1 100644 --- a/src/libstore/path-with-outputs.cc +++ b/src/libstore/path-with-outputs.cc @@ -1,7 +1,9 @@ +#include + #include "path-with-outputs.hh" #include "store-api.hh" +#include "strings.hh" -#include namespace nix { @@ -9,7 +11,7 @@ std::string StorePathWithOutputs::to_string(const StoreDirConfig & store) const { return outputs.empty() ? store.printStorePath(path) - : store.printStorePath(path) + "!" + dropEmptyInitThenConcatStringsSep(",", outputs); + : store.printStorePath(path) + "!" + concatStringsSep(",", outputs); } From f1966e22d9e959b6885bd9270d2789b484690aa8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:02:30 +0200 Subject: [PATCH 556/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: store paths are not empty --- src/libstore/build/derivation-goal.cc | 4 +++- src/libstore/path-info.cc | 3 ++- src/libstore/store-api.cc | 4 +++- src/nix/profile.cc | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 99d9cceda..f795b05a1 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -32,6 +32,8 @@ #include +#include "strings.hh" + namespace nix { DerivationGoal::DerivationGoal(const StorePath & drvPath, @@ -895,7 +897,7 @@ void runPostBuildHook( std::map hookEnvironment = getEnv(); hookEnvironment.emplace("DRV_PATH", store.printStorePath(drvPath)); - hookEnvironment.emplace("OUT_PATHS", chomp(dropEmptyInitThenConcatStringsSep(" ", store.printStorePathSet(outputPaths)))); + hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths)))); hookEnvironment.emplace("NIX_CONFIG", globalConfig.toKeyValue()); struct LogSink : Sink { diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index a13bb8bef..6e87e60f4 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -4,6 +4,7 @@ #include "store-api.hh" #include "json-utils.hh" #include "comparator.hh" +#include "strings.hh" namespace nix { @@ -30,7 +31,7 @@ std::string ValidPathInfo::fingerprint(const Store & store) const "1;" + store.printStorePath(path) + ";" + narHash.to_string(HashFormat::Nix32, true) + ";" + std::to_string(narSize) + ";" - + dropEmptyInitThenConcatStringsSep(",", store.printStorePathSet(references)); + + concatStringsSep(",", store.printStorePathSet(references)); } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 6904996b5..2c4dee518 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -22,6 +22,8 @@ #include #include +#include "strings.hh" + using json = nlohmann::json; namespace nix { @@ -1208,7 +1210,7 @@ std::string StoreDirConfig::showPaths(const StorePathSet & paths) std::string showPaths(const PathSet & paths) { - return dropEmptyInitThenConcatStringsSep(", ", quoteStrings(paths)); + return concatStringsSep(", ", quoteStrings(paths)); } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 548a793d8..1096f4386 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -815,7 +815,7 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro logger->cout("Original flake URL: %s", element.source->originalRef.to_string()); logger->cout("Locked flake URL: %s", element.source->lockedRef.to_string()); } - logger->cout("Store paths: %s", dropEmptyInitThenConcatStringsSep(" ", store->printStorePathSet(element.storePaths))); + logger->cout("Store paths: %s", concatStringsSep(" ", store->printStorePathSet(element.storePaths))); } } } From e64643bf6372af1ee7f56ac602f25564201df7f0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:12:28 +0200 Subject: [PATCH 557/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: feature should not be empty (System) features are unlikely to be empty strings, but when they come in through structuredAttrs, they probably can. I don't think this means we should drop them, but most likely they will be dropped after this because next time they'll be parsed with tokenizeString. TODO: We should forbid empty features. --- src/libstore/unix/build/local-derivation-goal.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 523fe07e7..c3a65e34b 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -498,10 +498,10 @@ void LocalDerivationGoal::startBuilder() if (!parsedDrv->canBuildLocally(worker.store)) throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", drv->platform, - dropEmptyInitThenConcatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), + concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), worker.store.printStorePath(drvPath), settings.thisSystem, - dropEmptyInitThenConcatStringsSep(", ", worker.store.systemFeatures)); + concatStringsSep(", ", worker.store.systemFeatures)); /* Create a temporary directory where the build will take place. */ From 3b77f134515866f28cc7b7ddb06274fccc5766f9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:16:33 +0200 Subject: [PATCH 558/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: experimental features do not render as empty strings --- src/libutil/config.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 81ec9a4c3..25bfe462f 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -9,6 +9,8 @@ #include +#include "strings.hh" + namespace nix { Config::Config(StringMap initials) @@ -362,7 +364,7 @@ template<> std::string BaseSetting>::to_string() c StringSet stringifiedXpFeatures; for (const auto & feature : value) stringifiedXpFeatures.insert(std::string(showExperimentalFeature(feature))); - return dropEmptyInitThenConcatStringsSep(" ", stringifiedXpFeatures); + return concatStringsSep(" ", stringifiedXpFeatures); } template<> StringMap BaseSetting::parse(const std::string & str) const From 837c3612d40c1e33be94080b9b2063c3ca0795ed Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:20:20 +0200 Subject: [PATCH 559/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: escaped shell args are never empty --- src/nix/develop.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 807c75d4a..7cc0965a9 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -19,6 +19,8 @@ #include #include +#include "strings.hh" + using namespace nix; struct DevelopSettings : Config @@ -608,7 +610,7 @@ struct CmdDevelop : Common, MixEnvironment std::vector args; for (auto s : command) args.push_back(shellEscape(s)); - script += fmt("exec %s\n", dropEmptyInitThenConcatStringsSep(" ", args)); + script += fmt("exec %s\n", concatStringsSep(" ", args)); } else { From 4b34feb4c2fded8b4ca0968f33f0e34514520b1a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:22:51 +0200 Subject: [PATCH 560/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: system string should not be empty --- src/nix/flake.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3ee2b1838..1ed071ec8 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -21,6 +21,8 @@ #include #include +#include "strings-inline.hh" + using namespace nix; using namespace nix::flake; using json = nlohmann::json; @@ -802,10 +804,11 @@ struct CmdFlakeCheck : FlakeCommand throw Error("some errors were encountered during the evaluation"); if (!omittedSystems.empty()) { + // TODO: empty system is not visible; render all as nix strings? warn( "The check omitted these incompatible systems: %s\n" "Use '--all-systems' to check all.", - dropEmptyInitThenConcatStringsSep(", ", omittedSystems) + concatStringsSep(", ", omittedSystems) ); }; }; From 0480bfe50bf1deb3b5c93761f7fbba1bc59ef059 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:23:44 +0200 Subject: [PATCH 561/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: do not drop attributes with empty names Empty attributes are probably not well supported, but the least we could do is leave a hint. Attribute path rendering and parsing should be done according to Nix expression syntax in my opinion. --- src/nix/flake.cc | 8 ++++---- src/nix/search.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 1ed071ec8..3f9f8f99b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1214,7 +1214,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); try { auto recurse = [&]() @@ -1294,7 +1294,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) @@ -1318,13 +1318,13 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--legacy' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); } } else if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) diff --git a/src/nix/search.cc b/src/nix/search.cc index 7c46f9fec..7f8504d3f 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -117,7 +117,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto aDescription = aMeta ? aMeta->maybeGetAttr(state->sDescription) : nullptr; auto description = aDescription ? aDescription->getString() : ""; std::replace(description.begin(), description.end(), '\n', ' '); - auto attrPath2 = dropEmptyInitThenConcatStringsSep(".", attrPathS); + auto attrPath2 = concatStringsSep(".", attrPathS); std::vector attrPathMatches; std::vector descriptionMatches; From 062672b022a35fb0251e37cc0f428778d82fb934 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:27:59 +0200 Subject: [PATCH 562/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: CLI commands are not empty --- src/nix/main.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index aa4ced623..21d364a18 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -41,6 +41,8 @@ extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); #endif +#include "strings.hh" + namespace nix { enum struct AliasStatus { @@ -185,7 +187,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs auto & info = i->second; if (info.status == AliasStatus::Deprecated) { warn("'%s' is a deprecated alias for '%s'", - arg, dropEmptyInitThenConcatStringsSep(" ", info.replacement)); + arg, concatStringsSep(" ", info.replacement)); } pos = args.erase(pos); for (auto j = info.replacement.rbegin(); j != info.replacement.rend(); ++j) From d9043021dfc4b7238f538c8064a3587a6a8d473d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:29:15 +0200 Subject: [PATCH 563/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: break nix help "" "" "" build Garbage in, error out. Experimental CLI. Zero derivations given. --- src/nix/main.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index 21d364a18..00ad6fe2c 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -240,7 +240,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs lowdown. */ static void showHelp(std::vector subcommand, NixArgs & toplevel) { - auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", dropEmptyInitThenConcatStringsSep("-", subcommand)); + auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); evalSettings.restrictEval = false; evalSettings.pureEval = false; @@ -275,7 +275,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) auto attr = vRes->attrs()->get(state.symbols.create(mdName + ".md")); if (!attr) - throw UsageError("Nix has no subcommand '%s'", dropEmptyInitThenConcatStringsSep("", subcommand)); + throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); auto markdown = state.forceString(*attr->value, noPos, "while evaluating the lowdown help text"); From cf3c5cd189b5818e837f308507db3e92a81d66d0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:34:41 +0200 Subject: [PATCH 564/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: showVersions version is not empty --- src/nix/diff-closures.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 9e5a7e4c3..46c94b211 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -6,6 +6,8 @@ #include +#include "strings.hh" + namespace nix { struct Info @@ -49,7 +51,7 @@ std::string showVersions(const std::set & versions) std::set versions2; for (auto & version : versions) versions2.insert(version.empty() ? "ε" : version); - return dropEmptyInitThenConcatStringsSep(", ", versions2); + return concatStringsSep(", ", versions2); } void printClosureDiff( From 0fe3525223976e1c0ad047cc706da66208214fb5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:39:06 +0200 Subject: [PATCH 565/910] illegal configuration line -> syntax error in configuration line The law has nothing to do with this, although I do feel like a badass when I mess with the config. I'm a conf artist. --- src/libutil/config.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 25bfe462f..b3e2d1a48 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -116,7 +116,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p if (tokens.empty()) continue; if (tokens.size() < 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("syntax error in configuration line '%1%' in '%2%'", line, path); auto include = false; auto ignoreMissing = false; @@ -129,7 +129,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p if (include) { if (tokens.size() != 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("syntax error in configuration line '%1%' in '%2%'", line, path); auto p = absPath(tokens[1], dirOf(path)); if (pathExists(p)) { try { @@ -145,7 +145,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p } if (tokens[1] != "=") - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("syntax error in configuration line '%1%' in '%2%'", line, path); std::string name = std::move(tokens[0]); From 4029426ca8e8d48a74222d2058ff152d7fd36027 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:41:49 +0200 Subject: [PATCH 566/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: tokens from tokenizeString are not empty --- src/libutil/config.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index b3e2d1a48..6d929b7f7 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -154,7 +154,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p parsedContents.push_back({ std::move(name), - dropEmptyInitThenConcatStringsSep(" ", Strings(i, tokens.end())), + concatStringsSep(" ", Strings(i, tokens.end())), }); }; } From 9ca42d5da20cbbb76cd2f35f2a442d70505bf862 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:43:11 +0200 Subject: [PATCH 567/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: setting value was already harmed Considering that `value` was probably parsed with tokenizeString prior, it's unlikely to contain empty strings, and we have no reason to remove them either. --- src/libutil/config.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 6d929b7f7..726e5091e 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -320,7 +320,7 @@ template<> void BaseSetting::appendOrSet(Strings newValue, bool append) template<> std::string BaseSetting::to_string() const { - return dropEmptyInitThenConcatStringsSep(" ", value); + return concatStringsSep(" ", value); } template<> StringSet BaseSetting::parse(const std::string & str) const @@ -336,7 +336,7 @@ template<> void BaseSetting::appendOrSet(StringSet newValue, bool app template<> std::string BaseSetting::to_string() const { - return dropEmptyInitThenConcatStringsSep(" ", value); + return concatStringsSep(" ", value); } template<> std::set BaseSetting>::parse(const std::string & str) const From 76b2d5ef3ddd876fbe825f5804f1331dfcf2ecfe Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:49:34 +0200 Subject: [PATCH 568/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: PATH handling It's still wrong, but one step closer to correct. Not that anyone should use "" or "." in their PATH, but that is not for us to intervene. --- src/nix/env.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/env.cc b/src/nix/env.cc index 7cc019c1d..bc4e1b5e5 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -3,6 +3,7 @@ #include "command.hh" #include "run.hh" +#include "strings.hh" using namespace nix; @@ -92,9 +93,10 @@ struct CmdShell : InstallablesCommand, MixEnvironment } } + // TODO: split losslessly; empty means . auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = dropEmptyInitThenConcatStringsSep(":", unixPath); + auto unixPathString = concatStringsSep(":", unixPath); setEnv("PATH", unixPathString.c_str()); Strings args; From 6b2c277c363f59b0e200882644deb5d0e658ec20 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:51:50 +0200 Subject: [PATCH 569/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: sigs are not empty ... but if they are, I'd like to see at least a hint of it so that I'd know to fix it. --- src/nix/path-info.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 2383fbe08..e7cfb6e7a 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -9,6 +9,8 @@ #include +#include "strings.hh" + using namespace nix; using nlohmann::json; @@ -185,7 +187,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON if (info->ultimate) ss.push_back("ultimate"); if (info->ca) ss.push_back("ca:" + renderContentAddress(*info->ca)); for (auto & sig : info->sigs) ss.push_back(sig); - std::cout << dropEmptyInitThenConcatStringsSep(" ", ss); + std::cout << concatStringsSep(" ", ss); } std::cout << std::endl; From 1c97718146264de4c4bc6e1694774da5d5b31188 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 02:16:13 +0200 Subject: [PATCH 570/910] dropEmptyInitThenConcatStringsSep: Allow it to drop items again It's usually harmless, if it occurs at all. --- src/libutil/util.hh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index c545afd9e..b653bf115 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -41,11 +41,13 @@ std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const { size_t size = 0; - for (auto & i : ss) { - // Make sure we don't rely on the empty item ignoring behavior - assert(!i.empty()); - break; - } + // TODO? remove to make sure we don't rely on the empty item ignoring behavior, + // or just get rid of this function by understanding the remaining calls. + // for (auto & i : ss) { + // // Make sure we don't rely on the empty item ignoring behavior + // assert(!i.empty()); + // break; + // } // need a cast to string_view since this is also called with Symbols for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); From d40fdb5711e8f7c7dff88f611d5bc26d37ba79e9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 11:50:14 +0200 Subject: [PATCH 571/910] dropEmptyInitThenConcatStringsSep: Update doc and deprecate --- src/libutil/util.hh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index b653bf115..971ecf63b 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -33,10 +33,14 @@ template C tokenizeString(std::string_view s, std::string_view separato /** - * Concatenate the given strings with a separator between the - * elements. + * Ignore any empty strings at the start of the list, and then concatenate the + * given strings with a separator between the elements. + * + * @deprecated This function exists for historical reasons. You probably just + * want to use `concatStringsSep`. */ template +[[deprecated("Consider removing the empty string dropping behavior. If acceptable, use concatStringsSep instead.")]] std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) { size_t size = 0; From 97e01107ecf4d5cea97bd20423409ba2a112612c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 11:51:53 +0200 Subject: [PATCH 572/910] dropEmptyInitThenConcatStringsSep -> concatStringSep: empty separator When the separator is empty, no difference is observable. Note that concatStringsSep has centralized definitions. This adds the required definitions. Alternatively, `strings-inline.hh` could be included at call sites. --- src/libutil/strings.cc | 7 +++++++ src/libutil/util.hh | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc index 15937d415..7ec618bf4 100644 --- a/src/libutil/strings.cc +++ b/src/libutil/strings.cc @@ -9,4 +9,11 @@ template std::string concatStringsSep(std::string_view, const Strings &); template std::string concatStringsSep(std::string_view, const StringSet &); template std::string concatStringsSep(std::string_view, const std::vector &); +typedef std::string_view strings_2[2]; +template std::string concatStringsSep(std::string_view, const strings_2 &); +typedef std::string_view strings_3[3]; +template std::string concatStringsSep(std::string_view, const strings_3 &); +typedef std::string_view strings_4[4]; +template std::string concatStringsSep(std::string_view, const strings_4 &); + } // namespace nix diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 971ecf63b..877d15279 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -11,6 +11,8 @@ #include #include +#include "strings.hh" + namespace nix { void initLibUtil(); @@ -69,7 +71,7 @@ auto concatStrings(Parts && ... parts) -> std::enable_if_t<(... && std::is_convertible_v), std::string> { std::string_view views[sizeof...(parts)] = { parts... }; - return dropEmptyInitThenConcatStringsSep({}, views); + return concatStringsSep({}, views); } From 7e604f716cd3a4bcd47407bda27874262212dcbc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 12:19:55 +0200 Subject: [PATCH 573/910] concatStrings: Give compiler access to definition for inlining ... at call sites that are may be in the hot path. I do not know how clever the compiler gets at these sites. My primary concern is to not regress performance and I am confident that this achieves it the easy way. --- src/libexpr/eval.cc | 2 ++ src/libexpr/nixexpr.cc | 2 ++ src/libstore/derivations.cc | 2 ++ src/libutil/canon-path.cc | 1 + src/libutil/file-system.cc | 2 ++ 5 files changed, 9 insertions(+) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2ede55de7..a4cf2e8c8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -49,6 +49,8 @@ #endif +#include "strings-inline.hh" + using json = nlohmann::json; namespace nix { diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 816389165..c1ffe3435 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -7,6 +7,8 @@ #include #include +#include "strings-inline.hh" + namespace nix { unsigned long Expr::nrExprs = 0; diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 6dfcc408c..8f9c71851 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -10,6 +10,8 @@ #include #include +#include "strings-inline.hh" + namespace nix { std::optional DerivationOutput::path(const StoreDirConfig & store, std::string_view drvName, OutputNameView outputName) const diff --git a/src/libutil/canon-path.cc b/src/libutil/canon-path.cc index 27f048697..03db6378a 100644 --- a/src/libutil/canon-path.cc +++ b/src/libutil/canon-path.cc @@ -1,6 +1,7 @@ #include "canon-path.hh" #include "util.hh" #include "file-path-impl.hh" +#include "strings-inline.hh" namespace nix { diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index f75851bbd..9042e3a5e 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -23,6 +23,8 @@ # include #endif +#include "strings-inline.hh" + namespace fs = std::filesystem; namespace nix { From a4ce96e5f1e78537e650025870011f6fa2ba7e3c Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Sun, 14 Jul 2024 19:07:18 -0700 Subject: [PATCH 574/910] doc: Add comment for fetchurl for name & url fetchurl can be given a name and url aside from just the url. Giving a name can be useful if the url has invalid characters such as tilde for the store. --- src/libexpr/primops/fetchTree.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 6a7accad7..a9956ad88 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -529,9 +529,20 @@ static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, V static RegisterPrimOp primop_fetchurl({ .name = "__fetchurl", - .args = {"url"}, + .args = {"args"}, .doc = R"( - Download the specified URL and return the path of the downloaded file. + If args is a URL, return the path of the downloaded file. + Otherwise, it can be an attribute with the following attributes + (all except url are optional): + + - `url` + + The URL of the file to download. + + - `name` (default: `url without the protocol`) + + A name for the file in the store. This can be useful if the URL has any + characters that are invalid for the store. Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). )", From bc801e2c593c79cf234e6980d026d4e7de1e4f5f Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Sun, 14 Jul 2024 19:55:02 -0700 Subject: [PATCH 575/910] lint: fix shellcheck for misc/systemv/nix-daemon Got shellcheck passing for misc/systemv/nix-daemon Not sure how to test this since it's not running on my NixOS machine and I see no references to it in the directory otherwise. See #10795 --- flake.nix | 1 + misc/systemv/nix-daemon | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index d83c2ecad..51dbc3091 100644 --- a/flake.nix +++ b/flake.nix @@ -324,6 +324,7 @@ ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs ++ [ pkgs.buildPackages.cmake + pkgs.shellcheck modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) diff --git a/misc/systemv/nix-daemon b/misc/systemv/nix-daemon index fea537167..e8326f947 100755 --- a/misc/systemv/nix-daemon +++ b/misc/systemv/nix-daemon @@ -34,6 +34,7 @@ else fi # Source function library. +# shellcheck source=/dev/null . /etc/init.d/functions LOCKFILE=/var/lock/subsys/nix-daemon @@ -41,14 +42,20 @@ RUNDIR=/var/run/nix PIDFILE=${RUNDIR}/nix-daemon.pid RETVAL=0 -base=${0##*/} +# https://www.shellcheck.net/wiki/SC3004 +# Check if gettext exists +if ! type gettext > /dev/null 2>&1 +then + # If not, create a dummy function that returns the input verbatim + gettext() { printf '%s' "$1"; } +fi start() { mkdir -p ${RUNDIR} chown ${NIX_DAEMON_USER}:${NIX_DAEMON_USER} ${RUNDIR} - echo -n $"Starting nix daemon... " + printf '%s' "$(gettext 'Starting nix daemon... ')" daemonize -u $NIX_DAEMON_USER -p ${PIDFILE} $NIX_DAEMON_BIN $NIX_DAEMON_OPTS RETVAL=$? @@ -58,7 +65,7 @@ start() { } stop() { - echo -n $"Shutting down nix daemon: " + printf '%s' "$(gettext 'Shutting down nix daemon: ')" killproc -p ${PIDFILE} $NIX_DAEMON_BIN RETVAL=$? [ $RETVAL -eq 0 ] && rm -f ${LOCKFILE} ${PIDFILE} @@ -67,7 +74,7 @@ stop() { } reload() { - echo -n $"Reloading nix daemon... " + printf '%s' "$(gettext 'Reloading nix daemon... ')" killproc -p ${PIDFILE} $NIX_DAEMON_BIN -HUP RETVAL=$? echo @@ -105,7 +112,7 @@ case "$1" in fi ;; *) - echo $"Usage: $0 {start|stop|status|restart|condrestart}" + printf '%s' "$(gettext "Usage: $0 {start|stop|status|restart|condrestart}")" exit 2 ;; esac From 104aba0fad44c0cf6f2b65bbd753c09226ca1d0c Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Sun, 14 Jul 2024 19:57:55 -0700 Subject: [PATCH 576/910] Remove nix-daemon from exclusion --- maintainers/flake-module.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 46b3e1363..7fbc2d2d2 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -495,7 +495,6 @@ excludes = [ # We haven't linted these files yet ''^config/install-sh$'' - ''^misc/systemv/nix-daemon$'' ''^misc/bash/completion\.sh$'' ''^misc/fish/completion\.fish$'' ''^misc/zsh/completion\.zsh$'' From 550b3479cf066b9e5a435d1cb42240f88021b31f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Jul 2024 15:46:30 +0200 Subject: [PATCH 577/910] Include the accessor in the SourcePath hash --- src/libutil/source-path.hh | 6 +++++- src/libutil/util.hh | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 941744127..d310231a7 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -9,6 +9,8 @@ #include "canon-path.hh" #include "source-accessor.hh" +#include // for boost::hash_combine + namespace nix { /** @@ -128,6 +130,8 @@ struct std::hash { std::size_t operator()(const nix::SourcePath & s) const noexcept { - return std::hash{}(s.path); + std::size_t hash = 0; + hash_combine(hash, s.accessor->number, s.path); + return hash; } }; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 23682ff7c..dd1139843 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -360,4 +360,18 @@ inline std::string operator + (std::string_view s1, const char * s2) return s; } +/** + * hash_combine() from Boost. Hash several hashable values together + * into a single hash. + */ +inline void hash_combine(std::size_t & seed) { } + +template +inline void hash_combine(std::size_t & seed, const T & v, Rest... rest) +{ + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + hash_combine(seed, rest...); +} + } From 945fff5674e4dc8c9a2a365d555d4561c770ec20 Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Mon, 15 Jul 2024 09:12:56 -0700 Subject: [PATCH 578/910] Apply suggestions from code review Add @edolstra suggestion fixes. Co-authored-by: Eelco Dolstra --- src/libexpr/primops/fetchTree.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a9956ad88..333e486fd 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -529,17 +529,16 @@ static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, V static RegisterPrimOp primop_fetchurl({ .name = "__fetchurl", - .args = {"args"}, + .args = {"arg"}, .doc = R"( - If args is a URL, return the path of the downloaded file. - Otherwise, it can be an attribute with the following attributes - (all except url are optional): + Download the specified URL and return the path of the downloaded file. + `arg` can be either a string denoting the URL, or an attribute set with the following attributes: - `url` The URL of the file to download. - - `name` (default: `url without the protocol`) + - `name` (default: the last path component of the URL) A name for the file in the store. This can be useful if the URL has any characters that are invalid for the store. From 63f520fd00ec86f63a0e036f0be52cac822b2ac1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 11:49:18 +0200 Subject: [PATCH 579/910] doc/testing: Typo --- doc/manual/src/contributing/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index a96ba997b..3949164d5 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -91,7 +91,7 @@ A environment variables that Google Test accepts are also worth knowing: Putting the two together, one might run ```bash -GTEST_BREIF=1 GTEST_FILTER='ErrorTraceTest.*' meson test nix-expr-tests -v +GTEST_BRIEF=1 GTEST_FILTER='ErrorTraceTest.*' meson test nix-expr-tests -v ``` for short but comprensive output. From e5af7cbeb9fdf5cff5f8cb25fbb99dccf13e47af Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 11:43:07 +0200 Subject: [PATCH 580/910] libutil: Add Pos::getSnippetUpTo(Pos) --- src/libutil/position.cc | 46 +++++++++++++ src/libutil/position.hh | 2 + tests/unit/libutil/position.cc | 120 +++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 tests/unit/libutil/position.cc diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 573efeeb2..508816d85 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -110,4 +110,50 @@ void Pos::LinesIterator::bump(bool atFirst) input.remove_prefix(eol); } +std::string Pos::getSnippetUpTo(const Pos & end) const { + assert(this->origin == end.origin); + + if (end.line < this->line) + return ""; + + if (auto source = getSource()) { + + auto firstLine = LinesIterator(*source); + for (auto i = 1; i < this->line; ++i) { + ++firstLine; + } + + auto lastLine = LinesIterator(*source); + for (auto i = 1; i < end.line; ++i) { + ++lastLine; + } + + LinesIterator linesEnd; + + std::string result; + for (auto i = firstLine; i != linesEnd; ++i) { + auto firstColumn = i == firstLine ? (this->column ? this->column - 1 : 0) : 0; + if (firstColumn > i->size()) + firstColumn = i->size(); + + auto lastColumn = i == lastLine ? (end.column ? end.column - 1 : 0) : std::numeric_limits::max(); + if (lastColumn < firstColumn) + lastColumn = firstColumn; + if (lastColumn > i->size()) + lastColumn = i->size(); + + result += i->substr(firstColumn, lastColumn - firstColumn); + + if (i == lastLine) { + break; + } else { + result += '\n'; + } + } + return result; + } + return ""; +} + + } diff --git a/src/libutil/position.hh b/src/libutil/position.hh index f8f34419b..729f2a523 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -63,6 +63,8 @@ struct Pos bool operator==(const Pos & rhs) const = default; auto operator<=>(const Pos & rhs) const = default; + std::string getSnippetUpTo(const Pos & end) const; + struct LinesIterator { using difference_type = size_t; using value_type = std::string_view; diff --git a/tests/unit/libutil/position.cc b/tests/unit/libutil/position.cc new file mode 100644 index 000000000..d38d2c538 --- /dev/null +++ b/tests/unit/libutil/position.cc @@ -0,0 +1,120 @@ +#include + +#include "position.hh" + +namespace nix { + +inline Pos::Origin makeStdin(std::string s) +{ + return Pos::Stdin{make_ref(s)}; +} + +TEST(Position, getSnippetUpTo_0) +{ + Pos::Origin o = makeStdin(""); + Pos p(1, 1, o); + ASSERT_EQ(p.getSnippetUpTo(p), ""); +} +TEST(Position, getSnippetUpTo_1) +{ + Pos::Origin o = makeStdin("x"); + { + // NOTE: line and column are actually 1-based indexes + Pos start(0, 0, o); + Pos end(99, 99, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "x"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + // NOTE: line and column are actually 1-based indexes + Pos start(0, 99, o); + Pos end(99, 0, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + + // "x" might be preferable, but we only care about not crashing for invalid inputs + ASSERT_EQ(start.getSnippetUpTo(end), ""); + + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + Pos start(1, 1, o); + Pos end(1, 99, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "x"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + Pos start(1, 1, o); + Pos end(99, 99, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "x"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } +} +TEST(Position, getSnippetUpTo_2) +{ + Pos::Origin o = makeStdin("asdf\njkl\nqwer"); + { + Pos start(1, 1, o); + Pos end(1, 2, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "a"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + Pos start(1, 2, o); + Pos end(1, 3, o); + ASSERT_EQ(start.getSnippetUpTo(end), "s"); + } + { + Pos start(1, 2, o); + Pos end(2, 2, o); + ASSERT_EQ(start.getSnippetUpTo(end), "sdf\nj"); + } + { + Pos start(1, 2, o); + Pos end(3, 2, o); + ASSERT_EQ(start.getSnippetUpTo(end), "sdf\njkl\nq"); + } + { + Pos start(1, 2, o); + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "sdf\njkl"); + } + { + Pos start(1, 4, o); + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "f\njkl"); + } + { + Pos start(1, 5, o); + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "\njkl"); + } + { + Pos start(1, 6, o); // invalid: starting column past last "line character", ie at the newline + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "\njkl"); // jkl might be acceptable for this invalid start position + } + { + Pos start(1, 1, o); + Pos end(2, 0, o); // invalid + ASSERT_EQ(start.getSnippetUpTo(end), "asdf\n"); + } +} + +TEST(Position, example_1) +{ + Pos::Origin o = makeStdin(" unambiguous = \n /** Very close */\n x: x;\n# ok\n"); + Pos start(2, 5, o); + Pos end(2, 22, o); + ASSERT_EQ(start.getSnippetUpTo(end), "/** Very close */"); +} + +} // namespace nix From 7fae378835534d2a17818fd7cd91aae91320aa03 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Jul 2024 17:39:26 +0200 Subject: [PATCH 581/910] Track doc comments and render them in :doc --- .../rl-next/repl-doc-renders-doc-comments.md | 53 ++++++++++++++++ src/libexpr/eval.cc | 61 +++++++++++++++++++ src/libexpr/lexer.l | 56 +++++++++++++++-- src/libexpr/nixexpr.cc | 14 +++++ src/libexpr/nixexpr.hh | 53 +++++++++++++++- src/libexpr/parser-state.hh | 41 +++++++++++++ src/libexpr/parser.y | 51 +++++++++++++--- tests/functional/repl.sh | 30 +++++++++ tests/functional/repl/characterisation/empty | 0 .../repl/doc-comment-function.expected | 8 +++ tests/functional/repl/doc-comment-function.in | 1 + .../functional/repl/doc-comment-function.nix | 3 + tests/functional/repl/doc-comments.nix | 57 +++++++++++++++++ tests/functional/repl/doc-compact.expected | 11 ++++ tests/functional/repl/doc-compact.in | 2 + tests/functional/repl/doc-floatedIn.expected | 11 ++++ tests/functional/repl/doc-floatedIn.in | 2 + .../repl/doc-lambda-flavors.expected | 29 +++++++++ tests/functional/repl/doc-lambda-flavors.in | 5 ++ .../functional/repl/doc-measurement.expected | 11 ++++ tests/functional/repl/doc-measurement.in | 2 + tests/functional/repl/doc-multiply.expected | 15 +++++ tests/functional/repl/doc-multiply.in | 2 + .../functional/repl/doc-unambiguous.expected | 11 ++++ tests/functional/repl/doc-unambiguous.in | 2 + 25 files changed, 515 insertions(+), 16 deletions(-) create mode 100644 doc/manual/rl-next/repl-doc-renders-doc-comments.md create mode 100644 tests/functional/repl/characterisation/empty create mode 100644 tests/functional/repl/doc-comment-function.expected create mode 100644 tests/functional/repl/doc-comment-function.in create mode 100644 tests/functional/repl/doc-comment-function.nix create mode 100644 tests/functional/repl/doc-comments.nix create mode 100644 tests/functional/repl/doc-compact.expected create mode 100644 tests/functional/repl/doc-compact.in create mode 100644 tests/functional/repl/doc-floatedIn.expected create mode 100644 tests/functional/repl/doc-floatedIn.in create mode 100644 tests/functional/repl/doc-lambda-flavors.expected create mode 100644 tests/functional/repl/doc-lambda-flavors.in create mode 100644 tests/functional/repl/doc-measurement.expected create mode 100644 tests/functional/repl/doc-measurement.in create mode 100644 tests/functional/repl/doc-multiply.expected create mode 100644 tests/functional/repl/doc-multiply.in create mode 100644 tests/functional/repl/doc-unambiguous.expected create mode 100644 tests/functional/repl/doc-unambiguous.in diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md new file mode 100644 index 000000000..c9213a88c --- /dev/null +++ b/doc/manual/rl-next/repl-doc-renders-doc-comments.md @@ -0,0 +1,53 @@ +--- +synopsis: "`nix-repl`'s `:doc` shows documentation comments" +significance: significant +issues: +- 3904 +- 10771 +prs: +- 1652 +- 9054 +- 11072 +--- + +`nix repl` has a `:doc` command that previously only rendered documentation for internally defined functions. +This feature has been extended to also render function documentation comments, in accordance with [RFC 145]. + +Example: + +``` +nix-repl> :doc lib.toFunction +Function toFunction + … defined at /home/user/h/nixpkgs/lib/trivial.nix:1072:5 + + Turns any non-callable values into constant functions. Returns + callable values as is. + +Inputs + + v + + : Any value + +Examples + + :::{.example} + +## lib.trivial.toFunction usage example + + | nix-repl> lib.toFunction 1 2 + | 1 + | + | nix-repl> lib.toFunction (x: x + 1) 2 + | 3 + + ::: +``` + +Known limitations: +- It currently only works for functions. We plan to extend this to attributes, which may contain arbitrary values. +- Some extensions to markdown are not yet supported, as you can see in the example above. + +We'd like to acknowledge Yingchi Long for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as @sternenseemann and Johannes Kirschbauer for their contributions, proposals, and their work on [RFC 145]. + +[RFC 145]: https://github.com/NixOS/rfcs/pull/145 diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a4cf2e8c8..bd7bac0f1 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -559,6 +559,67 @@ std::optional EvalState::getDoc(Value & v) .doc = doc, }; } + if (v.isLambda()) { + auto exprLambda = v.payload.lambda.fun; + + std::stringstream s(std::ios_base::out); + std::string name; + auto pos = positions[exprLambda->getPos()]; + std::string docStr; + + if (exprLambda->name) { + name = symbols[exprLambda->name]; + } + + if (exprLambda->docComment) { + auto begin = positions[exprLambda->docComment.begin]; + auto end = positions[exprLambda->docComment.end]; + auto docCommentStr = begin.getSnippetUpTo(end); + + // Strip "/**" and "*/" + constexpr size_t prefixLen = 3; + constexpr size_t suffixLen = 2; + docStr = docCommentStr.substr(prefixLen, docCommentStr.size() - prefixLen - suffixLen); + if (docStr.empty()) + return {}; + // Turn the now missing "/**" into indentation + docStr = " " + docStr; + // Strip indentation (for the whole, potentially multi-line string) + docStr = stripIndentation(docStr); + } + + if (name.empty()) { + s << "Function "; + } + else { + s << "Function **" << name << "**"; + if (pos) + s << "\\\n … " ; + else + s << "\\\n"; + } + if (pos) { + s << "defined at " << pos; + } + if (!docStr.empty()) { + s << "\n\n"; + } + + s << docStr; + + s << '\0'; // for making a c string below + std::string ss = s.str(); + + return Doc { + .pos = pos, + .name = name, + .arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though... + .args = {}, + .doc = + // FIXME: this leaks; make the field std::string? + strdup(ss.data()), + }; + } return {}; } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 8c0f9d1f2..459f1d6f3 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -5,7 +5,7 @@ %option stack %option nodefault %option nounput noyy_top_state - +%option extra-type="::nix::LexerState *" %s DEFAULT %x STRING @@ -23,6 +23,12 @@ #include "nixexpr.hh" #include "parser-tab.hh" +// !!! FIXME !!! +#define YY_EXTRA_TYPE ::nix::LexerState * +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); +#undef YY_EXTRA_TYPE + using namespace nix; namespace nix { @@ -35,10 +41,24 @@ static void initLoc(YYLTYPE * loc) loc->first_column = loc->last_column = 0; } -static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) +static void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) { loc->stash(); + LexerState & lexerState = *yyget_extra(yyscanner); + + if (lexerState.docCommentDistance == 1) { + // Preceding token was a doc comment. + ParserLocation doc; + doc.first_column = lexerState.lastDocCommentLoc.first_column; + ParserLocation docEnd; + docEnd.first_column = lexerState.lastDocCommentLoc.last_column; + DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; + PosIdx locPos = lexerState.at(*loc); + lexerState.positionToDocComment.emplace(locPos, docComment); + } + lexerState.docCommentDistance++; + loc->first_column = loc->last_column; loc->last_column += len; } @@ -79,7 +99,7 @@ static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #define YY_USER_INIT initLoc(yylloc) -#define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng); +#define YY_USER_ACTION adjustLoc(yyscanner, yylloc, yytext, yyleng); #define PUSH_STATE(state) yy_push_state(state, yyscanner) #define POP_STATE() yy_pop_state(yyscanner) @@ -279,9 +299,33 @@ or { return OR_KW; } {SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; } {URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; } -[ \t\r\n]+ /* eat up whitespace */ -\#[^\r\n]* /* single-line comments */ -\/\*([^*]|\*+[^*/])*\*+\/ /* long comments */ +%{ +// Doc comment rule +// +// \/\*\* /** +// [^/*] reject /**/ (empty comment) and /*** +// ([^*]|\*+[^*/])*\*+\/ same as the long comment rule +// ( )* zero or more non-ending sequences +// \* end(1) +// \/ end(2) +%} +\/\*\*[^/*]([^*]|\*+[^*/])*\*+\/ /* doc comments */ { + LexerState & lexerState = *yyget_extra(yyscanner); + lexerState.docCommentDistance = 0; + lexerState.lastDocCommentLoc.first_line = yylloc->first_line; + lexerState.lastDocCommentLoc.first_column = yylloc->first_column; + lexerState.lastDocCommentLoc.last_column = yylloc->last_column; +} + + +%{ +// The following rules have docCommentDistance-- +// This compensates for the docCommentDistance++ which happens by default to +// make all the other rules invalidate the doc comment. +%} +[ \t\r\n]+ /* eat up whitespace */ { yyget_extra(yyscanner)->docCommentDistance--; } +\#[^\r\n]* /* single-line comments */ { yyget_extra(yyscanner)->docCommentDistance--; } +\/\*([^*]|\*+[^*/])*\*+\/ /* long comments */ { yyget_extra(yyscanner)->docCommentDistance--; } {ANY} { /* Don't return a negative number, as this will cause diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index c1ffe3435..6e705c314 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -583,6 +583,20 @@ std::string ExprLambda::showNamePos(const EvalState & state) const return fmt("%1% at %2%", id, state.positions[pos]); } +void ExprLambda::setDocComment(DocComment docComment) { + if (!this->docComment) { + this->docComment = docComment; + + // Curried functions are defined by putting a function directly + // in the body of another function. To render docs for those, we + // need to propagate the doc comment to the innermost function. + // + // If we have our own comment, we've already propagated it, so this + // belongs in the same conditional. + body->setDocComment(docComment); + } +}; + /* Position table. */ diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 5152e3119..913f46ff9 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -11,13 +11,56 @@ namespace nix { - -struct Env; -struct Value; class EvalState; +class PosTable; +struct Env; struct ExprWith; struct StaticEnv; +struct Value; +/** + * A documentation comment, in the sense of [RFC 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md) + * + * Note that this does not implement the following: + * - argument attribute names ("formals"): TBD + * - argument names: these are internal to the function and their names may not be optimal for documentation + * - function arity (degree of currying or number of ':'s): + * - Functions returning partially applied functions have a higher arity + * than can be determined locally and without evaluation. + * We do not want to present false data. + * - Some functions should be thought of as transformations of other + * functions. For instance `overlay -> overlay -> overlay` is the simplest + * way to understand `composeExtensions`, but its implementation looks like + * `f: g: final: prev: <...>`. The parameters `final` and `prev` are part + * of the overlay concept, while distracting from the function's purpose. + */ +struct DocComment { + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcomment" // "nested comment start" is intentional + + /** + * Start of the comment, including `/**`. + */ + PosIdx begin; + +#pragma GCC diagnostic pop + + /** + * Position right after the final asterisk and `/` that terminate the comment. + */ + PosIdx end; + + /** + * Whether the comment is set. + * + * A `DocComment` is small enough that it makes sense to pass by value, and + * therefore baking optionality into it is also useful, to avoiding the memory + * overhead of `std::optional`. + */ + operator bool() const { return static_cast(begin); } + +}; /** * An attribute path is a sequence of attribute names. @@ -54,6 +97,7 @@ struct Expr virtual void eval(EvalState & state, Env & env, Value & v); virtual Value * maybeThunk(EvalState & state, Env & env); virtual void setName(Symbol name); + virtual void setDocComment(DocComment docComment) { }; virtual PosIdx getPos() const { return noPos; } }; @@ -278,6 +322,8 @@ struct ExprLambda : Expr Symbol arg; Formals * formals; Expr * body; + DocComment docComment; + ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body) : pos(pos), arg(arg), formals(formals), body(body) { @@ -290,6 +336,7 @@ struct ExprLambda : Expr std::string showNamePos(const EvalState & state) const; inline bool hasFormals() const { return formals != nullptr; } PosIdx getPos() const override { return pos; } + virtual void setDocComment(DocComment docComment) override; COMMON_METHODS }; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index cff6282fa..8dc910468 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -1,6 +1,8 @@ #pragma once ///@file +#include + #include "eval.hh" namespace nix { @@ -35,10 +37,44 @@ struct ParserLocation first_column = stashed_first_column; last_column = stashed_last_column; } + + /** Latest doc comment position, or 0. */ + int doc_comment_first_line, doc_comment_first_column, doc_comment_last_column; +}; + +struct LexerState +{ + /** + * Tracks the distance to the last doc comment, in terms of lexer tokens. + * + * The lexer sets this to 0 when reading a doc comment, and increments it + * for every matched rule; see `lexer-helpers.cc`. + * Whitespace and comment rules decrement the distance, so that they result + * in a net 0 change in distance. + */ + int docCommentDistance = std::numeric_limits::max(); + + /** + * The location of the last doc comment. + * + * (stashing fields are not used) + */ + ParserLocation lastDocCommentLoc; + + /** + * @brief Maps some positions to a DocComment, where the comment is relevant to the location. + */ + std::map positionToDocComment; + + PosTable & positions; + PosTable::Origin origin; + + PosIdx at(const ParserLocation & loc); }; struct ParserState { + const LexerState & lexerState; SymbolTable & symbols; PosTable & positions; Expr * result; @@ -270,6 +306,11 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, return new ExprConcatStrings(pos, true, es2); } +inline PosIdx LexerState::at(const ParserLocation & loc) +{ + return positions.add(origin, loc.first_column); +} + inline PosIdx ParserState::at(const ParserLocation & loc) { return positions.add(origin, loc.first_column); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 709a4532a..3c1bf95c8 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -74,6 +74,14 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * }); } +#define SET_DOC_POS(lambda, pos) setDocPosition(state->lexerState, lambda, state->at(pos)) +static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, PosIdx start) { + auto it = lexerState.positionToDocComment.find(start); + if (it != lexerState.positionToDocComment.end()) { + lambda->setDocComment(it->second); + } +} + %} @@ -119,6 +127,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * %token IND_STRING_OPEN IND_STRING_CLOSE %token ELLIPSIS + %right IMPL %left OR %left AND @@ -140,18 +149,28 @@ expr: expr_function; expr_function : ID ':' expr_function - { $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); } + { auto me = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); + $$ = me; + SET_DOC_POS(me, @1); + } | '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, state->validateFormals($2), $5); } + { auto me = new ExprLambda(CUR_POS, state->validateFormals($2), $5); + $$ = me; + SET_DOC_POS(me, @1); + } | '{' formals '}' '@' ID ':' expr_function { auto arg = state->symbols.create($5); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7); + $$ = me; + SET_DOC_POS(me, @1); } | ID '@' '{' formals '}' ':' expr_function { auto arg = state->symbols.create($1); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7); + $$ = me; + SET_DOC_POS(me, @1); } | ASSERT expr ';' expr_function { $$ = new ExprAssert(CUR_POS, $2, $4); } @@ -312,7 +331,20 @@ ind_string_parts ; binds - : binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; } + : binds attrpath '=' expr ';' { + $$ = $1; + + auto pos = state->at(@2); + { + auto it = state->lexerState.positionToDocComment.find(pos); + if (it != state->lexerState.positionToDocComment.end()) { + $4->setDocComment(it->second); + } + } + + state->addAttr($$, std::move(*$2), $4, pos); + delete $2; + } | binds INHERIT attrs ';' { $$ = $1; for (auto & [i, iPos] : *$3) { @@ -435,17 +467,22 @@ Expr * parseExprFromBuf( const Expr::AstSymbols & astSymbols) { yyscan_t scanner; + LexerState lexerState { + .positions = positions, + .origin = positions.addOrigin(origin, length), + }; ParserState state { + .lexerState = lexerState, .symbols = symbols, .positions = positions, .basePath = basePath, - .origin = positions.addOrigin(origin, length), + .origin = lexerState.origin, .rootFS = rootFS, .s = astSymbols, .settings = settings, }; - yylex_init(&scanner); + yylex_init_extra(&lexerState, &scanner); Finally _destroy([&] { yylex_destroy(scanner); }); yy_scan_buffer(text, length, scanner); diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index 86cd6f458..d356fe096 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash source common.sh +source characterisation/framework.sh testDir="$PWD" cd "$TEST_ROOT" @@ -244,3 +245,32 @@ testReplResponseNoRegex ' y = { a = 1 }; } ' + +# TODO: move init to characterisation/framework.sh +badDiff=0 +badExitCode=0 + +nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" + +runRepl () { + # TODO: pass arguments to nix repl; see lang.sh + nix repl 2>&1 \ + | stripColors \ + | sed \ + -e "s@$testDir@/path/to/tests/functional@g" \ + -e "s@$nixVersion@@g" \ + -e "s@Added [0-9]* variables@Added variables@g" \ + | grep -vF $'warning: you don\'t have Internet access; disabling some network-dependent features' \ + ; +} + +for test in $(cd "$testDir/repl"; echo *.in); do + test="$(basename "$test" .in)" + in="$testDir/repl/$test.in" + actual="$testDir/repl/$test.actual" + expected="$testDir/repl/$test.expected" + (cd "$testDir/repl"; set +x; runRepl 2>&1) < "$in" > "$actual" + diffAndAcceptInner "$test" "$actual" "$expected" +done + +characterisationTestExit diff --git a/tests/functional/repl/characterisation/empty b/tests/functional/repl/characterisation/empty new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/repl/doc-comment-function.expected b/tests/functional/repl/doc-comment-function.expected new file mode 100644 index 000000000..5ec465a96 --- /dev/null +++ b/tests/functional/repl/doc-comment-function.expected @@ -0,0 +1,8 @@ +Nix +Type :? for help. +Function defined at + /path/to/tests/functional/repl/doc-comment-function.nix:2:1 + + A doc comment for a file that only contains a function + + diff --git a/tests/functional/repl/doc-comment-function.in b/tests/functional/repl/doc-comment-function.in new file mode 100644 index 000000000..8f3c1388a --- /dev/null +++ b/tests/functional/repl/doc-comment-function.in @@ -0,0 +1 @@ +:doc import ./doc-comment-function.nix diff --git a/tests/functional/repl/doc-comment-function.nix b/tests/functional/repl/doc-comment-function.nix new file mode 100644 index 000000000..cdd241347 --- /dev/null +++ b/tests/functional/repl/doc-comment-function.nix @@ -0,0 +1,3 @@ +/** A doc comment for a file that only contains a function */ +{ ... }: +{ } diff --git a/tests/functional/repl/doc-comments.nix b/tests/functional/repl/doc-comments.nix new file mode 100644 index 000000000..a98f1c688 --- /dev/null +++ b/tests/functional/repl/doc-comments.nix @@ -0,0 +1,57 @@ +{ + /** + Perform *arithmetic* multiplication. It's kind of like repeated **addition**, very neat. + + ```nix + multiply 2 3 + => 6 + ``` + */ + multiply = x: y: x * y; + + /**👈 precisely this wide 👉*/ + measurement = x: x; + + floatedIn = /** This also works. */ + x: y: x; + + compact=/**boom*/x: x; + + /** Ignore!!! */ + unambiguous = + /** Very close */ + x: x; + + /** Firmly rigid. */ + constant = true; + + /** Immovably fixed. */ + lib.version = "9000"; + + /** Unchangeably constant. */ + lib.attr.empty = { }; + + lib.attr.undocumented = { }; + + nonStrict = /** My syntax is not strict, but I'm strict anyway. */ x: x; + strict = /** I don't have to be strict, but I am anyway. */ { ... }: null; + # Note that pre and post are the same here. I just had to name them somehow. + strictPre = /** Here's one way to do this */ a@{ ... }: a; + strictPost = /** Here's another way to do this */ { ... }@a: a; + + # TODO + + # /** This returns a documented function. */ + # documentedArgs = + # /** x */ + # x: + # /** y */ + # y: + # /** x + y */ + # x + y; + + # /** Documented formals */ + # documentedFormals = + # /** x */ + # x: x; +} diff --git a/tests/functional/repl/doc-compact.expected b/tests/functional/repl/doc-compact.expected new file mode 100644 index 000000000..4b05b653c --- /dev/null +++ b/tests/functional/repl/doc-compact.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function compact + … defined at + /path/to/tests/functional/repl/doc-comments.nix:18:20 + + boom + + diff --git a/tests/functional/repl/doc-compact.in b/tests/functional/repl/doc-compact.in new file mode 100644 index 000000000..c87c4e7ab --- /dev/null +++ b/tests/functional/repl/doc-compact.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc compact diff --git a/tests/functional/repl/doc-floatedIn.expected b/tests/functional/repl/doc-floatedIn.expected new file mode 100644 index 000000000..30f135725 --- /dev/null +++ b/tests/functional/repl/doc-floatedIn.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function floatedIn + … defined at + /path/to/tests/functional/repl/doc-comments.nix:16:5 + + This also works. + + diff --git a/tests/functional/repl/doc-floatedIn.in b/tests/functional/repl/doc-floatedIn.in new file mode 100644 index 000000000..97c12408e --- /dev/null +++ b/tests/functional/repl/doc-floatedIn.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc floatedIn diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected new file mode 100644 index 000000000..4ac52f39e --- /dev/null +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -0,0 +1,29 @@ +Nix +Type :? for help. +Added variables. + +Function nonStrict + … defined at + /path/to/tests/functional/repl/doc-comments.nix:36:70 + + My syntax is not strict, but I'm strict anyway. + +Function strict + … defined at + /path/to/tests/functional/repl/doc-comments.nix:37:63 + + I don't have to be strict, but I am anyway. + +Function strictPre + … defined at + /path/to/tests/functional/repl/doc-comments.nix:39:48 + + Here's one way to do this + +Function strictPost + … defined at + /path/to/tests/functional/repl/doc-comments.nix:40:53 + + Here's another way to do this + + diff --git a/tests/functional/repl/doc-lambda-flavors.in b/tests/functional/repl/doc-lambda-flavors.in new file mode 100644 index 000000000..760c99636 --- /dev/null +++ b/tests/functional/repl/doc-lambda-flavors.in @@ -0,0 +1,5 @@ +:l doc-comments.nix +:doc nonStrict +:doc strict +:doc strictPre +:doc strictPost diff --git a/tests/functional/repl/doc-measurement.expected b/tests/functional/repl/doc-measurement.expected new file mode 100644 index 000000000..8598aaedb --- /dev/null +++ b/tests/functional/repl/doc-measurement.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function measurement + … defined at + /path/to/tests/functional/repl/doc-comments.nix:13:17 + + 👈 precisely this wide 👉 + + diff --git a/tests/functional/repl/doc-measurement.in b/tests/functional/repl/doc-measurement.in new file mode 100644 index 000000000..fecd5f9d2 --- /dev/null +++ b/tests/functional/repl/doc-measurement.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc measurement diff --git a/tests/functional/repl/doc-multiply.expected b/tests/functional/repl/doc-multiply.expected new file mode 100644 index 000000000..db82af91f --- /dev/null +++ b/tests/functional/repl/doc-multiply.expected @@ -0,0 +1,15 @@ +Nix +Type :? for help. +Added variables. + +Function multiply + … defined at + /path/to/tests/functional/repl/doc-comments.nix:10:14 + + Perform arithmetic multiplication. It's kind of like + repeated addition, very neat. + + | multiply 2 3 + | => 6 + + diff --git a/tests/functional/repl/doc-multiply.in b/tests/functional/repl/doc-multiply.in new file mode 100644 index 000000000..bffc6696f --- /dev/null +++ b/tests/functional/repl/doc-multiply.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc multiply diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected new file mode 100644 index 000000000..0f89a6f64 --- /dev/null +++ b/tests/functional/repl/doc-unambiguous.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function unambiguous + … defined at + /path/to/tests/functional/repl/doc-comments.nix:23:5 + + Very close + + diff --git a/tests/functional/repl/doc-unambiguous.in b/tests/functional/repl/doc-unambiguous.in new file mode 100644 index 000000000..8282a5cb9 --- /dev/null +++ b/tests/functional/repl/doc-unambiguous.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc unambiguous From e68234c4f991071c0c7ff05852975a74d6a21771 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 15:39:24 +0200 Subject: [PATCH 582/910] libexpr: Rearrange lexer files so that yylex_init_extra can be found --- src/libexpr/lexer-helpers.cc | 31 ++++++++++++++++++++++++++ src/libexpr/lexer-helpers.hh | 9 ++++++++ src/libexpr/lexer.l | 43 ++++++++---------------------------- src/libexpr/meson.build | 2 ++ 4 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 src/libexpr/lexer-helpers.cc create mode 100644 src/libexpr/lexer-helpers.hh diff --git a/src/libexpr/lexer-helpers.cc b/src/libexpr/lexer-helpers.cc new file mode 100644 index 000000000..87c514c71 --- /dev/null +++ b/src/libexpr/lexer-helpers.cc @@ -0,0 +1,31 @@ +#include "lexer-tab.hh" +#include "lexer-helpers.hh" +#include "parser-tab.hh" + +void nix::lexer::internal::initLoc(YYLTYPE * loc) +{ + loc->first_line = loc->last_line = 0; + loc->first_column = loc->last_column = 0; +} + +void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) +{ + loc->stash(); + + LexerState & lexerState = *yyget_extra(yyscanner); + + if (lexerState.docCommentDistance == 1) { + // Preceding token was a doc comment. + ParserLocation doc; + doc.first_column = lexerState.lastDocCommentLoc.first_column; + ParserLocation docEnd; + docEnd.first_column = lexerState.lastDocCommentLoc.last_column; + DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; + PosIdx locPos = lexerState.at(*loc); + lexerState.positionToDocComment.emplace(locPos, docComment); + } + lexerState.docCommentDistance++; + + loc->first_column = loc->last_column; + loc->last_column += len; +} diff --git a/src/libexpr/lexer-helpers.hh b/src/libexpr/lexer-helpers.hh new file mode 100644 index 000000000..caba6e18f --- /dev/null +++ b/src/libexpr/lexer-helpers.hh @@ -0,0 +1,9 @@ +#pragma once + +namespace nix::lexer::internal { + +void initLoc(YYLTYPE * loc); + +void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len); + +} // namespace nix::lexer diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 459f1d6f3..7d4e6963f 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -14,6 +14,10 @@ %x INPATH_SLASH %x PATH_START +%top { +#include "parser-tab.hh" // YYSTYPE +#include "parser-state.hh" +} %{ #ifdef __clang__ @@ -22,48 +26,19 @@ #include "nixexpr.hh" #include "parser-tab.hh" +#include "lexer-helpers.hh" -// !!! FIXME !!! -#define YY_EXTRA_TYPE ::nix::LexerState * -int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); -YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); -#undef YY_EXTRA_TYPE +namespace nix { + struct LexerState; +} using namespace nix; +using namespace nix::lexer::internal; namespace nix { #define CUR_POS state->at(*yylloc) -static void initLoc(YYLTYPE * loc) -{ - loc->first_line = loc->last_line = 0; - loc->first_column = loc->last_column = 0; -} - -static void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) -{ - loc->stash(); - - LexerState & lexerState = *yyget_extra(yyscanner); - - if (lexerState.docCommentDistance == 1) { - // Preceding token was a doc comment. - ParserLocation doc; - doc.first_column = lexerState.lastDocCommentLoc.first_column; - ParserLocation docEnd; - docEnd.first_column = lexerState.lastDocCommentLoc.last_column; - DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; - PosIdx locPos = lexerState.at(*loc); - lexerState.positionToDocComment.emplace(locPos, docComment); - } - lexerState.docCommentDistance++; - - loc->first_column = loc->last_column; - loc->last_column += len; -} - - // we make use of the fact that the parser receives a private copy of the input // string and can munge around in it. static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index e65cf545f..fa90e7b41 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -139,6 +139,7 @@ sources = files( 'function-trace.cc', 'get-drvs.cc', 'json-to-value.cc', + 'lexer-helpers.cc', 'nixexpr.cc', 'paths.cc', 'primops.cc', @@ -165,6 +166,7 @@ headers = [config_h] + files( 'gc-small-vector.hh', 'get-drvs.hh', 'json-to-value.hh', + # internal: 'lexer-helpers.hh', 'nixexpr.hh', 'parser-state.hh', 'pos-idx.hh', From 491b9cf4154370df3da04977696dda3f053725f1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 19:26:22 +0200 Subject: [PATCH 583/910] Refactor: extract DocComment::getInnerText(PosTable) --- src/libexpr/eval.cc | 15 +-------------- src/libexpr/nixexpr.cc | 18 ++++++++++++++++++ src/libexpr/nixexpr.hh | 2 ++ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bd7bac0f1..f8282c400 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -572,20 +572,7 @@ std::optional EvalState::getDoc(Value & v) } if (exprLambda->docComment) { - auto begin = positions[exprLambda->docComment.begin]; - auto end = positions[exprLambda->docComment.end]; - auto docCommentStr = begin.getSnippetUpTo(end); - - // Strip "/**" and "*/" - constexpr size_t prefixLen = 3; - constexpr size_t suffixLen = 2; - docStr = docCommentStr.substr(prefixLen, docCommentStr.size() - prefixLen - suffixLen); - if (docStr.empty()) - return {}; - // Turn the now missing "/**" into indentation - docStr = " " + docStr; - // Strip indentation (for the whole, potentially multi-line string) - docStr = stripIndentation(docStr); + docStr = exprLambda->docComment.getInnerText(positions); } if (name.empty()) { diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 6e705c314..5479dd79e 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -641,4 +641,22 @@ size_t SymbolTable::totalSize() const return n; } +std::string DocComment::getInnerText(const PosTable & positions) const { + auto beginPos = positions[begin]; + auto endPos = positions[end]; + auto docCommentStr = beginPos.getSnippetUpTo(endPos); + + // Strip "/**" and "*/" + constexpr size_t prefixLen = 3; + constexpr size_t suffixLen = 2; + std::string docStr = docCommentStr.substr(prefixLen, docCommentStr.size() - prefixLen - suffixLen); + if (docStr.empty()) + return {}; + // Turn the now missing "/**" into indentation + docStr = " " + docStr; + // Strip indentation (for the whole, potentially multi-line string) + docStr = stripIndentation(docStr); + return docStr; +} + } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 913f46ff9..803b5ed8f 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -60,6 +60,8 @@ struct DocComment { */ operator bool() const { return static_cast(begin); } + std::string getInnerText(const PosTable & positions) const; + }; /** From d4f576b0b281265b58bb497108f8d063e2a0f06a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 19:25:45 +0200 Subject: [PATCH 584/910] nix repl: Render docs for attributes --- .../rl-next/repl-doc-renders-doc-comments.md | 2 +- src/libcmd/repl.cc | 41 +++++++ src/libexpr/eval.cc | 42 +++++++- src/libexpr/eval.hh | 10 ++ src/libexpr/nixexpr.hh | 11 ++ src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 7 ++ src/libutil/position.hh | 8 ++ tests/functional/repl/doc-constant.expected | 102 ++++++++++++++++++ tests/functional/repl/doc-constant.in | 15 +++ 10 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 tests/functional/repl/doc-constant.expected create mode 100644 tests/functional/repl/doc-constant.in diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md index c9213a88c..05023697c 100644 --- a/doc/manual/rl-next/repl-doc-renders-doc-comments.md +++ b/doc/manual/rl-next/repl-doc-renders-doc-comments.md @@ -45,7 +45,7 @@ Examples ``` Known limitations: -- It currently only works for functions. We plan to extend this to attributes, which may contain arbitrary values. +- It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. - Some extensions to markdown are not yet supported, as you can see in the example above. We'd like to acknowledge Yingchi Long for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as @sternenseemann and Johannes Kirschbauer for their contributions, proposals, and their work on [RFC 145]. diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 37a34e3de..fb8106c46 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -27,6 +27,7 @@ #include "local-fs-store.hh" #include "print.hh" #include "ref.hh" +#include "value.hh" #if HAVE_BOEHMGC #define GC_INCLUDE_NEW @@ -616,6 +617,33 @@ ProcessLineResult NixRepl::processLine(std::string line) else if (command == ":doc") { Value v; + + auto expr = parseString(arg); + std::string fallbackName; + PosIdx fallbackPos; + DocComment fallbackDoc; + if (auto select = dynamic_cast(expr)) { + Value vAttrs; + auto name = select->evalExceptFinalSelect(*state, *env, vAttrs); + fallbackName = state->symbols[name]; + + state->forceAttrs(vAttrs, noPos, "while evaluating an attribute set to look for documentation"); + auto attrs = vAttrs.attrs(); + assert(attrs); + auto attr = attrs->get(name); + if (!attr) { + // Trigger the normal error + evalString(arg, v); + } + if (attr->pos) { + fallbackPos = attr->pos; + fallbackDoc = state->getDocCommentForPos(fallbackPos); + } + + } else { + evalString(arg, v); + } + evalString(arg, v); if (auto doc = state->getDoc(v)) { std::string markdown; @@ -633,6 +661,19 @@ ProcessLineResult NixRepl::processLine(std::string line) markdown += stripIndentation(doc->doc); logger->cout(trim(renderMarkdownToTerminal(markdown))); + } else if (fallbackPos) { + std::stringstream ss; + ss << "Attribute `" << fallbackName << "`\n\n"; + ss << " … defined at " << state->positions[fallbackPos] << "\n\n"; + if (fallbackDoc) { + ss << fallbackDoc.getInnerText(state->positions); + } else { + ss << "No documentation found.\n\n"; + } + + auto markdown = ss.str(); + logger->cout(trim(renderMarkdownToTerminal(markdown))); + } else throw Error("value does not have documentation"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f8282c400..81c64ba71 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1415,6 +1415,22 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) v = *vAttrs; } +Symbol ExprSelect::evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs) +{ + Value vTmp; + Symbol name = getName(attrPath[attrPath.size() - 1], state, env); + + if (attrPath.size() == 1) { + e->eval(state, env, vTmp); + } else { + ExprSelect init(*this); + init.attrPath.pop_back(); + init.eval(state, env, vTmp); + } + attrs = vTmp; + return name; +} + void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) { @@ -2876,13 +2892,37 @@ Expr * EvalState::parse( const SourcePath & basePath, std::shared_ptr & staticEnv) { - auto result = parseExprFromBuf(text, length, origin, basePath, symbols, settings, positions, rootFS, exprSymbols); + DocCommentMap tmpDocComments; // Only used when not origin is not a SourcePath + DocCommentMap *docComments = &tmpDocComments; + + if (auto sourcePath = std::get_if(&origin)) { + auto [it, _] = positionToDocComment.try_emplace(*sourcePath); + docComments = &it->second; + } + + auto result = parseExprFromBuf(text, length, origin, basePath, symbols, settings, positions, *docComments, rootFS, exprSymbols); result->bindVars(*this, staticEnv); return result; } +DocComment EvalState::getDocCommentForPos(PosIdx pos) +{ + auto pos2 = positions[pos]; + auto path = pos2.getSourcePath(); + if (!path) + return {}; + + auto table = positionToDocComment.find(*path); + if (table == positionToDocComment.end()) + return {}; + + auto it = table->second.find(pos); + if (it == table->second.end()) + return {}; + return it->second; +} std::string ExternalValueBase::coerceToString(EvalState & state, const PosIdx & pos, NixStringContext & context, bool copyMore, bool copyToStore) const { diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7dbf61c5d..3918fb092 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,6 +130,8 @@ struct Constant typedef std::map ValMap; #endif +typedef std::map DocCommentMap; + struct Env { Env * up; @@ -329,6 +331,12 @@ private: #endif FileEvalCache fileEvalCache; + /** + * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. + * Grouped by file. + */ + std::map positionToDocComment; + LookupPath lookupPath; std::map> lookupPathResolved; @@ -771,6 +779,8 @@ public: std::string_view pathArg, PosIdx pos); + DocComment getDocCommentForPos(PosIdx pos); + private: /** diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 803b5ed8f..4c4c8af19 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -202,6 +202,17 @@ struct ExprSelect : Expr ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { }; ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; PosIdx getPos() const override { return pos; } + + /** + * Evaluate the `a.b.c` part of `a.b.c.d`. This exists mostly for the purpose of :doc in the repl. + * + * @param[out] v The attribute set that should contain the last attribute name (if it exists). + * @return The last attribute name in `attrPath` + * + * @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not exist. + */ + Symbol evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs); + COMMON_METHODS }; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 8dc910468..1df64e73d 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::map positionToDocComment; + std::map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 3c1bf95c8..a25c6dd87 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -33,6 +33,8 @@ namespace nix { +typedef std::map DocCommentMap; + Expr * parseExprFromBuf( char * text, size_t length, @@ -41,6 +43,7 @@ Expr * parseExprFromBuf( SymbolTable & symbols, const EvalSettings & settings, PosTable & positions, + DocCommentMap & docComments, const ref rootFS, const Expr::AstSymbols & astSymbols); @@ -335,10 +338,12 @@ binds $$ = $1; auto pos = state->at(@2); + auto exprPos = state->at(@4); { auto it = state->lexerState.positionToDocComment.find(pos); if (it != state->lexerState.positionToDocComment.end()) { $4->setDocComment(it->second); + state->lexerState.positionToDocComment.emplace(exprPos, it->second); } } @@ -463,11 +468,13 @@ Expr * parseExprFromBuf( SymbolTable & symbols, const EvalSettings & settings, PosTable & positions, + DocCommentMap & docComments, const ref rootFS, const Expr::AstSymbols & astSymbols) { yyscan_t scanner; LexerState lexerState { + .positionToDocComment = docComments, .positions = positions, .origin = positions.addOrigin(origin, length), }; diff --git a/src/libutil/position.hh b/src/libutil/position.hh index 729f2a523..aba263fdf 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -7,6 +7,7 @@ #include #include +#include #include "source-path.hh" @@ -65,6 +66,13 @@ struct Pos std::string getSnippetUpTo(const Pos & end) const; + /** + * Get the SourcePath, if the source was loaded from a file. + */ + std::optional getSourcePath() const { + return *std::get_if(&origin); + } + struct LinesIterator { using difference_type = size_t; using value_type = std::string_view; diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected new file mode 100644 index 000000000..9aca06178 --- /dev/null +++ b/tests/functional/repl/doc-constant.expected @@ -0,0 +1,102 @@ +Nix +Type :? for help. +Added variables. + +error: value does not have documentation + +Attribute version + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:29:3 + + Immovably fixed. + +Attribute empty + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:32:3 + + Unchangeably constant. + +error: + … while evaluating the attribute 'attr.undocument' + at /path/to/tests/functional/repl/doc-comments.nix:32:3: + 31| /** Unchangeably constant. */ + 32| lib.attr.empty = { }; + | ^ + 33| + + error: attribute 'undocument' missing + at «string»:1:1: + 1| lib.attr.undocument + | ^ + Did you mean undocumented? + +Attribute constant + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:26:3 + + Firmly rigid. + +Attribute version + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:29:3 + + Immovably fixed. + +Attribute empty + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:32:3 + + Unchangeably constant. + +Attribute undocumented + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:34:3 + + No documentation found. + +error: undefined variable 'missing' + at «string»:1:1: + 1| missing + | ^ + +error: undefined variable 'constanz' + at «string»:1:1: + 1| constanz + | ^ + +error: undefined variable 'missing' + at «string»:1:1: + 1| missing.attr + | ^ + +error: attribute 'missing' missing + at «string»:1:1: + 1| lib.missing + | ^ + +error: attribute 'missing' missing + at «string»:1:1: + 1| lib.missing.attr + | ^ + +error: + … while evaluating the attribute 'attr.undocumental' + at /path/to/tests/functional/repl/doc-comments.nix:32:3: + 31| /** Unchangeably constant. */ + 32| lib.attr.empty = { }; + | ^ + 33| + + error: attribute 'undocumental' missing + at «string»:1:1: + 1| lib.attr.undocumental + | ^ + Did you mean undocumented? + + diff --git a/tests/functional/repl/doc-constant.in b/tests/functional/repl/doc-constant.in new file mode 100644 index 000000000..9c0dde5e1 --- /dev/null +++ b/tests/functional/repl/doc-constant.in @@ -0,0 +1,15 @@ +:l doc-comments.nix +:doc constant +:doc lib.version +:doc lib.attr.empty +:doc lib.attr.undocument +:doc (import ./doc-comments.nix).constant +:doc (import ./doc-comments.nix).lib.version +:doc (import ./doc-comments.nix).lib.attr.empty +:doc (import ./doc-comments.nix).lib.attr.undocumented +:doc missing +:doc constanz +:doc missing.attr +:doc lib.missing +:doc lib.missing.attr +:doc lib.attr.undocumental From cef11b23e89b71f81bc8c4b4b47c3ac87a1a8315 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 19:27:36 +0200 Subject: [PATCH 585/910] Add missing .sh in _NIX_TEST_ACCEPT=1 message --- src/libexpr/eval.cc | 2 +- tests/functional/characterisation/framework.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 81c64ba71..c309e7e98 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -579,7 +579,7 @@ std::optional EvalState::getDoc(Value & v) s << "Function "; } else { - s << "Function **" << name << "**"; + s << "Function `" << name << "`"; if (pos) s << "\\\n … " ; else diff --git a/tests/functional/characterisation/framework.sh b/tests/functional/characterisation/framework.sh index 913fdd967..5ca125ab5 100644 --- a/tests/functional/characterisation/framework.sh +++ b/tests/functional/characterisation/framework.sh @@ -63,7 +63,7 @@ function characterisationTestExit() { echo '' echo 'You can rerun this test with:' echo '' - echo " _NIX_TEST_ACCEPT=1 make tests/functional/${TEST_NAME}.test" + echo " _NIX_TEST_ACCEPT=1 make tests/functional/${TEST_NAME}.sh.test" echo '' echo 'to regenerate the files containing the expected output,' echo 'and then view the git diff to decide whether a change is' From f9243eca75b0847a3984721564cf098baa185698 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 00:48:23 +0200 Subject: [PATCH 586/910] tests/functional/repl.sh: Work around GHA failure --- tests/functional/repl.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index d356fe096..c1f265df3 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -253,11 +253,21 @@ badExitCode=0 nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" runRepl () { + + # That is right, we are also filtering out the testdir _without underscores_. + # This is crazy, but without it, GHA will fail to run the tests, showing paths + # _with_ underscores in the set -x log, but _without_ underscores in the + # supposed nix repl output. I have looked in a number of places, but I cannot + # find a mechanism that could cause this to happen. + local testDirNoUnderscores + testDirNoUnderscores="${testDir//_/}" + # TODO: pass arguments to nix repl; see lang.sh nix repl 2>&1 \ | stripColors \ | sed \ -e "s@$testDir@/path/to/tests/functional@g" \ + -e "s@$testDirNoUnderscores@/path/to/tests/functional@g" \ -e "s@$nixVersion@@g" \ -e "s@Added [0-9]* variables@Added variables@g" \ | grep -vF $'warning: you don\'t have Internet access; disabling some network-dependent features' \ From 77e9f9ee82db9c77ef67f3916df746383ff451c3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 12:58:20 +0200 Subject: [PATCH 587/910] libexpr: Get rid of unused line tracking fields --- src/libexpr/lexer-helpers.cc | 1 - src/libexpr/lexer.l | 1 - src/libexpr/parser-state.hh | 6 +++--- src/libexpr/parser.y | 16 +++++++++++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libexpr/lexer-helpers.cc b/src/libexpr/lexer-helpers.cc index 87c514c71..f7d1017c3 100644 --- a/src/libexpr/lexer-helpers.cc +++ b/src/libexpr/lexer-helpers.cc @@ -4,7 +4,6 @@ void nix::lexer::internal::initLoc(YYLTYPE * loc) { - loc->first_line = loc->last_line = 0; loc->first_column = loc->last_column = 0; } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 7d4e6963f..b1b87b96a 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -287,7 +287,6 @@ or { return OR_KW; } \/\*\*[^/*]([^*]|\*+[^*/])*\*+\/ /* doc comments */ { LexerState & lexerState = *yyget_extra(yyscanner); lexerState.docCommentDistance = 0; - lexerState.lastDocCommentLoc.first_line = yylloc->first_line; lexerState.lastDocCommentLoc.first_column = yylloc->first_column; lexerState.lastDocCommentLoc.last_column = yylloc->last_column; } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 1df64e73d..3e801c13a 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -22,8 +22,8 @@ struct StringToken struct ParserLocation { - int first_line, first_column; - int last_line, last_column; + int first_column; + int last_column; // backup to recover from yyless(0) int stashed_first_column, stashed_last_column; @@ -39,7 +39,7 @@ struct ParserLocation } /** Latest doc comment position, or 0. */ - int doc_comment_first_line, doc_comment_first_column, doc_comment_last_column; + int doc_comment_first_column, doc_comment_last_column; }; struct LexerState diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index a25c6dd87..76190c0fa 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -31,6 +31,21 @@ #define YY_DECL int yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParserState * state) +// For efficiency, we only track offsets; not line,column coordinates +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) + namespace nix { typedef std::map DocCommentMap; @@ -69,7 +84,6 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * { if (std::string_view(error).starts_with("syntax error, unexpected end of file")) { loc->first_column = loc->last_column; - loc->first_line = loc->last_line; } throw ParseError({ .msg = HintFmt(error), From 71cb8bf509cf0e180e19230350318e2b453e6dbe Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 13:06:39 +0200 Subject: [PATCH 588/910] libexpr: Rename "column" fields to offset ... because that's what they are. --- src/libexpr/lexer-helpers.cc | 10 +++++----- src/libexpr/lexer.l | 4 ++-- src/libexpr/parser-state.hh | 18 +++++++++--------- src/libexpr/parser.y | 10 +++++----- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/libexpr/lexer-helpers.cc b/src/libexpr/lexer-helpers.cc index f7d1017c3..d9eeb73e2 100644 --- a/src/libexpr/lexer-helpers.cc +++ b/src/libexpr/lexer-helpers.cc @@ -4,7 +4,7 @@ void nix::lexer::internal::initLoc(YYLTYPE * loc) { - loc->first_column = loc->last_column = 0; + loc->beginOffset = loc->endOffset = 0; } void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) @@ -16,15 +16,15 @@ void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const ch if (lexerState.docCommentDistance == 1) { // Preceding token was a doc comment. ParserLocation doc; - doc.first_column = lexerState.lastDocCommentLoc.first_column; + doc.beginOffset = lexerState.lastDocCommentLoc.beginOffset; ParserLocation docEnd; - docEnd.first_column = lexerState.lastDocCommentLoc.last_column; + docEnd.beginOffset = lexerState.lastDocCommentLoc.endOffset; DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; PosIdx locPos = lexerState.at(*loc); lexerState.positionToDocComment.emplace(locPos, docComment); } lexerState.docCommentDistance++; - loc->first_column = loc->last_column; - loc->last_column += len; + loc->beginOffset = loc->endOffset; + loc->endOffset += len; } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index b1b87b96a..58401be8e 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -287,8 +287,8 @@ or { return OR_KW; } \/\*\*[^/*]([^*]|\*+[^*/])*\*+\/ /* doc comments */ { LexerState & lexerState = *yyget_extra(yyscanner); lexerState.docCommentDistance = 0; - lexerState.lastDocCommentLoc.first_column = yylloc->first_column; - lexerState.lastDocCommentLoc.last_column = yylloc->last_column; + lexerState.lastDocCommentLoc.beginOffset = yylloc->beginOffset; + lexerState.lastDocCommentLoc.endOffset = yylloc->endOffset; } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 3e801c13a..983a17a2e 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -22,20 +22,20 @@ struct StringToken struct ParserLocation { - int first_column; - int last_column; + int beginOffset; + int endOffset; // backup to recover from yyless(0) - int stashed_first_column, stashed_last_column; + int stashedBeginOffset, stashedEndOffset; void stash() { - stashed_first_column = first_column; - stashed_last_column = last_column; + stashedBeginOffset = beginOffset; + stashedEndOffset = endOffset; } void unstash() { - first_column = stashed_first_column; - last_column = stashed_last_column; + beginOffset = stashedBeginOffset; + endOffset = stashedEndOffset; } /** Latest doc comment position, or 0. */ @@ -308,12 +308,12 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, inline PosIdx LexerState::at(const ParserLocation & loc) { - return positions.add(origin, loc.first_column); + return positions.add(origin, loc.beginOffset); } inline PosIdx ParserState::at(const ParserLocation & loc) { - return positions.add(origin, loc.first_column); + return positions.add(origin, loc.beginOffset); } } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 76190c0fa..452d265bc 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -36,13 +36,13 @@ do \ if (N) \ { \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + (Current).beginOffset = YYRHSLOC (Rhs, 1).beginOffset; \ + (Current).endOffset = YYRHSLOC (Rhs, N).endOffset; \ } \ else \ { \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ + (Current).beginOffset = (Current).endOffset = \ + YYRHSLOC (Rhs, 0).endOffset; \ } \ while (0) @@ -83,7 +83,7 @@ using namespace nix; void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error) { if (std::string_view(error).starts_with("syntax error, unexpected end of file")) { - loc->first_column = loc->last_column; + loc->beginOffset = loc->endOffset; } throw ParseError({ .msg = HintFmt(error), From 8a855296f555fdb5a334acb5f482f3c781e87657 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 14:27:09 +0200 Subject: [PATCH 589/910] tests/function/repl: Characterise the missing doc comment behavior --- src/libexpr/nixexpr.cc | 2 ++ .../repl/doc-comment-curried-args.expected | 24 ++++++++++++++++ .../repl/doc-comment-curried-args.in | 7 +++++ .../repl/doc-comment-formals.expected | 13 +++++++++ tests/functional/repl/doc-comment-formals.in | 3 ++ tests/functional/repl/doc-comments.nix | 27 ++++++++++-------- tests/functional/repl/doc-constant.expected | 28 +++++++++---------- .../repl/doc-lambda-flavors.expected | 8 +++--- .../functional/repl/doc-unambiguous.expected | 2 +- 9 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 tests/functional/repl/doc-comment-curried-args.expected create mode 100644 tests/functional/repl/doc-comment-curried-args.in create mode 100644 tests/functional/repl/doc-comment-formals.expected create mode 100644 tests/functional/repl/doc-comment-formals.in diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 5479dd79e..d54f0b684 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -584,6 +584,8 @@ std::string ExprLambda::showNamePos(const EvalState & state) const } void ExprLambda::setDocComment(DocComment docComment) { + // RFC 145 specifies that the innermost doc comment wins. + // See https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md#ambiguous-placement if (!this->docComment) { this->docComment = docComment; diff --git a/tests/functional/repl/doc-comment-curried-args.expected b/tests/functional/repl/doc-comment-curried-args.expected new file mode 100644 index 000000000..c10c171e1 --- /dev/null +++ b/tests/functional/repl/doc-comment-curried-args.expected @@ -0,0 +1,24 @@ +Nix +Type :? for help. +Added variables. + +Function curriedArgs + … defined at + /path/to/tests/functional/repl/doc-comments.nix:48:5 + + A documented function. + + +"Note that users may not expect this to behave as it currently does" + +Function curriedArgs + … defined at + /path/to/tests/functional/repl/doc-comments.nix:50:5 + + The function returned by applying once + +"This won't produce documentation, because we can't actually document arbitrary values" + +error: value does not have documentation + + diff --git a/tests/functional/repl/doc-comment-curried-args.in b/tests/functional/repl/doc-comment-curried-args.in new file mode 100644 index 000000000..8dbbfc370 --- /dev/null +++ b/tests/functional/repl/doc-comment-curried-args.in @@ -0,0 +1,7 @@ +:l doc-comments.nix +:doc curriedArgs +x = curriedArgs 1 +"Note that users may not expect this to behave as it currently does" +:doc x +"This won't produce documentation, because we can't actually document arbitrary values" +:doc x 2 diff --git a/tests/functional/repl/doc-comment-formals.expected b/tests/functional/repl/doc-comment-formals.expected new file mode 100644 index 000000000..704c0050b --- /dev/null +++ b/tests/functional/repl/doc-comment-formals.expected @@ -0,0 +1,13 @@ +Nix +Type :? for help. +Added variables. + +"Note that this is not yet complete" + +Function documentedFormals + … defined at + /path/to/tests/functional/repl/doc-comments.nix:57:5 + + Finds x + + diff --git a/tests/functional/repl/doc-comment-formals.in b/tests/functional/repl/doc-comment-formals.in new file mode 100644 index 000000000..e32fb8ab1 --- /dev/null +++ b/tests/functional/repl/doc-comment-formals.in @@ -0,0 +1,3 @@ +:l doc-comments.nix +"Note that this is not yet complete" +:doc documentedFormals diff --git a/tests/functional/repl/doc-comments.nix b/tests/functional/repl/doc-comments.nix index a98f1c688..e91ee0b51 100644 --- a/tests/functional/repl/doc-comments.nix +++ b/tests/functional/repl/doc-comments.nix @@ -17,6 +17,7 @@ compact=/**boom*/x: x; + # https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md#ambiguous-placement /** Ignore!!! */ unambiguous = /** Very close */ @@ -41,17 +42,19 @@ # TODO - # /** This returns a documented function. */ - # documentedArgs = - # /** x */ - # x: - # /** y */ - # y: - # /** x + y */ - # x + y; + /** You won't see this. */ + curriedArgs = + /** A documented function. */ + x: + /** The function returned by applying once */ + y: + /** A function body performing summation of two items */ + x + y; - # /** Documented formals */ - # documentedFormals = - # /** x */ - # x: x; + /** Documented formals (but you won't see this comment) */ + documentedFormals = + /** Finds x */ + { /** The x attribute */ + x + }: x; } diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected index 9aca06178..c66558333 100644 --- a/tests/functional/repl/doc-constant.expected +++ b/tests/functional/repl/doc-constant.expected @@ -7,24 +7,24 @@ error: value does not have documentation Attribute version … defined at - /path/to/tests/functional/repl/doc-comments.nix:29:3 + /path/to/tests/functional/repl/doc-comments.nix:30:3 Immovably fixed. Attribute empty … defined at - /path/to/tests/functional/repl/doc-comments.nix:32:3 + /path/to/tests/functional/repl/doc-comments.nix:33:3 Unchangeably constant. error: … while evaluating the attribute 'attr.undocument' - at /path/to/tests/functional/repl/doc-comments.nix:32:3: - 31| /** Unchangeably constant. */ - 32| lib.attr.empty = { }; + at /path/to/tests/functional/repl/doc-comments.nix:33:3: + 32| /** Unchangeably constant. */ + 33| lib.attr.empty = { }; | ^ - 33| + 34| error: attribute 'undocument' missing at «string»:1:1: @@ -35,28 +35,28 @@ error: Attribute constant … defined at - /path/to/tests/functional/repl/doc-comments.nix:26:3 + /path/to/tests/functional/repl/doc-comments.nix:27:3 Firmly rigid. Attribute version … defined at - /path/to/tests/functional/repl/doc-comments.nix:29:3 + /path/to/tests/functional/repl/doc-comments.nix:30:3 Immovably fixed. Attribute empty … defined at - /path/to/tests/functional/repl/doc-comments.nix:32:3 + /path/to/tests/functional/repl/doc-comments.nix:33:3 Unchangeably constant. Attribute undocumented … defined at - /path/to/tests/functional/repl/doc-comments.nix:34:3 + /path/to/tests/functional/repl/doc-comments.nix:35:3 No documentation found. @@ -87,11 +87,11 @@ error: attribute 'missing' missing error: … while evaluating the attribute 'attr.undocumental' - at /path/to/tests/functional/repl/doc-comments.nix:32:3: - 31| /** Unchangeably constant. */ - 32| lib.attr.empty = { }; + at /path/to/tests/functional/repl/doc-comments.nix:33:3: + 32| /** Unchangeably constant. */ + 33| lib.attr.empty = { }; | ^ - 33| + 34| error: attribute 'undocumental' missing at «string»:1:1: diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected index 4ac52f39e..43f483ce9 100644 --- a/tests/functional/repl/doc-lambda-flavors.expected +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -4,25 +4,25 @@ Added variables. Function nonStrict … defined at - /path/to/tests/functional/repl/doc-comments.nix:36:70 + /path/to/tests/functional/repl/doc-comments.nix:37:70 My syntax is not strict, but I'm strict anyway. Function strict … defined at - /path/to/tests/functional/repl/doc-comments.nix:37:63 + /path/to/tests/functional/repl/doc-comments.nix:38:63 I don't have to be strict, but I am anyway. Function strictPre … defined at - /path/to/tests/functional/repl/doc-comments.nix:39:48 + /path/to/tests/functional/repl/doc-comments.nix:40:48 Here's one way to do this Function strictPost … defined at - /path/to/tests/functional/repl/doc-comments.nix:40:53 + /path/to/tests/functional/repl/doc-comments.nix:41:53 Here's another way to do this diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected index 0f89a6f64..825aa1ee1 100644 --- a/tests/functional/repl/doc-unambiguous.expected +++ b/tests/functional/repl/doc-unambiguous.expected @@ -4,7 +4,7 @@ Added variables. Function unambiguous … defined at - /path/to/tests/functional/repl/doc-comments.nix:23:5 + /path/to/tests/functional/repl/doc-comments.nix:24:5 Very close From 6bbd493d49fa58aa128a2419db5b1139d5c7a944 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 15:08:41 +0200 Subject: [PATCH 590/910] libcmd/repl-interacter: INT_MAX -> numeric_limits --- src/libcmd/repl-interacter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 420cce1db..b285c8a9a 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -73,7 +73,7 @@ static int listPossibleCallback(char * s, char *** avp) { auto possible = curRepl->completePrefix(s); - if (possible.size() > (INT_MAX / sizeof(char *))) + if (possible.size() > (std::numeric_limits::max() / sizeof(char *))) throw Error("too many completions"); int ac = 0; From 131b6ccc716d1807f4ea91bc3fc1239ff7775648 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 18:04:33 +0200 Subject: [PATCH 591/910] nixexpr.hh: Avoid the warning and pragmas --- src/libexpr/nixexpr.hh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 4c4c8af19..1bcc962c5 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -36,16 +36,11 @@ struct Value; */ struct DocComment { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcomment" // "nested comment start" is intentional - /** - * Start of the comment, including `/**`. + * Start of the comment, including the opening, ie `/` and `**`. */ PosIdx begin; -#pragma GCC diagnostic pop - /** * Position right after the final asterisk and `/` that terminate the comment. */ From 21817473e8035e2231c083c46184724057a30d7f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:04:37 +0200 Subject: [PATCH 592/910] Doc comments: use std::unordered_map Co-authored-by: Eelco Dolstra --- src/libexpr/eval.hh | 4 ++-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 3918fb092..d9a3e80bc 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,7 +130,7 @@ struct Constant typedef std::map ValMap; #endif -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; struct Env { @@ -335,7 +335,7 @@ private: * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. * Grouped by file. */ - std::map positionToDocComment; + std::unordered_map positionToDocComment; LookupPath lookupPath; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 983a17a2e..5a7bcb717 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::map & positionToDocComment; + std::unordered_map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 452d265bc..8ea176b24 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -48,7 +48,7 @@ namespace nix { -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; Expr * parseExprFromBuf( char * text, From ac89df815d90eec38935f6c238df8811bd907cf9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:23:23 +0200 Subject: [PATCH 593/910] libcmd/repl.cc: Explain evalString call and defend --- src/libcmd/repl.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index fb8106c46..b5d0816dd 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -632,8 +632,13 @@ ProcessLineResult NixRepl::processLine(std::string line) assert(attrs); auto attr = attrs->get(name); if (!attr) { - // Trigger the normal error + // When missing, trigger the normal exception + // e.g. :doc builtins.foo + // behaves like + // nix-repl> builtins.foo + // error: attribute 'foo' missing evalString(arg, v); + assert(false); } if (attr->pos) { fallbackPos = attr->pos; From 6a125e65d0f2dac90cdc69f16be0a4bd888c7a2f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:33:56 +0200 Subject: [PATCH 594/910] Revert "Doc comments: use std::unordered_map" hash isn't implemented yet, and I can't cherry-pick a bug-free commit yet. This reverts commit 95529f31e3bbda99111c5ce98a33484dc6e7a462. --- src/libexpr/eval.hh | 4 ++-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index d9a3e80bc..3918fb092 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,7 +130,7 @@ struct Constant typedef std::map ValMap; #endif -typedef std::unordered_map DocCommentMap; +typedef std::map DocCommentMap; struct Env { @@ -335,7 +335,7 @@ private: * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. * Grouped by file. */ - std::unordered_map positionToDocComment; + std::map positionToDocComment; LookupPath lookupPath; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 5a7bcb717..983a17a2e 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::unordered_map & positionToDocComment; + std::map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 8ea176b24..452d265bc 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -48,7 +48,7 @@ namespace nix { -typedef std::unordered_map DocCommentMap; +typedef std::map DocCommentMap; Expr * parseExprFromBuf( char * text, From ce31a0457f1e2e32d751200bb0a964d4a3e8f253 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:55:01 +0200 Subject: [PATCH 595/910] Use HintFmt for doc comments --- src/libcmd/repl.cc | 6 +++--- src/libexpr/eval.cc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index b5d0816dd..a555fcfcc 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -668,12 +668,12 @@ ProcessLineResult NixRepl::processLine(std::string line) logger->cout(trim(renderMarkdownToTerminal(markdown))); } else if (fallbackPos) { std::stringstream ss; - ss << "Attribute `" << fallbackName << "`\n\n"; - ss << " … defined at " << state->positions[fallbackPos] << "\n\n"; + ss << HintFmt("Attribute '%1%'", fallbackName) << "\n\n"; + ss << HintFmt(" … defined at %1%", state->positions[fallbackPos]) << "\n\n"; if (fallbackDoc) { ss << fallbackDoc.getInnerText(state->positions); } else { - ss << "No documentation found.\n\n"; + ss << HintFmt("No documentation found.") << "\n\n"; } auto markdown = ss.str(); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c309e7e98..dd3677e39 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -576,17 +576,17 @@ std::optional EvalState::getDoc(Value & v) } if (name.empty()) { - s << "Function "; + s << HintFmt("Function "); } else { - s << "Function `" << name << "`"; + s << HintFmt("Function '%s'", name); if (pos) s << "\\\n … " ; else s << "\\\n"; } if (pos) { - s << "defined at " << pos; + s << HintFmt("defined at %1%", pos); } if (!docStr.empty()) { s << "\n\n"; From 03d33703ef60ec40d8c376e4d935e991fc176294 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:55:05 +0200 Subject: [PATCH 596/910] Revert "Use HintFmt for doc comments" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately these don't render correctly, because they go into the markdown renderer, instead of the terminal. ``` nix-repl> :doc lib.version Attribute '[35;1mversion[0m' … defined at [35;1m/home/user/h/nixpkgs/lib/default.nix:73:40[0m ``` We could switch that to go direct to the terminal, but then we should do the same for the primops, to get a consistent look. Reverting for now. This reverts commit 3413e0338cbee1c7734d5cb614b5325e51815cde. --- src/libcmd/repl.cc | 6 +++--- src/libexpr/eval.cc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a555fcfcc..b5d0816dd 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -668,12 +668,12 @@ ProcessLineResult NixRepl::processLine(std::string line) logger->cout(trim(renderMarkdownToTerminal(markdown))); } else if (fallbackPos) { std::stringstream ss; - ss << HintFmt("Attribute '%1%'", fallbackName) << "\n\n"; - ss << HintFmt(" … defined at %1%", state->positions[fallbackPos]) << "\n\n"; + ss << "Attribute `" << fallbackName << "`\n\n"; + ss << " … defined at " << state->positions[fallbackPos] << "\n\n"; if (fallbackDoc) { ss << fallbackDoc.getInnerText(state->positions); } else { - ss << HintFmt("No documentation found.") << "\n\n"; + ss << "No documentation found.\n\n"; } auto markdown = ss.str(); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index dd3677e39..c309e7e98 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -576,17 +576,17 @@ std::optional EvalState::getDoc(Value & v) } if (name.empty()) { - s << HintFmt("Function "); + s << "Function "; } else { - s << HintFmt("Function '%s'", name); + s << "Function `" << name << "`"; if (pos) s << "\\\n … " ; else s << "\\\n"; } if (pos) { - s << HintFmt("defined at %1%", pos); + s << "defined at " << pos; } if (!docStr.empty()) { s << "\n\n"; From 61a4d3d45c91cb19a114796846e5af014f59a6b6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 20:08:41 +0200 Subject: [PATCH 597/910] getSnippetUpTo: Return optional This makes it possible to certain discern failures from empty snippets, which I think is an ok review comment. Maybe it should do so for swapped column indexes too, but I'm not sure. I don't think it matters in the grand scheme. We don't even have a real use case for `nullopt` now anyway. Since we don't have a use case, I'm not applying this logic to higher level functions yet. --- src/libexpr/nixexpr.cc | 2 +- src/libutil/position.cc | 6 +++--- src/libutil/position.hh | 2 +- tests/unit/libutil/position.cc | 8 +++++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index d54f0b684..93c8bdef6 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -646,7 +646,7 @@ size_t SymbolTable::totalSize() const std::string DocComment::getInnerText(const PosTable & positions) const { auto beginPos = positions[begin]; auto endPos = positions[end]; - auto docCommentStr = beginPos.getSnippetUpTo(endPos); + auto docCommentStr = beginPos.getSnippetUpTo(endPos).value_or(""); // Strip "/**" and "*/" constexpr size_t prefixLen = 3; diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 508816d85..3289dbe8b 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -110,11 +110,11 @@ void Pos::LinesIterator::bump(bool atFirst) input.remove_prefix(eol); } -std::string Pos::getSnippetUpTo(const Pos & end) const { +std::optional Pos::getSnippetUpTo(const Pos & end) const { assert(this->origin == end.origin); if (end.line < this->line) - return ""; + return std::nullopt; if (auto source = getSource()) { @@ -152,7 +152,7 @@ std::string Pos::getSnippetUpTo(const Pos & end) const { } return result; } - return ""; + return std::nullopt; } diff --git a/src/libutil/position.hh b/src/libutil/position.hh index aba263fdf..25217069c 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -64,7 +64,7 @@ struct Pos bool operator==(const Pos & rhs) const = default; auto operator<=>(const Pos & rhs) const = default; - std::string getSnippetUpTo(const Pos & end) const; + std::optional getSnippetUpTo(const Pos & end) const; /** * Get the SourcePath, if the source was loaded from a file. diff --git a/tests/unit/libutil/position.cc b/tests/unit/libutil/position.cc index d38d2c538..484ecc247 100644 --- a/tests/unit/libutil/position.cc +++ b/tests/unit/libutil/position.cc @@ -25,7 +25,7 @@ TEST(Position, getSnippetUpTo_1) ASSERT_EQ(start.getSnippetUpTo(start), ""); ASSERT_EQ(start.getSnippetUpTo(end), "x"); ASSERT_EQ(end.getSnippetUpTo(end), ""); - ASSERT_EQ(end.getSnippetUpTo(start), ""); + ASSERT_EQ(end.getSnippetUpTo(start), std::nullopt); } { // NOTE: line and column are actually 1-based indexes @@ -37,7 +37,7 @@ TEST(Position, getSnippetUpTo_1) ASSERT_EQ(start.getSnippetUpTo(end), ""); ASSERT_EQ(end.getSnippetUpTo(end), ""); - ASSERT_EQ(end.getSnippetUpTo(start), ""); + ASSERT_EQ(end.getSnippetUpTo(start), std::nullopt); } { Pos start(1, 1, o); @@ -53,7 +53,7 @@ TEST(Position, getSnippetUpTo_1) ASSERT_EQ(start.getSnippetUpTo(start), ""); ASSERT_EQ(start.getSnippetUpTo(end), "x"); ASSERT_EQ(end.getSnippetUpTo(end), ""); - ASSERT_EQ(end.getSnippetUpTo(start), ""); + ASSERT_EQ(end.getSnippetUpTo(start), std::nullopt); } } TEST(Position, getSnippetUpTo_2) @@ -65,6 +65,8 @@ TEST(Position, getSnippetUpTo_2) ASSERT_EQ(start.getSnippetUpTo(start), ""); ASSERT_EQ(start.getSnippetUpTo(end), "a"); ASSERT_EQ(end.getSnippetUpTo(end), ""); + + // nullopt? I feel like changing the column handling would just make it more fragile ASSERT_EQ(end.getSnippetUpTo(start), ""); } { From 1bec90e3c4792289b960875d050ec65fd273a780 Mon Sep 17 00:00:00 2001 From: Goldstein Date: Mon, 15 Jul 2024 23:11:26 +0300 Subject: [PATCH 598/910] tests/functional/repl.sh: fail test on wrong stdout Previous test implementation assumed that grep supports newlines in patterns. It doesn't, so tests spuriously passed, even though some tests outputs were broken. This patches output (and expected output) before grepping, so there're no newlines in pattern. --- tests/functional/repl.sh | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index c1f265df3..4f5bb36aa 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -74,21 +74,31 @@ testReplResponseGeneral () { local grepMode commands expectedResponse response grepMode="$1"; shift commands="$1"; shift - expectedResponse="$1"; shift - response="$(nix repl "$@" <<< "$commands" | stripColors)" - echo "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \ - || fail "repl command set: + # Expected response can contain newlines. + # grep can't handle multiline patterns, so replace newlines with TEST_NEWLINE + # in both expectedResponse and response. + # awk ORS always adds a trailing record separator, so we strip it with sed. + expectedResponse="$(printf '%s' "$1" | awk 1 ORS=TEST_NEWLINE | sed 's/TEST_NEWLINE$//')"; shift + # We don't need to strip trailing record separator here, since extra data is ok. + response="$(nix repl "$@" <<< "$commands" 2>&1 | stripColors | awk 1 ORS=TEST_NEWLINE)" + printf '%s' "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \ + || fail "$(echo "repl command set: $commands does not respond with: +--- $expectedResponse +--- but with: +--- $response -" +--- + +" | sed 's/TEST_NEWLINE/\n/g')" } testReplResponse () { @@ -190,7 +200,7 @@ testReplResponseNoRegex ' let x = { y = { a = 1; }; inherit x; }; in x ' \ '{ - x = { ... }; + x = «repeated»; y = { ... }; } ' @@ -242,7 +252,7 @@ testReplResponseNoRegex ' ' \ '{ x = «repeated»; - y = { a = 1 }; + y = { a = 1; }; } ' @@ -256,7 +266,7 @@ runRepl () { # That is right, we are also filtering out the testdir _without underscores_. # This is crazy, but without it, GHA will fail to run the tests, showing paths - # _with_ underscores in the set -x log, but _without_ underscores in the + # _with_ underscores in the set -x log, but _without_ underscores in the # supposed nix repl output. I have looked in a number of places, but I cannot # find a mechanism that could cause this to happen. local testDirNoUnderscores From 846869da0ed0580beb7f827b303fef9a8386de37 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Mon, 15 Jul 2024 21:49:15 +0100 Subject: [PATCH 599/910] Make goals use C++20 coroutines (#11005) undefined --- src/libstore/build/derivation-goal.cc | 141 +++++---- src/libstore/build/derivation-goal.hh | 32 +- .../build/drv-output-substitution-goal.cc | 204 ++++++------- .../build/drv-output-substitution-goal.hh | 37 +-- src/libstore/build/goal.cc | 114 ++++++- src/libstore/build/goal.hh | 283 +++++++++++++++++- src/libstore/build/substitution-goal.cc | 244 +++++++-------- src/libstore/build/substitution-goal.hh | 63 +--- src/libstore/build/worker.cc | 40 ++- .../unix/build/local-derivation-goal.cc | 19 +- .../unix/build/local-derivation-goal.hh | 2 +- 11 files changed, 708 insertions(+), 471 deletions(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index f795b05a1..010f905d6 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -36,6 +36,14 @@ namespace nix { +Goal::Co DerivationGoal::init() { + if (useDerivation) { + co_return getDerivation(); + } else { + co_return haveDerivation(); + } +} + DerivationGoal::DerivationGoal(const StorePath & drvPath, const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode) : Goal(worker, DerivedPath::Built { .drvPath = makeConstantStorePathRef(drvPath), .outputs = wantedOutputs }) @@ -44,7 +52,6 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, , wantedOutputs(wantedOutputs) , buildMode(buildMode) { - state = &DerivationGoal::getDerivation; name = fmt( "building of '%s' from .drv file", DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store)); @@ -65,7 +72,6 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation { this->drv = std::make_unique(drv); - state = &DerivationGoal::haveDerivation; name = fmt( "building of '%s' from in-memory derivation", DerivedPath::Built { makeConstantStorePathRef(drvPath), drv.outputNames() }.to_string(worker.store)); @@ -109,13 +115,9 @@ void DerivationGoal::killChild() void DerivationGoal::timedOut(Error && ex) { killChild(); - done(BuildResult::TimedOut, {}, std::move(ex)); -} - - -void DerivationGoal::work() -{ - (this->*state)(); + // We're not inside a coroutine, hence we can't use co_return here. + // Thus we ignore the return value. + [[maybe_unused]] Done _ = done(BuildResult::TimedOut, {}, std::move(ex)); } void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) @@ -139,7 +141,7 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) } -void DerivationGoal::getDerivation() +Goal::Co DerivationGoal::getDerivation() { trace("init"); @@ -147,23 +149,22 @@ void DerivationGoal::getDerivation() exists. If it doesn't, it may be created through a substitute. */ if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) { - loadDerivation(); - return; + co_return loadDerivation(); } addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath))); - state = &DerivationGoal::loadDerivation; + co_await Suspend{}; + co_return loadDerivation(); } -void DerivationGoal::loadDerivation() +Goal::Co DerivationGoal::loadDerivation() { trace("loading derivation"); if (nrFailed != 0) { - done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))); - return; + co_return done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))); } /* `drvPath' should already be a root, but let's be on the safe @@ -185,11 +186,11 @@ void DerivationGoal::loadDerivation() } assert(drv); - haveDerivation(); + co_return haveDerivation(); } -void DerivationGoal::haveDerivation() +Goal::Co DerivationGoal::haveDerivation() { trace("have derivation"); @@ -217,8 +218,7 @@ void DerivationGoal::haveDerivation() }); } - gaveUpOnSubstitution(); - return; + co_return gaveUpOnSubstitution(); } for (auto & i : drv->outputsAndOptPaths(worker.store)) @@ -240,8 +240,7 @@ void DerivationGoal::haveDerivation() /* If they are all valid, then we're done. */ if (allValid && buildMode == bmNormal) { - done(BuildResult::AlreadyValid, std::move(validOutputs)); - return; + co_return done(BuildResult::AlreadyValid, std::move(validOutputs)); } /* We are first going to try to create the invalid output paths @@ -268,24 +267,21 @@ void DerivationGoal::haveDerivation() } } - if (waitees.empty()) /* to prevent hang (no wake-up event) */ - outputsSubstitutionTried(); - else - state = &DerivationGoal::outputsSubstitutionTried; + if (!waitees.empty()) co_await Suspend{}; /* to prevent hang (no wake-up event) */ + co_return outputsSubstitutionTried(); } -void DerivationGoal::outputsSubstitutionTried() +Goal::Co DerivationGoal::outputsSubstitutionTried() { trace("all outputs substituted (maybe)"); assert(!drv->type().isImpure()); if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) { - done(BuildResult::TransientFailure, {}, + co_return done(BuildResult::TransientFailure, {}, Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ", worker.store.printStorePath(drvPath))); - return; } /* If the substitutes form an incomplete closure, then we should @@ -319,32 +315,29 @@ void DerivationGoal::outputsSubstitutionTried() if (needRestart == NeedRestartForMoreOutputs::OutputsAddedDoNeed) { needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed; - haveDerivation(); - return; + co_return haveDerivation(); } auto [allValid, validOutputs] = checkPathValidity(); if (buildMode == bmNormal && allValid) { - done(BuildResult::Substituted, std::move(validOutputs)); - return; + co_return done(BuildResult::Substituted, std::move(validOutputs)); } if (buildMode == bmRepair && allValid) { - repairClosure(); - return; + co_return repairClosure(); } if (buildMode == bmCheck && !allValid) throw Error("some outputs of '%s' are not valid, so checking is not possible", worker.store.printStorePath(drvPath)); /* Nothing to wait for; tail call */ - gaveUpOnSubstitution(); + co_return gaveUpOnSubstitution(); } /* At least one of the output paths could not be produced using a substitute. So we have to build instead. */ -void DerivationGoal::gaveUpOnSubstitution() +Goal::Co DerivationGoal::gaveUpOnSubstitution() { /* At this point we are building all outputs, so if more are wanted there is no need to restart. */ @@ -405,14 +398,12 @@ void DerivationGoal::gaveUpOnSubstitution() addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i))); } - if (waitees.empty()) /* to prevent hang (no wake-up event) */ - inputsRealised(); - else - state = &DerivationGoal::inputsRealised; + if (!waitees.empty()) co_await Suspend{}; /* to prevent hang (no wake-up event) */ + co_return inputsRealised(); } -void DerivationGoal::repairClosure() +Goal::Co DerivationGoal::repairClosure() { assert(!drv->type().isImpure()); @@ -466,41 +457,39 @@ void DerivationGoal::repairClosure() } if (waitees.empty()) { - done(BuildResult::AlreadyValid, assertPathValidity()); - return; + co_return done(BuildResult::AlreadyValid, assertPathValidity()); + } else { + co_await Suspend{}; + co_return closureRepaired(); } - - state = &DerivationGoal::closureRepaired; } -void DerivationGoal::closureRepaired() +Goal::Co DerivationGoal::closureRepaired() { trace("closure repaired"); if (nrFailed > 0) throw Error("some paths in the output closure of derivation '%s' could not be repaired", worker.store.printStorePath(drvPath)); - done(BuildResult::AlreadyValid, assertPathValidity()); + co_return done(BuildResult::AlreadyValid, assertPathValidity()); } -void DerivationGoal::inputsRealised() +Goal::Co DerivationGoal::inputsRealised() { trace("all inputs realised"); if (nrFailed != 0) { if (!useDerivation) throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath)); - done(BuildResult::DependencyFailed, {}, Error( + co_return done(BuildResult::DependencyFailed, {}, Error( "%s dependencies of derivation '%s' failed to build", nrFailed, worker.store.printStorePath(drvPath))); - return; } if (retrySubstitution == RetrySubstitution::YesNeed) { retrySubstitution = RetrySubstitution::AlreadyRetried; - haveDerivation(); - return; + co_return haveDerivation(); } /* Gather information necessary for computing the closure and/or @@ -566,8 +555,8 @@ void DerivationGoal::inputsRealised() pathResolved, wantedOutputs, buildMode); addWaitee(resolvedDrvGoal); - state = &DerivationGoal::resolvedFinished; - return; + co_await Suspend{}; + co_return resolvedFinished(); } std::function::ChildNode &)> accumInputPaths; @@ -631,8 +620,9 @@ void DerivationGoal::inputsRealised() /* Okay, try to build. Note that here we don't wait for a build slot to become available, since we don't need one if there is a build hook. */ - state = &DerivationGoal::tryToBuild; worker.wakeUp(shared_from_this()); + co_await Suspend{}; + co_return tryToBuild(); } void DerivationGoal::started() @@ -657,7 +647,7 @@ void DerivationGoal::started() worker.updateProgress(); } -void DerivationGoal::tryToBuild() +Goal::Co DerivationGoal::tryToBuild() { trace("trying to build"); @@ -693,7 +683,8 @@ void DerivationGoal::tryToBuild() actLock = std::make_unique(*logger, lvlWarn, actBuildWaiting, fmt("waiting for lock on %s", Magenta(showPaths(lockFiles)))); worker.waitForAWhile(shared_from_this()); - return; + co_await Suspend{}; + co_return tryToBuild(); } actLock.reset(); @@ -710,8 +701,7 @@ void DerivationGoal::tryToBuild() if (buildMode != bmCheck && allValid) { debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath)); outputLocks.setDeletion(true); - done(BuildResult::AlreadyValid, std::move(validOutputs)); - return; + co_return done(BuildResult::AlreadyValid, std::move(validOutputs)); } /* If any of the outputs already exist but are not valid, delete @@ -737,9 +727,9 @@ void DerivationGoal::tryToBuild() EOF from the hook. */ actLock.reset(); buildResult.startTime = time(0); // inexact - state = &DerivationGoal::buildDone; started(); - return; + co_await Suspend{}; + co_return buildDone(); case rpPostpone: /* Not now; wait until at least one child finishes or the wake-up timeout expires. */ @@ -748,7 +738,8 @@ void DerivationGoal::tryToBuild() fmt("waiting for a machine to build '%s'", Magenta(worker.store.printStorePath(drvPath)))); worker.waitForAWhile(shared_from_this()); outputLocks.unlock(); - return; + co_await Suspend{}; + co_return tryToBuild(); case rpDecline: /* We should do it ourselves. */ break; @@ -757,11 +748,12 @@ void DerivationGoal::tryToBuild() actLock.reset(); - state = &DerivationGoal::tryLocalBuild; worker.wakeUp(shared_from_this()); + co_await Suspend{}; + co_return tryLocalBuild(); } -void DerivationGoal::tryLocalBuild() { +Goal::Co DerivationGoal::tryLocalBuild() { throw Error( R"( Unable to build with a primary store that isn't a local store; @@ -938,7 +930,7 @@ void runPostBuildHook( }); } -void DerivationGoal::buildDone() +Goal::Co DerivationGoal::buildDone() { trace("build done"); @@ -1033,7 +1025,7 @@ void DerivationGoal::buildDone() outputLocks.setDeletion(true); outputLocks.unlock(); - done(BuildResult::Built, std::move(builtOutputs)); + co_return done(BuildResult::Built, std::move(builtOutputs)); } catch (BuildError & e) { outputLocks.unlock(); @@ -1058,12 +1050,11 @@ void DerivationGoal::buildDone() BuildResult::PermanentFailure; } - done(st, {}, std::move(e)); - return; + co_return done(st, {}, std::move(e)); } } -void DerivationGoal::resolvedFinished() +Goal::Co DerivationGoal::resolvedFinished() { trace("resolved derivation finished"); @@ -1131,7 +1122,7 @@ void DerivationGoal::resolvedFinished() if (status == BuildResult::AlreadyValid) status = BuildResult::ResolvesToAlreadyValid; - done(status, std::move(builtOutputs)); + co_return done(status, std::move(builtOutputs)); } HookReply DerivationGoal::tryBuildHook() @@ -1325,7 +1316,9 @@ void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) logSize += data.size(); if (settings.maxLogSize && logSize > settings.maxLogSize) { killChild(); - done( + // We're not inside a coroutine, hence we can't use co_return here. + // Thus we ignore the return value. + [[maybe_unused]] Done _ = done( BuildResult::LogLimitExceeded, {}, Error("%s killed after writing more than %d bytes of log output", getName(), settings.maxLogSize)); @@ -1531,7 +1524,7 @@ SingleDrvOutputs DerivationGoal::assertPathValidity() } -void DerivationGoal::done( +Goal::Done DerivationGoal::done( BuildResult::Status status, SingleDrvOutputs builtOutputs, std::optional ex) @@ -1568,7 +1561,7 @@ void DerivationGoal::done( fs << worker.store.printStorePath(drvPath) << "\t" << buildResult.toString() << std::endl; } - amDone(buildResult.success() ? ecSuccess : ecFailed, std::move(ex)); + return amDone(buildResult.success() ? ecSuccess : ecFailed, std::move(ex)); } diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh index 04f13aedd..ad3d9ca2a 100644 --- a/src/libstore/build/derivation-goal.hh +++ b/src/libstore/build/derivation-goal.hh @@ -194,9 +194,6 @@ struct DerivationGoal : public Goal */ std::optional derivationType; - typedef void (DerivationGoal::*GoalState)(); - GoalState state; - BuildMode buildMode; std::unique_ptr> mcExpectedBuilds, mcRunningBuilds; @@ -227,8 +224,6 @@ struct DerivationGoal : public Goal std::string key() override; - void work() override; - /** * Add wanted outputs to an already existing derivation goal. */ @@ -237,18 +232,19 @@ struct DerivationGoal : public Goal /** * The states. */ - void getDerivation(); - void loadDerivation(); - void haveDerivation(); - void outputsSubstitutionTried(); - void gaveUpOnSubstitution(); - void closureRepaired(); - void inputsRealised(); - void tryToBuild(); - virtual void tryLocalBuild(); - void buildDone(); + Co init() override; + Co getDerivation(); + Co loadDerivation(); + Co haveDerivation(); + Co outputsSubstitutionTried(); + Co gaveUpOnSubstitution(); + Co closureRepaired(); + Co inputsRealised(); + Co tryToBuild(); + virtual Co tryLocalBuild(); + Co buildDone(); - void resolvedFinished(); + Co resolvedFinished(); /** * Is the build hook willing to perform the build? @@ -329,11 +325,11 @@ struct DerivationGoal : public Goal */ virtual void killChild(); - void repairClosure(); + Co repairClosure(); void started(); - void done( + Done done( BuildResult::Status status, SingleDrvOutputs builtOutputs = {}, std::optional ex = {}); diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc index 13a07e4ea..02284d93c 100644 --- a/src/libstore/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -14,146 +14,135 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal( : Goal(worker, DerivedPath::Opaque { StorePath::dummy }) , id(id) { - state = &DrvOutputSubstitutionGoal::init; name = fmt("substitution of '%s'", id.to_string()); trace("created"); } -void DrvOutputSubstitutionGoal::init() +Goal::Co DrvOutputSubstitutionGoal::init() { trace("init"); /* If the derivation already exists, we’re done */ if (worker.store.queryRealisation(id)) { - amDone(ecSuccess); - return; + co_return amDone(ecSuccess); } - subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); - tryNext(); -} + auto subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); -void DrvOutputSubstitutionGoal::tryNext() -{ - trace("trying next substituter"); + bool substituterFailed = false; - if (subs.size() == 0) { - /* None left. Terminate this goal and let someone else deal - with it. */ - debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string()); + for (auto sub : subs) { + trace("trying next substituter"); - /* Hack: don't indicate failure if there were no substituters. - In that case the calling derivation should just do a - build. */ - amDone(substituterFailed ? ecFailed : ecNoSubstituters); + /* 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. */ + auto outPipe = std::make_shared(); + #ifndef _WIN32 + outPipe->create(); + #else + outPipe->createAsyncPipe(worker.ioport.get()); + #endif - if (substituterFailed) { - worker.failedSubstitutions++; - worker.updateProgress(); + auto promise = std::make_shared>>(); + + sub->queryRealisation( + id, + { [outPipe(outPipe), promise(promise)](std::future> res) { + try { + Finally updateStats([&]() { outPipe->writeSide.close(); }); + promise->set_value(res.get()); + } catch (...) { + promise->set_exception(std::current_exception()); + } + } }); + + worker.childStarted(shared_from_this(), { + #ifndef _WIN32 + outPipe->readSide.get() + #else + &outPipe + #endif + }, true, false); + + co_await Suspend{}; + + worker.childTerminated(this); + + /* + * The realisation corresponding to the given output id. + * Will be filled once we can get it. + */ + std::shared_ptr outputInfo; + + try { + outputInfo = promise->get_future().get(); + } catch (std::exception & e) { + printError(e.what()); + substituterFailed = true; } - return; + if (!outputInfo) continue; + + bool failed = false; + + for (const auto & [depId, depPath] : outputInfo->dependentRealisations) { + if (depId != id) { + if (auto localOutputInfo = worker.store.queryRealisation(depId); + localOutputInfo && localOutputInfo->outPath != depPath) { + warn( + "substituter '%s' has an incompatible realisation for '%s', ignoring.\n" + "Local: %s\n" + "Remote: %s", + sub->getUri(), + depId.to_string(), + worker.store.printStorePath(localOutputInfo->outPath), + worker.store.printStorePath(depPath) + ); + failed = true; + break; + } + addWaitee(worker.makeDrvOutputSubstitutionGoal(depId)); + } + } + + if (failed) continue; + + co_return realisationFetched(outputInfo, sub); } - sub = subs.front(); - subs.pop_front(); + /* None left. Terminate this goal and let someone else deal + with it. */ + debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string()); - // FIXME: Make async - // outputInfo = sub->queryRealisation(id); + if (substituterFailed) { + worker.failedSubstitutions++; + worker.updateProgress(); + } - /* 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(); -#ifndef _WIN32 - downloadState->outPipe.create(); -#else - downloadState->outPipe.createAsyncPipe(worker.ioport.get()); -#endif - - sub->queryRealisation( - id, - { [downloadState(downloadState)](std::future> res) { - try { - Finally updateStats([&]() { downloadState->outPipe.writeSide.close(); }); - downloadState->promise.set_value(res.get()); - } catch (...) { - downloadState->promise.set_exception(std::current_exception()); - } - } }); - - worker.childStarted(shared_from_this(), { -#ifndef _WIN32 - downloadState->outPipe.readSide.get() -#else - &downloadState->outPipe -#endif - }, true, false); - - state = &DrvOutputSubstitutionGoal::realisationFetched; + /* Hack: don't indicate failure if there were no substituters. + In that case the calling derivation should just do a + build. */ + co_return amDone(substituterFailed ? ecFailed : ecNoSubstituters); } -void DrvOutputSubstitutionGoal::realisationFetched() -{ - worker.childTerminated(this); - - try { - outputInfo = downloadState->promise.get_future().get(); - } catch (std::exception & e) { - printError(e.what()); - substituterFailed = true; - } - - if (!outputInfo) { - return tryNext(); - } - - for (const auto & [depId, depPath] : outputInfo->dependentRealisations) { - if (depId != id) { - if (auto localOutputInfo = worker.store.queryRealisation(depId); - localOutputInfo && localOutputInfo->outPath != depPath) { - warn( - "substituter '%s' has an incompatible realisation for '%s', ignoring.\n" - "Local: %s\n" - "Remote: %s", - sub->getUri(), - depId.to_string(), - worker.store.printStorePath(localOutputInfo->outPath), - worker.store.printStorePath(depPath) - ); - tryNext(); - return; - } - addWaitee(worker.makeDrvOutputSubstitutionGoal(depId)); - } - } - +Goal::Co DrvOutputSubstitutionGoal::realisationFetched(std::shared_ptr outputInfo, nix::ref sub) { addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath)); - if (waitees.empty()) outPathValid(); - else state = &DrvOutputSubstitutionGoal::outPathValid; -} + if (!waitees.empty()) co_await Suspend{}; -void DrvOutputSubstitutionGoal::outPathValid() -{ - assert(outputInfo); trace("output path substituted"); if (nrFailed > 0) { debug("The output path of the derivation output '%s' could not be substituted", id.to_string()); - amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed); - return; + co_return amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed); } worker.store.registerDrvOutput(*outputInfo); - finished(); -} -void DrvOutputSubstitutionGoal::finished() -{ trace("finished"); - amDone(ecSuccess); + co_return amDone(ecSuccess); } std::string DrvOutputSubstitutionGoal::key() @@ -163,14 +152,9 @@ std::string DrvOutputSubstitutionGoal::key() return "a$" + std::string(id.to_string()); } -void DrvOutputSubstitutionGoal::work() -{ - (this->*state)(); -} - void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd) { - if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this()); + 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 6967ca84f..807054926 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -27,52 +27,19 @@ class DrvOutputSubstitutionGoal : public Goal { */ DrvOutput id; - /** - * The realisation corresponding to the given output id. - * Will be filled once we can get it. - */ - std::shared_ptr outputInfo; - - /** - * The remaining substituters. - */ - std::list> subs; - - /** - * The current substituter. - */ - std::shared_ptr sub; - - struct DownloadState - { - MuxablePipe outPipe; - std::promise> promise; - }; - - std::shared_ptr downloadState; - - /** - * Whether a substituter failed. - */ - bool substituterFailed = false; - public: DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); typedef void (DrvOutputSubstitutionGoal::*GoalState)(); GoalState state; - void init(); - void tryNext(); - void realisationFetched(); - void outPathValid(); - void finished(); + Co init() override; + Co realisationFetched(std::shared_ptr outputInfo, nix::ref sub); void timedOut(Error && ex) override { abort(); }; std::string key() override; - void work() override; void handleEOF(Descriptor fd) override; JobCategory jobCategory() const override { diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc index f8db98280..9a16da145 100644 --- a/src/libstore/build/goal.cc +++ b/src/libstore/build/goal.cc @@ -3,6 +3,97 @@ namespace nix { +using Co = nix::Goal::Co; +using promise_type = nix::Goal::promise_type; +using handle_type = nix::Goal::handle_type; +using Suspend = nix::Goal::Suspend; + +Co::Co(Co&& rhs) { + this->handle = rhs.handle; + rhs.handle = nullptr; +} +void Co::operator=(Co&& rhs) { + this->handle = rhs.handle; + rhs.handle = nullptr; +} +Co::~Co() { + if (handle) { + handle.promise().alive = false; + handle.destroy(); + } +} + +Co promise_type::get_return_object() { + auto handle = handle_type::from_promise(*this); + return Co{handle}; +}; + +std::coroutine_handle<> promise_type::final_awaiter::await_suspend(handle_type h) noexcept { + auto& p = h.promise(); + auto goal = p.goal; + assert(goal); + goal->trace("in final_awaiter"); + auto c = std::move(p.continuation); + + if (c) { + // We still have a continuation, i.e. work to do. + // We assert that the goal is still busy. + assert(goal->exitCode == ecBusy); + assert(goal->top_co); // Goal must have an active coroutine. + assert(goal->top_co->handle == h); // The active coroutine must be us. + assert(p.alive); // We must not have been destructed. + + // we move continuation to the top, + // note: previous top_co is actually h, so by moving into it, + // we're calling the destructor on h, DON'T use h and p after this! + + // We move our continuation into `top_co`, i.e. the marker for the active continuation. + // By doing this we destruct the old `top_co`, i.e. us, so `h` can't be used anymore. + // Be careful not to access freed memory! + goal->top_co = std::move(c); + + // We resume `top_co`. + return goal->top_co->handle; + } else { + // We have no continuation, i.e. no more work to do, + // so the goal must not be busy anymore. + assert(goal->exitCode != ecBusy); + + // We reset `top_co` for good measure. + p.goal->top_co = {}; + + // We jump to the noop coroutine, which doesn't do anything and immediately suspends. + // This passes control back to the caller of goal.work(). + return std::noop_coroutine(); + } +} + +void promise_type::return_value(Co&& next) { + goal->trace("return_value(Co&&)"); + // Save old continuation. + auto old_continuation = std::move(continuation); + // We set next as our continuation. + continuation = std::move(next); + // We set next's goal, and thus it must not have one already. + assert(!continuation->handle.promise().goal); + continuation->handle.promise().goal = goal; + // Nor can next have a continuation, as we set it to our old one. + assert(!continuation->handle.promise().continuation); + continuation->handle.promise().continuation = std::move(old_continuation); +} + +std::coroutine_handle<> nix::Goal::Co::await_suspend(handle_type caller) { + assert(handle); // we must be a valid coroutine + auto& p = handle.promise(); + assert(!p.continuation); // we must have no continuation + assert(!p.goal); // we must not have a goal yet + auto goal = caller.promise().goal; + assert(goal); + p.goal = goal; + p.continuation = std::move(goal->top_co); // we set our continuation to be top_co (i.e. caller) + goal->top_co = std::move(*this); // we set top_co to ourselves, don't use this anymore after this! + return p.goal->top_co->handle; // we execute ourselves +} bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const { std::string s1 = a->key(); @@ -75,10 +166,10 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result) } } - -void Goal::amDone(ExitCode result, std::optional ex) +Goal::Done Goal::amDone(ExitCode result, std::optional ex) { trace("done"); + assert(top_co); assert(exitCode == ecBusy); assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure); exitCode = result; @@ -98,6 +189,13 @@ void Goal::amDone(ExitCode result, std::optional ex) worker.removeGoal(shared_from_this()); cleanup(); + + // We drop the continuation. + // In `final_awaiter` this will signal that there is no more work to be done. + top_co->handle.promise().continuation = {}; + + // won't return to caller because of logic in final_awaiter + return Done{}; } @@ -106,4 +204,16 @@ void Goal::trace(std::string_view s) debug("%1%: %2%", name, s); } +void Goal::work() +{ + assert(top_co); + assert(top_co->handle); + assert(top_co->handle.promise().alive); + top_co->handle.resume(); + // We either should be in a state where we can be work()-ed again, + // or we should be done. + assert(top_co || exitCode != ecBusy); +} + + } diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh index 0d9b828e1..162c392d0 100644 --- a/src/libstore/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -1,10 +1,11 @@ #pragma once ///@file -#include "types.hh" #include "store-api.hh" #include "build-result.hh" +#include + namespace nix { /** @@ -103,9 +104,263 @@ protected: * Build result. */ BuildResult buildResult; - public: + /** + * Suspend our goal and wait until we get @ref work()-ed again. + * `co_await`-able by @ref Co. + */ + struct Suspend {}; + + /** + * Return from the current coroutine and suspend our goal + * if we're not busy anymore, or jump to the next coroutine + * set to be executed/resumed. + */ + struct Return {}; + + /** + * `co_return`-ing this will end the goal. + * If you're not inside a coroutine, you can safely discard this. + */ + struct [[nodiscard]] Done { + private: + Done(){} + + friend Goal; + }; + + // forward declaration of promise_type, see below + struct promise_type; + + /** + * Handle to coroutine using @ref Co and @ref promise_type. + */ + using handle_type = std::coroutine_handle; + + /** + * C++20 coroutine wrapper for use in goal logic. + * Coroutines are functions that use `co_await`/`co_return` (and `co_yield`, but not supported by @ref Co). + * + * @ref Co is meant to be used by methods of subclasses of @ref Goal. + * The main functionality provided by `Co` is + * - `co_await Suspend{}`: Suspends the goal. + * - `co_await f()`: Waits until `f()` finishes. + * - `co_return f()`: Tail-calls `f()`. + * - `co_return Return{}`: Ends coroutine. + * + * The idea is that you implement the goal logic using coroutines, + * and do the core thing a goal can do, suspension, when you have + * children you're waiting for. + * Coroutines allow you to resume the work cleanly. + * + * @note Brief explanation of C++20 coroutines: + * When you `Co f()`, a `std::coroutine_handle` is created, + * alongside its @ref promise_type. + * There are suspension points at the beginning of the coroutine, + * at every `co_await`, and at the final (possibly implicit) `co_return`. + * Once suspended, you can resume the `std::coroutine_handle` by doing `coroutine_handle.resume()`. + * Suspension points are implemented by passing a struct to the compiler + * that implements `await_sus`pend. + * `await_suspend` can either say "cancel suspension", in which case execution resumes, + * "suspend", in which case control is passed back to the caller of `coroutine_handle.resume()` + * or the place where the coroutine function is initially executed in the case of the initial + * suspension, or `await_suspend` can specify another coroutine to jump to, which is + * how tail calls are implemented. + * + * @note Resources: + * - https://lewissbaker.github.io/ + * - https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/coroutines-c++20/ + * - https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html + * + * @todo Allocate explicitly on stack since HALO thing doesn't really work, + * specifically, there's no way to uphold the requirements when trying to do + * tail-calls without using a trampoline AFAICT. + * + * @todo Support returning data natively + */ + struct [[nodiscard]] Co { + /** + * The underlying handle. + */ + handle_type handle; + + explicit Co(handle_type handle) : handle(handle) {}; + void operator=(Co&&); + Co(Co&& rhs); + ~Co(); + + bool await_ready() { return false; }; + /** + * When we `co_await` another @ref Co-returning coroutine, + * we tell the caller of `caller_coroutine.resume()` to switch to our coroutine (@ref handle). + * To make sure we return to the original coroutine, we set it as the continuation of our + * coroutine. In @ref promise_type::final_awaiter we check if it's set and if so we return to it. + * + * To explain in more understandable terms: + * When we `co_await Co_returning_function()`, this function is called on the resultant @ref Co of + * the _called_ function, and C++ automatically passes the caller in. + * + * `goal` field of @ref promise_type is also set here by copying it from the caller. + */ + std::coroutine_handle<> await_suspend(handle_type handle); + void await_resume() {}; + }; + + /** + * Used on initial suspend, does the same as @ref std::suspend_always, + * but asserts that everything has been set correctly. + */ + struct InitialSuspend { + /** + * Handle of coroutine that does the + * initial suspend + */ + handle_type handle; + + bool await_ready() { return false; }; + void await_suspend(handle_type handle_) { + handle = handle_; + } + void await_resume() { + assert(handle); + assert(handle.promise().goal); // goal must be set + assert(handle.promise().goal->top_co); // top_co of goal must be set + assert(handle.promise().goal->top_co->handle == handle); // top_co of goal must be us + } + }; + + /** + * Promise type for coroutines defined using @ref Co. + * Attached to coroutine handle. + */ + struct promise_type { + /** + * Either this is who called us, or it is who we will tail-call. + * It is what we "jump" to once we are done. + */ + std::optional continuation; + + /** + * The goal that we're a part of. + * Set either in @ref Co::await_suspend or in constructor of @ref Goal. + */ + Goal* goal = nullptr; + + /** + * Is set to false when destructed to ensure we don't use a + * destructed coroutine by accident + */ + bool alive = true; + + /** + * The awaiter used by @ref final_suspend. + */ + struct final_awaiter { + bool await_ready() noexcept { return false; }; + /** + * Here we execute our continuation, by passing it back to the caller. + * C++ compiler will create code that takes that and executes it promptly. + * `h` is the handle for the coroutine that is finishing execution, + * thus it must be destroyed. + */ + std::coroutine_handle<> await_suspend(handle_type h) noexcept; + void await_resume() noexcept { assert(false); }; + }; + + /** + * Called by compiler generated code to construct the @ref Co + * that is returned from a @ref Co-returning coroutine. + */ + Co get_return_object(); + + /** + * Called by compiler generated code before body of coroutine. + * We use this opportunity to set the @ref goal field + * and `top_co` field of @ref Goal. + */ + InitialSuspend initial_suspend() { return {}; }; + + /** + * Called on `co_return`. Creates @ref final_awaiter which + * either jumps to continuation or suspends goal. + */ + final_awaiter final_suspend() noexcept { return {}; }; + + /** + * Does nothing, but provides an opportunity for + * @ref final_suspend to happen. + */ + void return_value(Return) {} + + /** + * Does nothing, but provides an opportunity for + * @ref final_suspend to happen. + */ + void return_value(Done) {} + + /** + * When "returning" another coroutine, what happens is that + * we set it as our own continuation, thus once the final suspend + * happens, we transfer control to it. + * The original continuation we had is set as the continuation + * of the coroutine passed in. + * @ref final_suspend is called after this, and @ref final_awaiter will + * pass control off to @ref continuation. + * + * If we already have a continuation, that continuation is set as + * the continuation of the new continuation. Thus, the continuation + * passed to @ref return_value must not have a continuation set. + */ + void return_value(Co&&); + + /** + * If an exception is thrown inside a coroutine, + * we re-throw it in the context of the "resumer" of the continuation. + */ + void unhandled_exception() { throw; }; + + /** + * Allows awaiting a @ref Co. + */ + Co&& await_transform(Co&& co) { return static_cast(co); } + + /** + * Allows awaiting a @ref Suspend. + * Always suspends. + */ + std::suspend_always await_transform(Suspend) { return {}; }; + }; + + /** + * The coroutine being currently executed. + * MUST be updated when switching the coroutine being executed. + * This is used both for memory management and to resume the last + * coroutine executed. + * Destroying this should destroy all coroutines created for this goal. + */ + std::optional top_co; + + /** + * The entry point for the goal + */ + virtual Co init() = 0; + + /** + * Wrapper around @ref init since virtual functions + * can't be used in constructors. + */ + inline Co init_wrapper(); + + /** + * Signals that the goal is done. + * `co_return` the result. If you're not inside a coroutine, you can ignore + * the return value safely. + */ + Done amDone(ExitCode result, std::optional ex = {}); + + virtual void cleanup() { } + /** * Project a `BuildResult` with just the information that pertains * to the given request. @@ -124,15 +379,20 @@ public: std::optional ex; Goal(Worker & worker, DerivedPath path) - : worker(worker) - { } + : worker(worker), top_co(init_wrapper()) + { + // top_co shouldn't have a goal already, should be nullptr. + assert(!top_co->handle.promise().goal); + // we set it such that top_co can pass it down to its subcoroutines. + top_co->handle.promise().goal = this; + } virtual ~Goal() { trace("goal destroyed"); } - virtual void work() = 0; + void work(); void addWaitee(GoalPtr waitee); @@ -164,10 +424,6 @@ public: virtual std::string key() = 0; - void amDone(ExitCode result, std::optional ex = {}); - - virtual void cleanup() { } - /** * @brief Hint for the scheduler, which concurrency limit applies. * @see JobCategory @@ -178,3 +434,12 @@ public: void addToWeakGoals(WeakGoals & goals, GoalPtr p); } + +template +struct std::coroutine_traits { + using promise_type = nix::Goal::promise_type; +}; + +nix::Goal::Co nix::Goal::init_wrapper() { + co_return init(); +} diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index 0be3d1e8d..7deeb4748 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -3,6 +3,7 @@ #include "nar-info.hh" #include "finally.hh" #include "signals.hh" +#include namespace nix { @@ -12,7 +13,6 @@ PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & , repair(repair) , ca(ca) { - state = &PathSubstitutionGoal::init; name = fmt("substitution of '%s'", worker.store.printStorePath(this->storePath)); trace("created"); maintainExpectedSubstitutions = std::make_unique>(worker.expectedSubstitutions); @@ -25,7 +25,7 @@ PathSubstitutionGoal::~PathSubstitutionGoal() } -void PathSubstitutionGoal::done( +Goal::Done PathSubstitutionGoal::done( ExitCode result, BuildResult::Status status, std::optional errorMsg) @@ -35,17 +35,11 @@ void PathSubstitutionGoal::done( debug(*errorMsg); buildResult.errorMsg = *errorMsg; } - amDone(result); + return amDone(result); } -void PathSubstitutionGoal::work() -{ - (this->*state)(); -} - - -void PathSubstitutionGoal::init() +Goal::Co PathSubstitutionGoal::init() { trace("init"); @@ -53,152 +47,135 @@ void PathSubstitutionGoal::init() /* If the path already exists we're done. */ if (!repair && worker.store.isValidPath(storePath)) { - done(ecSuccess, BuildResult::AlreadyValid); - return; + co_return done(ecSuccess, BuildResult::AlreadyValid); } if (settings.readOnlyMode) throw Error("cannot substitute path '%s' - no write access to the Nix store", worker.store.printStorePath(storePath)); - subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); + auto subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); - tryNext(); -} + bool substituterFailed = false; + for (auto sub : subs) { + trace("trying next substituter"); -void PathSubstitutionGoal::tryNext() -{ - trace("trying next substituter"); + cleanup(); - cleanup(); + /* The path the substituter refers to the path as. This will be + * different when the stores have different names. */ + std::optional subPath; - if (subs.size() == 0) { - /* None left. Terminate this goal and let someone else deal - with it. */ + /* Path info returned by the substituter's query info operation. */ + std::shared_ptr info; - /* Hack: don't indicate failure if there were no substituters. - In that case the calling derivation should just do a - build. */ - done( - substituterFailed ? ecFailed : ecNoSubstituters, - BuildResult::NoSubstituters, - fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath))); - - if (substituterFailed) { - worker.failedSubstitutions++; - worker.updateProgress(); + if (ca) { + subPath = sub->makeFixedOutputPathFromCA( + std::string { storePath.name() }, + ContentAddressWithReferences::withoutRefs(*ca)); + if (sub->storeDir == worker.store.storeDir) + assert(subPath == storePath); + } else if (sub->storeDir != worker.store.storeDir) { + continue; } - return; - } - - sub = subs.front(); - subs.pop_front(); - - if (ca) { - subPath = sub->makeFixedOutputPathFromCA( - std::string { storePath.name() }, - ContentAddressWithReferences::withoutRefs(*ca)); - if (sub->storeDir == worker.store.storeDir) - assert(subPath == storePath); - } else if (sub->storeDir != worker.store.storeDir) { - tryNext(); - return; - } - - try { - // FIXME: make async - info = sub->queryPathInfo(subPath ? *subPath : storePath); - } catch (InvalidPath &) { - tryNext(); - return; - } catch (SubstituterDisabled &) { - if (settings.tryFallback) { - tryNext(); - return; + try { + // FIXME: make async + info = sub->queryPathInfo(subPath ? *subPath : storePath); + } catch (InvalidPath &) { + continue; + } catch (SubstituterDisabled & e) { + if (settings.tryFallback) continue; + else throw e; + } catch (Error & e) { + if (settings.tryFallback) { + logError(e.info()); + continue; + } else throw e; } - throw; - } catch (Error & e) { - if (settings.tryFallback) { - logError(e.info()); - tryNext(); - return; + + if (info->path != storePath) { + if (info->isContentAddressed(*sub) && info->references.empty()) { + auto info2 = std::make_shared(*info); + info2->path = storePath; + info = info2; + } else { + printError("asked '%s' for '%s' but got '%s'", + sub->getUri(), worker.store.printStorePath(storePath), sub->printStorePath(info->path)); + continue; + } } - throw; + + /* Update the total expected download size. */ + auto narInfo = std::dynamic_pointer_cast(info); + + maintainExpectedNar = std::make_unique>(worker.expectedNarSize, info->narSize); + + maintainExpectedDownload = + narInfo && narInfo->fileSize + ? std::make_unique>(worker.expectedDownloadSize, narInfo->fileSize) + : nullptr; + + worker.updateProgress(); + + /* Bail out early if this substituter lacks a valid + signature. LocalStore::addToStore() also checks for this, but + only after we've downloaded the path. */ + if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info)) + { + warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'", + worker.store.printStorePath(storePath), sub->getUri()); + continue; + } + + /* To maintain the closure invariant, we first have to realise the + paths referenced by this one. */ + for (auto & i : info->references) + if (i != storePath) /* ignore self-references */ + addWaitee(worker.makePathSubstitutionGoal(i)); + + if (!waitees.empty()) co_await Suspend{}; + + // FIXME: consider returning boolean instead of passing in reference + bool out = false; // is mutated by tryToRun + co_await tryToRun(subPath ? *subPath : storePath, sub, info, out); + substituterFailed = substituterFailed || out; } - if (info->path != storePath) { - if (info->isContentAddressed(*sub) && info->references.empty()) { - auto info2 = std::make_shared(*info); - info2->path = storePath; - info = info2; - } else { - printError("asked '%s' for '%s' but got '%s'", - sub->getUri(), worker.store.printStorePath(storePath), sub->printStorePath(info->path)); - tryNext(); - return; - } - } - - /* Update the total expected download size. */ - auto narInfo = std::dynamic_pointer_cast(info); - - maintainExpectedNar = std::make_unique>(worker.expectedNarSize, info->narSize); - - maintainExpectedDownload = - narInfo && narInfo->fileSize - ? std::make_unique>(worker.expectedDownloadSize, narInfo->fileSize) - : nullptr; + /* None left. Terminate this goal and let someone else deal + with it. */ + worker.failedSubstitutions++; worker.updateProgress(); - /* Bail out early if this substituter lacks a valid - signature. LocalStore::addToStore() also checks for this, but - only after we've downloaded the path. */ - if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info)) - { - warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'", - worker.store.printStorePath(storePath), sub->getUri()); - tryNext(); - return; - } - - /* To maintain the closure invariant, we first have to realise the - paths referenced by this one. */ - for (auto & i : info->references) - if (i != storePath) /* ignore self-references */ - addWaitee(worker.makePathSubstitutionGoal(i)); - - if (waitees.empty()) /* to prevent hang (no wake-up event) */ - referencesValid(); - else - state = &PathSubstitutionGoal::referencesValid; + /* Hack: don't indicate failure if there were no substituters. + In that case the calling derivation should just do a + build. */ + co_return done( + substituterFailed ? ecFailed : ecNoSubstituters, + BuildResult::NoSubstituters, + fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath))); } -void PathSubstitutionGoal::referencesValid() +Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool& substituterFailed) { trace("all references realised"); if (nrFailed > 0) { - done( + co_return done( nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed, BuildResult::DependencyFailed, fmt("some references of path '%s' could not be realised", worker.store.printStorePath(storePath))); - return; } for (auto & i : info->references) if (i != storePath) /* ignore self-references */ assert(worker.store.isValidPath(i)); - state = &PathSubstitutionGoal::tryToRun; worker.wakeUp(shared_from_this()); -} + co_await Suspend{}; - -void PathSubstitutionGoal::tryToRun() -{ trace("trying to run"); /* Make sure that we are allowed to start a substitution. Note that even @@ -206,10 +183,10 @@ void PathSubstitutionGoal::tryToRun() prevents infinite waiting. */ if (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) { worker.waitForBuildSlot(shared_from_this()); - return; + co_await Suspend{}; } - maintainRunningSubstitutions = std::make_unique>(worker.runningSubstitutions); + auto maintainRunningSubstitutions = std::make_unique>(worker.runningSubstitutions); worker.updateProgress(); #ifndef _WIN32 @@ -218,9 +195,9 @@ void PathSubstitutionGoal::tryToRun() outPipe.createAsyncPipe(worker.ioport.get()); #endif - promise = std::promise(); + auto promise = std::promise(); - thr = std::thread([this]() { + thr = std::thread([this, &promise, &subPath, &sub]() { try { ReceiveInterrupts receiveInterrupts; @@ -231,7 +208,7 @@ void PathSubstitutionGoal::tryToRun() PushActivity pact(act.id); copyStorePath(*sub, worker.store, - subPath ? *subPath : storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs); + subPath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs); promise.set_value(); } catch (...) { @@ -247,12 +224,8 @@ void PathSubstitutionGoal::tryToRun() #endif }, true, false); - state = &PathSubstitutionGoal::finished; -} + co_await Suspend{}; - -void PathSubstitutionGoal::finished() -{ trace("substitute finished"); thr.join(); @@ -274,10 +247,7 @@ void PathSubstitutionGoal::finished() substituterFailed = true; } - /* Try the next substitute. */ - state = &PathSubstitutionGoal::tryNext; - worker.wakeUp(shared_from_this()); - return; + co_return Return{}; } worker.markContentsGood(storePath); @@ -295,23 +265,19 @@ void PathSubstitutionGoal::finished() worker.doneDownloadSize += fileSize; } + assert(maintainExpectedNar); worker.doneNarSize += maintainExpectedNar->delta; maintainExpectedNar.reset(); worker.updateProgress(); - done(ecSuccess, BuildResult::Substituted); -} - - -void PathSubstitutionGoal::handleChildOutput(Descriptor fd, std::string_view data) -{ + co_return done(ecSuccess, BuildResult::Substituted); } void PathSubstitutionGoal::handleEOF(Descriptor fd) { - if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this()); + worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index 1a051fc1f..86e4f5423 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -1,14 +1,16 @@ #pragma once ///@file +#include "worker.hh" #include "store-api.hh" #include "goal.hh" #include "muxable-pipe.hh" +#include +#include +#include namespace nix { -class Worker; - struct PathSubstitutionGoal : public Goal { /** @@ -17,30 +19,9 @@ struct PathSubstitutionGoal : public Goal StorePath storePath; /** - * The path the substituter refers to the path as. This will be - * different when the stores have different names. + * Whether to try to repair a valid path. */ - std::optional subPath; - - /** - * The remaining substituters. - */ - std::list> subs; - - /** - * The current substituter. - */ - std::shared_ptr sub; - - /** - * Whether a substituter failed. - */ - bool substituterFailed = false; - - /** - * Path info returned by the substituter's query info operation. - */ - std::shared_ptr info; + RepairFlag repair; /** * Pipe for the substituter's standard output. @@ -52,31 +33,15 @@ struct PathSubstitutionGoal : public Goal */ std::thread thr; - std::promise promise; - - /** - * Whether to try to repair a valid path. - */ - RepairFlag repair; - - /** - * Location where we're downloading the substitute. Differs from - * storePath when doing a repair. - */ - Path destPath; - std::unique_ptr> maintainExpectedSubstitutions, maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload; - typedef void (PathSubstitutionGoal::*GoalState)(); - GoalState state; - /** * Content address for recomputing store path */ std::optional ca; - void done( + Done done( ExitCode result, BuildResult::Status status, std::optional errorMsg = {}); @@ -96,22 +61,18 @@ public: return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath); } - void work() override; - /** * The states. */ - void init(); - void tryNext(); - void gotInfo(); - void referencesValid(); - void tryToRun(); - void finished(); + Co init() override; + Co gotInfo(); + Co tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool& substituterFailed); + Co finished(); /** * Callback used by the worker to write to the log. */ - void handleChildOutput(Descriptor fd, std::string_view data) override; + void handleChildOutput(Descriptor fd, std::string_view data) override {}; void handleEOF(Descriptor fd) override; /* Called by destructor, can't be overridden */ diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 8a5d6de72..7fc41b121 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -337,31 +337,27 @@ void Worker::run(const Goals & _topGoals) /* Wait for input. */ if (!children.empty() || !waitingForAWhile.empty()) waitForInput(); - else { - if (awake.empty() && 0U == settings.maxBuildJobs) - { - if (getMachines().empty()) - throw Error( - R"( - Unable to start any build; - either increase '--max-jobs' or enable remote builds. + else if (awake.empty() && 0U == settings.maxBuildJobs) { + if (getMachines().empty()) + throw Error( + R"( + Unable to start any build; + either increase '--max-jobs' or enable remote builds. - For more information run 'man nix.conf' and search for '/machines'. - )" - ); - else - throw Error( - R"( - Unable to start any build; - remote machines may not have all required system features. + For more information run 'man nix.conf' and search for '/machines'. + )" + ); + else + throw Error( + R"( + Unable to start any build; + remote machines may not have all required system features. - For more information run 'man nix.conf' and search for '/machines'. - )" - ); + For more information run 'man nix.conf' and search for '/machines'. + )" + ); - } - assert(!awake.empty()); - } + } else assert(!awake.empty()); } /* If --keep-going is not set, it's possible that the main goal diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index c3a65e34b..f968bbc5b 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -177,7 +177,7 @@ void LocalDerivationGoal::killSandbox(bool getStats) } -void LocalDerivationGoal::tryLocalBuild() +Goal::Co LocalDerivationGoal::tryLocalBuild() { #if __APPLE__ additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); @@ -185,10 +185,10 @@ void LocalDerivationGoal::tryLocalBuild() unsigned int curBuilds = worker.getNrLocalBuilds(); if (curBuilds >= settings.maxBuildJobs) { - state = &DerivationGoal::tryToBuild; worker.waitForBuildSlot(shared_from_this()); outputLocks.unlock(); - return; + co_await Suspend{}; + co_return tryToBuild(); } assert(derivationType); @@ -242,7 +242,8 @@ void LocalDerivationGoal::tryLocalBuild() actLock = std::make_unique(*logger, lvlWarn, actBuildWaiting, fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath)))); worker.waitForAWhile(shared_from_this()); - return; + co_await Suspend{}; + co_return tryLocalBuild(); } } @@ -257,15 +258,13 @@ void LocalDerivationGoal::tryLocalBuild() outputLocks.unlock(); buildUser.reset(); worker.permanentFailure = true; - done(BuildResult::InputRejected, {}, std::move(e)); - return; + co_return done(BuildResult::InputRejected, {}, std::move(e)); } - /* This state will be reached when we get EOF on the child's - log pipe. */ - state = &DerivationGoal::buildDone; - started(); + co_await Suspend{}; + // after EOF on child + co_return buildDone(); } static void chmod_(const Path & path, mode_t mode) diff --git a/src/libstore/unix/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh index 4bcf5c9d4..bf25cf2a6 100644 --- a/src/libstore/unix/build/local-derivation-goal.hh +++ b/src/libstore/unix/build/local-derivation-goal.hh @@ -198,7 +198,7 @@ struct LocalDerivationGoal : public DerivationGoal /** * The additional states. */ - void tryLocalBuild() override; + Goal::Co tryLocalBuild() override; /** * Start building a derivation. From 1a273a623f4ecaabafe56cba50d014ceb2beb4a3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 2 Jul 2024 15:00:39 -0400 Subject: [PATCH 600/910] Inline `settings.pluginFiles.name` In theory the warning is more noisy now, but in practice this will not happen unless the client is older than 2.14 (highly unlikely). --- src/libstore/daemon.cc | 7 +++---- src/libstore/remote-store.cc | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 40163a621..5c5080f8a 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -246,10 +246,9 @@ struct ClientSettings // the daemon, as that could cause some pretty weird stuff 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()) - warn("Ignoring the client-specified plugin-files.\n" - "The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14."); + } else if (name == "plugin-files") { + warn("Ignoring the client-specified plugin-files.\n" + "The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14."); } else if (trusted || name == settings.buildTimeout.name diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index d749ccd0a..6e8931ca2 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -128,7 +128,7 @@ void RemoteStore::setOptions(Connection & conn) overrides.erase(settings.useSubstitutes.name); overrides.erase(loggerSettings.showTrace.name); overrides.erase(experimentalFeatureSettings.experimentalFeatures.name); - overrides.erase(settings.pluginFiles.name); + overrides.erase("plugin-files"); conn.to << overrides.size(); for (auto & i : overrides) conn.to << i.first << i.second.value; From 0feeab755a19acd426cdae6887019f9886b016b4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 2 Jul 2024 15:15:17 -0400 Subject: [PATCH 601/910] Move plugins infra to `libnixmain` They are not actually part of the store layer, but instead part of the Nix executable infra (libraries don't need plugins, executables do). This is part of a larger project of moving all of our legacy settings infra to libmain, and having the underlying libraries just have plain configuration structs detached from any settings infra / UI layer. Progress on #5638 --- meson.build | 1 + packaging/components.nix | 1 + packaging/hydra.nix | 1 + src/build-remote/build-remote.cc | 1 + src/libmain-c/.version | 1 + src/libmain-c/build-utils-meson | 1 + src/libmain-c/meson.build | 84 ++++++++++++++++++++++ src/libmain-c/nix_api_main.cc | 16 +++++ src/libmain-c/nix_api_main.h | 40 +++++++++++ src/libmain-c/package.nix | 83 ++++++++++++++++++++++ src/libmain/common-args.cc | 1 + src/libmain/meson.build | 2 + src/libmain/plugin.cc | 117 +++++++++++++++++++++++++++++++ src/libmain/plugin.hh | 12 ++++ src/libstore-c/nix_api_store.cc | 10 --- src/libstore-c/nix_api_store.h | 11 --- src/libstore/globals.cc | 55 --------------- src/libstore/globals.hh | 50 ------------- 18 files changed, 361 insertions(+), 126 deletions(-) create mode 120000 src/libmain-c/.version create mode 120000 src/libmain-c/build-utils-meson create mode 100644 src/libmain-c/meson.build create mode 100644 src/libmain-c/nix_api_main.cc create mode 100644 src/libmain-c/nix_api_main.h create mode 100644 src/libmain-c/package.nix create mode 100644 src/libmain/plugin.cc create mode 100644 src/libmain/plugin.hh diff --git a/meson.build b/meson.build index e6bdc2eac..1c46c5c28 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,7 @@ subproject('external-api-docs') subproject('libutil-c') subproject('libstore-c') subproject('libexpr-c') +subproject('libmain-c') # Language Bindings subproject('perl') diff --git a/packaging/components.nix b/packaging/components.nix index 0e369a055..870e9ae61 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -29,6 +29,7 @@ in nix-flake-tests = callPackage ../tests/unit/libflake/package.nix { }; nix-main = callPackage ../src/libmain/package.nix { }; + nix-main-c = callPackage ../src/libmain-c/package.nix { }; nix-cmd = callPackage ../src/libcmd/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 4dfaf9bbf..dbe992476 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -52,6 +52,7 @@ let "nix-flake" "nix-flake-tests" "nix-main" + "nix-main-c" "nix-cmd" "nix-ng" ]; diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 600fc7ee2..a0a404e57 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -11,6 +11,7 @@ #include "machines.hh" #include "shared.hh" +#include "plugin.hh" #include "pathlocks.hh" #include "globals.hh" #include "serialise.hh" diff --git a/src/libmain-c/.version b/src/libmain-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libmain-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libmain-c/build-utils-meson b/src/libmain-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libmain-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libmain-c/meson.build b/src/libmain-c/meson.build new file mode 100644 index 000000000..1d6b2f959 --- /dev/null +++ b/src/libmain-c/meson.build @@ -0,0 +1,84 @@ +project('nix-main-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +configdata = configuration_data() + +deps_private_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-main'), +] +deps_public_maybe_subproject = [ + dependency('nix-util-c'), + dependency('nix-store-c'), +] +subdir('build-utils-meson/subprojects') + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-main.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-main.hh', + + # From C libraries, for our public, installed headers too + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-main.h', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') + +sources = files( + 'nix_api_main.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'nix_api_main.h', +) + +subdir('build-utils-meson/export-all-symbols') + +this_library = library( + 'nixmainc', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + prelink : true, # For C++ static initializers + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +subdir('build-utils-meson/export') diff --git a/src/libmain-c/nix_api_main.cc b/src/libmain-c/nix_api_main.cc new file mode 100644 index 000000000..692d53f47 --- /dev/null +++ b/src/libmain-c/nix_api_main.cc @@ -0,0 +1,16 @@ +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" + +#include "plugin.hh" + +nix_err nix_init_plugins(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::initPlugins(); + } + NIXC_CATCH_ERRS +} diff --git a/src/libmain-c/nix_api_main.h b/src/libmain-c/nix_api_main.h new file mode 100644 index 000000000..3957b992f --- /dev/null +++ b/src/libmain-c/nix_api_main.h @@ -0,0 +1,40 @@ +#ifndef NIX_API_MAIN_H +#define NIX_API_MAIN_H +/** + * @defgroup libmain libmain + * @brief C bindings for nix libmain + * + * libmain has misc utilities for CLI commands + * @{ + */ +/** @file + * @brief Main entry for the libmain C bindings + */ + +#include "nix_api_util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif +// cffi start + +/** + * @brief Loads the plugins specified in Nix's plugin-files setting. + * + * Call this once, after calling your desired init functions and setting + * relevant settings. + * + * @param[out] context Optional, stores error information + * @return NIX_OK if the initialization was successful, an error code otherwise. + */ +nix_err nix_init_plugins(nix_c_context * context); + +// cffi end +#ifdef __cplusplus +} +#endif +/** + * @} + */ +#endif // NIX_API_MAIN_H diff --git a/src/libmain-c/package.nix b/src/libmain-c/package.nix new file mode 100644 index 000000000..478e34a85 --- /dev/null +++ b/src/libmain-c/package.nix @@ -0,0 +1,83 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util-c +, nix-store +, nix-store-c +, nix-main + +# Configuration Options + +, version +}: + +let + inherit (lib) fileset; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix-main-c"; + inherit version; + + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util-c + nix-store + nix-store-c + nix-main + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../.version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index a94845ab8..768b2177c 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -5,6 +5,7 @@ #include "logging.hh" #include "loggers.hh" #include "util.hh" +#include "plugin.hh" namespace nix { diff --git a/src/libmain/meson.build b/src/libmain/meson.build index 859ce22f8..fe6133596 100644 --- a/src/libmain/meson.build +++ b/src/libmain/meson.build @@ -64,6 +64,7 @@ subdir('build-utils-meson/diagnostics') sources = files( 'common-args.cc', 'loggers.cc', + 'plugin.cc', 'progress-bar.cc', 'shared.cc', ) @@ -79,6 +80,7 @@ include_dirs = [include_directories('.')] headers = [config_h] + files( 'common-args.hh', 'loggers.hh', + 'plugin.hh', 'progress-bar.hh', 'shared.hh', ) diff --git a/src/libmain/plugin.cc b/src/libmain/plugin.cc new file mode 100644 index 000000000..52b5b60e4 --- /dev/null +++ b/src/libmain/plugin.cc @@ -0,0 +1,117 @@ +#ifndef _WIN32 +# include +#endif + +#include "config-global.hh" +#include "signals.hh" + +namespace nix { + +struct PluginFilesSetting : public BaseSetting +{ + bool pluginsLoaded = false; + + PluginFilesSetting( + Config * options, + const Paths & def, + const std::string & name, + const std::string & description, + const std::set & aliases = {}) + : BaseSetting(def, true, name, description, aliases) + { + options->addSetting(this); + } + + Paths parse(const std::string & str) const override; +}; + +Paths PluginFilesSetting::parse(const std::string & str) const +{ + if (pluginsLoaded) + throw UsageError( + "plugin-files set after plugins were loaded, you may need to move the flag before the subcommand"); + return BaseSetting::parse(str); +} + +struct PluginSettings : Config +{ + PluginFilesSetting pluginFiles{ + this, + {}, + "plugin-files", + R"( + A list of plugin files to be loaded by Nix. Each of these files will + be dlopened by Nix. If they contain the symbol `nix_plugin_entry()`, + this symbol will be called. Alternatively, they can affect execution + through static initialization. In particular, these plugins may construct + static instances of RegisterPrimOp to add new primops or constants to the + expression language, RegisterStoreImplementation to add new store + implementations, RegisterCommand to add new subcommands to the `nix` + command, and RegisterSetting to add new nix config settings. See the + constructors for those types for more details. + + Warning! These APIs are inherently unstable and may change from + release to release. + + Since these files are loaded into the same address space as Nix + itself, they must be DSOs compatible with the instance of Nix + running at the time (i.e. compiled against the same headers, not + linked to any incompatible libraries). They should not be linked to + any Nix libs directly, as those will be available already at load + time. + + If an entry in the list is a directory, all files in the directory + are loaded as plugins (non-recursively). + )"}; +}; + +static PluginSettings pluginSettings; + +static GlobalConfig::Register rPluginSettings(&pluginSettings); + +void initPlugins() +{ + assert(!pluginSettings.pluginFiles.pluginsLoaded); + for (const auto & pluginFile : pluginSettings.pluginFiles.get()) { + std::vector pluginFiles; + try { + auto ents = std::filesystem::directory_iterator{pluginFile}; + for (const auto & ent : ents) { + checkInterrupt(); + pluginFiles.emplace_back(ent.path()); + } + } catch (std::filesystem::filesystem_error & e) { + if (e.code() != std::errc::not_a_directory) + throw; + pluginFiles.emplace_back(pluginFile); + } + for (const auto & file : pluginFiles) { + checkInterrupt(); + /* handle is purposefully leaked as there may be state in the + DSO needed by the action of the plugin. */ +#ifndef _WIN32 // TODO implement via DLL loading on Windows + void * handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); + if (!handle) + throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); + + /* Older plugins use a statically initialized object to run their code. + Newer plugins can also export nix_plugin_entry() */ + void (*nix_plugin_entry)() = (void (*)()) dlsym(handle, "nix_plugin_entry"); + if (nix_plugin_entry) + nix_plugin_entry(); +#else + throw Error("could not dynamically open plugin file '%s'", file); +#endif + } + } + + /* Since plugins can add settings, try to re-apply previously + unknown settings. */ + globalConfig.reapplyUnknownSettings(); + globalConfig.warnUnknownSettings(); + + /* Tell the user if they try to set plugin-files after we've already loaded */ + pluginSettings.pluginFiles.pluginsLoaded = true; +} + +} diff --git a/src/libmain/plugin.hh b/src/libmain/plugin.hh new file mode 100644 index 000000000..4221c1b17 --- /dev/null +++ b/src/libmain/plugin.hh @@ -0,0 +1,12 @@ +#pragma once +///@file + +namespace nix { + +/** + * This should be called after settings are initialized, but before + * anything else + */ +void initPlugins(); + +} diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index 4fe25c7d4..79841ca49 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -29,16 +29,6 @@ nix_err nix_libstore_init_no_load_config(nix_c_context * context) NIXC_CATCH_ERRS } -nix_err nix_init_plugins(nix_c_context * context) -{ - if (context) - context->last_err_code = NIX_OK; - try { - nix::initPlugins(); - } - NIXC_CATCH_ERRS -} - Store * nix_store_open(nix_c_context * context, const char * uri, const char *** params) { if (context) diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index d3cb8fab8..4b2134457 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -42,17 +42,6 @@ nix_err nix_libstore_init(nix_c_context * context); */ nix_err nix_libstore_init_no_load_config(nix_c_context * context); -/** - * @brief Loads the plugins specified in Nix's plugin-files setting. - * - * Call this once, after calling your desired init functions and setting - * relevant settings. - * - * @param[out] context Optional, stores error information - * @return NIX_OK if the initialization was successful, an error code otherwise. - */ -nix_err nix_init_plugins(nix_c_context * context); - /** * @brief Open a nix store. * diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 4eabf6054..fa4c0ba7f 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -15,7 +15,6 @@ #include #ifndef _WIN32 -# include # include #endif @@ -335,60 +334,6 @@ unsigned int MaxBuildJobsSetting::parse(const std::string & str) const } -Paths PluginFilesSetting::parse(const std::string & str) const -{ - if (pluginsLoaded) - throw UsageError("plugin-files set after plugins were loaded, you may need to move the flag before the subcommand"); - return BaseSetting::parse(str); -} - - -void initPlugins() -{ - assert(!settings.pluginFiles.pluginsLoaded); - for (const auto & pluginFile : settings.pluginFiles.get()) { - std::vector pluginFiles; - try { - auto ents = std::filesystem::directory_iterator{pluginFile}; - for (const auto & ent : ents) { - checkInterrupt(); - pluginFiles.emplace_back(ent.path()); - } - } catch (std::filesystem::filesystem_error & e) { - if (e.code() != std::errc::not_a_directory) - throw; - pluginFiles.emplace_back(pluginFile); - } - for (const auto & file : pluginFiles) { - checkInterrupt(); - /* handle is purposefully leaked as there may be state in the - DSO needed by the action of the plugin. */ -#ifndef _WIN32 // TODO implement via DLL loading on Windows - void *handle = - dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (!handle) - throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); - - /* Older plugins use a statically initialized object to run their code. - Newer plugins can also export nix_plugin_entry() */ - void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry"); - if (nix_plugin_entry) - nix_plugin_entry(); -#else - throw Error("could not dynamically open plugin file '%s'", file); -#endif - } - } - - /* Since plugins can add settings, try to re-apply previously - unknown settings. */ - globalConfig.reapplyUnknownSettings(); - globalConfig.warnUnknownSettings(); - - /* Tell the user if they try to set plugin-files after we've already loaded */ - settings.pluginFiles.pluginsLoaded = true; -} - static void preloadNSS() { /* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index dfe25f317..30d7537bd 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -31,23 +31,6 @@ struct MaxBuildJobsSetting : public BaseSetting unsigned int parse(const std::string & str) const override; }; -struct PluginFilesSetting : public BaseSetting -{ - bool pluginsLoaded = false; - - PluginFilesSetting(Config * options, - const Paths & def, - const std::string & name, - const std::string & description, - const std::set & aliases = {}) - : BaseSetting(def, true, name, description, aliases) - { - options->addSetting(this); - } - - Paths parse(const std::string & str) const override; -}; - const uint32_t maxIdsPerBuild = #if __linux__ 1 << 16 @@ -1158,33 +1141,6 @@ public: Setting minFreeCheckInterval{this, 5, "min-free-check-interval", "Number of seconds between checking free disk space."}; - PluginFilesSetting pluginFiles{ - this, {}, "plugin-files", - R"( - A list of plugin files to be loaded by Nix. Each of these files will - be dlopened by Nix. If they contain the symbol `nix_plugin_entry()`, - this symbol will be called. Alternatively, they can affect execution - through static initialization. In particular, these plugins may construct - static instances of RegisterPrimOp to add new primops or constants to the - expression language, RegisterStoreImplementation to add new store - implementations, RegisterCommand to add new subcommands to the `nix` - command, and RegisterSetting to add new nix config settings. See the - constructors for those types for more details. - - Warning! These APIs are inherently unstable and may change from - release to release. - - Since these files are loaded into the same address space as Nix - itself, they must be DSOs compatible with the instance of Nix - running at the time (i.e. compiled against the same headers, not - linked to any incompatible libraries). They should not be linked to - any Nix libs directly, as those will be available already at load - time. - - If an entry in the list is a directory, all files in the directory - are loaded as plugins (non-recursively). - )"}; - Setting narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size", "Maximum size of NARs before spilling them to disk."}; @@ -1278,12 +1234,6 @@ public: // FIXME: don't use a global variable. extern Settings settings; -/** - * This should be called after settings are initialized, but before - * anything else - */ -void initPlugins(); - /** * Load the configuration (from `nix.conf`, `NIX_CONFIG`, etc.) into the * given configuration object. From 808082ea031126ac8738897c43f26240875c02a8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Jul 2024 13:13:11 -0400 Subject: [PATCH 602/910] Ensure we can construct remote store configs in isolation Progress towards #10766 I thought that #10768 achieved, but when I went to use this stuff (in Hydra), turns out it did not. (Those `using FooConfig;` lines were not working --- they are so finicky!) This PR gets the job done, and adds some trivial unit tests to make sure I did what I intended. I had to add add a header to expose `SSHStoreConfig`, after which the preexisting `ssh-store-config.*` were very confusingly named files, so I renamed them to `common-ssh-store-config.hh` to match the type defined therein. --- maintainers/flake-module.nix | 2 +- ...e-config.cc => common-ssh-store-config.cc} | 2 +- ...e-config.hh => common-ssh-store-config.hh} | 0 src/libstore/legacy-ssh-store.cc | 13 +++++-- src/libstore/legacy-ssh-store.hh | 7 +++- src/libstore/meson.build | 5 +-- src/libstore/ssh-store.cc | 35 +++++++++---------- src/libstore/ssh-store.hh | 28 +++++++++++++++ tests/unit/libstore/legacy-ssh-store.cc | 26 ++++++++++++++ tests/unit/libstore/meson.build | 2 ++ tests/unit/libstore/ssh-store.cc | 26 ++++++++++++++ 11 files changed, 120 insertions(+), 26 deletions(-) rename src/libstore/{ssh-store-config.cc => common-ssh-store-config.cc} (96%) rename src/libstore/{ssh-store-config.hh => common-ssh-store-config.hh} (100%) create mode 100644 src/libstore/ssh-store.hh create mode 100644 tests/unit/libstore/legacy-ssh-store.cc create mode 100644 tests/unit/libstore/ssh-store.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 46b3e1363..66bfcf609 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -143,6 +143,7 @@ ''^src/libstore/common-protocol-impl\.hh$'' ''^src/libstore/common-protocol\.cc$'' ''^src/libstore/common-protocol\.hh$'' + ''^src/libstore/common-ssh-store-config\.hh$'' ''^src/libstore/content-address\.cc$'' ''^src/libstore/content-address\.hh$'' ''^src/libstore/daemon\.cc$'' @@ -215,7 +216,6 @@ ''^src/libstore/serve-protocol\.hh$'' ''^src/libstore/sqlite\.cc$'' ''^src/libstore/sqlite\.hh$'' - ''^src/libstore/ssh-store-config\.hh$'' ''^src/libstore/ssh-store\.cc$'' ''^src/libstore/ssh\.cc$'' ''^src/libstore/ssh\.hh$'' diff --git a/src/libstore/ssh-store-config.cc b/src/libstore/common-ssh-store-config.cc similarity index 96% rename from src/libstore/ssh-store-config.cc rename to src/libstore/common-ssh-store-config.cc index e81a94874..05332b9bb 100644 --- a/src/libstore/ssh-store-config.cc +++ b/src/libstore/common-ssh-store-config.cc @@ -1,6 +1,6 @@ #include -#include "ssh-store-config.hh" +#include "common-ssh-store-config.hh" #include "ssh.hh" namespace nix { diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/common-ssh-store-config.hh similarity index 100% rename from src/libstore/ssh-store-config.hh rename to src/libstore/common-ssh-store-config.hh diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 9664b126e..eac360a4f 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -1,5 +1,5 @@ #include "legacy-ssh-store.hh" -#include "ssh-store-config.hh" +#include "common-ssh-store-config.hh" #include "archive.hh" #include "pool.hh" #include "remote-store.hh" @@ -15,6 +15,15 @@ namespace nix { +LegacySSHStoreConfig::LegacySSHStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , CommonSSHStoreConfig(scheme, authority, params) +{ +} + std::string LegacySSHStoreConfig::doc() { return @@ -35,7 +44,7 @@ LegacySSHStore::LegacySSHStore( const Params & params) : StoreConfig(params) , CommonSSHStoreConfig(scheme, host, params) - , LegacySSHStoreConfig(params) + , LegacySSHStoreConfig(scheme, host, params) , Store(params) , connections(make_ref>( std::max(1, (int) maxConnections), diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index db49188ec..f26651898 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "ssh-store-config.hh" +#include "common-ssh-store-config.hh" #include "store-api.hh" #include "ssh.hh" #include "callback.hh" @@ -13,6 +13,11 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig { using CommonSSHStoreConfig::CommonSSHStoreConfig; + LegacySSHStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params); + const Setting remoteProgram{this, {"nix-store"}, "remote-program", "Path to the `nix-store` executable on the remote machine."}; diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 5324b2a1f..297bf7187 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -162,6 +162,7 @@ sources = files( 'builtins/fetchurl.cc', 'builtins/unpack-channel.cc', 'common-protocol.cc', + 'common-ssh-store-config.cc', 'content-address.cc', 'daemon.cc', 'derivations.cc', @@ -206,7 +207,6 @@ sources = files( 'serve-protocol-connection.cc', 'serve-protocol.cc', 'sqlite.cc', - 'ssh-store-config.cc', 'ssh-store.cc', 'ssh.cc', 'store-api.cc', @@ -233,6 +233,7 @@ headers = [config_h] + files( 'builtins/buildenv.hh', 'common-protocol-impl.hh', 'common-protocol.hh', + 'common-ssh-store-config.hh', 'content-address.hh', 'daemon.hh', 'derivations.hh', @@ -272,11 +273,11 @@ headers = [config_h] + files( 'remote-store.hh', 's3-binary-cache-store.hh', 's3.hh', + 'ssh-store.hh', 'serve-protocol-connection.hh', 'serve-protocol-impl.hh', 'serve-protocol.hh', 'sqlite.hh', - 'ssh-store-config.hh', 'ssh.hh', 'store-api.hh', 'store-cast.hh', diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 7ad934b73..b21c22d7e 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -1,7 +1,5 @@ -#include "ssh-store-config.hh" -#include "store-api.hh" +#include "ssh-store.hh" #include "local-fs-store.hh" -#include "remote-store.hh" #include "remote-store-connection.hh" #include "source-accessor.hh" #include "archive.hh" @@ -12,23 +10,22 @@ namespace nix { -struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig +SSHStoreConfig::SSHStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, authority, params) { - using RemoteStoreConfig::RemoteStoreConfig; - using CommonSSHStoreConfig::CommonSSHStoreConfig; +} - const Setting remoteProgram{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 - { - return - #include "ssh-store.md" - ; - } -}; +std::string SSHStoreConfig::doc() +{ + return + #include "ssh-store.md" + ; +} class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore { @@ -41,7 +38,7 @@ public: : StoreConfig(params) , RemoteStoreConfig(params) , CommonSSHStoreConfig(scheme, host, params) - , SSHStoreConfig(params) + , SSHStoreConfig(scheme, host, params) , Store(params) , RemoteStore(params) , master(createSSHMaster( diff --git a/src/libstore/ssh-store.hh b/src/libstore/ssh-store.hh new file mode 100644 index 000000000..6ef2219a2 --- /dev/null +++ b/src/libstore/ssh-store.hh @@ -0,0 +1,28 @@ +#pragma once +///@file + +#include "common-ssh-store-config.hh" +#include "store-api.hh" +#include "remote-store.hh" + +namespace nix { + +struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig +{ + using CommonSSHStoreConfig::CommonSSHStoreConfig; + using RemoteStoreConfig::RemoteStoreConfig; + + SSHStoreConfig(std::string_view scheme, std::string_view authority, const Params & params); + + const Setting remoteProgram{ + 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; +}; + +} diff --git a/tests/unit/libstore/legacy-ssh-store.cc b/tests/unit/libstore/legacy-ssh-store.cc new file mode 100644 index 000000000..eb31a2408 --- /dev/null +++ b/tests/unit/libstore/legacy-ssh-store.cc @@ -0,0 +1,26 @@ +#include + +#include "legacy-ssh-store.hh" + +namespace nix { + +TEST(LegacySSHStore, constructConfig) +{ + LegacySSHStoreConfig config{ + "ssh", + "localhost", + StoreConfig::Params{ + { + "remote-program", + // TODO #11106, no more split on space + "foo bar", + }, + }}; + EXPECT_EQ( + config.remoteProgram.get(), + (Strings{ + "foo", + "bar", + })); +} +} diff --git a/tests/unit/libstore/meson.build b/tests/unit/libstore/meson.build index 90e7d3047..41b2fb0ab 100644 --- a/tests/unit/libstore/meson.build +++ b/tests/unit/libstore/meson.build @@ -58,6 +58,7 @@ sources = files( 'derivation.cc', 'derived-path.cc', 'downstream-placeholder.cc', + 'legacy-ssh-store.cc', 'machines.cc', 'nar-info-disk-cache.cc', 'nar-info.cc', @@ -67,6 +68,7 @@ sources = files( 'path.cc', 'references.cc', 'serve-protocol.cc', + 'ssh-store.cc', 'store-reference.cc', 'worker-protocol.cc', ) diff --git a/tests/unit/libstore/ssh-store.cc b/tests/unit/libstore/ssh-store.cc new file mode 100644 index 000000000..7010ad157 --- /dev/null +++ b/tests/unit/libstore/ssh-store.cc @@ -0,0 +1,26 @@ +#include + +#include "ssh-store.hh" + +namespace nix { + +TEST(SSHStore, constructConfig) +{ + SSHStoreConfig config{ + "ssh", + "localhost", + StoreConfig::Params{ + { + "remote-program", + // TODO #11106, no more split on space + "foo bar", + }, + }}; + EXPECT_EQ( + config.remoteProgram.get(), + (Strings{ + "foo", + "bar", + })); +} +} From 783a8341ee2dedb8fc0790e803a5f3d0362d67d9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:28:28 +0200 Subject: [PATCH 603/910] tests/functional: Support negative codes in expect, expectStderr --- tests/functional/common/vars-and-functions.sh | 6 ++++-- tests/functional/test-infra.sh | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 4316a30d5..e5cc04bb3 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -236,7 +236,8 @@ expect() { expected="$1" shift "$@" && res=0 || res="$?" - if [[ $res -ne $expected ]]; then + # also match "negative" codes, which wrap around to >127 + if [[ $res -ne $expected && $res -ne $[256 + expected] ]]; then echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 return 1 fi @@ -250,7 +251,8 @@ expectStderr() { expected="$1" shift "$@" 2>&1 && res=0 || res="$?" - if [[ $res -ne $expected ]]; then + # also match "negative" codes, which wrap around to >127 + if [[ $res -ne $expected && $res -ne $[256 + expected] ]]; then echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 return 1 fi diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 37322b356..f6f84eae9 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -13,6 +13,25 @@ expect 1 false # `expect` will fail when we get it wrong expect 1 expect 0 false +function ret() { + return $1 +} + +# `expect` can call functions, not just executables +expect 0 ret 0 +expect 1 ret 1 + +# `expect` supports negative exit codes +expect -1 ret -1 + +# or high positive ones, equivalent to negative ones +expect 255 ret 255 +expect 255 ret -1 +expect -1 ret 255 + +# but it doesn't confuse negative exit codes with positive ones +expect 1 expect -10 ret 10 + noisyTrue () { echo YAY! >&2 true From f2df3f0c6c78cb742a87dbe2d2f9bcf5d5395795 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:40:14 +0200 Subject: [PATCH 604/910] tests/vars-and-functions: Add callerPrefix helper --- tests/functional/common/vars-and-functions.sh | 39 +++++++++++++++++++ tests/functional/test-infra.sh | 4 ++ 2 files changed, 43 insertions(+) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index e5cc04bb3..062f3d9f9 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -297,6 +297,45 @@ onError() { done } +# Prints an error message prefix referring to the last call into this file. +# Ignores `expect` and `expectStderr` calls. +# Set a special exit code when test suite functions are misused, so that +# functions like expectStderr won't mistake them for expected Nix CLI errors. +# Suggestion: -101 (negative to indicate very abnormal, and beyond the normal +# range of signals) +# Example (showns as string): 'repl.sh:123: in call to grepQuiet: ' +# This function is inefficient, so it should only be used in error messages. +callerPrefix() { + # Find the closes caller that's not from this file + local i file line fn savedFn + # Use `caller` + for i in $(seq 0 100); do + caller $i > /dev/null || { + if [[ -n "${file:-}" ]]; then + echo "$file:$line: ${savedFn+in call to $savedFn: }" + fi + break + } + line="$(caller $i | cut -d' ' -f1)" + fn="$(caller $i | cut -d' ' -f2)" + file="$(caller $i | cut -d' ' -f3)" + if [[ $file != "${BASH_SOURCE[0]}" ]]; then + echo "$file:$line: ${savedFn+in call to $savedFn: }" + return + fi + case "$fn" in + # Ignore higher order functions that don't report any misuse of themselves + # This way a misuse of a foo in `expectStderr 1 foo` will be reported as + # calling foo, not expectStderr. + expect|expectStderr|callerPrefix) + ;; + *) + savedFn="$fn" + ;; + esac + done +} + # `grep -v` doesn't work well for exit codes. We want `!(exist line l. l # matches)`. It gives us `exist line l. !(l matches)`. # diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index f6f84eae9..93e0bd64b 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -88,6 +88,10 @@ funBang () { expect 1 funBang unset funBang +# callerPrefix can be used by the test framework to improve error messages +# it reports about our call site here +echo "<[$(callerPrefix)]>" | grepQuiet -F "<[test-infra.sh:$LINENO: ]>" + # `grep -v -q` is not what we want for exit codes, but `grepInverse` is # Avoid `grep -v -q`. The following line proves the point, and if it fails, # we'll know that `grep` had a breaking change or `-v -q` may not be portable. From 644b97ce2574fe22a3fe14daeb6a3d0711d75731 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:41:22 +0200 Subject: [PATCH 605/910] tests/functional: Make our grep* helpers reject newlines in the query Newlines behave like *OR*; not "and then". --- tests/functional/common/vars-and-functions.sh | 19 ++++++++++++++++--- tests/functional/test-infra.sh | 5 +++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 062f3d9f9..6cce08fbc 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -336,13 +336,24 @@ callerPrefix() { done } +checkGrepArgs() { + local arg + for arg in "$@"; do + if [[ "$arg" != "${arg//$'\n'/_}" ]]; then + echo "$(callerPrefix)newline not allowed in arguments; grep would try each line individually as if connected by an OR operator" >&2 + return -101 + fi + done +} + # `grep -v` doesn't work well for exit codes. We want `!(exist line l. l # matches)`. It gives us `exist line l. !(l matches)`. # # `!` normally doesn't work well with `set -e`, but when we wrap in a # function it *does*. grepInverse() { - ! grep "$@" + checkGrepArgs "$@" && \ + ! grep "$@" } # A shorthand, `> /dev/null` is a bit noisy. @@ -357,12 +368,14 @@ grepInverse() { # the producer into the pipe. But rest assured we've seen it happen in # CI reliably. grepQuiet() { - grep "$@" > /dev/null + checkGrepArgs "$@" && \ + grep "$@" > /dev/null } # The previous two, combined grepQuietInverse() { - ! grep "$@" > /dev/null + checkGrepArgs "$@" && \ + ! grep "$@" > /dev/null } # Return the number of arguments diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 93e0bd64b..983b4e860 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -108,3 +108,8 @@ unset res res=$(set -eu -o pipefail; echo foo | expect 1 grepQuietInverse foo | wc -c) (( res == 0 )) unset res + +# `grepQuiet` does not allow newlines in its arguments, because grep quietly +# treats them as multiple queries. +( echo foo; echo bar; ) | expectStderr -101 grepQuiet $'foo\nbar' \ + | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grepQuiet: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' From 41a03738d63b94366d96dfc3e5cfb052c0ad5e2a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:54:12 +0200 Subject: [PATCH 606/910] tests/functional: Also keep plain grep calls safe from newlines --- tests/functional/common/vars-and-functions.sh | 20 ++++++++++++++++--- tests/functional/test-infra.sh | 4 ++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 6cce08fbc..7a399f6d4 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -351,9 +351,12 @@ checkGrepArgs() { # # `!` normally doesn't work well with `set -e`, but when we wrap in a # function it *does*. +# +# `command grep` lets us avoid re-checking the args by going directly to the +# executable. grepInverse() { checkGrepArgs "$@" && \ - ! grep "$@" + ! command grep "$@" } # A shorthand, `> /dev/null` is a bit noisy. @@ -367,15 +370,26 @@ grepInverse() { # the closing of the pipe, the buffering of the pipe, and the speed of # the producer into the pipe. But rest assured we've seen it happen in # CI reliably. +# +# `command grep` lets us avoid re-checking the args by going directly to the +# executable. grepQuiet() { checkGrepArgs "$@" && \ - grep "$@" > /dev/null + command grep "$@" > /dev/null } # The previous two, combined grepQuietInverse() { checkGrepArgs "$@" && \ - ! grep "$@" > /dev/null + ! command grep "$@" > /dev/null +} + +# Wrap grep to remove its newline footgun; see checkGrepArgs. +# Note that we keep the checkGrepArgs calls in the other helpers, because some +# of them are negated and that would defeat this check. +grep() { + checkGrepArgs "$@" && \ + command grep "$@" } # Return the number of arguments diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 983b4e860..1dab069fb 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -113,3 +113,7 @@ unset res # treats them as multiple queries. ( echo foo; echo bar; ) | expectStderr -101 grepQuiet $'foo\nbar' \ | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grepQuiet: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' + +# We took the blue pill and woke up in a world where `grep` is moderately safe. +expectStderr -101 grep $'foo\nbar' \ + | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grep: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' From 6c9d62dcebbb042e24cb7e07c5cf5369f1db6ba0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:04:37 +0200 Subject: [PATCH 607/910] Doc comments: use std::unordered_map Co-authored-by: Eelco Dolstra --- src/libexpr/eval.hh | 4 ++-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index d376046ae..f09e6223a 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,7 +130,7 @@ struct Constant typedef std::map ValMap; #endif -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; struct Env { @@ -335,7 +335,7 @@ private: * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. * Grouped by file. */ - std::map positionToDocComment; + std::unordered_map positionToDocComment; LookupPath lookupPath; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 983a17a2e..5a7bcb717 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::map & positionToDocComment; + std::unordered_map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 452d265bc..8ea176b24 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -48,7 +48,7 @@ namespace nix { -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; Expr * parseExprFromBuf( char * text, From 3d8fa9f6688c7a1db27d216bb2d5897687e4af9a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Jul 2024 16:34:13 +0200 Subject: [PATCH 608/910] Pos::getSnippetUpTo(): Fix warning --- src/libutil/position.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 3289dbe8b..5a2529262 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -119,12 +119,12 @@ std::optional Pos::getSnippetUpTo(const Pos & end) const { if (auto source = getSource()) { auto firstLine = LinesIterator(*source); - for (auto i = 1; i < this->line; ++i) { + for (uint32_t i = 1; i < this->line; ++i) { ++firstLine; } auto lastLine = LinesIterator(*source); - for (auto i = 1; i < end.line; ++i) { + for (uint32_t i = 1; i < end.line; ++i) { ++lastLine; } From 64b46000ad92e772cd30f691bd75d937a2b84158 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 16:46:41 +0200 Subject: [PATCH 609/910] Add std::hash --- src/libexpr/pos-idx.hh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index e13491560..1d711681f 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -2,12 +2,15 @@ #include +#include "util.hh" + namespace nix { class PosIdx { friend struct LazyPosAcessors; friend class PosTable; + friend class std::hash; private: uint32_t id; @@ -37,8 +40,28 @@ public: { return id == other.id; } + + size_t hash() const noexcept + { + size_t h = 854125; + hash_combine(h, id); + return h; + } }; inline PosIdx noPos = {}; } + +namespace std { + +template<> +struct hash +{ + std::size_t operator()(nix::PosIdx pos) const noexcept + { + return pos.hash(); + } +}; + +} // namespace std From 74698d54c82fa302dc0c82f68c796eda673090d9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 13:34:42 +0200 Subject: [PATCH 610/910] Document builtins.derivation --- src/libexpr/primops/derivation.nix | 29 +++++++++++++++++-- .../lang/eval-fail-derivation-name.err.exp | 24 +++++++-------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/libexpr/primops/derivation.nix b/src/libexpr/primops/derivation.nix index c0fbe8082..f329ff71e 100644 --- a/src/libexpr/primops/derivation.nix +++ b/src/libexpr/primops/derivation.nix @@ -1,6 +1,31 @@ -/* This is the implementation of the ‘derivation’ builtin function. - It's actually a wrapper around the ‘derivationStrict’ primop. */ +# This is the implementation of the ‘derivation’ builtin function. +# It's actually a wrapper around the ‘derivationStrict’ primop. +# Note that the following comment will be shown in :doc in the repl, but not in the manual. +/** + Create a derivation. + + # Inputs + + The single argument is an attribute set that describes what to build and how to build it. + See https://nix.dev/manual/nix/2.23/language/derivations + + # Output + + The result is an attribute set that describes the derivation. + Notably it contains the outputs, which in the context of the Nix language are special strings that refer to the output paths, which may not yet exist. + The realisation of these outputs only occurs when needed; for example + + * When `nix-build` or a similar command is run, it realises the outputs that were requested on its command line. + See https://nix.dev/manual/nix/2.23/command-ref/nix-build + + * When `import`, `readFile`, `readDir` or some other functions are called, they have to realise the outputs they depend on. + This is referred to as "import from derivation". + See https://nix.dev/manual/nix/2.23/language/import-from-derivation + + Note that `derivation` is very bare-bones, and provides almost no commands during the build. + Most likely, you'll want to use functions like `stdenv.mkDerivation` in Nixpkgs to set up a basic environment. +*/ drvAttrs @ { outputs ? [ "out" ], ... }: let diff --git a/tests/functional/lang/eval-fail-derivation-name.err.exp b/tests/functional/lang/eval-fail-derivation-name.err.exp index eb2206df1..ae7b47712 100644 --- a/tests/functional/lang/eval-fail-derivation-name.err.exp +++ b/tests/functional/lang/eval-fail-derivation-name.err.exp @@ -1,24 +1,24 @@ error: … while evaluating the attribute 'outPath' - at :19:9: - 18| value = commonAttrs // { - 19| outPath = builtins.getAttr outputName strict; + at :44:9: + 43| value = commonAttrs // { + 44| outPath = builtins.getAttr outputName strict; | ^ - 20| drvPath = strict.drvPath; + 45| drvPath = strict.drvPath; … while calling the 'getAttr' builtin - at :19:19: - 18| value = commonAttrs // { - 19| outPath = builtins.getAttr outputName strict; + at :44:19: + 43| value = commonAttrs // { + 44| outPath = builtins.getAttr outputName strict; | ^ - 20| drvPath = strict.drvPath; + 45| drvPath = strict.drvPath; … while calling the 'derivationStrict' builtin - at :9:12: - 8| - 9| strict = derivationStrict drvAttrs; + at :34:12: + 33| + 34| strict = derivationStrict drvAttrs; | ^ - 10| + 35| … while evaluating derivation '~jiggle~' whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:2:3 From f9a1d6b0188a2d0e9ad84f1e735e560320380501 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 17:36:30 +0200 Subject: [PATCH 611/910] tests/functional/lang: Add post processing and remove certain line numbers --- tests/functional/lang.sh | 15 +++++++++++ .../lang/eval-fail-derivation-name.err.exp | 26 +++++++++---------- .../eval-fail-derivation-name.postprocess | 9 +++++++ 3 files changed, 37 insertions(+), 13 deletions(-) create mode 100755 tests/functional/lang/eval-fail-derivation-name.postprocess diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 8cb8e98fb..5a8cefd13 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -50,11 +50,22 @@ set +x badDiff=0 badExitCode=0 +# Extra post-processing that's specific to each test case +postprocess() { + if [[ -e "lang/$1.postprocess" ]]; then + ( + set -x; + "lang/$1.postprocess" "lang/$1" + ) + fi +} + for i in lang/parse-fail-*.nix; do echo "parsing $i (should fail)"; i=$(basename "$i" .nix) if expectStderr 1 nix-instantiate --parse - < "lang/$i.nix" > "lang/$i.err" then + postprocess "$i" diffAndAccept "$i" err err.exp else echo "FAIL: $i shouldn't parse" @@ -71,6 +82,7 @@ for i in lang/parse-okay-*.nix; do 2> "lang/$i.err" then sed "s!$(pwd)!/pwd!g" "lang/$i.out" "lang/$i.err" + postprocess "$i" diffAndAccept "$i" out exp diffAndAccept "$i" err err.exp else @@ -94,6 +106,7 @@ for i in lang/eval-fail-*.nix; do expectStderr 1 nix-instantiate $flags "lang/$i.nix" \ | sed "s!$(pwd)!/pwd!g" > "lang/$i.err" then + postprocess "$i" diffAndAccept "$i" err err.exp else echo "FAIL: $i shouldn't evaluate" @@ -109,6 +122,7 @@ for i in lang/eval-okay-*.nix; do if expect 0 nix-instantiate --eval --xml --no-location --strict \ "lang/$i.nix" > "lang/$i.out.xml" then + postprocess "$i" diffAndAccept "$i" out.xml exp.xml else echo "FAIL: $i should evaluate" @@ -129,6 +143,7 @@ for i in lang/eval-okay-*.nix; do 2> "lang/$i.err" then sed -i "s!$(pwd)!/pwd!g" "lang/$i.out" "lang/$i.err" + postprocess "$i" diffAndAccept "$i" out exp diffAndAccept "$i" err err.exp else diff --git a/tests/functional/lang/eval-fail-derivation-name.err.exp b/tests/functional/lang/eval-fail-derivation-name.err.exp index ae7b47712..0ef98674d 100644 --- a/tests/functional/lang/eval-fail-derivation-name.err.exp +++ b/tests/functional/lang/eval-fail-derivation-name.err.exp @@ -1,26 +1,26 @@ error: … while evaluating the attribute 'outPath' - at :44:9: - 43| value = commonAttrs // { - 44| outPath = builtins.getAttr outputName strict; + at ::: + | value = commonAttrs // { + | outPath = builtins.getAttr outputName strict; | ^ - 45| drvPath = strict.drvPath; + | drvPath = strict.drvPath; … while calling the 'getAttr' builtin - at :44:19: - 43| value = commonAttrs // { - 44| outPath = builtins.getAttr outputName strict; + at ::: + | value = commonAttrs // { + | outPath = builtins.getAttr outputName strict; | ^ - 45| drvPath = strict.drvPath; + | drvPath = strict.drvPath; … while calling the 'derivationStrict' builtin - at :34:12: - 33| - 34| strict = derivationStrict drvAttrs; + at ::: + | + | strict = derivationStrict drvAttrs; | ^ - 35| + | … while evaluating derivation '~jiggle~' - whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:2:3 + whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:: error: invalid derivation name: name '~jiggle~' contains illegal character '~'. Please pass a different 'name'. diff --git a/tests/functional/lang/eval-fail-derivation-name.postprocess b/tests/functional/lang/eval-fail-derivation-name.postprocess new file mode 100755 index 000000000..ab9fa5b5d --- /dev/null +++ b/tests/functional/lang/eval-fail-derivation-name.postprocess @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail +testcaseBasename=$1 + +# Line numbers change when derivation.nix docs are updated. +sed -i "$testcaseBasename.err" \ + -e 's/[0-9 ][0-9 ][0-9 ][0-9 ][0-9 ][0-9 ][0-9 ][0-9]\([^0-9]\)/\1/g' \ + -e 's/[0-9][0-9]*//g' \ + ; From d0e9878389cc00caff84d0bbaa37fe008af638ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 22:22:15 +0200 Subject: [PATCH 612/910] Remove unused boost include and split out std-hash.hh Splitting it out immediately answers questions like [this], without increasing the number of compilation units. I did consider using boost::hash_combine instead, but it doesn't seem to be quite as capable, accepting only two arguments. [this]: https://github.com/NixOS/nix/pull/11113#discussion_r1679991573 --- src/libexpr/pos-idx.hh | 2 +- src/libutil/meson.build | 1 + src/libutil/source-path.hh | 3 +-- src/libutil/std-hash.hh | 24 ++++++++++++++++++++++++ src/libutil/util.hh | 14 -------------- 5 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 src/libutil/std-hash.hh diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index 1d711681f..f3ea3a2e5 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -2,7 +2,7 @@ #include -#include "util.hh" +#include "std-hash.hh" namespace nix { diff --git a/src/libutil/meson.build b/src/libutil/meson.build index fbfcbe67c..04c778c31 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -216,6 +216,7 @@ headers = [config_h] + files( 'source-accessor.hh', 'source-path.hh', 'split.hh', + 'std-hash.hh', 'strings.hh', 'strings-inline.hh', 'suggestions.hh', diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 1e96b72e5..fc2288f74 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -8,8 +8,7 @@ #include "ref.hh" #include "canon-path.hh" #include "source-accessor.hh" - -#include // for boost::hash_combine +#include "std-hash.hh" namespace nix { diff --git a/src/libutil/std-hash.hh b/src/libutil/std-hash.hh new file mode 100644 index 000000000..c359d11ca --- /dev/null +++ b/src/libutil/std-hash.hh @@ -0,0 +1,24 @@ +#pragma once + +//!@file Hashing utilities for use with unordered_map, etc. (ie low level implementation logic, not domain logic like +//! Nix hashing) + +#include + +namespace nix { + +/** + * hash_combine() from Boost. Hash several hashable values together + * into a single hash. + */ +inline void hash_combine(std::size_t & seed) {} + +template +inline void hash_combine(std::size_t & seed, const T & v, Rest... rest) +{ + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + hash_combine(seed, rest...); +} + +} // namespace nix diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 83b42a528..877d15279 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -375,18 +375,4 @@ inline std::string operator + (std::string_view s1, const char * s2) return s; } -/** - * hash_combine() from Boost. Hash several hashable values together - * into a single hash. - */ -inline void hash_combine(std::size_t & seed) { } - -template -inline void hash_combine(std::size_t & seed, const T & v, Rest... rest) -{ - std::hash hasher; - seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); - hash_combine(seed, rest...); -} - } From 7dce07463429bb4a42a3b47a869f5eb488206c26 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 22:43:04 +0200 Subject: [PATCH 613/910] tests/functional/lang: Avoid /usr/bin/env for sandbox --- tests/functional/lang.sh | 5 ++++- tests/functional/lang/eval-fail-derivation-name.postprocess | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) mode change 100755 => 100644 tests/functional/lang/eval-fail-derivation-name.postprocess diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 5a8cefd13..46cf3f1fe 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -54,8 +54,11 @@ badExitCode=0 postprocess() { if [[ -e "lang/$1.postprocess" ]]; then ( + # We could allow arbitrary interpreters in .postprocess, but that + # just exposes us to the complexity of not having /usr/bin/env in + # the sandbox. So let's just hardcode bash for now. set -x; - "lang/$1.postprocess" "lang/$1" + bash "lang/$1.postprocess" "lang/$1" ) fi } diff --git a/tests/functional/lang/eval-fail-derivation-name.postprocess b/tests/functional/lang/eval-fail-derivation-name.postprocess old mode 100755 new mode 100644 index ab9fa5b5d..ffbc2b5d4 --- a/tests/functional/lang/eval-fail-derivation-name.postprocess +++ b/tests/functional/lang/eval-fail-derivation-name.postprocess @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash set -euo pipefail testcaseBasename=$1 From 0a1a116f4b2fd6840a61d8f85494cade6fd6a306 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Tue, 16 Jul 2024 13:51:52 -0700 Subject: [PATCH 614/910] builtins.genericClosure: fix documentation typo --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 127823d49..5a373a43b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -719,7 +719,7 @@ static RegisterPrimOp primop_genericClosure(PrimOp { .doc = R"( `builtins.genericClosure` iteratively computes the transitive closure over an arbitrary relation defined by a function. - It takes *attrset* with two attributes named `startSet` and `operator`, and returns a list of attrbute sets: + It takes *attrset* with two attributes named `startSet` and `operator`, and returns a list of attribute sets: - `startSet`: The initial list of attribute sets. From 5b6a21acc5a4c14e4d5f79bd256e667f8e01ae30 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Tue, 16 Jul 2024 21:05:16 +0000 Subject: [PATCH 615/910] Avoid casting function pointer in libutil test support Casting function pointers seems to be almost always UB. See https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type Fixed by doing the casting of `void*` to `std::string*` inside the function instead. Caught by UBSan. --- tests/unit/libutil-support/tests/string_callback.cc | 5 +++-- tests/unit/libutil-support/tests/string_callback.hh | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit/libutil-support/tests/string_callback.cc b/tests/unit/libutil-support/tests/string_callback.cc index 2d0e0dad0..7a13bd4ff 100644 --- a/tests/unit/libutil-support/tests/string_callback.cc +++ b/tests/unit/libutil-support/tests/string_callback.cc @@ -2,9 +2,10 @@ namespace nix::testing { -void observe_string_cb(const char * start, unsigned int n, std::string * user_data) +void observe_string_cb(const char * start, unsigned int n, void * user_data) { - *user_data = std::string(start); + auto user_data_casted = reinterpret_cast(user_data); + *user_data_casted = std::string(start); } } diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh index a02ea3a1b..9a7e8d85d 100644 --- a/tests/unit/libutil-support/tests/string_callback.hh +++ b/tests/unit/libutil-support/tests/string_callback.hh @@ -3,14 +3,13 @@ namespace nix::testing { -void observe_string_cb(const char * start, unsigned int n, std::string * user_data); +void observe_string_cb(const char * start, unsigned int n, void * user_data); inline void * observe_string_cb_data(std::string & out) { return (void *) &out; }; -#define OBSERVE_STRING(str) \ - (nix_get_string_callback) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) +#define OBSERVE_STRING(str) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) } From a1f3f103bc1a618a83b51a97dd0fb0c2f66a6b41 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Tue, 16 Jul 2024 21:38:19 +0000 Subject: [PATCH 616/910] Check if drv is initialized in DerivationGoal::waiteeDone It might not be set, in which case we shouldn't do anything. Surprisingly, this somehow did not cause segfaults before? Caught by UBSan. --- src/libstore/build/derivation-goal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 010f905d6..b809e3ffe 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1569,7 +1569,7 @@ void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result) { Goal::waiteeDone(waitee, result); - if (!useDerivation) return; + if (!useDerivation || !drv) return; auto & fullDrv = *dynamic_cast(drv.get()); auto * dg = dynamic_cast(&*waitee); From 9fae50ed4be6c7f8bd16ece9626709d78bb4b01c Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 17 Jul 2024 02:42:18 +0200 Subject: [PATCH 617/910] Add parser test for indented strings So that in the next commit we can see what changes about this test --- .../functional/lang/parse-okay-ind-string.exp | 1 + .../functional/lang/parse-okay-ind-string.nix | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/functional/lang/parse-okay-ind-string.exp create mode 100644 tests/functional/lang/parse-okay-ind-string.nix diff --git a/tests/functional/lang/parse-okay-ind-string.exp b/tests/functional/lang/parse-okay-ind-string.exp new file mode 100644 index 000000000..5f8d48688 --- /dev/null +++ b/tests/functional/lang/parse-okay-ind-string.exp @@ -0,0 +1 @@ +(let string = "str"; in [ (/some/path) ((/some/path)) (("" + /some/path)) ((/some/path + "\n end")) (string) ((string)) (("" + string)) ((string + "\n end")) ("") ("") ("end") ]) diff --git a/tests/functional/lang/parse-okay-ind-string.nix b/tests/functional/lang/parse-okay-ind-string.nix new file mode 100644 index 000000000..97c9de3cd --- /dev/null +++ b/tests/functional/lang/parse-okay-ind-string.nix @@ -0,0 +1,31 @@ +let + string = "str"; +in [ + /some/path + + ''${/some/path}'' + + '' + ${/some/path}'' + + ''${/some/path} + end'' + + string + + ''${string}'' + + '' + ${string}'' + + ''${string} + end'' + + '''' + + '' + '' + + '' + end'' +] From f5ebaea2775da9991c5f7258bf8059b1b5770bab Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Jul 2024 13:31:31 +0200 Subject: [PATCH 618/910] Simplify PosIdx::hash() In C++ we don't need to salt the hash. --- src/libexpr/pos-idx.hh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index f3ea3a2e5..2faa6b7fe 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -1,8 +1,7 @@ #pragma once #include - -#include "std-hash.hh" +#include namespace nix { @@ -43,9 +42,7 @@ public: size_t hash() const noexcept { - size_t h = 854125; - hash_combine(h, id); - return h; + return std::hash{}(id); } }; From ece334b53284c2718515d35a81862712c9df06df Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 13:42:12 +0200 Subject: [PATCH 619/910] tests/functional/repl: Characterize side effecting print behavior Reported on matrix by aleksana: https://matrix.to/#/!VRULIdgoKmKPzJZzjj:nixos.org/$7wZp5lUDTd-_u6MYo8kWWcysjtqTiQqP8dLI0RDNVVM?via=nixos.org&via=matrix.org&via=nixos.dev --- .../repl/pretty-print-idempotent.expected | 33 +++++++++++++++++++ .../repl/pretty-print-idempotent.in | 9 +++++ .../repl/pretty-print-idempotent.nix | 19 +++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/functional/repl/pretty-print-idempotent.expected create mode 100644 tests/functional/repl/pretty-print-idempotent.in create mode 100644 tests/functional/repl/pretty-print-idempotent.nix diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected new file mode 100644 index 000000000..e74239564 --- /dev/null +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -0,0 +1,33 @@ +Nix +Type :? for help. +Added variables. + +{ + homepage = "https://example.com"; +} + +{ homepage = "https://example.com"; } + +{ + layerOne = { ... }; +} + +{ + layerOne = { ... }; +} + +[ + "https://example.com" +] + +[ "https://example.com" ] + +[ + [ ... ] +] + +[ + [ ... ] +] + + diff --git a/tests/functional/repl/pretty-print-idempotent.in b/tests/functional/repl/pretty-print-idempotent.in new file mode 100644 index 000000000..5f865316f --- /dev/null +++ b/tests/functional/repl/pretty-print-idempotent.in @@ -0,0 +1,9 @@ +:l pretty-print-idempotent.nix +oneDeep +oneDeep +twoDeep +twoDeep +oneDeepList +oneDeepList +twoDeepList +twoDeepList diff --git a/tests/functional/repl/pretty-print-idempotent.nix b/tests/functional/repl/pretty-print-idempotent.nix new file mode 100644 index 000000000..68929f387 --- /dev/null +++ b/tests/functional/repl/pretty-print-idempotent.nix @@ -0,0 +1,19 @@ +{ + oneDeep = { + homepage = "https://" + "example.com"; + }; + twoDeep = { + layerOne = { + homepage = "https://" + "example.com"; + }; + }; + + oneDeepList = [ + ("https://" + "example.com") + ]; + twoDeepList = [ + [ + ("https://" + "example.com") + ] + ]; +} From a0635a80b2c3bc89baffd64b1d91d7efa2d2fd3a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 14:28:21 +0200 Subject: [PATCH 620/910] printAttrs: Force item before determining whether to print multi-line --- src/libexpr/print.cc | 6 ++++++ tests/functional/repl/pretty-print-idempotent.expected | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 2f377e588..6167abee8 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -299,6 +299,9 @@ private: output << ANSI_NORMAL; } + /** + * @note This may force items. + */ bool shouldPrettyPrintAttrs(AttrVec & v) { if (!options.shouldPrettyPrint() || v.empty()) { @@ -315,6 +318,9 @@ private: return true; } + // It is ok to force the item(s) here, because they will be printed anyway. + state.forceValue(*item, item->determinePos(noPos)); + // Pretty-print single-item attrsets only if they contain nested // structures. auto itemType = item->type(); diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index e74239564..949d7a0e6 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -2,9 +2,7 @@ Nix Type :? for help. Added variables. -{ - homepage = "https://example.com"; -} +{ homepage = "https://example.com"; } { homepage = "https://example.com"; } From da3eff60bc35763270f57c3cc17e613a19513709 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 14:31:20 +0200 Subject: [PATCH 621/910] printList: Force item before determining whether to print multi-line --- src/libexpr/print.cc | 6 ++++++ tests/functional/repl/pretty-print-idempotent.expected | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 6167abee8..bc17d6bfe 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -377,6 +377,9 @@ private: } } + /** + * @note This may force items. + */ bool shouldPrettyPrintList(std::span list) { if (!options.shouldPrettyPrint() || list.empty()) { @@ -393,6 +396,9 @@ private: return true; } + // It is ok to force the item(s) here, because they will be printed anyway. + state.forceValue(*item, item->determinePos(noPos)); + // Pretty-print single-item lists only if they contain nested // structures. auto itemType = item->type(); diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index 949d7a0e6..f38b9b569 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -14,9 +14,7 @@ Added variables. layerOne = { ... }; } -[ - "https://example.com" -] +[ "https://example.com" ] [ "https://example.com" ] From 464e5925cb21150e3c94f31224efabd3c1e74237 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Wed, 17 Jul 2024 13:10:01 +0100 Subject: [PATCH 622/910] Avoid accessing uninitialized settings in own init (#11117) The default value for the setting was evaluated by calling a method on the object _being currently constructed_, so we were using it before all fields were initialized. This has been fixed by making the called method static, and not using the previously used fields at all. But functionality hasn't changed! The fields were usually always zero (by chance?) anyway, meaning the conditional path was always taken. Thus the current logic has been kept, the code simplified, and UB removed. This was found with the helper of UBSan. --- src/libexpr/eval-settings.cc | 10 ++++------ src/libexpr/eval-settings.hh | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index e2151aa7f..eb5761638 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -56,7 +56,7 @@ EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lo builtinsAbortOnWarn = true; } -Strings EvalSettings::getDefaultNixPath() const +Strings EvalSettings::getDefaultNixPath() { Strings res; auto add = [&](const Path & p, const std::string & s = std::string()) { @@ -69,11 +69,9 @@ Strings EvalSettings::getDefaultNixPath() const } }; - if (!restrictEval && !pureEval) { - add(getNixDefExpr() + "/channels"); - add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); - add(rootChannelsDir()); - } + add(getNixDefExpr() + "/channels"); + add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); + add(rootChannelsDir()); return res; } diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index b915ba530..89a42caba 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -43,7 +43,7 @@ struct EvalSettings : Config bool & readOnlyMode; - Strings getDefaultNixPath() const; + static Strings getDefaultNixPath(); static bool isPseudoUrl(std::string_view s); From 87f8ff23fe68c597f2090d2c024e8336ba0d2f2d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 16:44:34 +0200 Subject: [PATCH 623/910] BasicClientConnection::handshake(): Don't send our version twice This was accidentally introduced in f71b4da0b3ed994f2bfc3764df6f524ebe72c4da. We didn't notice this because the version got interpreted by the daemon as the obsolete "CPU affinity will follow" field, and being non-zero, it would then read another integer for the ignored CPU affinity. --- src/libstore/worker-protocol-connection.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc index 072bae8da..3a640051e 100644 --- a/src/libstore/worker-protocol-connection.cc +++ b/src/libstore/worker-protocol-connection.cc @@ -152,7 +152,6 @@ WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, throw Error("Nix daemon protocol version not supported"); if (GET_PROTOCOL_MINOR(daemonVersion) < 10) throw Error("the Nix daemon version is too old"); - to << localVersion; return std::min(daemonVersion, localVersion); } From 83d585b423737591f865d905882122211995c308 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Jul 2024 15:19:17 +0200 Subject: [PATCH 624/910] C API: Make nix_err an enum This generally gives a better experience with bindings generators, possibly other tooling. A possible risk is that some generators may not represent unknown codes correctly. Rust bindgen by default generates suitable code: * a type alias nix_err = c_int * individual constants for the known enum values It does _not_ generate a closed type that can only hold the values that were known at code generation time. If this proves to be a problem, we could instead split the type: `typedef int nix_err;` for return values `enum nix_known_err` for code generation. This would complicate the interface, so let's not do it unless it is shown to be needed. --- src/libutil-c/nix_api_util.h | 76 +++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/libutil-c/nix_api_util.h b/src/libutil-c/nix_api_util.h index e0ca04e69..ad6f32859 100644 --- a/src/libutil-c/nix_api_util.h +++ b/src/libutil-c/nix_api_util.h @@ -56,47 +56,51 @@ extern "C" { * - NIX_ERR_KEY: A key error occurred (-3) * - NIX_ERR_NIX_ERROR: A generic Nix error occurred (-4) */ -typedef int nix_err; +enum nix_err { -/** - * @brief No error occurred. - * - * This error code is returned when no error has occurred during the function - * execution. - */ -#define NIX_OK 0 + /** + * @brief No error occurred. + * + * This error code is returned when no error has occurred during the function + * execution. + */ + NIX_OK = 0, -/** - * @brief An unknown error occurred. - * - * This error code is returned when an unknown error occurred during the - * function execution. - */ -#define NIX_ERR_UNKNOWN -1 + /** + * @brief An unknown error occurred. + * + * This error code is returned when an unknown error occurred during the + * function execution. + */ + NIX_ERR_UNKNOWN = -1, -/** - * @brief An overflow error occurred. - * - * This error code is returned when an overflow error occurred during the - * function execution. - */ -#define NIX_ERR_OVERFLOW -2 + /** + * @brief An overflow error occurred. + * + * This error code is returned when an overflow error occurred during the + * function execution. + */ + NIX_ERR_OVERFLOW = -2, -/** - * @brief A key error occurred. - * - * This error code is returned when a key error occurred during the function - * execution. - */ -#define NIX_ERR_KEY -3 + /** + * @brief A key error occurred. + * + * This error code is returned when a key error occurred during the function + * execution. + */ + NIX_ERR_KEY = -3, -/** - * @brief A generic Nix error occurred. - * - * This error code is returned when a generic Nix error occurred during the - * function execution. - */ -#define NIX_ERR_NIX_ERROR -4 + /** + * @brief A generic Nix error occurred. + * + * This error code is returned when a generic Nix error occurred during the + * function execution. + */ + NIX_ERR_NIX_ERROR = -4, + +}; + +typedef enum nix_err nix_err; /** * @brief This object stores error state. From f0a1c130a1a5543d09baf731f59845f8cff45b9f Mon Sep 17 00:00:00 2001 From: RTUnreal <22859658+RTUnreal@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:08:33 +0200 Subject: [PATCH 625/910] doc: add example usage for Gitea in tarball fetcher (#11116) Co-authored-by: Valentin Gagarin --- doc/manual/src/protocols/tarball-fetcher.md | 26 +++++++++++++++++++++ src/nix/flake.md | 2 ++ 2 files changed, 28 insertions(+) diff --git a/doc/manual/src/protocols/tarball-fetcher.md b/doc/manual/src/protocols/tarball-fetcher.md index 24ec7ae14..5cff05d66 100644 --- a/doc/manual/src/protocols/tarball-fetcher.md +++ b/doc/manual/src/protocols/tarball-fetcher.md @@ -41,4 +41,30 @@ Link: ///archive/.tar.gz +``` + +> **Example** +> +> +> ```nix +> # flake.nix +> { +> inputs = { +> foo.url = "https://gitea.example.org/some-person/some-flake/archive/main.tar.gz"; +> bar.url = "https://gitea.example.org/some-other-person/other-flake/archive/442793d9ec0584f6a6e82fa253850c8085bb150a.tar.gz"; +> qux = { +> url = "https://forgejo.example.org/another-person/some-non-flake-repo/archive/development.tar.gz"; +> flake = false; +> }; +> }; +> outputs = { foo, bar, qux }: { /* ... */ }; +> } +``` + [Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive diff --git a/src/nix/flake.md b/src/nix/flake.md index 2f43d0264..46d5a3867 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -259,6 +259,8 @@ Currently the `type` attribute can be one of the following: `.tgz`, `.tar.gz`, `.tar.xz`, `.tar.bz2` or `.tar.zst`), then the `tarball+` can be dropped. + This can also be used to set the location of gitea/forgejo branches. [See here](@docroot@/protocols/tarball-fetcher.md#gitea-and-forgejo-support) + * `file`: Plain files or directory tarballs, either over http(s) or from the local disk. From 57399bfc0e438d699700c6a4f2b6b63b157bcd2a Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Wed, 17 Jul 2024 23:32:27 -0400 Subject: [PATCH 626/910] Refactor unix domain socket store config (#11109) Following what is outlined in #10766 refactor the uds-remote-store such that the member variables (state) don't live in the store itself but in the config object. Additionally, the config object includes a new necessary constructor that takes a scheme & authority. Tests are commented out because of linking errors with the current config system. When there is a new config system we can reenable them. Co-authored-by: John Ericson --- src/libstore/dummy-store.cc | 20 ++-- src/libstore/http-binary-cache-store.cc | 50 +++++----- src/libstore/http-binary-cache-store.hh | 21 +++++ src/libstore/local-binary-cache-store.cc | 36 ++++--- src/libstore/local-binary-cache-store.hh | 21 +++++ src/libstore/local-fs-store.cc | 14 +++ src/libstore/local-fs-store.hh | 9 ++ src/libstore/local-overlay-store.cc | 6 +- src/libstore/local-overlay-store.hh | 21 +++-- src/libstore/local-store.cc | 33 +++---- src/libstore/local-store.hh | 5 + src/libstore/meson.build | 2 + src/libstore/s3-binary-cache-store.cc | 94 +++++-------------- src/libstore/s3-binary-cache-store.hh | 90 ++++++++++++++++++ src/libstore/ssh-store.cc | 55 +++++------ src/libstore/ssh-store.hh | 23 +++++ src/libstore/uds-remote-store.cc | 54 ++++++----- src/libstore/uds-remote-store.hh | 41 ++++++-- .../unit/libstore/http-binary-cache-store.cc | 21 +++++ .../unit/libstore/local-binary-cache-store.cc | 14 +++ tests/unit/libstore/local-overlay-store.cc | 34 +++++++ tests/unit/libstore/local-store.cc | 40 ++++++++ tests/unit/libstore/meson.build | 6 ++ tests/unit/libstore/s3-binary-cache-store.cc | 18 ++++ tests/unit/libstore/ssh-store.cc | 35 ++++++- tests/unit/libstore/uds-remote-store.cc | 23 +++++ 26 files changed, 571 insertions(+), 215 deletions(-) create mode 100644 src/libstore/http-binary-cache-store.hh create mode 100644 src/libstore/local-binary-cache-store.hh create mode 100644 tests/unit/libstore/http-binary-cache-store.cc create mode 100644 tests/unit/libstore/local-binary-cache-store.cc create mode 100644 tests/unit/libstore/local-overlay-store.cc create mode 100644 tests/unit/libstore/local-store.cc create mode 100644 tests/unit/libstore/s3-binary-cache-store.cc create mode 100644 tests/unit/libstore/uds-remote-store.cc diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index 17ebaace6..af0dd9092 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -6,6 +6,13 @@ namespace nix { struct DummyStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + DummyStoreConfig(std::string_view scheme, std::string_view authority, const Params & params) + : StoreConfig(params) + { + if (!authority.empty()) + throw UsageError("`%s` store URIs must not contain an authority part %s", scheme, authority); + } + const std::string name() override { return "Dummy Store"; } std::string doc() override @@ -19,16 +26,13 @@ struct DummyStoreConfig : virtual StoreConfig { struct DummyStore : public virtual DummyStoreConfig, public virtual Store { DummyStore(std::string_view scheme, std::string_view authority, const Params & params) - : DummyStore(params) - { - if (!authority.empty()) - throw UsageError("`%s` store URIs must not contain an authority part %s", scheme, authority); - } + : StoreConfig(params) + , DummyStoreConfig(scheme, authority, params) + , Store(params) + { } DummyStore(const Params & params) - : StoreConfig(params) - , DummyStoreConfig(params) - , Store(params) + : DummyStore("dummy", "", params) { } std::string getUri() override diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 3328caef9..49ec26b9f 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -1,4 +1,4 @@ -#include "binary-cache-store.hh" +#include "http-binary-cache-store.hh" #include "filetransfer.hh" #include "globals.hh" #include "nar-info-disk-cache.hh" @@ -8,26 +8,37 @@ namespace nix { MakeError(UploadToHTTP, Error); -struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig + +HttpBinaryCacheStoreConfig::HttpBinaryCacheStoreConfig( + std::string_view scheme, + std::string_view _cacheUri, + const Params & params) + : StoreConfig(params) + , BinaryCacheStoreConfig(params) + , cacheUri( + std::string { scheme } + + "://" + + (!_cacheUri.empty() + ? _cacheUri + : throw UsageError("`%s` Store requires a non-empty authority in Store URL", scheme))) { - using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + while (!cacheUri.empty() && cacheUri.back() == '/') + cacheUri.pop_back(); +} - const std::string name() override { return "HTTP Binary Cache Store"; } - std::string doc() override - { - return - #include "http-binary-cache-store.md" - ; - } -}; +std::string HttpBinaryCacheStoreConfig::doc() +{ + return + #include "http-binary-cache-store.md" + ; +} + class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore { private: - Path cacheUri; - struct State { bool enabled = true; @@ -40,23 +51,14 @@ public: HttpBinaryCacheStore( std::string_view scheme, - PathView _cacheUri, + PathView cacheUri, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) - , HttpBinaryCacheStoreConfig(params) + , HttpBinaryCacheStoreConfig(scheme, cacheUri, params) , Store(params) , BinaryCacheStore(params) - , cacheUri( - std::string { scheme } - + "://" - + (!_cacheUri.empty() - ? _cacheUri - : throw UsageError("`%s` Store requires a non-empty authority in Store URL", scheme))) { - while (!cacheUri.empty() && cacheUri.back() == '/') - cacheUri.pop_back(); - diskCache = getNarInfoDiskCache(); } diff --git a/src/libstore/http-binary-cache-store.hh b/src/libstore/http-binary-cache-store.hh new file mode 100644 index 000000000..230e52b33 --- /dev/null +++ b/src/libstore/http-binary-cache-store.hh @@ -0,0 +1,21 @@ +#include "binary-cache-store.hh" + +namespace nix { + +struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +{ + using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + + HttpBinaryCacheStoreConfig(std::string_view scheme, std::string_view _cacheUri, const Params & params); + + Path cacheUri; + + const std::string name() override + { + return "HTTP Binary Cache Store"; + } + + std::string doc() override; +}; + +} diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index aa2efdb8f..106663132 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -1,4 +1,4 @@ -#include "binary-cache-store.hh" +#include "local-binary-cache-store.hh" #include "globals.hh" #include "nar-info-disk-cache.hh" #include "signals.hh" @@ -7,28 +7,27 @@ namespace nix { -struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +LocalBinaryCacheStoreConfig::LocalBinaryCacheStoreConfig( + std::string_view scheme, + PathView binaryCacheDir, + const Params & params) + : StoreConfig(params) + , BinaryCacheStoreConfig(params) + , binaryCacheDir(binaryCacheDir) { - 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 +std::string LocalBinaryCacheStoreConfig::doc() { -private: + return + #include "local-binary-cache-store.md" + ; +} - Path binaryCacheDir; - -public: +struct LocalBinaryCacheStore : virtual LocalBinaryCacheStoreConfig, virtual BinaryCacheStore +{ /** * @param binaryCacheDir `file://` is a short-hand for `file:///` * for now. @@ -39,10 +38,9 @@ public: const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) - , LocalBinaryCacheStoreConfig(params) + , LocalBinaryCacheStoreConfig(scheme, binaryCacheDir, params) , Store(params) , BinaryCacheStore(params) - , binaryCacheDir(binaryCacheDir) { } diff --git a/src/libstore/local-binary-cache-store.hh b/src/libstore/local-binary-cache-store.hh new file mode 100644 index 000000000..7701eb38b --- /dev/null +++ b/src/libstore/local-binary-cache-store.hh @@ -0,0 +1,21 @@ +#include "binary-cache-store.hh" + +namespace nix { + +struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +{ + using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + + LocalBinaryCacheStoreConfig(std::string_view scheme, PathView binaryCacheDir, const Params & params); + + Path binaryCacheDir; + + const std::string name() override + { + return "Local Binary Cache Store"; + } + + std::string doc() override; +}; + +} diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc index 843c0d288..5449b20eb 100644 --- a/src/libstore/local-fs-store.cc +++ b/src/libstore/local-fs-store.cc @@ -8,6 +8,20 @@ namespace nix { +LocalFSStoreConfig::LocalFSStoreConfig(PathView rootDir, const Params & params) + : StoreConfig(params) + // Default `?root` from `rootDir` if non set + // FIXME don't duplicate description once we don't have root setting + , rootDir{ + this, + !rootDir.empty() && params.count("root") == 0 + ? (std::optional{rootDir}) + : std::nullopt, + "root", + "Directory prefixed to all other paths."} +{ +} + LocalFSStore::LocalFSStore(const Params & params) : Store(params) { diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index 8fb081200..9bb569f0b 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -11,6 +11,15 @@ struct LocalFSStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + /** + * Used to override the `root` settings. Can't be done via modifying + * `params` reliably because this parameter is unused except for + * passing to base class constructors. + * + * @todo Make this less error-prone with new store settings system. + */ + LocalFSStoreConfig(PathView path, const Params & params); + const OptionalPathSetting rootDir{this, std::nullopt, "root", "Directory prefixed to all other paths."}; diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 598415db8..ec2c5f4e9 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -18,11 +18,11 @@ Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) { return upperLayer + "/" + path.to_string(); } -LocalOverlayStore::LocalOverlayStore(const Params & params) +LocalOverlayStore::LocalOverlayStore(std::string_view scheme, PathView path, const Params & params) : StoreConfig(params) - , LocalFSStoreConfig(params) + , LocalFSStoreConfig(path, params) , LocalStoreConfig(params) - , LocalOverlayStoreConfig(params) + , LocalOverlayStoreConfig(scheme, path, params) , Store(params) , LocalFSStore(params) , LocalStore(params) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 35a301013..a3f15e484 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -8,11 +8,16 @@ namespace nix { struct LocalOverlayStoreConfig : virtual LocalStoreConfig { LocalOverlayStoreConfig(const StringMap & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , LocalStoreConfig(params) + : LocalOverlayStoreConfig("local-overlay", "", params) { } + LocalOverlayStoreConfig(std::string_view scheme, PathView path, const Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(path, params) + , LocalStoreConfig(scheme, path, params) + { + } + const Setting lowerStoreUri{(StoreConfig*) this, "", "lower-store", R"( [Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) @@ -90,15 +95,13 @@ class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual ref lowerStore; public: - LocalOverlayStore(const Params & params); - - LocalOverlayStore(std::string_view scheme, PathView path, const Params & params) - : LocalOverlayStore(params) + LocalOverlayStore(const Params & params) + : LocalOverlayStore("local-overlay", "", params) { - if (!path.empty()) - throw UsageError("local-overlay:// store url doesn't support path part, only scheme and query params"); } + LocalOverlayStore(std::string_view scheme, PathView path, const Params & params); + static std::set uriSchemes() { return { "local-overlay" }; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 82b70ff21..819cee345 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -56,6 +56,15 @@ namespace nix { +LocalStoreConfig::LocalStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(authority, params) +{ +} + std::string LocalStoreConfig::doc() { return @@ -183,10 +192,13 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd) } } -LocalStore::LocalStore(const Params & params) +LocalStore::LocalStore( + std::string_view scheme, + PathView path, + const Params & params) : StoreConfig(params) - , LocalFSStoreConfig(params) - , LocalStoreConfig(params) + , LocalFSStoreConfig(path, params) + , LocalStoreConfig(scheme, path, params) , Store(params) , LocalFSStore(params) , dbDir(stateDir + "/db") @@ -465,19 +477,8 @@ LocalStore::LocalStore(const Params & params) } -LocalStore::LocalStore( - std::string_view scheme, - PathView path, - const Params & _params) - : LocalStore([&]{ - // Default `?root` from `path` if non set - if (!path.empty() && _params.count("root") == 0) { - auto params = _params; - params.insert_or_assign("root", std::string { path }); - return params; - } - return _params; - }()) +LocalStore::LocalStore(const Params & params) + : LocalStore("local", "", params) { } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index b0a0def9a..026729bf2 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -38,6 +38,11 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig { using LocalFSStoreConfig::LocalFSStoreConfig; + LocalStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params); + Setting requireSigs{this, settings.requireSigs, "require-sigs", diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 297bf7187..29ee95b75 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -243,10 +243,12 @@ headers = [config_h] + files( 'filetransfer.hh', 'gc-store.hh', 'globals.hh', + 'http-binary-cache-store.hh', 'indirect-root-store.hh', 'keys.hh', 'legacy-ssh-store.hh', 'length-prefixed-protocol-helper.hh', + 'local-binary-cache-store.hh', 'local-fs-store.hh', 'local-overlay-store.hh', 'local-store.hh', diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 27013c0f1..d865684d7 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -1,5 +1,7 @@ #if ENABLE_S3 +#include + #include "s3.hh" #include "s3-binary-cache-store.hh" #include "nar-info.hh" @@ -190,76 +192,31 @@ S3BinaryCacheStore::S3BinaryCacheStore(const Params & params) , BinaryCacheStore(params) { } -struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig + +S3BinaryCacheStoreConfig::S3BinaryCacheStoreConfig( + std::string_view uriScheme, + std::string_view bucketName, + const Params & params) + : StoreConfig(params) + , BinaryCacheStoreConfig(params) + , bucketName(bucketName) { - using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + // Don't want to use use AWS SDK in header, so we check the default + // here. TODO do this better after we overhaul the store settings + // system. + assert(std::string{defaultRegion} == std::string{Aws::Region::US_EAST_1}); - const Setting profile{this, "", "profile", - R"( - The name of the AWS configuration profile to use. By default - Nix will use the `default` profile. - )"}; + if (bucketName.empty()) + throw UsageError("`%s` store requires a bucket name in its Store URI", uriScheme); +} - const Setting region{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. - )"}; +std::string S3BinaryCacheStoreConfig::doc() +{ + return + #include "s3-binary-cache-store.md" + ; +} - const Setting scheme{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{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{this, "", "narinfo-compression", - "Compression method for `.narinfo` files."}; - - const Setting lsCompression{this, "", "ls-compression", - "Compression method for `.ls` files."}; - - const Setting logCompression{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{ - this, false, "multipart-upload", - "Whether to use multi-part uploads."}; - - const Setting bufferSize{ - 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 { @@ -275,15 +232,12 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) - , S3BinaryCacheStoreConfig(params) + , S3BinaryCacheStoreConfig(uriScheme, bucketName, params) , Store(params) , BinaryCacheStore(params) , S3BinaryCacheStore(params) - , bucketName(bucketName) , s3Helper(profile, region, scheme, endpoint) { - if (bucketName.empty()) - throw UsageError("`%s` store requires a bucket name in its Store URI", uriScheme); diskCache = getNarInfoDiskCache(); } diff --git a/src/libstore/s3-binary-cache-store.hh b/src/libstore/s3-binary-cache-store.hh index c62ea5147..bca5196cd 100644 --- a/src/libstore/s3-binary-cache-store.hh +++ b/src/libstore/s3-binary-cache-store.hh @@ -7,6 +7,96 @@ namespace nix { +struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +{ + std::string bucketName; + + using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + + S3BinaryCacheStoreConfig(std::string_view uriScheme, std::string_view bucketName, const Params & params); + + const Setting profile{ + this, + "", + "profile", + R"( + The name of the AWS configuration profile to use. By default + Nix will use the `default` profile. + )"}; + +protected: + + constexpr static const char * defaultRegion = "us-east-1"; + +public: + + const Setting region{ + this, + defaultRegion, + "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{ + 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{ + 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{ + this, "", "narinfo-compression", "Compression method for `.narinfo` files."}; + + const Setting lsCompression{this, "", "ls-compression", "Compression method for `.ls` files."}; + + const Setting logCompression{ + 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{this, false, "multipart-upload", "Whether to use multi-part uploads."}; + + const Setting bufferSize{ + 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; +}; + class S3BinaryCacheStore : public virtual BinaryCacheStore { protected: diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index b21c22d7e..d63995bf3 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -89,43 +89,32 @@ protected: }; }; -struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfig + +MountedSSHStoreConfig::MountedSSHStoreConfig(StringMap params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(params) + , SSHStoreConfig(params) + , LocalFSStoreConfig(params) { - using SSHStoreConfig::SSHStoreConfig; - using LocalFSStoreConfig::LocalFSStoreConfig; +} - MountedSSHStoreConfig(StringMap params) - : StoreConfig(params) - , RemoteStoreConfig(params) - , CommonSSHStoreConfig(params) - , SSHStoreConfig(params) - , LocalFSStoreConfig(params) - { - } +MountedSSHStoreConfig::MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) + , SSHStoreConfig(params) + , LocalFSStoreConfig(params) +{ +} - MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params) - : StoreConfig(params) - , RemoteStoreConfig(params) - , CommonSSHStoreConfig(scheme, host, params) - , SSHStoreConfig(params) - , LocalFSStoreConfig(params) - { - } +std::string MountedSSHStoreConfig::doc() +{ + return + #include "mounted-ssh-store.md" + ; +} - const std::string name() override { return "Experimental SSH Store with filesystem mounted"; } - - std::string doc() override - { - return - #include "mounted-ssh-store.md" - ; - } - - std::optional experimentalFeature() const override - { - return ExperimentalFeature::MountedSSHStore; - } -}; /** * The mounted ssh store assumes that filesystems on the remote host are diff --git a/src/libstore/ssh-store.hh b/src/libstore/ssh-store.hh index 6ef2219a2..feb57ccba 100644 --- a/src/libstore/ssh-store.hh +++ b/src/libstore/ssh-store.hh @@ -3,6 +3,7 @@ #include "common-ssh-store-config.hh" #include "store-api.hh" +#include "local-fs-store.hh" #include "remote-store.hh" namespace nix { @@ -25,4 +26,26 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig std::string doc() override; }; +struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfig +{ + using LocalFSStoreConfig::LocalFSStoreConfig; + using SSHStoreConfig::SSHStoreConfig; + + MountedSSHStoreConfig(StringMap params); + + MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params); + + const std::string name() override + { + return "Experimental SSH Store with filesystem mounted"; + } + + std::string doc() override; + + std::optional experimentalFeature() const override + { + return ExperimentalFeature::MountedSSHStore; + } +}; + } diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index 499f76967..3c445eb13 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -2,10 +2,8 @@ #include "unix-domain-socket.hh" #include "worker-protocol.hh" -#include #include #include -#include #include #include @@ -19,6 +17,21 @@ namespace nix { +UDSRemoteStoreConfig::UDSRemoteStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(params) + , RemoteStoreConfig(params) + , path{authority.empty() ? settings.nixDaemonSocketFile : authority} +{ + if (scheme != UDSRemoteStoreConfig::scheme) { + throw UsageError("Scheme must be 'unix'"); + } +} + + std::string UDSRemoteStoreConfig::doc() { return @@ -27,11 +40,20 @@ std::string UDSRemoteStoreConfig::doc() } +// A bit gross that we now pass empty string but this is knowing that +// empty string will later default to the same nixDaemonSocketFile. Why +// don't we just wire it all through? I believe there are cases where it +// will live reload so we want to continue to account for that. UDSRemoteStore::UDSRemoteStore(const Params & params) + : UDSRemoteStore(scheme, "", params) +{} + + +UDSRemoteStore::UDSRemoteStore(std::string_view scheme, std::string_view authority, const Params & params) : StoreConfig(params) , LocalFSStoreConfig(params) , RemoteStoreConfig(params) - , UDSRemoteStoreConfig(params) + , UDSRemoteStoreConfig(scheme, authority, params) , Store(params) , LocalFSStore(params) , RemoteStore(params) @@ -39,25 +61,15 @@ UDSRemoteStore::UDSRemoteStore(const Params & params) } -UDSRemoteStore::UDSRemoteStore( - std::string_view scheme, - PathView socket_path, - const Params & params) - : UDSRemoteStore(params) -{ - if (!socket_path.empty()) - path.emplace(socket_path); -} - - std::string UDSRemoteStore::getUri() { - if (path) { - return std::string("unix://") + *path; - } else { - // unix:// with no path also works. Change what we return? - return "daemon"; - } + return path == settings.nixDaemonSocketFile + ? // FIXME: Not clear why we return daemon here and not default + // to settings.nixDaemonSocketFile + // + // unix:// with no path also works. Change what we return? + "daemon" + : std::string(scheme) + "://" + path; } @@ -74,7 +86,7 @@ ref UDSRemoteStore::openConnection() /* Connect to a daemon that does the privileged work for us. */ conn->fd = createUnixDomainSocket(); - nix::connect(toSocket(conn->fd.get()), path ? *path : settings.nixDaemonSocketFile); + nix::connect(toSocket(conn->fd.get()), path); conn->from.fd = conn->fd.get(); conn->to.fd = conn->fd.get(); diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index 6f0494bb6..0e9542eab 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -9,16 +9,33 @@ namespace nix { struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig { - UDSRemoteStoreConfig(const Params & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , RemoteStoreConfig(params) - { - } + // TODO(fzakaria): Delete this constructor once moved over to the factory pattern + // outlined in https://github.com/NixOS/nix/issues/10766 + using LocalFSStoreConfig::LocalFSStoreConfig; + using RemoteStoreConfig::RemoteStoreConfig; + + /** + * @param authority is the socket path. + */ + UDSRemoteStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params); const std::string name() override { return "Local Daemon Store"; } std::string doc() override; + + /** + * The path to the unix domain socket. + * + * The default is `settings.nixDaemonSocketFile`, but we don't write + * that below, instead putting in the constructor. + */ + Path path; + +protected: + static constexpr char const * scheme = "unix"; }; class UDSRemoteStore : public virtual UDSRemoteStoreConfig @@ -27,16 +44,23 @@ class UDSRemoteStore : public virtual UDSRemoteStoreConfig { public: + /** + * @deprecated This is the old API to construct the store. + */ UDSRemoteStore(const Params & params); + + /** + * @param authority is the socket path. + */ UDSRemoteStore( std::string_view scheme, - PathView path, + std::string_view authority, const Params & params); std::string getUri() override; static std::set uriSchemes() - { return {"unix"}; } + { return {scheme}; } ref getFSAccessor(bool requireValidPath = true) override { return LocalFSStore::getFSAccessor(requireValidPath); } @@ -63,7 +87,6 @@ private: }; ref openConnection() override; - std::optional path; }; } diff --git a/tests/unit/libstore/http-binary-cache-store.cc b/tests/unit/libstore/http-binary-cache-store.cc new file mode 100644 index 000000000..1e415f625 --- /dev/null +++ b/tests/unit/libstore/http-binary-cache-store.cc @@ -0,0 +1,21 @@ +#include + +#include "http-binary-cache-store.hh" + +namespace nix { + +TEST(HttpBinaryCacheStore, constructConfig) +{ + HttpBinaryCacheStoreConfig config{"http", "foo.bar.baz", {}}; + + EXPECT_EQ(config.cacheUri, "http://foo.bar.baz"); +} + +TEST(HttpBinaryCacheStore, constructConfigNoTrailingSlash) +{ + HttpBinaryCacheStoreConfig config{"https", "foo.bar.baz/a/b/", {}}; + + EXPECT_EQ(config.cacheUri, "https://foo.bar.baz/a/b"); +} + +} // namespace nix diff --git a/tests/unit/libstore/local-binary-cache-store.cc b/tests/unit/libstore/local-binary-cache-store.cc new file mode 100644 index 000000000..2e840228d --- /dev/null +++ b/tests/unit/libstore/local-binary-cache-store.cc @@ -0,0 +1,14 @@ +#include + +#include "local-binary-cache-store.hh" + +namespace nix { + +TEST(LocalBinaryCacheStore, constructConfig) +{ + LocalBinaryCacheStoreConfig config{"local", "/foo/bar/baz", {}}; + + EXPECT_EQ(config.binaryCacheDir, "/foo/bar/baz"); +} + +} // namespace nix diff --git a/tests/unit/libstore/local-overlay-store.cc b/tests/unit/libstore/local-overlay-store.cc new file mode 100644 index 000000000..b34ca9237 --- /dev/null +++ b/tests/unit/libstore/local-overlay-store.cc @@ -0,0 +1,34 @@ +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include + +# include "local-overlay-store.hh" + +namespace nix { + +TEST(LocalOverlayStore, constructConfig_rootQueryParam) +{ + LocalOverlayStoreConfig config{ + "local-overlay", + "", + { + { + "root", + "/foo/bar", + }, + }, + }; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +TEST(LocalOverlayStore, constructConfig_rootPath) +{ + LocalOverlayStoreConfig config{"local-overlay", "/foo/bar", {}}; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +} // namespace nix +#endif diff --git a/tests/unit/libstore/local-store.cc b/tests/unit/libstore/local-store.cc new file mode 100644 index 000000000..abc3ea796 --- /dev/null +++ b/tests/unit/libstore/local-store.cc @@ -0,0 +1,40 @@ +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include + +# include "local-store.hh" + +// Needed for template specialisations. This is not good! When we +// overhaul how store configs work, this should be fixed. +# include "args.hh" +# include "config-impl.hh" +# include "abstract-setting-to-json.hh" + +namespace nix { + +TEST(LocalStore, constructConfig_rootQueryParam) +{ + LocalStoreConfig config{ + "local", + "", + { + { + "root", + "/foo/bar", + }, + }, + }; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +TEST(LocalStore, constructConfig_rootPath) +{ + LocalStoreConfig config{"local", "/foo/bar", {}}; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +} // namespace nix +#endif diff --git a/tests/unit/libstore/meson.build b/tests/unit/libstore/meson.build index 41b2fb0ab..8534ba8c5 100644 --- a/tests/unit/libstore/meson.build +++ b/tests/unit/libstore/meson.build @@ -58,7 +58,11 @@ sources = files( 'derivation.cc', 'derived-path.cc', 'downstream-placeholder.cc', + 'http-binary-cache-store.cc', 'legacy-ssh-store.cc', + 'local-binary-cache-store.cc', + 'local-overlay-store.cc', + 'local-store.cc', 'machines.cc', 'nar-info-disk-cache.cc', 'nar-info.cc', @@ -67,9 +71,11 @@ sources = files( 'path-info.cc', 'path.cc', 'references.cc', + 's3-binary-cache-store.cc', 'serve-protocol.cc', 'ssh-store.cc', 'store-reference.cc', + 'uds-remote-store.cc', 'worker-protocol.cc', ) diff --git a/tests/unit/libstore/s3-binary-cache-store.cc b/tests/unit/libstore/s3-binary-cache-store.cc new file mode 100644 index 000000000..7aa5f2f2c --- /dev/null +++ b/tests/unit/libstore/s3-binary-cache-store.cc @@ -0,0 +1,18 @@ +#if ENABLE_S3 + +# include + +# include "s3-binary-cache-store.hh" + +namespace nix { + +TEST(S3BinaryCacheStore, constructConfig) +{ + S3BinaryCacheStoreConfig config{"s3", "foobar", {}}; + + EXPECT_EQ(config.bucketName, "foobar"); +} + +} // namespace nix + +#endif diff --git a/tests/unit/libstore/ssh-store.cc b/tests/unit/libstore/ssh-store.cc index 7010ad157..b853a5f1f 100644 --- a/tests/unit/libstore/ssh-store.cc +++ b/tests/unit/libstore/ssh-store.cc @@ -1,6 +1,9 @@ -#include +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include -#include "ssh-store.hh" +# include "ssh-store.hh" namespace nix { @@ -15,7 +18,9 @@ TEST(SSHStore, constructConfig) // TODO #11106, no more split on space "foo bar", }, - }}; + }, + }; + EXPECT_EQ( config.remoteProgram.get(), (Strings{ @@ -23,4 +28,28 @@ TEST(SSHStore, constructConfig) "bar", })); } + +TEST(MountedSSHStore, constructConfig) +{ + MountedSSHStoreConfig config{ + "mounted-ssh", + "localhost", + StoreConfig::Params{ + { + "remote-program", + // TODO #11106, no more split on space + "foo bar", + }, + }, + }; + + EXPECT_EQ( + config.remoteProgram.get(), + (Strings{ + "foo", + "bar", + })); } + +} +#endif diff --git a/tests/unit/libstore/uds-remote-store.cc b/tests/unit/libstore/uds-remote-store.cc new file mode 100644 index 000000000..5ccb20871 --- /dev/null +++ b/tests/unit/libstore/uds-remote-store.cc @@ -0,0 +1,23 @@ +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include + +# include "uds-remote-store.hh" + +namespace nix { + +TEST(UDSRemoteStore, constructConfig) +{ + UDSRemoteStoreConfig config{"unix", "/tmp/socket", {}}; + + EXPECT_EQ(config.path, "/tmp/socket"); +} + +TEST(UDSRemoteStore, constructConfigWrongScheme) +{ + EXPECT_THROW(UDSRemoteStoreConfig("http", "/tmp/socket", {}), UsageError); +} + +} // namespace nix +#endif From 2aa9cf34dda0115f07bc7aef020ffb0cda8944e0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Jul 2024 23:26:39 -0400 Subject: [PATCH 627/910] Move `uriSchemes` to `*StoreConfig` It is a property of the configuration of a store --- how a store URL is parsed into a store config, not a store itself. Progress towards #10766 --- src/libstore/dummy-store.cc | 8 ++++---- src/libstore/http-binary-cache-store.cc | 8 -------- src/libstore/http-binary-cache-store.hh | 9 +++++++++ src/libstore/legacy-ssh-store.hh | 4 ++-- src/libstore/local-binary-cache-store.cc | 4 +--- src/libstore/local-binary-cache-store.hh | 2 ++ src/libstore/local-overlay-store.hh | 10 +++++----- src/libstore/local-store.hh | 6 +++--- src/libstore/s3-binary-cache-store.cc | 3 --- src/libstore/s3-binary-cache-store.hh | 5 +++++ src/libstore/ssh-store.cc | 7 ------- src/libstore/ssh-store.hh | 10 ++++++++++ src/libstore/store-api.hh | 6 +++++- src/libstore/uds-remote-store.hh | 7 ++++--- 14 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index af0dd9092..c1e871e93 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -21,6 +21,10 @@ struct DummyStoreConfig : virtual StoreConfig { #include "dummy-store.md" ; } + + static std::set uriSchemes() { + return {"dummy"}; + } }; struct DummyStore : public virtual DummyStoreConfig, public virtual Store @@ -54,10 +58,6 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store return Trusted; } - static std::set uriSchemes() { - return {"dummy"}; - } - std::optional queryPathFromHashPart(const std::string & hashPart) override { unsupported("queryPathFromHashPart"); } diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 49ec26b9f..b15ef4e4c 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -83,14 +83,6 @@ public: } } - static std::set uriSchemes() - { - static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; - auto ret = std::set({"http", "https"}); - if (forceHttp) ret.insert("file"); - return ret; - } - protected: void maybeDisable() diff --git a/src/libstore/http-binary-cache-store.hh b/src/libstore/http-binary-cache-store.hh index 230e52b33..d2fc43210 100644 --- a/src/libstore/http-binary-cache-store.hh +++ b/src/libstore/http-binary-cache-store.hh @@ -15,6 +15,15 @@ struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig return "HTTP Binary Cache Store"; } + static std::set uriSchemes() + { + static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; + auto ret = std::set({"http", "https"}); + if (forceHttp) + ret.insert("file"); + return ret; + } + std::string doc() override; }; diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index f26651898..b541455b4 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -26,6 +26,8 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig const std::string name() override { return "SSH Store"; } + static std::set uriSchemes() { return {"ssh"}; } + std::string doc() override; }; @@ -46,8 +48,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor SSHMaster master; - static std::set uriSchemes() { return {"ssh"}; } - LegacySSHStore( std::string_view scheme, std::string_view host, diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 106663132..dcc6affe4 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -51,8 +51,6 @@ struct LocalBinaryCacheStore : virtual LocalBinaryCacheStoreConfig, virtual Bina return "file://" + binaryCacheDir; } - static std::set uriSchemes(); - protected: bool fileExists(const std::string & path) override; @@ -121,7 +119,7 @@ bool LocalBinaryCacheStore::fileExists(const std::string & path) return pathExists(binaryCacheDir + "/" + path); } -std::set LocalBinaryCacheStore::uriSchemes() +std::set LocalBinaryCacheStoreConfig::uriSchemes() { if (getEnv("_NIX_FORCE_HTTP") == "1") return {}; diff --git a/src/libstore/local-binary-cache-store.hh b/src/libstore/local-binary-cache-store.hh index 7701eb38b..997e8ecbb 100644 --- a/src/libstore/local-binary-cache-store.hh +++ b/src/libstore/local-binary-cache-store.hh @@ -15,6 +15,8 @@ struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig return "Local Binary Cache Store"; } + static std::set uriSchemes(); + std::string doc() override; }; diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index a3f15e484..63628abed 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -63,6 +63,11 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig return ExperimentalFeature::LocalOverlayStore; } + static std::set uriSchemes() + { + return { "local-overlay" }; + } + std::string doc() override; protected: @@ -102,11 +107,6 @@ public: LocalOverlayStore(std::string_view scheme, PathView path, const Params & params); - static std::set uriSchemes() - { - return { "local-overlay" }; - } - std::string getUri() override { return "local-overlay://"; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 026729bf2..a03cfc03b 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -67,6 +67,9 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig const std::string name() override { return "Local Store"; } + static std::set uriSchemes() + { return {"local"}; } + std::string doc() override; }; @@ -149,9 +152,6 @@ public: ~LocalStore(); - static std::set uriSchemes() - { return {"local"}; } - /** * Implementations of abstract store API methods. */ diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index d865684d7..1a0ec1111 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -475,9 +475,6 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual { return std::nullopt; } - - static std::set uriSchemes() { return {"s3"}; } - }; static RegisterStoreImplementation regS3BinaryCacheStore; diff --git a/src/libstore/s3-binary-cache-store.hh b/src/libstore/s3-binary-cache-store.hh index bca5196cd..7d303a115 100644 --- a/src/libstore/s3-binary-cache-store.hh +++ b/src/libstore/s3-binary-cache-store.hh @@ -94,6 +94,11 @@ public: return "S3 Binary Cache Store"; } + static std::set uriSchemes() + { + return {"s3"}; + } + std::string doc() override; }; diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index d63995bf3..954a97467 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -47,8 +47,6 @@ public: { } - static std::set uriSchemes() { return {"ssh-ng"}; } - std::string getUri() override { return *uriSchemes().begin() + "://" + host; @@ -154,11 +152,6 @@ public: }; } - static std::set uriSchemes() - { - return {"mounted-ssh-ng"}; - } - std::string getUri() override { return *uriSchemes().begin() + "://" + host; diff --git a/src/libstore/ssh-store.hh b/src/libstore/ssh-store.hh index feb57ccba..29a2a8b2c 100644 --- a/src/libstore/ssh-store.hh +++ b/src/libstore/ssh-store.hh @@ -23,6 +23,11 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig return "Experimental SSH Store"; } + static std::set uriSchemes() + { + return {"ssh-ng"}; + } + std::string doc() override; }; @@ -40,6 +45,11 @@ struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfi return "Experimental SSH Store with filesystem mounted"; } + static std::set uriSchemes() + { + return {"mounted-ssh-ng"}; + } + std::string doc() override; std::optional experimentalFeature() const override diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 749d7ea09..7d5f533c5 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -216,6 +216,10 @@ public: virtual ~Store() { } + /** + * @todo move to `StoreConfig` one we store enough information in + * those to recover the scheme and authority in all cases. + */ virtual std::string getUri() = 0; /** @@ -897,7 +901,7 @@ struct Implementations { if (!registered) registered = new std::vector(); StoreFactory factory{ - .uriSchemes = T::uriSchemes(), + .uriSchemes = TConfig::uriSchemes(), .create = ([](auto scheme, auto uri, auto & params) -> std::shared_ptr diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index 0e9542eab..a8e571664 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -36,6 +36,10 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon protected: static constexpr char const * scheme = "unix"; + +public: + static std::set uriSchemes() + { return {scheme}; } }; class UDSRemoteStore : public virtual UDSRemoteStoreConfig @@ -59,9 +63,6 @@ public: std::string getUri() override; - static std::set uriSchemes() - { return {scheme}; } - ref getFSAccessor(bool requireValidPath = true) override { return LocalFSStore::getFSAccessor(requireValidPath); } From c1d5cf6f349a459c8b5dee9dad271a529cee6679 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 15:27:15 +0200 Subject: [PATCH 628/910] Factor out commonality between WorkerProto::Basic{Client,Server}Connection This also renames clientVersion and daemonVersion to the more correct protoVersion (since it's the version agreed to by both sides). --- src/libstore/daemon.cc | 29 +++-- src/libstore/daemon.hh | 4 +- src/libstore/remote-store.cc | 38 +++--- .../unix/build/local-derivation-goal.cc | 7 +- src/libstore/worker-protocol-connection.cc | 20 +-- src/libstore/worker-protocol-connection.hh | 114 ++++++------------ src/libstore/worker-protocol.hh | 1 + src/nix/unix/daemon.cc | 17 ++- 8 files changed, 100 insertions(+), 130 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 5c5080f8a..fc14eda3c 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1020,8 +1020,8 @@ static void performOp(TunnelLogger * logger, ref store, void processConnection( ref store, - FdSource & from, - FdSink & to, + FdSource && from, + FdSink && to, TrustedFlag trusted, RecursiveFlag recursive) { @@ -1037,7 +1037,12 @@ void processConnection( if (clientVersion < 0x10a) throw Error("the Nix client version is too old"); - auto tunnelLogger = new TunnelLogger(to, clientVersion); + WorkerProto::BasicServerConnection conn; + conn.to = std::move(to); + conn.from = std::move(from); + conn.protoVersion = clientVersion; + + auto tunnelLogger = new TunnelLogger(conn.to, clientVersion); auto prevLogger = nix::logger; // FIXME if (!recursive) @@ -1050,12 +1055,6 @@ void processConnection( printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); }); - WorkerProto::BasicServerConnection conn { - .to = to, - .from = from, - .clientVersion = clientVersion, - }; - conn.postHandshake(*store, { .daemonNixVersion = nixVersion, // We and the underlying store both need to trust the client for @@ -1071,13 +1070,13 @@ void processConnection( try { tunnelLogger->stopWork(); - to.flush(); + conn.to.flush(); /* Process client requests. */ while (true) { WorkerProto::Op op; try { - op = (enum WorkerProto::Op) readInt(from); + op = (enum WorkerProto::Op) readInt(conn.from); } catch (Interrupted & e) { break; } catch (EndOfFile & e) { @@ -1091,7 +1090,7 @@ void processConnection( debug("performing daemon worker op: %d", op); try { - performOp(tunnelLogger, store, trusted, recursive, clientVersion, from, to, op); + performOp(tunnelLogger, store, trusted, recursive, clientVersion, conn.from, conn.to, op); } catch (Error & e) { /* If we're not in a state where we can send replies, then something went wrong processing the input of the @@ -1107,19 +1106,19 @@ void processConnection( throw; } - to.flush(); + conn.to.flush(); assert(!tunnelLogger->state_.lock()->canSendStderr); }; } catch (Error & e) { tunnelLogger->stopWork(&e); - to.flush(); + conn.to.flush(); return; } catch (std::exception & e) { auto ex = Error(e.what()); tunnelLogger->stopWork(&ex); - to.flush(); + conn.to.flush(); return; } } diff --git a/src/libstore/daemon.hh b/src/libstore/daemon.hh index 1964c0d99..a8ce32d8d 100644 --- a/src/libstore/daemon.hh +++ b/src/libstore/daemon.hh @@ -10,8 +10,8 @@ enum RecursiveFlag : bool { NotRecursive = false, Recursive = true }; void processConnection( ref store, - FdSource & from, - FdSink & to, + FdSource && from, + FdSink && to, TrustedFlag trusted, RecursiveFlag recursive); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 6e8931ca2..ebb0864c5 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -73,7 +73,7 @@ void RemoteStore::initConnection(Connection & conn) StringSink saved; TeeSource tee(conn.from, saved); try { - conn.daemonVersion = WorkerProto::BasicClientConnection::handshake( + conn.protoVersion = WorkerProto::BasicClientConnection::handshake( conn.to, tee, PROTOCOL_VERSION); } catch (SerialisationError & e) { /* In case the other side is waiting for our input, close @@ -115,7 +115,7 @@ void RemoteStore::setOptions(Connection & conn) << settings.buildCores << settings.useSubstitutes; - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 12) { std::map overrides; settings.getSettings(overrides, true); // libstore settings fileTransferSettings.getSettings(overrides, true); @@ -175,7 +175,7 @@ bool RemoteStore::isValidPathUncached(const StorePath & path) StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute) { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 12) { StorePathSet res; for (auto & i : paths) if (isValidPath(i)) res.insert(i); @@ -198,7 +198,7 @@ StorePathSet RemoteStore::queryAllValidPaths() StorePathSet RemoteStore::querySubstitutablePaths(const StorePathSet & paths) { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 12) { StorePathSet res; for (auto & i : paths) { conn->to << WorkerProto::Op::HasSubstitutes << printStorePath(i); @@ -221,7 +221,7 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, S auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 12) { for (auto & i : pathsMap) { SubstitutablePathInfo info; @@ -241,7 +241,7 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, S } else { conn->to << WorkerProto::Op::QuerySubstitutablePathInfos; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 22) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 22) { StorePathSet paths; for (auto & path : pathsMap) paths.insert(path.first); @@ -368,7 +368,7 @@ ref RemoteStore::addCAToStore( std::optional conn_(getConnection()); auto & conn = *conn_; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 25) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 25) { conn->to << WorkerProto::Op::AddToStore @@ -485,7 +485,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 18) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 18) { auto source2 = sinkToSource([&](Sink & sink) { sink << 1 // == path follows ; @@ -513,11 +513,11 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, << info.ultimate << info.sigs << renderContentAddress(info.ca) << repair << !checkSigs; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 23) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 23) { conn.withFramedSink([&](Sink & sink) { copyNAR(source, sink); }); - } else if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 21) { + } else if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 21) { conn.processStderr(0, &source); } else { copyNAR(source, conn->to); @@ -554,7 +554,7 @@ void RemoteStore::addMultipleToStore( RepairFlag repair, CheckSigsFlag checkSigs) { - if (GET_PROTOCOL_MINOR(getConnection()->daemonVersion) >= 32) { + if (GET_PROTOCOL_MINOR(getConnection()->protoVersion) >= 32) { auto conn(getConnection()); conn->to << WorkerProto::Op::AddMultipleToStore @@ -572,7 +572,7 @@ void RemoteStore::registerDrvOutput(const Realisation & info) { auto conn(getConnection()); conn->to << WorkerProto::Op::RegisterDrvOutput; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) { conn->to << info.id.to_string(); conn->to << std::string(info.outPath.to_string()); } else { @@ -587,7 +587,7 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id, try { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 27) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 27) { warn("the daemon is too old to support content-addressed derivations, please upgrade it to 2.4"); return callback(nullptr); } @@ -597,7 +597,7 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id, conn.processStderr(); auto real = [&]() -> std::shared_ptr { - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) { auto outPaths = WorkerProto::Serialise>::read( *this, *conn); if (outPaths.empty()) @@ -644,9 +644,9 @@ void RemoteStore::buildPaths(const std::vector & drvPaths, BuildMod auto conn(getConnection()); conn->to << WorkerProto::Op::BuildPaths; - assert(GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13); + assert(GET_PROTOCOL_MINOR(conn->protoVersion) >= 13); WorkerProto::write(*this, *conn, drvPaths); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 15) + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 15) conn->to << buildMode; else /* Old daemons did not take a 'buildMode' parameter, so we @@ -667,7 +667,7 @@ std::vector RemoteStore::buildPathsWithResults( std::optional conn_(getConnection()); auto & conn = *conn_; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 34) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 34) { conn->to << WorkerProto::Op::BuildPathsWithResults; WorkerProto::write(*this, *conn, paths); conn->to << buildMode; @@ -841,7 +841,7 @@ void RemoteStore::queryMissing(const std::vector & targets, { { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 19) + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 19) // Don't hold the connection handle in the fallback case // to prevent a deadlock. goto fallback; @@ -889,7 +889,7 @@ void RemoteStore::connect() unsigned int RemoteStore::getProtocol() { auto conn(connections->get()); - return conn->daemonVersion; + return conn->protoVersion; } std::optional RemoteStore::isTrustedClient() diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index f968bbc5b..0dd102200 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1526,10 +1526,11 @@ void LocalDerivationGoal::startDaemon() debug("received daemon connection"); auto workerThread = std::thread([store, remote{std::move(remote)}]() { - FdSource from(remote.get()); - FdSink to(remote.get()); try { - daemon::processConnection(store, from, to, + daemon::processConnection( + store, + FdSource(remote.get()), + FdSink(remote.get()), NotTrusted, daemon::Recursive); debug("terminated daemon connection"); } catch (SystemError &) { diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc index 3a640051e..93d13d48e 100644 --- a/src/libstore/worker-protocol-connection.cc +++ b/src/libstore/worker-protocol-connection.cc @@ -58,7 +58,7 @@ std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink } else if (msg == STDERR_ERROR) { - if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 26) { ex = std::make_exception_ptr(readError(from)); } else { auto error = readString(from); @@ -114,7 +114,7 @@ std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink // explain to users what's going on when their daemon is // older than #4628 (2023). if (experimentalFeatureSettings.isEnabled(Xp::DynamicDerivations) - && GET_PROTOCOL_MINOR(daemonVersion) <= 35) { + && GET_PROTOCOL_MINOR(protoVersion) <= 35) { auto m = e.msg(); if (m.find("parsing derivation") != std::string::npos && m.find("expected string") != std::string::npos && m.find("Derive([") != std::string::npos) @@ -172,15 +172,15 @@ WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandsha { WorkerProto::ClientHandshakeInfo res; - if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 14) { // Obsolete CPU affinity. to << 0; } - if (GET_PROTOCOL_MINOR(daemonVersion) >= 11) + if (GET_PROTOCOL_MINOR(protoVersion) >= 11) to << false; // obsolete reserveSpace - if (GET_PROTOCOL_MINOR(daemonVersion) >= 33) + if (GET_PROTOCOL_MINOR(protoVersion) >= 33) to.flush(); return WorkerProto::Serialise::read(store, *this); @@ -188,12 +188,12 @@ WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandsha void WorkerProto::BasicServerConnection::postHandshake(const StoreDirConfig & store, const ClientHandshakeInfo & info) { - if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 14 && readInt(from)) { // Obsolete CPU affinity. readInt(from); } - if (GET_PROTOCOL_MINOR(clientVersion) >= 11) + if (GET_PROTOCOL_MINOR(protoVersion) >= 11) readInt(from); // obsolete reserveSpace WorkerProto::write(store, *this, info); @@ -211,7 +211,7 @@ UnkeyedValidPathInfo WorkerProto::BasicClientConnection::queryPathInfo( throw InvalidPath(std::move(e.info())); throw; } - if (GET_PROTOCOL_MINOR(daemonVersion) >= 17) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 17) { bool valid; from >> valid; if (!valid) @@ -223,10 +223,10 @@ UnkeyedValidPathInfo WorkerProto::BasicClientConnection::queryPathInfo( StorePathSet WorkerProto::BasicClientConnection::queryValidPaths( const StoreDirConfig & store, bool * daemonException, const StorePathSet & paths, SubstituteFlag maybeSubstitute) { - assert(GET_PROTOCOL_MINOR(daemonVersion) >= 12); + assert(GET_PROTOCOL_MINOR(protoVersion) >= 12); to << WorkerProto::Op::QueryValidPaths; WorkerProto::write(store, *this, paths); - if (GET_PROTOCOL_MINOR(daemonVersion) >= 27) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 27) { to << maybeSubstitute; } processStderr(daemonException); diff --git a/src/libstore/worker-protocol-connection.hh b/src/libstore/worker-protocol-connection.hh index 9dd723fd0..38287d08e 100644 --- a/src/libstore/worker-protocol-connection.hh +++ b/src/libstore/worker-protocol-connection.hh @@ -6,7 +6,7 @@ namespace nix { -struct WorkerProto::BasicClientConnection +struct WorkerProto::BasicConnection { /** * Send with this. @@ -19,14 +19,45 @@ struct WorkerProto::BasicClientConnection FdSource from; /** - * Worker protocol version used for the connection. - * - * Despite its name, it is actually the maximum version both - * sides support. (If the maximum doesn't exist, we would fail to - * establish a connection and produce a value of this type.) + * The protocol version agreed by both sides. */ - WorkerProto::Version daemonVersion; + WorkerProto::Version protoVersion; + /** + * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::ReadConn() + { + return WorkerProto::ReadConn{ + .from = from, + .version = protoVersion, + }; + } + + /** + * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::WriteConn() + { + return WorkerProto::WriteConn{ + .to = to, + .version = protoVersion, + }; + } +}; + +struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection +{ /** * Flush to direction */ @@ -60,38 +91,6 @@ struct WorkerProto::BasicClientConnection */ ClientHandshakeInfo postHandshake(const StoreDirConfig & store); - /** - * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::ReadConn() - { - return WorkerProto::ReadConn{ - .from = from, - .version = daemonVersion, - }; - } - - /** - * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::WriteConn() - { - return WorkerProto::WriteConn{ - .to = to, - .version = daemonVersion, - }; - } - void addTempRoot(const StoreDirConfig & remoteStore, bool * daemonException, const StorePath & path); StorePathSet queryValidPaths( @@ -124,43 +123,8 @@ struct WorkerProto::BasicClientConnection void importPaths(const StoreDirConfig & store, bool * daemonException, Source & source); }; -struct WorkerProto::BasicServerConnection +struct WorkerProto::BasicServerConnection : WorkerProto::BasicConnection { - /** - * Send with this. - */ - FdSink & to; - - /** - * Receive with this. - */ - FdSource & from; - - /** - * Worker protocol version used for the connection. - * - * Despite its name, it is actually the maximum version both - * sides support. (If the maximum doesn't exist, we would fail to - * establish a connection and produce a value of this type.) - */ - WorkerProto::Version clientVersion; - - operator WorkerProto::ReadConn() - { - return WorkerProto::ReadConn{ - .from = from, - .version = clientVersion, - }; - } - - operator WorkerProto::WriteConn() - { - return WorkerProto::WriteConn{ - .to = to, - .version = clientVersion, - }; - } - /** * Establishes connection, negotiating version. * diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 62a12d182..f4023f7ea 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -82,6 +82,7 @@ struct WorkerProto * * @todo remove once Hydra uses Store abstraction consistently. */ + struct BasicConnection; struct BasicClientConnection; struct BasicServerConnection; diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index 4a7997b1f..66d8dbcf0 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -370,9 +370,12 @@ static void daemonLoop(std::optional forceTrustClientOpt) } // Handle the connection. - FdSource from(remote.get()); - FdSink to(remote.get()); - processConnection(openUncachedStore(), from, to, trusted, NotRecursive); + processConnection( + openUncachedStore(), + FdSource(remote.get()), + FdSink(remote.get()), + trusted, + NotRecursive); exit(0); }, options); @@ -437,9 +440,11 @@ static void forwardStdioConnection(RemoteStore & store) { */ static void processStdioConnection(ref store, TrustedFlag trustClient) { - FdSource from(STDIN_FILENO); - FdSink to(STDOUT_FILENO); - processConnection(store, from, to, trustClient, NotRecursive); + processConnection( + store, + FdSource(STDIN_FILENO), + FdSink(STDOUT_FILENO), + trustClient, NotRecursive); } /** From d231d802f501a1992ebb5138b58883ced56234fe Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 16:30:24 +0200 Subject: [PATCH 629/910] Typo --- src/libstore/worker-protocol.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index f4023f7ea..9fc16d015 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -184,8 +184,7 @@ enum struct WorkerProto::Op : uint64_t struct WorkerProto::ClientHandshakeInfo { /** - * The version of the Nix daemon that is processing our requests -. + * The version of the Nix daemon that is processing our requests. * * Do note, it may or may not communicating with another daemon, * rather than being an "end" `LocalStore` or similar. From fa7aa0389a2c13f5253eb4bf529aafab5d4a1d5f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 20:37:22 +0200 Subject: [PATCH 630/910] FdSource: Fix operator = This wasn't moving the underlying buffer, so if the buffer was non-empty, it could lose data. --- src/libutil/serialise.hh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 18f4a79c3..8254a5f89 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -159,13 +159,7 @@ struct FdSource : BufferedSource FdSource(Descriptor fd) : fd(fd) { } FdSource(FdSource &&) = default; - FdSource & operator=(FdSource && s) - { - fd = s.fd; - s.fd = INVALID_DESCRIPTOR; - read = s.read; - return *this; - } + FdSource & operator=(FdSource && s) = default; bool good() override; protected: From 31e151386b1ce086de6a6ec4a6f0688b1b5dced6 Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 18 Jul 2024 17:56:12 +0100 Subject: [PATCH 631/910] libmain: add missing header include --- src/libmain/plugin.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libmain/plugin.cc b/src/libmain/plugin.cc index 52b5b60e4..ccfd7f900 100644 --- a/src/libmain/plugin.cc +++ b/src/libmain/plugin.cc @@ -2,6 +2,8 @@ # include #endif +#include + #include "config-global.hh" #include "signals.hh" From a6dccae2235ce4bb6a1f1552770eb59ff96f7e05 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 17 Jul 2024 02:05:38 +0200 Subject: [PATCH 632/910] Fix non-deterministic parser printing In _very_ rare cases (I had about 7 cases out of 32200 files!), the order of how inherit-from bindings are printed when using `nix-instantiate --parse` gets messed up. The cause of this seems to be because the std::map the bindings are placed in is keyed on a _pointer_, which then uses an [implementation-defined strict total order](https://en.cppreference.com/w/cpp/language/operator_comparison#Pointer_total_order). The fix here is to key the bindings on their displacement instead, which maintains the same order as they appear in the file. Unfortunately I wasn't able to make a reproducible test for this in the source, there's something about the local environment that makes it unreproducible for me. However I was able to make a reproducible test in a Nix build on a Nix version from a very recent master: nix build github:infinisil/non-det-nix-parsing-repro Co-authored-by: Robert Hensing --- src/libexpr/nixexpr.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 93c8bdef6..6c6769cfd 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -82,7 +82,9 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co return sa < sb; }); std::vector inherits; - std::map> inheritsFrom; + // We can use the displacement as a proxy for the order in which the symbols were parsed. + // The assignment of displacements should be deterministic, so that showBindings is deterministic. + std::map> inheritsFrom; for (auto & i : sorted) { switch (i->second.kind) { case AttrDef::Kind::Plain: @@ -93,7 +95,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co case AttrDef::Kind::InheritedFrom: { auto & select = dynamic_cast(*i->second.e); auto & from = dynamic_cast(*select.e); - inheritsFrom[&from].push_back(i->first); + inheritsFrom[from.displ].push_back(i->first); break; } } @@ -105,7 +107,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co } for (const auto & [from, syms] : inheritsFrom) { str << "inherit ("; - (*inheritFromExprs)[from->displ]->show(symbols, str); + (*inheritFromExprs)[from]->show(symbols, str); str << ")"; for (auto sym : syms) str << " " << symbols[sym]; str << "; "; From 0c91bb97e5579169fdac94ebfbb352ee1cc4a5ae Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 17 Jul 2024 02:43:42 +0200 Subject: [PATCH 633/910] parser: Remove empty multiline string parts earlier Makes parsing more consistent and is a super minor optimisation Co-authored-by: Robert Hensing --- src/libexpr/parser-state.hh | 13 ++++++++++++- tests/functional/lang/parse-okay-ind-string.exp | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 983a17a2e..d8287db9b 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -291,12 +291,23 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, s2 = std::string(s2, 0, p + 1); } - es2->emplace_back(i->first, new ExprString(std::move(s2))); + // Ignore empty strings for a minor optimisation and AST simplification + if (s2 != "") { + es2->emplace_back(i->first, new ExprString(std::move(s2))); + } }; for (; i != es.end(); ++i, --n) { std::visit(overloaded { trimExpr, trimString }, i->second); } + // If there is nothing at all, return the empty string directly. + // This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters. + if (es2->size() == 0) { + auto *const result = new ExprString(""); + delete es2; + return result; + } + /* If this is a single string, then don't do a concatenation. */ if (es2->size() == 1 && dynamic_cast((*es2)[0].second)) { auto *const result = (*es2)[0].second; diff --git a/tests/functional/lang/parse-okay-ind-string.exp b/tests/functional/lang/parse-okay-ind-string.exp index 5f8d48688..82e9940a2 100644 --- a/tests/functional/lang/parse-okay-ind-string.exp +++ b/tests/functional/lang/parse-okay-ind-string.exp @@ -1 +1 @@ -(let string = "str"; in [ (/some/path) ((/some/path)) (("" + /some/path)) ((/some/path + "\n end")) (string) ((string)) (("" + string)) ((string + "\n end")) ("") ("") ("end") ]) +(let string = "str"; in [ (/some/path) ((/some/path)) ((/some/path)) ((/some/path + "\n end")) (string) ((string)) ((string)) ((string + "\n end")) ("") ("") ("end") ]) From 58a79b6943c7b102d876a2fc0af19ba45082f175 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Jul 2024 13:35:46 +0200 Subject: [PATCH 634/910] performOp(): Take a WorkerProto::BasicServerConnection --- src/libstore/daemon.cc | 253 ++++++++++++++++++++--------------------- 1 file changed, 124 insertions(+), 129 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index fc14eda3c..6533b2f58 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -269,26 +269,21 @@ struct ClientSettings }; static void performOp(TunnelLogger * logger, ref store, - TrustedFlag trusted, RecursiveFlag recursive, WorkerProto::Version clientVersion, - Source & from, BufferedSink & to, WorkerProto::Op op) + TrustedFlag trusted, RecursiveFlag recursive, + WorkerProto::BasicServerConnection & conn, + WorkerProto::Op op) { - WorkerProto::ReadConn rconn { - .from = from, - .version = clientVersion, - }; - WorkerProto::WriteConn wconn { - .to = to, - .version = clientVersion, - }; + WorkerProto::ReadConn rconn(conn); + WorkerProto::WriteConn wconn(conn); switch (op) { case WorkerProto::Op::IsValidPath: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); bool result = store->isValidPath(path); logger->stopWork(); - to << result; + conn.to << result; break; } @@ -296,8 +291,8 @@ static void performOp(TunnelLogger * logger, ref store, auto paths = WorkerProto::Serialise::read(*store, rconn); SubstituteFlag substitute = NoSubstitute; - if (GET_PROTOCOL_MINOR(clientVersion) >= 27) { - substitute = readInt(from) ? Substitute : NoSubstitute; + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 27) { + substitute = readInt(conn.from) ? Substitute : NoSubstitute; } logger->startWork(); @@ -311,13 +306,13 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::HasSubstitutes: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); StorePathSet paths; // FIXME paths.insert(path); auto res = store->querySubstitutablePaths(paths); logger->stopWork(); - to << (res.count(path) != 0); + conn.to << (res.count(path) != 0); break; } @@ -331,11 +326,11 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryPathHash: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto hash = store->queryPathInfo(path)->narHash; logger->stopWork(); - to << hash.to_string(HashFormat::Base16, false); + conn.to << hash.to_string(HashFormat::Base16, false); break; } @@ -343,7 +338,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::QueryReferrers: case WorkerProto::Op::QueryValidDerivers: case WorkerProto::Op::QueryDerivationOutputs: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); StorePathSet paths; if (op == WorkerProto::Op::QueryReferences) @@ -360,16 +355,16 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryDerivationOutputNames: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto names = store->readDerivation(path).outputNames(); logger->stopWork(); - to << names; + conn.to << names; break; } case WorkerProto::Op::QueryDerivationOutputMap: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto outputs = store->queryPartialDerivationOutputMap(path); logger->stopWork(); @@ -378,37 +373,37 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryDeriver: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto info = store->queryPathInfo(path); logger->stopWork(); - to << (info->deriver ? store->printStorePath(*info->deriver) : ""); + conn.to << (info->deriver ? store->printStorePath(*info->deriver) : ""); break; } case WorkerProto::Op::QueryPathFromHashPart: { - auto hashPart = readString(from); + auto hashPart = readString(conn.from); logger->startWork(); auto path = store->queryPathFromHashPart(hashPart); logger->stopWork(); - to << (path ? store->printStorePath(*path) : ""); + conn.to << (path ? store->printStorePath(*path) : ""); break; } case WorkerProto::Op::AddToStore: { - if (GET_PROTOCOL_MINOR(clientVersion) >= 25) { - auto name = readString(from); - auto camStr = readString(from); + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 25) { + auto name = readString(conn.from); + auto camStr = readString(conn.from); auto refs = WorkerProto::Serialise::read(*store, rconn); bool repairBool; - from >> repairBool; + conn.from >> repairBool; auto repair = RepairFlag{repairBool}; logger->startWork(); auto pathInfo = [&]() { // NB: FramedSource must be out of scope before logger->stopWork(); auto [contentAddressMethod, hashAlgo] = ContentAddressMethod::parseWithAlgo(camStr); - FramedSource source(from); + FramedSource source(conn.from); FileSerialisationMethod dumpMethod; switch (contentAddressMethod.getFileIngestionMethod()) { case FileIngestionMethod::Flat: @@ -439,7 +434,7 @@ static void performOp(TunnelLogger * logger, ref store, bool fixed; uint8_t recursive; std::string hashAlgoRaw; - from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; + conn.from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; if (recursive > true) throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive); method = recursive @@ -459,11 +454,11 @@ static void performOp(TunnelLogger * logger, ref store, so why all this extra work? We still parse the NAR so that we aren't sending arbitrary data to `saved` unwittingly`, and we know when the NAR ends so we don't - consume the rest of `from` and can't parse another + consume the rest of `conn.from` and can't parse another command. (We don't trust `addToStoreFromDump` to not eagerly consume the entire stream it's given, past the length of the Nar. */ - TeeSource savedNARSource(from, saved); + TeeSource savedNARSource(conn.from, saved); NullFileSystemObjectSink sink; /* just parse the NAR */ parseDump(sink, savedNARSource); }); @@ -472,20 +467,20 @@ static void performOp(TunnelLogger * logger, ref store, *dumpSource, baseName, FileSerialisationMethod::NixArchive, method, hashAlgo); logger->stopWork(); - to << store->printStorePath(path); + conn.to << store->printStorePath(path); } break; } case WorkerProto::Op::AddMultipleToStore: { bool repair, dontCheckSigs; - from >> repair >> dontCheckSigs; + conn.from >> repair >> dontCheckSigs; if (!trusted && dontCheckSigs) dontCheckSigs = false; logger->startWork(); { - FramedSource source(from); + FramedSource source(conn.from); store->addMultipleToStore(source, RepairFlag{repair}, dontCheckSigs ? NoCheckSigs : CheckSigs); @@ -495,8 +490,8 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::AddTextToStore: { - std::string suffix = readString(from); - std::string s = readString(from); + std::string suffix = readString(conn.from); + std::string s = readString(conn.from); auto refs = WorkerProto::Serialise::read(*store, rconn); logger->startWork(); auto path = ({ @@ -504,37 +499,37 @@ static void performOp(TunnelLogger * logger, ref store, store->addToStoreFromDump(source, suffix, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, refs, NoRepair); }); logger->stopWork(); - to << store->printStorePath(path); + conn.to << store->printStorePath(path); break; } case WorkerProto::Op::ExportPath: { - auto path = store->parseStorePath(readString(from)); - readInt(from); // obsolete + auto path = store->parseStorePath(readString(conn.from)); + readInt(conn.from); // obsolete logger->startWork(); - TunnelSink sink(to); + TunnelSink sink(conn.to); store->exportPath(path, sink); logger->stopWork(); - to << 1; + conn.to << 1; break; } case WorkerProto::Op::ImportPaths: { logger->startWork(); - TunnelSource source(from, to); + TunnelSource source(conn.from, conn.to); auto paths = store->importPaths(source, trusted ? NoCheckSigs : CheckSigs); logger->stopWork(); Strings paths2; for (auto & i : paths) paths2.push_back(store->printStorePath(i)); - to << paths2; + conn.to << paths2; break; } case WorkerProto::Op::BuildPaths: { auto drvs = WorkerProto::Serialise::read(*store, rconn); BuildMode mode = bmNormal; - if (GET_PROTOCOL_MINOR(clientVersion) >= 15) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 15) { mode = WorkerProto::Serialise::read(*store, rconn); /* Repairing is not atomic, so disallowed for "untrusted" @@ -552,7 +547,7 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); store->buildPaths(drvs, mode); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -578,7 +573,7 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::BuildDerivation: { - auto drvPath = store->parseStorePath(readString(from)); + auto drvPath = store->parseStorePath(readString(conn.from)); BasicDerivation drv; /* * Note: unlike wopEnsurePath, this operation reads a @@ -589,7 +584,7 @@ static void performOp(TunnelLogger * logger, ref store, * it cannot be trusted that its outPath was calculated * correctly. */ - readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath)); + readDerivation(conn.from, *store, drv, Derivation::nameFromPath(drvPath)); auto buildMode = WorkerProto::Serialise::read(*store, rconn); logger->startWork(); @@ -655,20 +650,20 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::EnsurePath: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); store->ensurePath(path); logger->stopWork(); - to << 1; + conn.to << 1; break; } case WorkerProto::Op::AddTempRoot: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); store->addTempRoot(path); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -678,24 +673,24 @@ static void performOp(TunnelLogger * logger, ref store, "you are not privileged to create perm roots\n\n" "hint: you can just do this client-side without special privileges, and probably want to do that instead."); auto storePath = WorkerProto::Serialise::read(*store, rconn); - Path gcRoot = absPath(readString(from)); + Path gcRoot = absPath(readString(conn.from)); logger->startWork(); auto & localFSStore = require(*store); localFSStore.addPermRoot(storePath, gcRoot); logger->stopWork(); - to << gcRoot; + conn.to << gcRoot; break; } case WorkerProto::Op::AddIndirectRoot: { - Path path = absPath(readString(from)); + Path path = absPath(readString(conn.from)); logger->startWork(); auto & indirectRootStore = require(*store); indirectRootStore.addIndirectRoot(path); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -703,7 +698,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::SyncWithGC: { logger->startWork(); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -717,24 +712,24 @@ static void performOp(TunnelLogger * logger, ref store, for (auto & i : roots) size += i.second.size(); - to << size; + conn.to << size; for (auto & [target, links] : roots) for (auto & link : links) - to << link << store->printStorePath(target); + conn.to << link << store->printStorePath(target); break; } case WorkerProto::Op::CollectGarbage: { GCOptions options; - options.action = (GCOptions::GCAction) readInt(from); + options.action = (GCOptions::GCAction) readInt(conn.from); options.pathsToDelete = WorkerProto::Serialise::read(*store, rconn); - from >> options.ignoreLiveness >> options.maxFreed; + conn.from >> options.ignoreLiveness >> options.maxFreed; // obsolete fields - readInt(from); - readInt(from); - readInt(from); + readInt(conn.from); + readInt(conn.from); + readInt(conn.from); GCResults results; @@ -745,7 +740,7 @@ static void performOp(TunnelLogger * logger, ref store, gcStore.collectGarbage(options, results); logger->stopWork(); - to << results.paths << results.bytesFreed << 0 /* obsolete */; + conn.to << results.paths << results.bytesFreed << 0 /* obsolete */; break; } @@ -754,24 +749,24 @@ static void performOp(TunnelLogger * logger, ref store, ClientSettings clientSettings; - clientSettings.keepFailed = readInt(from); - clientSettings.keepGoing = readInt(from); - clientSettings.tryFallback = readInt(from); - clientSettings.verbosity = (Verbosity) readInt(from); - clientSettings.maxBuildJobs = readInt(from); - clientSettings.maxSilentTime = readInt(from); - readInt(from); // obsolete useBuildHook - clientSettings.verboseBuild = lvlError == (Verbosity) readInt(from); - readInt(from); // obsolete logType - readInt(from); // obsolete printBuildTrace - clientSettings.buildCores = readInt(from); - clientSettings.useSubstitutes = readInt(from); + clientSettings.keepFailed = readInt(conn.from); + clientSettings.keepGoing = readInt(conn.from); + clientSettings.tryFallback = readInt(conn.from); + clientSettings.verbosity = (Verbosity) readInt(conn.from); + clientSettings.maxBuildJobs = readInt(conn.from); + clientSettings.maxSilentTime = readInt(conn.from); + readInt(conn.from); // obsolete useBuildHook + clientSettings.verboseBuild = lvlError == (Verbosity) readInt(conn.from); + readInt(conn.from); // obsolete logType + readInt(conn.from); // obsolete printBuildTrace + clientSettings.buildCores = readInt(conn.from); + clientSettings.useSubstitutes = readInt(conn.from); - if (GET_PROTOCOL_MINOR(clientVersion) >= 12) { - unsigned int n = readInt(from); + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 12) { + unsigned int n = readInt(conn.from); for (unsigned int i = 0; i < n; i++) { - auto name = readString(from); - auto value = readString(from); + auto name = readString(conn.from); + auto value = readString(conn.from); clientSettings.overrides.emplace(name, value); } } @@ -788,20 +783,20 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QuerySubstitutablePathInfo: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); SubstitutablePathInfos infos; store->querySubstitutablePathInfos({{path, std::nullopt}}, infos); logger->stopWork(); auto i = infos.find(path); if (i == infos.end()) - to << 0; + conn.to << 0; else { - to << 1 + conn.to << 1 << (i->second.deriver ? store->printStorePath(*i->second.deriver) : ""); WorkerProto::write(*store, wconn, i->second.references); - to << i->second.downloadSize - << i->second.narSize; + conn.to << i->second.downloadSize + << i->second.narSize; } break; } @@ -809,7 +804,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::QuerySubstitutablePathInfos: { SubstitutablePathInfos infos; StorePathCAMap pathsMap = {}; - if (GET_PROTOCOL_MINOR(clientVersion) < 22) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 22) { auto paths = WorkerProto::Serialise::read(*store, rconn); for (auto & path : paths) pathsMap.emplace(path, std::nullopt); @@ -818,12 +813,12 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); store->querySubstitutablePathInfos(pathsMap, infos); logger->stopWork(); - to << infos.size(); + conn.to << infos.size(); for (auto & i : infos) { - to << store->printStorePath(i.first) - << (i.second.deriver ? store->printStorePath(*i.second.deriver) : ""); + conn.to << store->printStorePath(i.first) + << (i.second.deriver ? store->printStorePath(*i.second.deriver) : ""); WorkerProto::write(*store, wconn, i.second.references); - to << i.second.downloadSize << i.second.narSize; + conn.to << i.second.downloadSize << i.second.narSize; } break; } @@ -837,22 +832,22 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryPathInfo: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); std::shared_ptr info; logger->startWork(); try { info = store->queryPathInfo(path); } catch (InvalidPath &) { - if (GET_PROTOCOL_MINOR(clientVersion) < 17) throw; + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 17) throw; } logger->stopWork(); if (info) { - if (GET_PROTOCOL_MINOR(clientVersion) >= 17) - to << 1; + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 17) + conn.to << 1; WorkerProto::write(*store, wconn, static_cast(*info)); } else { - assert(GET_PROTOCOL_MINOR(clientVersion) >= 17); - to << 0; + assert(GET_PROTOCOL_MINOR(conn.protoVersion) >= 17); + conn.to << 0; } break; } @@ -861,61 +856,61 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); store->optimiseStore(); logger->stopWork(); - to << 1; + conn.to << 1; break; case WorkerProto::Op::VerifyStore: { bool checkContents, repair; - from >> checkContents >> repair; + conn.from >> checkContents >> repair; logger->startWork(); if (repair && !trusted) throw Error("you are not privileged to repair paths"); bool errors = store->verifyStore(checkContents, (RepairFlag) repair); logger->stopWork(); - to << errors; + conn.to << errors; break; } case WorkerProto::Op::AddSignatures: { - auto path = store->parseStorePath(readString(from)); - StringSet sigs = readStrings(from); + auto path = store->parseStorePath(readString(conn.from)); + StringSet sigs = readStrings(conn.from); logger->startWork(); store->addSignatures(path, sigs); logger->stopWork(); - to << 1; + conn.to << 1; break; } case WorkerProto::Op::NarFromPath: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); logger->stopWork(); - dumpPath(store->toRealPath(path), to); + dumpPath(store->toRealPath(path), conn.to); break; } case WorkerProto::Op::AddToStoreNar: { bool repair, dontCheckSigs; - auto path = store->parseStorePath(readString(from)); - auto deriver = readString(from); - auto narHash = Hash::parseAny(readString(from), HashAlgorithm::SHA256); + auto path = store->parseStorePath(readString(conn.from)); + auto deriver = readString(conn.from); + auto narHash = Hash::parseAny(readString(conn.from), HashAlgorithm::SHA256); ValidPathInfo info { path, narHash }; if (deriver != "") info.deriver = store->parseStorePath(deriver); info.references = WorkerProto::Serialise::read(*store, rconn); - from >> info.registrationTime >> info.narSize >> info.ultimate; - info.sigs = readStrings(from); - info.ca = ContentAddress::parseOpt(readString(from)); - from >> repair >> dontCheckSigs; + conn.from >> info.registrationTime >> info.narSize >> info.ultimate; + info.sigs = readStrings(conn.from); + info.ca = ContentAddress::parseOpt(readString(conn.from)); + conn.from >> repair >> dontCheckSigs; if (!trusted && dontCheckSigs) dontCheckSigs = false; if (!trusted) info.ultimate = false; - if (GET_PROTOCOL_MINOR(clientVersion) >= 23) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 23) { logger->startWork(); { - FramedSource source(from); + FramedSource source(conn.from); store->addToStore(info, source, (RepairFlag) repair, dontCheckSigs ? NoCheckSigs : CheckSigs); } @@ -925,10 +920,10 @@ static void performOp(TunnelLogger * logger, ref store, else { std::unique_ptr source; StringSink saved; - if (GET_PROTOCOL_MINOR(clientVersion) >= 21) - source = std::make_unique(from, to); + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 21) + source = std::make_unique(conn.from, conn.to); else { - TeeSource tee { from, saved }; + TeeSource tee { conn.from, saved }; NullFileSystemObjectSink ether; parseDump(ether, tee); source = std::make_unique(saved.s); @@ -956,15 +951,15 @@ static void performOp(TunnelLogger * logger, ref store, WorkerProto::write(*store, wconn, willBuild); WorkerProto::write(*store, wconn, willSubstitute); WorkerProto::write(*store, wconn, unknown); - to << downloadSize << narSize; + conn.to << downloadSize << narSize; break; } case WorkerProto::Op::RegisterDrvOutput: { logger->startWork(); - if (GET_PROTOCOL_MINOR(clientVersion) < 31) { - auto outputId = DrvOutput::parse(readString(from)); - auto outputPath = StorePath(readString(from)); + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) { + auto outputId = DrvOutput::parse(readString(conn.from)); + auto outputPath = StorePath(readString(conn.from)); store->registerDrvOutput(Realisation{ .id = outputId, .outPath = outputPath}); } else { @@ -977,10 +972,10 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::QueryRealisation: { logger->startWork(); - auto outputId = DrvOutput::parse(readString(from)); + auto outputId = DrvOutput::parse(readString(conn.from)); auto info = store->queryRealisation(outputId); logger->stopWork(); - if (GET_PROTOCOL_MINOR(clientVersion) < 31) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) { std::set outPaths; if (info) outPaths.insert(info->outPath); WorkerProto::write(*store, wconn, outPaths); @@ -993,19 +988,19 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::AddBuildLog: { - StorePath path{readString(from)}; + StorePath path{readString(conn.from)}; logger->startWork(); if (!trusted) throw Error("you are not privileged to add logs"); auto & logStore = require(*store); { - FramedSource source(from); + FramedSource source(conn.from); StringSink sink; source.drainInto(sink); logStore.addBuildLog(path, sink.s); } logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -1090,7 +1085,7 @@ void processConnection( debug("performing daemon worker op: %d", op); try { - performOp(tunnelLogger, store, trusted, recursive, clientVersion, conn.from, conn.to, op); + performOp(tunnelLogger, store, trusted, recursive, conn, op); } catch (Error & e) { /* If we're not in a state where we can send replies, then something went wrong processing the input of the From 74dccef004baed15c957715a8f3c93f10854d105 Mon Sep 17 00:00:00 2001 From: detroyejr Date: Thu, 18 Jul 2024 18:30:31 -0400 Subject: [PATCH 635/910] addFlag: use aliases --- src/libstore/globals.cc | 3 +++ src/libutil/config-impl.hh | 2 ++ src/libutil/config.cc | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index fa4c0ba7f..60b3ee34e 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -304,18 +304,21 @@ template<> void BaseSetting::convertToArg(Args & args, const std::s { args.addFlag({ .longName = name, + .aliases = aliases, .description = "Enable sandboxing.", .category = category, .handler = {[this]() { override(smEnabled); }} }); args.addFlag({ .longName = "no-" + name, + .aliases = aliases, .description = "Disable sandboxing.", .category = category, .handler = {[this]() { override(smDisabled); }} }); args.addFlag({ .longName = "relaxed-" + name, + .aliases = aliases, .description = "Enable sandboxing, but allow builds to disable it.", .category = category, .handler = {[this]() { override(smRelaxed); }} diff --git a/src/libutil/config-impl.hh b/src/libutil/config-impl.hh index 1d349fab5..c3aa61ddb 100644 --- a/src/libutil/config-impl.hh +++ b/src/libutil/config-impl.hh @@ -81,6 +81,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) { args.addFlag({ .longName = name, + .aliases = aliases, .description = fmt("Set the `%s` setting.", name), .category = category, .labels = {"value"}, @@ -91,6 +92,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) if (isAppendable()) args.addFlag({ .longName = "extra-" + name, + .aliases = aliases, .description = fmt("Append to the `%s` setting.", name), .category = category, .labels = {"value"}, diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 726e5091e..b39948261 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -292,6 +292,7 @@ template<> void BaseSetting::convertToArg(Args & args, const std::string & { args.addFlag({ .longName = name, + .aliases = aliases, .description = fmt("Enable the `%s` setting.", name), .category = category, .handler = {[this] { override(true); }}, @@ -299,6 +300,7 @@ template<> void BaseSetting::convertToArg(Args & args, const std::string & }); args.addFlag({ .longName = "no-" + name, + .aliases = aliases, .description = fmt("Disable the `%s` setting.", name), .category = category, .handler = {[this] { override(false); }}, From d54dfbf879d8c4e2f98aca54221e757fd1035b10 Mon Sep 17 00:00:00 2001 From: detroyejr Date: Fri, 19 Jul 2024 15:49:56 -0400 Subject: [PATCH 636/910] addFlag: test that alias flags are allowed --- tests/functional/build.sh | 12 ++++++++++++ tests/functional/eval.sh | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 9de953d8c..5396a465f 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -140,6 +140,18 @@ nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status (.outputs | keys == ["a_a", "b"])) ' +# Make sure that the 3 types of aliases work +# BaseSettings, BaseSettings, and BaseSettings. +nix build --impure -f multiple-outputs.nix --json e --no-link \ + --build-max-jobs 3 \ + --gc-keep-outputs \ + --build-use-sandbox | \ + jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-e.drv")) and + (.outputs | keys == ["a_a", "b"])) +' + # Make sure that `--stdin` works and does not apply any defaults printf "" | nix build --no-link --stdin --json | jq --exit-status '. == []' printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status '.[0]|has("drvPath")' diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index 27cdce478..22d2d02a2 100755 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -58,3 +58,7 @@ fi # Test that unknown settings are warned about out="$(expectStderr 0 nix eval --option foobar baz --expr '""' --raw)" [[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] + +# Test flag alias +out="$(nix eval --expr '{}' --build-cores 1)" +[[ "$(echo "$out" | wc -l)" = 1 ]] From 0ec5e3a1bccb17b79a1927fd4b9e99195062db7f Mon Sep 17 00:00:00 2001 From: poweredbypie <67135060+poweredbypie@users.noreply.github.com> Date: Sun, 21 Jul 2024 15:03:04 -0700 Subject: [PATCH 637/910] Progress on Wine CI support, MinGW dev shell with Meson (#10975) * Only build perl subproject on Linux * Fix various Windows regressions * Don't put the emulator hook in test builds We run the tests in a separate derivation. Only need it for the dev shell. * Fix native dev shells * Fix cross dev shells we don't know how to emulate Co-authored-by: PoweredByPie Co-authored-by: Joachim Schiele Co-authored-by: John Ericson --- flake.nix | 20 ++++++++++++----- meson.build | 4 +++- src/libexpr-c/nix_api_expr.cc | 12 +++++----- src/libexpr-c/nix_api_external.cc | 4 ++-- src/libexpr-c/nix_api_value.cc | 4 ++-- .../build/drv-output-substitution-goal.cc | 2 +- src/libutil/file-system.cc | 6 ++++- src/libutil/fs-sink.cc | 4 ++-- src/nix/meson.build | 22 +++++++++++-------- 9 files changed, 49 insertions(+), 29 deletions(-) diff --git a/flake.nix b/flake.nix index 51dbc3091..ff2c8ecfa 100644 --- a/flake.nix +++ b/flake.nix @@ -278,6 +278,7 @@ in "-D${prefix}:${rest}"; havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix; + ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags; in { pname = "shell-for-" + attrs.pname; @@ -309,10 +310,12 @@ }; mesonFlags = - map (transformFlag "libutil") pkgs.nixComponents.nix-util.mesonFlags - ++ map (transformFlag "libstore") pkgs.nixComponents.nix-store.mesonFlags - ++ map (transformFlag "libfetchers") pkgs.nixComponents.nix-fetchers.mesonFlags - ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nixComponents.nix-perl-bindings.mesonFlags) + map (transformFlag "libutil") (ignoreCrossFile pkgs.nixComponents.nix-util.mesonFlags) + ++ map (transformFlag "libstore") (ignoreCrossFile pkgs.nixComponents.nix-store.mesonFlags) + ++ map (transformFlag "libfetchers") (ignoreCrossFile pkgs.nixComponents.nix-fetchers.mesonFlags) + ++ lib.optionals havePerl (map (transformFlag "perl") (ignoreCrossFile pkgs.nixComponents.nix-perl-bindings.mesonFlags)) + ++ map (transformFlag "libexpr") (ignoreCrossFile pkgs.nixComponents.nix-expr.mesonFlags) + ++ map (transformFlag "libcmd") (ignoreCrossFile pkgs.nixComponents.nix-cmd.mesonFlags) ; nativeBuildInputs = attrs.nativeBuildInputs or [] @@ -322,9 +325,16 @@ ++ lib.optionals havePerl pkgs.nixComponents.nix-perl-bindings.nativeBuildInputs ++ pkgs.nixComponents.nix-internal-api-docs.nativeBuildInputs ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs + ++ lib.optional + (!stdenv.buildPlatform.canExecute stdenv.hostPlatform + # Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479 + && !(stdenv.hostPlatform.isWindows && stdenv.buildPlatform.isDarwin) + && stdenv.hostPlatform.emulatorAvailable pkgs.buildPackages + && lib.meta.availableOn stdenv.buildPlatform (stdenv.hostPlatform.emulator pkgs.buildPackages)) + pkgs.buildPackages.mesonEmulatorHook ++ [ pkgs.buildPackages.cmake - pkgs.shellcheck + pkgs.buildPackages.shellcheck modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) diff --git a/meson.build b/meson.build index 1c46c5c28..1554244ab 100644 --- a/meson.build +++ b/meson.build @@ -29,7 +29,9 @@ subproject('libexpr-c') subproject('libmain-c') # Language Bindings -subproject('perl') +if not meson.is_cross_build() + subproject('perl') +endif # Testing subproject('nix-util-test-support') diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 547453f8f..8f21d7022 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -14,10 +14,10 @@ #include "nix_api_util.h" #include "nix_api_util_internal.h" -#ifdef HAVE_BOEHMGC -#include -#define GC_INCLUDE_NEW 1 -#include "gc_cpp.h" +#if HAVE_BOEHMGC +# include +# define GC_INCLUDE_NEW 1 +# include "gc_cpp.h" #endif nix_err nix_libexpr_init(nix_c_context * context) @@ -131,7 +131,7 @@ void nix_state_free(EvalState * state) delete state; } -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC std::unordered_map< const void *, unsigned int, @@ -207,7 +207,7 @@ nix_err nix_value_decref(nix_c_context * context, nix_value *x) void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * obj, void * cd)) { -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0); #endif } diff --git a/src/libexpr-c/nix_api_external.cc b/src/libexpr-c/nix_api_external.cc index 3565092a4..fa78eb5df 100644 --- a/src/libexpr-c/nix_api_external.cc +++ b/src/libexpr-c/nix_api_external.cc @@ -14,7 +14,7 @@ #include -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC # include "gc/gc.h" # define GC_INCLUDE_NEW 1 # include "gc_cpp.h" @@ -174,7 +174,7 @@ ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalV context->last_err_code = NIX_OK; try { auto ret = new -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC (GC) #endif NixCExternalValue(*desc, v); diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index cb5d9ee89..845e87935 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -14,7 +14,7 @@ #include "nix_api_value.h" #include "value/context.hh" -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC # include "gc/gc.h" # define GC_INCLUDE_NEW 1 # include "gc_cpp.h" @@ -131,7 +131,7 @@ PrimOp * nix_alloc_primop( try { using namespace std::placeholders; auto p = new -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC (GC) #endif nix::PrimOp{ diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc index 02284d93c..dedcad2b1 100644 --- a/src/libstore/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -62,7 +62,7 @@ Goal::Co DrvOutputSubstitutionGoal::init() #ifndef _WIN32 outPipe->readSide.get() #else - &outPipe + &*outPipe #endif }, true, false); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9042e3a5e..060a806fb 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -416,7 +416,11 @@ void deletePath(const fs::path & path) void createDir(const Path & path, mode_t mode) { - if (mkdir(path.c_str(), mode) == -1) + if (mkdir(path.c_str() +#ifndef _WIN32 + , mode +#endif + ) == -1) throw SysError("creating directory '%1%'", path); } diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 194e86fdd..3246e0902 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -97,7 +97,7 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function Date: Mon, 22 Jul 2024 12:05:50 +0200 Subject: [PATCH 638/910] maintainers/README: Update Monday meeting time (#11147) --- maintainers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainers/README.md b/maintainers/README.md index 2a718e283..b92833497 100644 --- a/maintainers/README.md +++ b/maintainers/README.md @@ -50,7 +50,7 @@ The team meets twice a week (times are denoted in the [Europe/Amsterdam](https:/ - mark it as draft if it is blocked on the contributor - escalate it back to the team by moving it to To discuss, and leaving a comment as to why the issue needs to be discussed again. -- Work meeting: [Mondays 13:00-15:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=Ym52NDdzYnRic2NzcDcybjZiNDhpNzhpa3NfMjAyNDA1MTNUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) +- Work meeting: [Mondays 14:00-16:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=Ym52NDdzYnRic2NzcDcybjZiNDhpNzhpa3NfMjAyNDA1MTNUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) 1. Code review on pull requests from [In review](#in-review). 2. Other chores and tasks. From b16861d82eab5627f79d4573221f5f97048711f5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 22 Jul 2024 12:56:01 +0200 Subject: [PATCH 639/910] libexpr: Track and show GC time and cycle number --- src/libexpr/eval-gc.cc | 12 ++++++++++++ src/libexpr/eval-gc.hh | 7 +++++++ src/libexpr/eval.cc | 13 +++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index baf9df332..c8e2adb94 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -155,6 +155,10 @@ static inline void initGCReal() there. */ GC_set_no_dls(1); + /* Enable perf measurements. This is just a setting; not much of a + start of something. */ + GC_start_performance_measurement(); + GC_INIT(); GC_set_oom_fn(oomHandler); @@ -205,6 +209,7 @@ static inline void initGCReal() #endif static bool gcInitialised = false; +static GC_word gcCyclesAfterInit = 0; void initGC() { @@ -216,6 +221,7 @@ void initGC() #endif gcInitialised = true; + gcCyclesAfterInit = GC_get_gc_no(); } void assertGCInitialized() @@ -223,4 +229,10 @@ void assertGCInitialized() assert(gcInitialised); } +size_t getGCCycles() +{ + assertGCInitialized(); + return GC_get_gc_no() - gcCyclesAfterInit; } + +} // namespace nix diff --git a/src/libexpr/eval-gc.hh b/src/libexpr/eval-gc.hh index cd4ea914d..76f019729 100644 --- a/src/libexpr/eval-gc.hh +++ b/src/libexpr/eval-gc.hh @@ -1,6 +1,8 @@ #pragma once ///@file +#include + namespace nix { /** @@ -13,4 +15,9 @@ void initGC(); */ void assertGCInitialized(); +/** + * The number of GC cycles since initGC(). + */ +size_t getGCCycles(); + } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 4f01d0a62..11c3282ad 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2610,6 +2610,11 @@ void EvalState::printStatistics() #if HAVE_BOEHMGC GC_word heapSize, totalBytes; GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes); + double gcFullOnlyTime = ({ + auto ms = GC_get_full_gc_total_time(); + ms * 0.001; + }); + auto gcCycles = getGCCycles(); #endif auto outPath = getEnv("NIX_SHOW_STATS_PATH").value_or("-"); @@ -2620,6 +2625,13 @@ void EvalState::printStatistics() #ifndef _WIN32 // TODO implement topObj["cpuTime"] = cpuTime; #endif + topObj["time"] = { + {"cpu", cpuTime}, +#ifdef HAVE_BOEHMGC + {GC_is_incremental_mode() ? "gcNonIncremental" : "gc", gcFullOnlyTime}, + {GC_is_incremental_mode() ? "gcNonIncrementalFraction" : "gcFraction", gcFullOnlyTime / cpuTime}, +#endif + }; topObj["envs"] = { {"number", nrEnvs}, {"elements", nrValuesInEnvs}, @@ -2661,6 +2673,7 @@ void EvalState::printStatistics() topObj["gc"] = { {"heapSize", heapSize}, {"totalBytes", totalBytes}, + {"cycles", gcCycles}, }; #endif From 380becf0dbbf700c32aaa2e574cc8e05b6411056 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 22 Jul 2024 14:48:18 +0200 Subject: [PATCH 640/910] Fix #11141 broken sp corrector --- src/libexpr/eval-gc.cc | 2 +- tests/functional/lang-gc.sh | 34 ++++++++++ .../lang-gc/issue-11141-gc-coroutine-test.nix | 65 +++++++++++++++++++ tests/functional/local.mk | 1 + 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/functional/lang-gc.sh create mode 100644 tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index baf9df332..914d48da8 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -121,7 +121,7 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack - sp = osStackBase; + sp = osStackLow; } } diff --git a/tests/functional/lang-gc.sh b/tests/functional/lang-gc.sh new file mode 100644 index 000000000..8e2383854 --- /dev/null +++ b/tests/functional/lang-gc.sh @@ -0,0 +1,34 @@ +# shellcheck shell=bash + +# Regression tests for the evaluator +# These are not in lang.sh because they generally only need to run in CI, +# whereas lang.sh is often run locally during development + + +source common.sh + +set -o pipefail + +# Regression test for #11141. The stack pointer corrector assigned the base +# instead of the top (which resides at the low end of the stack). Sounds confusing? +# Stacks grow downwards, so that's why this mistake happened. +# My manual testing did not uncover this, because it didn't rely on the stack enough. +# https://github.com/NixOS/nix/issues/11141 +test_issue_11141() { + mkdir -p "$TEST_ROOT/issue-11141/src" + cp lang-gc/issue-11141-gc-coroutine-test.nix "$TEST_ROOT/issue-11141/" + ( + set +x; + n=10 + echo "populating $TEST_ROOT/issue-11141/src with $((n*100)) files..." + for i in $(seq 0 $n); do + touch "$TEST_ROOT/issue-11141/src/file-$i"{0,1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9} + done + ) + + GC_INITIAL_HEAP_SIZE=$((1024 * 1024)) \ + NIX_SHOW_STATS=1 \ + nix eval -vvv\ + -f "$TEST_ROOT/issue-11141/issue-11141-gc-coroutine-test.nix" +} +test_issue_11141 diff --git a/tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix b/tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix new file mode 100644 index 000000000..4f311af75 --- /dev/null +++ b/tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix @@ -0,0 +1,65 @@ + +# Run: +# GC_INITIAL_HEAP_SIZE=$[1024 * 1024] NIX_SHOW_STATS=1 nix eval -f gc-coroutine-test.nix -vvvv + +let + inherit (builtins) + foldl' + isList + ; + + # Generate a tree of numbers, n deep, such that the numbers add up to (1 + salt) * 10^n. + # The salting makes the numbers all different, increasing the likelihood of catching + # any memory corruptions that might be caused by the GC or otherwise. + garbage = salt: n: + if n == 0 + then [(1 + salt)] + else [ + (garbage (10 * salt + 1) (n - 1)) + (garbage (10 * salt - 1) (n - 1)) + (garbage (10 * salt + 2) (n - 1)) + (garbage (10 * salt - 2) (n - 1)) + (garbage (10 * salt + 3) (n - 1)) + (garbage (10 * salt - 3) (n - 1)) + (garbage (10 * salt + 4) (n - 1)) + (garbage (10 * salt - 4) (n - 1)) + (garbage (10 * salt + 5) (n - 1)) + (garbage (10 * salt - 5) (n - 1)) + ]; + + pow = base: n: + if n == 0 + then 1 + else base * (pow base (n - 1)); + + sumNestedLists = l: + if isList l + then foldl' (a: b: a + sumNestedLists b) 0 l + else l; + +in + assert sumNestedLists (garbage 0 3) == pow 10 3; + assert sumNestedLists (garbage 0 6) == pow 10 6; + builtins.foldl' + (a: b: + assert + "${ + builtins.path { + path = ./src; + filter = path: type: + # We're not doing common subexpression elimination, so this reallocates + # the fairly big tree over and over, producing a lot of garbage during + # source filtering, whose filter runs in a coroutine. + assert sumNestedLists (garbage 0 3) == pow 10 3; + true; + } + }" + == "${./src}"; + + # These asserts don't seem necessary, as the lambda value get corrupted first + assert a.okay; + assert b.okay; + { okay = true; } + ) + { okay = true; } + [ { okay = true; } { okay = true; } { okay = true; } ] diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 49ee31284..797002e92 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -23,6 +23,7 @@ nix_tests = \ remote-store.sh \ legacy-ssh-store.sh \ lang.sh \ + lang-gc.sh \ characterisation-test-infra.sh \ experimental-features.sh \ fetchMercurial.sh \ From 112373c03cade928e2417a53623a2a1fd4773609 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 21 Jul 2024 12:16:47 -0400 Subject: [PATCH 641/910] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/88269ab3044128b7c2f4c7d68448b2fb50456870' (2024-06-03) → 'github:NixOS/nixpkgs/be3ca229c85e978880babdeda9748b14e6aa008f' (2024-07-21) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index f64e3ea37..1d59439da 100644 --- a/flake.lock +++ b/flake.lock @@ -69,11 +69,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1717432640, - "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "lastModified": 1721560568, + "narHash": "sha256-L61BXz7n/yNzOeZ3FqlnUmxj4145JOVeq9fvQTQzbNM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "rev": "be3ca229c85e978880babdeda9748b14e6aa008f", "type": "github" }, "original": { From dc6dbbc1a5226fe6e25035bf5eb284df3c5c4607 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 21 Jul 2024 12:24:21 -0400 Subject: [PATCH 642/910] Test exes do not need dev outputs --- tests/unit/libexpr/package.nix | 2 -- tests/unit/libfetchers/package.nix | 2 -- tests/unit/libflake/package.nix | 2 -- tests/unit/libstore/package.nix | 2 -- tests/unit/libutil/package.nix | 2 -- 5 files changed, 10 deletions(-) diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 6b7e12c4a..5f52170ba 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -41,8 +41,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index 9522f9639..88ccaca7f 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -40,8 +40,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index 859bc49d0..3c99efbba 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -40,8 +40,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index efffd0063..e68cc3a62 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -42,8 +42,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index ad5ff7d1e..b635aa811 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -40,8 +40,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja From eea63d5f993f0ce27ecee41d0c80f3e521f4f247 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 21 Jul 2024 12:57:49 -0400 Subject: [PATCH 643/910] Run unit tests with wine Fix #10547 The tests do not all run successfully, but that is a separate issue. --- tests/unit/libexpr/package.nix | 13 +++++++++---- tests/unit/libfetchers/package.nix | 13 +++++++++---- tests/unit/libflake/package.nix | 13 +++++++++---- tests/unit/libstore/package.nix | 14 ++++++++++---- tests/unit/libutil/package.nix | 13 +++++++++---- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 5f52170ba..015e3fbc6 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -81,17 +82,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${resolvePath ./data} - nix-expr-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index 88ccaca7f..cf75f68e5 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -79,17 +80,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${resolvePath ./data} - nix-fetchers-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index 3c99efbba..d2c9fdb89 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -79,17 +80,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${resolvePath ./data} - nix-flake-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index e68cc3a62..39bf77585 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -92,17 +93,22 @@ mkMesonDerivation (finalAttrs: { ../../functional/derivation ]; }; - in runCommand "${finalAttrs.pname}-run" {} '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + in runCommand "${finalAttrs.pname}-run" { + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${data + "/unit/libstore/data"} - nix-store-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index b635aa811..c7827e74f 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -80,17 +81,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${./data} - nix-util-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) From 4878c3181575287b65c67aa0efa766b123cce5db Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Jul 2024 16:39:02 +0200 Subject: [PATCH 644/910] nix ping-store: Redirect to 'nix store info' This avoids the double warning warning: 'ping-store' is a deprecated alias for 'store ping' warning: 'nix store ping' is a deprecated alias for 'nix store info' --- src/nix/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index 00ad6fe2c..9d7d617cc 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -164,7 +164,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs {"ls-store", { AliasStatus::Deprecated, {"store", "ls"}}}, {"make-content-addressable", { AliasStatus::Deprecated, {"store", "make-content-addressed"}}}, {"optimise-store", { AliasStatus::Deprecated, {"store", "optimise"}}}, - {"ping-store", { AliasStatus::Deprecated, {"store", "ping"}}}, + {"ping-store", { AliasStatus::Deprecated, {"store", "info"}}}, {"sign-paths", { AliasStatus::Deprecated, {"store", "sign"}}}, {"shell", { AliasStatus::AcceptedShorthand, {"env", "shell"}}}, {"show-derivation", { AliasStatus::Deprecated, {"derivation", "show"}}}, From 823baa25f35fd9047c715de675e2fef6e62d3583 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 10:41:19 -0400 Subject: [PATCH 645/910] Meson build: libstore check for `statvfs` --- src/libstore/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 29ee95b75..3b7f7a57a 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -55,6 +55,7 @@ configdata.set('CAN_LINK_SYMLINK', can_link_symlink.to_int()) check_funcs = [ # Optionally used for canonicalising files from the build 'lchown', + 'statvfs', ] foreach funcspec : check_funcs define_name = 'HAVE_' + funcspec.underscorify().to_upper() From d7024ac9b75032f2cfaabc53c27dbd4584bfd6db Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 10:40:21 -0400 Subject: [PATCH 646/910] Add S3 opt dep to Meson, and simplify build Numeric version macros are now defined upstream, so we don't need roll our own. --- configure.ac | 7 ------- src/libstore/meson.build | 17 +++++++++++++++++ src/libstore/s3-binary-cache-store.cc | 4 ++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 4f66a3efc..5c22ed176 100644 --- a/configure.ac +++ b/configure.ac @@ -340,13 +340,6 @@ AC_CHECK_HEADERS([aws/s3/S3Client.h], AC_SUBST(ENABLE_S3, [$enable_s3]) AC_LANG_POP(C++) -if test -n "$enable_s3"; then - declare -a aws_version_tokens=($(printf '#include \nAWS_SDK_VERSION_STRING' | $CPP $CPPFLAGS - | grep -v '^#.*' | sed 's/"//g' | tr '.' ' ')) - AC_DEFINE_UNQUOTED([AWS_VERSION_MAJOR], ${aws_version_tokens@<:@0@:>@}, [Major version of aws-sdk-cpp.]) - AC_DEFINE_UNQUOTED([AWS_VERSION_MINOR], ${aws_version_tokens@<:@1@:>@}, [Minor version of aws-sdk-cpp.]) - AC_DEFINE_UNQUOTED([AWS_VERSION_PATCH], ${aws_version_tokens@<:@2@:>@}, [Patch version of aws-sdk-cpp.]) -fi - # Whether to use the Boehm garbage collector. AC_ARG_ENABLE(gc, AS_HELP_STRING([--enable-gc],[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]), diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 3b7f7a57a..cb8110f3f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -100,6 +100,23 @@ deps_public += nlohmann_json sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') deps_private += sqlite +# AWS C++ SDK has bad pkg-config +aws_s3 = dependency('aws-cpp-sdk-s3', required : false) +configdata.set('ENABLE_S3', aws_s3.found().to_int()) +if aws_s3.found() + aws_s3 = declare_dependency( + include_directories: include_directories(aws_s3.get_variable('includedir')), + link_args: [ + '-L' + aws_s3.get_variable('libdir'), + '-laws-cpp-sdk-transfer', + '-laws-cpp-sdk-s3', + '-laws-cpp-sdk-core', + '-laws-crt-cpp', + ], + ) +endif +deps_other += aws_s3 + subdir('build-utils-meson/generate-header') generated_headers = [] diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 1a0ec1111..92ab47cd6 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -60,7 +60,7 @@ class AwsLogger : public Aws::Utils::Logging::FormattedLogSystem debug("AWS: %s", chomp(statement)); } -#if !(AWS_VERSION_MAJOR <= 1 && AWS_VERSION_MINOR <= 7 && AWS_VERSION_PATCH <= 115) +#if !(AWS_SDK_VERSION_MAJOR <= 1 && AWS_SDK_VERSION_MINOR <= 7 && AWS_SDK_VERSION_PATCH <= 115) void Flush() override {} #endif }; @@ -103,7 +103,7 @@ S3Helper::S3Helper( std::make_shared(profile.c_str())), *config, // FIXME: https://github.com/aws/aws-sdk-cpp/issues/759 -#if AWS_VERSION_MAJOR == 1 && AWS_VERSION_MINOR < 3 +#if AWS_SDK_VERSION_MAJOR == 1 && AWS_SDK_VERSION_MINOR < 3 false, #else Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, From f2e0cecf34f6e3b23c2499bb65478a620c4340aa Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 22 Jul 2024 17:45:19 +0200 Subject: [PATCH 647/910] tests/functional/lang-gc: Disable for now --- tests/functional/lang-gc.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/lang-gc.sh b/tests/functional/lang-gc.sh index 8e2383854..1746fa4c1 100644 --- a/tests/functional/lang-gc.sh +++ b/tests/functional/lang-gc.sh @@ -9,6 +9,8 @@ source common.sh set -o pipefail +skipTest "Too memory instensive for CI. Attempt to reduce memory usage was unsuccessful, because it made detection of the bug unreliable." + # Regression test for #11141. The stack pointer corrector assigned the base # instead of the top (which resides at the low end of the stack). Sounds confusing? # Stacks grow downwards, so that's why this mistake happened. From d3cee8160cecf4902f7f26b1dbfa0110a6a195bd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 11:33:37 -0400 Subject: [PATCH 648/910] Add missing threads deps --- src/libcmd/meson.build | 2 ++ src/libexpr-c/meson.build | 2 ++ src/libfetchers/meson.build | 2 ++ src/libflake/meson.build | 2 ++ src/libmain-c/meson.build | 2 ++ src/libmain/meson.build | 1 + src/libstore-c/meson.build | 2 ++ src/libutil-c/meson.build | 2 ++ src/nix/meson.build | 2 ++ tests/unit/libexpr-support/meson.build | 2 ++ tests/unit/libexpr/meson.build | 2 ++ tests/unit/libfetchers/meson.build | 2 ++ tests/unit/libflake/meson.build | 2 ++ tests/unit/libstore-support/meson.build | 2 ++ tests/unit/libstore/meson.build | 2 ++ tests/unit/libutil-support/meson.build | 2 ++ tests/unit/libutil/meson.build | 2 ++ 17 files changed, 33 insertions(+) diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index 687b37aac..c484cf998 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -30,6 +30,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 2a2669b3e..6db5b83b8 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -29,6 +29,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index c39fe99f3..d4f202796 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -26,6 +26,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 38d70c678..d2bb179df 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -26,6 +26,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libmain-c/meson.build b/src/libmain-c/meson.build index 1d6b2f959..345382712 100644 --- a/src/libmain-c/meson.build +++ b/src/libmain-c/meson.build @@ -29,6 +29,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libmain/meson.build b/src/libmain/meson.build index fe6133596..7fcadf06d 100644 --- a/src/libmain/meson.build +++ b/src/libmain/meson.build @@ -26,6 +26,7 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') pubsetbuf_test = ''' #include diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 917de4cda..4bfd944c6 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -27,6 +27,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 3f0d96282..b5ed19631 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/nix/meson.build b/src/nix/meson.build index b42f6169b..53bb083a9 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -28,6 +28,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') add_project_arguments( diff --git a/tests/unit/libexpr-support/meson.build b/tests/unit/libexpr-support/meson.build index 705672204..4f50478aa 100644 --- a/tests/unit/libexpr-support/meson.build +++ b/tests/unit/libexpr-support/meson.build @@ -27,6 +27,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/tests/unit/libexpr/meson.build b/tests/unit/libexpr/meson.build index ee35258cf..21c321334 100644 --- a/tests/unit/libexpr/meson.build +++ b/tests/unit/libexpr/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') diff --git a/tests/unit/libfetchers/meson.build b/tests/unit/libfetchers/meson.build index d2de93829..dc9818e27 100644 --- a/tests/unit/libfetchers/meson.build +++ b/tests/unit/libfetchers/meson.build @@ -24,6 +24,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') diff --git a/tests/unit/libflake/meson.build b/tests/unit/libflake/meson.build index 2d6bbca0f..c022d7f41 100644 --- a/tests/unit/libflake/meson.build +++ b/tests/unit/libflake/meson.build @@ -24,6 +24,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') diff --git a/tests/unit/libstore-support/meson.build b/tests/unit/libstore-support/meson.build index ddb067c1b..f09d26a31 100644 --- a/tests/unit/libstore-support/meson.build +++ b/tests/unit/libstore-support/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/tests/unit/libstore/meson.build b/tests/unit/libstore/meson.build index 8534ba8c5..3b36cd62f 100644 --- a/tests/unit/libstore/meson.build +++ b/tests/unit/libstore/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build index 7d0e9c2fc..6be4972c6 100644 --- a/tests/unit/libutil-support/meson.build +++ b/tests/unit/libutil-support/meson.build @@ -23,6 +23,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index 4f055cabd..7f024e6f2 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') From d39bbcabb99415db916e8d9abf8e82eccc77e0e0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 11:44:53 -0400 Subject: [PATCH 649/910] Fix some BSD builds missing pthread functions In addition to adding the missing thread deps in the last commit, we also appear to need to skip `-Wl,--as-needed` flags that Meson wants to use, but doesn't work with our *BSD toolchains. See https://github.com/mesonbuild/meson/issues/3593 --- packaging/dependencies.nix | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 73ba9cd58..f09ca5d18 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -61,6 +61,12 @@ let workDir = null; }; + # Work around weird `--as-needed` linker behavior with BSD, see + # https://github.com/mesonbuild/meson/issues/3593 + bsdNoLinkAsNeeded = finalAttrs: prevAttrs: lib.optionalAttrs stdenv.hostPlatform.isBSD { + mesonFlags = [ (lib.mesonBool "b_asneeded" false) ] ++ prevAttrs.mesonFlags or []; + }; + in scope: { inherit stdenv versionSuffix; @@ -130,5 +136,8 @@ scope: { inherit resolvePath filesetToSource; - mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); + mkMesonDerivation = f: stdenv.mkDerivation + (lib.extends + (lib.composeExtensions bsdNoLinkAsNeeded localSourceLayer) + f); } From 4457cebe05c444facc68db281c58f90a87f785ce Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 10:24:18 +0200 Subject: [PATCH 650/910] Update comment in tests//vars-and-functions.sh Co-authored-by: tomberek --- tests/functional/common/vars-and-functions.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 7a399f6d4..954435593 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -306,7 +306,8 @@ onError() { # Example (showns as string): 'repl.sh:123: in call to grepQuiet: ' # This function is inefficient, so it should only be used in error messages. callerPrefix() { - # Find the closes caller that's not from this file + # Find the closest caller that's not from this file + # using the bash `caller` builtin. local i file line fn savedFn # Use `caller` for i in $(seq 0 100); do From 498eed0a25ee1707677d56e0ea3628584991453b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Jul 2024 10:56:29 +0200 Subject: [PATCH 651/910] ~FramedSource(): Don't throw an exception if the remote has disconnected This would cause the daemon to crash with a call to terminate(). --- src/libutil/serialise.hh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 8254a5f89..8137db5f4 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -483,13 +483,17 @@ struct FramedSource : Source ~FramedSource() { - if (!eof) { - while (true) { - auto n = readInt(from); - if (!n) break; - std::vector data(n); - from(data.data(), n); + try { + if (!eof) { + while (true) { + auto n = readInt(from); + if (!n) break; + std::vector data(n); + from(data.data(), n); + } } + } catch (...) { + ignoreException(); } } From eb89e50cbb4584bb42a425089640b2931b723cca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:21:43 +0200 Subject: [PATCH 652/910] Rejiggle getGCCycles() for buildNoGc --- src/libexpr/eval-gc.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 97b24e3fb..73ab809e5 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -206,10 +206,17 @@ static inline void initGCReal() } } +static size_t gcCyclesAfterInit = 0; + +size_t getGCCycles() +{ + assertGCInitialized(); + return static_cast(GC_get_gc_no()) - gcCyclesAfterInit; +} + #endif static bool gcInitialised = false; -static GC_word gcCyclesAfterInit = 0; void initGC() { @@ -218,10 +225,11 @@ void initGC() #if HAVE_BOEHMGC initGCReal(); + + gcCyclesAfterInit = GC_get_gc_no(); #endif gcInitialised = true; - gcCyclesAfterInit = GC_get_gc_no(); } void assertGCInitialized() @@ -229,10 +237,4 @@ void assertGCInitialized() assert(gcInitialised); } -size_t getGCCycles() -{ - assertGCInitialized(); - return GC_get_gc_no() - gcCyclesAfterInit; -} - } // namespace nix From 5d6bc484beab6ff822815b5b26745710d8c86305 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:24:43 +0200 Subject: [PATCH 653/910] Hide getGCCycles when we have no GC Alternatively, we could make it return 0, but we don't need it in the first place because the caller exists conditionally too. --- src/libexpr/eval-gc.hh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval-gc.hh b/src/libexpr/eval-gc.hh index 76f019729..005175eb7 100644 --- a/src/libexpr/eval-gc.hh +++ b/src/libexpr/eval-gc.hh @@ -15,9 +15,11 @@ void initGC(); */ void assertGCInitialized(); +#ifdef HAVE_BOEHMGC /** * The number of GC cycles since initGC(). */ size_t getGCCycles(); +#endif -} +} // namespace nix From 2b4e3f04a433d96e779fb91fcd9fa01329413d02 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:57:45 +0200 Subject: [PATCH 654/910] markdown.cc: Format Slightly custom because the automated formatting messes up the braced initializer with named fields. --- src/libcmd/markdown.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index 88c3f640b..882f95faa 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -4,8 +4,8 @@ #include "terminal.hh" #if HAVE_LOWDOWN -# include -# include +# include +# include #endif namespace nix { @@ -15,7 +15,8 @@ std::string renderMarkdownToTerminal(std::string_view markdown) #if HAVE_LOWDOWN int windowWidth = getWindowSize().second; - struct lowdown_opts opts { + struct lowdown_opts opts + { .type = LOWDOWN_TERM, .maxdepth = 20, .cols = (size_t) std::max(windowWidth - 5, 60), From 97b0114ab84144e3574c74c7fb0e0e38681f3b6a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:54:04 +0200 Subject: [PATCH 655/910] renderMarkdownToTerminal: Add _NIX_TEST_RAW_MARKDOWN env var For testing only. --- src/libcmd/markdown.cc | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index 882f95faa..6a0d05d9f 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -1,5 +1,6 @@ #include "markdown.hh" -#include "util.hh" +#include "environment-variables.hh" +#include "error.hh" #include "finally.hh" #include "terminal.hh" @@ -10,9 +11,9 @@ namespace nix { -std::string renderMarkdownToTerminal(std::string_view markdown) -{ #if HAVE_LOWDOWN +static std::string doRenderMarkdownToTerminal(std::string_view markdown) +{ int windowWidth = getWindowSize().second; struct lowdown_opts opts @@ -52,9 +53,21 @@ std::string renderMarkdownToTerminal(std::string_view markdown) throw Error("allocation error while rendering Markdown"); return filterANSIEscapes(std::string(buf->data, buf->size), !isTTY()); -#else - return std::string(markdown); -#endif } +std::string renderMarkdownToTerminal(std::string_view markdown) +{ + if (auto e = getEnv("_NIX_TEST_RAW_MARKDOWN"); e && *e == "1") + return std::string(markdown); + else + return doRenderMarkdownToTerminal(markdown); } + +#else +std::string renderMarkdownToTerminal(std::string_view markdown) +{ + return std::string(markdown); +} +#endif + +} // namespace nix From 712ce2feacdc0cde7249ae6da5f8063858e80ae2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 17:50:51 +0200 Subject: [PATCH 656/910] ReadlineLikeInteracter::getLine: Add _NIX_TEST_REPL_ECHO env var ... for testing --- src/libcmd/repl-interacter.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index b285c8a9a..a12e7d8c7 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -19,6 +19,7 @@ extern "C" { #include "repl-interacter.hh" #include "file-system.hh" #include "repl.hh" +#include "environment-variables.hh" namespace nix { @@ -179,6 +180,19 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT return false; input += s; input += '\n'; + +#ifndef USE_READLINE + // editline doesn't echo the input to the output when non-interactive, unlike readline + // this results in a different behavior when running tests. The echoing is + // quite useful for reading the test output, so we add it here. + if (auto e = getEnv("_NIX_TEST_REPL_ECHO"); s && e && *e == "1") + { + // This is probably not right for multi-line input, but we don't use that + // in the characterisation tests, so it's fine. + std::cout << "nix-repl> " << s << std::endl; + } +#endif + return true; } From ca2cc26e12d7264fb141206e0bc35ade2378b8f8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 17:52:28 +0200 Subject: [PATCH 657/910] tests/functional/repl: Improve precision and readability ... as well as match buildReadlineNoMarkdown. Unfortunately it doesn't support long inputs or multiline inputs for now. This needs to make better use of the interacter interface. --- src/libcmd/repl-interacter.cc | 16 ++--- tests/functional/repl.sh | 11 ++++ .../repl/doc-comment-curried-args.expected | 26 ++++---- .../repl/doc-comment-curried-args.in | 2 +- .../repl/doc-comment-formals.expected | 13 ++-- .../repl/doc-comment-function.expected | 7 ++- tests/functional/repl/doc-compact.expected | 12 ++-- tests/functional/repl/doc-constant.expected | 60 +++++++++++-------- tests/functional/repl/doc-floatedIn.expected | 12 ++-- .../repl/doc-lambda-flavors.expected | 36 +++++------ .../functional/repl/doc-measurement.expected | 12 ++-- tests/functional/repl/doc-multiply.expected | 22 ++++--- .../functional/repl/doc-unambiguous.expected | 12 ++-- .../repl/pretty-print-idempotent.expected | 12 +++- 14 files changed, 154 insertions(+), 99 deletions(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index a12e7d8c7..76fe38780 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -176,22 +176,22 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT return true; } - if (!s) - return false; - input += s; - input += '\n'; - -#ifndef USE_READLINE // editline doesn't echo the input to the output when non-interactive, unlike readline // this results in a different behavior when running tests. The echoing is // quite useful for reading the test output, so we add it here. if (auto e = getEnv("_NIX_TEST_REPL_ECHO"); s && e && *e == "1") { +#ifndef USE_READLINE // This is probably not right for multi-line input, but we don't use that // in the characterisation tests, so it's fine. - std::cout << "nix-repl> " << s << std::endl; - } + std::cout << promptForType(promptType) << s << std::endl; #endif + } + + if (!s) + return false; + input += s; + input += '\n'; return true; } diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index 4f5bb36aa..a6c075abd 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -262,6 +262,14 @@ badExitCode=0 nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" +# I couldn't get readline and editline to agree on the newline before the prompt, +# so let's just force it to be one empty line. Ideally we get the two to agree +# or use a simpler interacter for testing. +stripEmptyLinesBeforePrompt() { + # --null-data: treat input as NUL-terminated instead of newline-terminated + sed --null-data 's/\n\n*nix-repl>/\n\nnix-repl>/g' +} + runRepl () { # That is right, we are also filtering out the testdir _without underscores_. @@ -273,8 +281,11 @@ runRepl () { testDirNoUnderscores="${testDir//_/}" # TODO: pass arguments to nix repl; see lang.sh + _NIX_TEST_RAW_MARKDOWN=1 \ + _NIX_TEST_REPL_ECHO=1 \ nix repl 2>&1 \ | stripColors \ + | stripEmptyLinesBeforePrompt \ | sed \ -e "s@$testDir@/path/to/tests/functional@g" \ -e "s@$testDirNoUnderscores@/path/to/tests/functional@g" \ diff --git a/tests/functional/repl/doc-comment-curried-args.expected b/tests/functional/repl/doc-comment-curried-args.expected index c10c171e1..f97f593c6 100644 --- a/tests/functional/repl/doc-comment-curried-args.expected +++ b/tests/functional/repl/doc-comment-curried-args.expected @@ -1,24 +1,30 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function curriedArgs - … defined at - /path/to/tests/functional/repl/doc-comments.nix:48:5 +nix-repl> :doc curriedArgs +Function `curriedArgs`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:48:5 - A documented function. +A documented function. +nix-repl> x = curriedArgs 1 +nix-repl> "Note that users may not expect this to behave as it currently does" "Note that users may not expect this to behave as it currently does" -Function curriedArgs - … defined at - /path/to/tests/functional/repl/doc-comments.nix:50:5 +nix-repl> :doc x +Function `curriedArgs`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:50:5 - The function returned by applying once +The function returned by applying once -"This won't produce documentation, because we can't actually document arbitrary values" +nix-repl> "This won't produce docs; no support for arbitrary values" +"This won't produce docs; no support for arbitrary values" +nix-repl> :doc x 2 error: value does not have documentation - +nix-repl> diff --git a/tests/functional/repl/doc-comment-curried-args.in b/tests/functional/repl/doc-comment-curried-args.in index 8dbbfc370..06ba21dcc 100644 --- a/tests/functional/repl/doc-comment-curried-args.in +++ b/tests/functional/repl/doc-comment-curried-args.in @@ -3,5 +3,5 @@ x = curriedArgs 1 "Note that users may not expect this to behave as it currently does" :doc x -"This won't produce documentation, because we can't actually document arbitrary values" +"This won't produce docs; no support for arbitrary values" :doc x 2 diff --git a/tests/functional/repl/doc-comment-formals.expected b/tests/functional/repl/doc-comment-formals.expected index 704c0050b..4c9757f89 100644 --- a/tests/functional/repl/doc-comment-formals.expected +++ b/tests/functional/repl/doc-comment-formals.expected @@ -1,13 +1,16 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. +nix-repl> "Note that this is not yet complete" "Note that this is not yet complete" -Function documentedFormals - … defined at - /path/to/tests/functional/repl/doc-comments.nix:57:5 - - Finds x +nix-repl> :doc documentedFormals +Function `documentedFormals`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:57:5 +Finds x +nix-repl> diff --git a/tests/functional/repl/doc-comment-function.expected b/tests/functional/repl/doc-comment-function.expected index 5ec465a96..9718ccc66 100644 --- a/tests/functional/repl/doc-comment-function.expected +++ b/tests/functional/repl/doc-comment-function.expected @@ -1,8 +1,9 @@ Nix Type :? for help. -Function defined at - /path/to/tests/functional/repl/doc-comment-function.nix:2:1 - A doc comment for a file that only contains a function +nix-repl> :doc import ./doc-comment-function.nix +Function defined at /path/to/tests/functional/repl/doc-comment-function.nix:2:1 +A doc comment for a file that only contains a function +nix-repl> diff --git a/tests/functional/repl/doc-compact.expected b/tests/functional/repl/doc-compact.expected index 4b05b653c..17603d813 100644 --- a/tests/functional/repl/doc-compact.expected +++ b/tests/functional/repl/doc-compact.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function compact - … defined at - /path/to/tests/functional/repl/doc-comments.nix:18:20 - - boom +nix-repl> :doc compact +Function `compact`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:18:20 +boom +nix-repl> diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected index c66558333..e0092453e 100644 --- a/tests/functional/repl/doc-constant.expected +++ b/tests/functional/repl/doc-constant.expected @@ -1,23 +1,27 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. +nix-repl> :doc constant error: value does not have documentation -Attribute version +nix-repl> :doc lib.version +Attribute `version` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:30:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:30:3 - Immovably fixed. +Immovably fixed. -Attribute empty +nix-repl> :doc lib.attr.empty +Attribute `empty` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:33:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:33:3 - Unchangeably constant. +Unchangeably constant. +nix-repl> :doc lib.attr.undocument error: … while evaluating the attribute 'attr.undocument' at /path/to/tests/functional/repl/doc-comments.nix:33:3: @@ -32,59 +36,65 @@ error: | ^ Did you mean undocumented? -Attribute constant +nix-repl> :doc (import ./doc-comments.nix).constant +Attribute `constant` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:27:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:27:3 - Firmly rigid. +Firmly rigid. -Attribute version +nix-repl> :doc (import ./doc-comments.nix).lib.version +Attribute `version` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:30:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:30:3 - Immovably fixed. +Immovably fixed. -Attribute empty +nix-repl> :doc (import ./doc-comments.nix).lib.attr.empty +Attribute `empty` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:33:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:33:3 - Unchangeably constant. +Unchangeably constant. -Attribute undocumented +nix-repl> :doc (import ./doc-comments.nix).lib.attr.undocumented +Attribute `undocumented` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:35:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:35:3 - No documentation found. +No documentation found. +nix-repl> :doc missing error: undefined variable 'missing' at «string»:1:1: 1| missing | ^ +nix-repl> :doc constanz error: undefined variable 'constanz' at «string»:1:1: 1| constanz | ^ +nix-repl> :doc missing.attr error: undefined variable 'missing' at «string»:1:1: 1| missing.attr | ^ +nix-repl> :doc lib.missing error: attribute 'missing' missing at «string»:1:1: 1| lib.missing | ^ +nix-repl> :doc lib.missing.attr error: attribute 'missing' missing at «string»:1:1: 1| lib.missing.attr | ^ +nix-repl> :doc lib.attr.undocumental error: … while evaluating the attribute 'attr.undocumental' at /path/to/tests/functional/repl/doc-comments.nix:33:3: @@ -99,4 +109,4 @@ error: | ^ Did you mean undocumented? - +nix-repl> diff --git a/tests/functional/repl/doc-floatedIn.expected b/tests/functional/repl/doc-floatedIn.expected index 30f135725..d3f1c3f65 100644 --- a/tests/functional/repl/doc-floatedIn.expected +++ b/tests/functional/repl/doc-floatedIn.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function floatedIn - … defined at - /path/to/tests/functional/repl/doc-comments.nix:16:5 - - This also works. +nix-repl> :doc floatedIn +Function `floatedIn`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:16:5 +This also works. +nix-repl> diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected index 43f483ce9..268cac05e 100644 --- a/tests/functional/repl/doc-lambda-flavors.expected +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -1,29 +1,31 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function nonStrict - … defined at - /path/to/tests/functional/repl/doc-comments.nix:37:70 +nix-repl> :doc nonStrict +Function `nonStrict`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:37:70 - My syntax is not strict, but I'm strict anyway. +My syntax is not strict, but I'm strict anyway. -Function strict - … defined at - /path/to/tests/functional/repl/doc-comments.nix:38:63 +nix-repl> :doc strict +Function `strict`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:38:63 - I don't have to be strict, but I am anyway. +I don't have to be strict, but I am anyway. -Function strictPre - … defined at - /path/to/tests/functional/repl/doc-comments.nix:40:48 +nix-repl> :doc strictPre +Function `strictPre`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:40:48 - Here's one way to do this +Here's one way to do this -Function strictPost - … defined at - /path/to/tests/functional/repl/doc-comments.nix:41:53 - - Here's another way to do this +nix-repl> :doc strictPost +Function `strictPost`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:41:53 +Here's another way to do this +nix-repl> diff --git a/tests/functional/repl/doc-measurement.expected b/tests/functional/repl/doc-measurement.expected index 8598aaedb..14ae11e80 100644 --- a/tests/functional/repl/doc-measurement.expected +++ b/tests/functional/repl/doc-measurement.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function measurement - … defined at - /path/to/tests/functional/repl/doc-comments.nix:13:17 - - 👈 precisely this wide 👉 +nix-repl> :doc measurement +Function `measurement`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:13:17 +👈 precisely this wide 👉 +nix-repl> diff --git a/tests/functional/repl/doc-multiply.expected b/tests/functional/repl/doc-multiply.expected index db82af91f..5cefda516 100644 --- a/tests/functional/repl/doc-multiply.expected +++ b/tests/functional/repl/doc-multiply.expected @@ -1,15 +1,19 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function multiply - … defined at - /path/to/tests/functional/repl/doc-comments.nix:10:14 - - Perform arithmetic multiplication. It's kind of like - repeated addition, very neat. - - | multiply 2 3 - | => 6 +nix-repl> :doc multiply +Function `multiply`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:10:14 +Perform *arithmetic* multiplication. It's kind of like repeated **addition**, very neat. + +```nix +multiply 2 3 +=> 6 +``` + +nix-repl> diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected index 825aa1ee1..0872750a1 100644 --- a/tests/functional/repl/doc-unambiguous.expected +++ b/tests/functional/repl/doc-unambiguous.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function unambiguous - … defined at - /path/to/tests/functional/repl/doc-comments.nix:24:5 - - Very close +nix-repl> :doc unambiguous +Function `unambiguous`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:24:5 +Very close +nix-repl> diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index f38b9b569..f09aec88d 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -1,29 +1,39 @@ Nix Type :? for help. + +nix-repl> :l pretty-print-idempotent.nix Added variables. +nix-repl> oneDeep { homepage = "https://example.com"; } +nix-repl> oneDeep { homepage = "https://example.com"; } +nix-repl> twoDeep { layerOne = { ... }; } +nix-repl> twoDeep { layerOne = { ... }; } +nix-repl> oneDeepList [ "https://example.com" ] +nix-repl> oneDeepList [ "https://example.com" ] +nix-repl> twoDeepList [ [ ... ] ] +nix-repl> twoDeepList [ [ ... ] ] - +nix-repl> From c4ae9bb45b72c852ecff8bf5d5153951116e2039 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 01:01:00 +0200 Subject: [PATCH 658/910] tests/functional/repl: Normalize final prompt --- tests/functional/repl.sh | 15 +++++++++++++-- .../repl/doc-comment-curried-args.expected | 2 -- .../functional/repl/doc-comment-formals.expected | 2 -- .../functional/repl/doc-comment-function.expected | 2 -- tests/functional/repl/doc-compact.expected | 2 -- tests/functional/repl/doc-constant.expected | 2 -- tests/functional/repl/doc-floatedIn.expected | 2 -- tests/functional/repl/doc-lambda-flavors.expected | 2 -- tests/functional/repl/doc-measurement.expected | 2 -- tests/functional/repl/doc-multiply.expected | 2 -- tests/functional/repl/doc-unambiguous.expected | 2 -- .../repl/pretty-print-idempotent.expected | 2 -- 12 files changed, 13 insertions(+), 24 deletions(-) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index a6c075abd..a149916ac 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -262,14 +262,23 @@ badExitCode=0 nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" +# TODO: write a repl interacter for testing. Papering over the differences between readline / editline and between platforms is a pain. + # I couldn't get readline and editline to agree on the newline before the prompt, -# so let's just force it to be one empty line. Ideally we get the two to agree -# or use a simpler interacter for testing. +# so let's just force it to be one empty line. stripEmptyLinesBeforePrompt() { # --null-data: treat input as NUL-terminated instead of newline-terminated sed --null-data 's/\n\n*nix-repl>/\n\nnix-repl>/g' } +# We don't get a final prompt on darwin, so we strip this as well. +stripFinalPrompt() { + # Strip the final prompt and/or any trailing spaces + sed --null-data \ + -e 's/\(.*[^\n]\)\n\n*nix-repl>[ \n]*$/\1/' \ + -e 's/[ \n]*$/\n/' +} + runRepl () { # That is right, we are also filtering out the testdir _without underscores_. @@ -285,7 +294,9 @@ runRepl () { _NIX_TEST_REPL_ECHO=1 \ nix repl 2>&1 \ | stripColors \ + | tr -d '\0' \ | stripEmptyLinesBeforePrompt \ + | stripFinalPrompt \ | sed \ -e "s@$testDir@/path/to/tests/functional@g" \ -e "s@$testDirNoUnderscores@/path/to/tests/functional@g" \ diff --git a/tests/functional/repl/doc-comment-curried-args.expected b/tests/functional/repl/doc-comment-curried-args.expected index f97f593c6..56607e911 100644 --- a/tests/functional/repl/doc-comment-curried-args.expected +++ b/tests/functional/repl/doc-comment-curried-args.expected @@ -26,5 +26,3 @@ nix-repl> "This won't produce docs; no support for arbitrary values" nix-repl> :doc x 2 error: value does not have documentation - -nix-repl> diff --git a/tests/functional/repl/doc-comment-formals.expected b/tests/functional/repl/doc-comment-formals.expected index 4c9757f89..1024919f4 100644 --- a/tests/functional/repl/doc-comment-formals.expected +++ b/tests/functional/repl/doc-comment-formals.expected @@ -12,5 +12,3 @@ Function `documentedFormals`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:57:5 Finds x - -nix-repl> diff --git a/tests/functional/repl/doc-comment-function.expected b/tests/functional/repl/doc-comment-function.expected index 9718ccc66..3889c4f78 100644 --- a/tests/functional/repl/doc-comment-function.expected +++ b/tests/functional/repl/doc-comment-function.expected @@ -5,5 +5,3 @@ nix-repl> :doc import ./doc-comment-function.nix Function defined at /path/to/tests/functional/repl/doc-comment-function.nix:2:1 A doc comment for a file that only contains a function - -nix-repl> diff --git a/tests/functional/repl/doc-compact.expected b/tests/functional/repl/doc-compact.expected index 17603d813..79f1fd44f 100644 --- a/tests/functional/repl/doc-compact.expected +++ b/tests/functional/repl/doc-compact.expected @@ -9,5 +9,3 @@ Function `compact`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:18:20 boom - -nix-repl> diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected index e0092453e..5787e04dc 100644 --- a/tests/functional/repl/doc-constant.expected +++ b/tests/functional/repl/doc-constant.expected @@ -108,5 +108,3 @@ error: 1| lib.attr.undocumental | ^ Did you mean undocumented? - -nix-repl> diff --git a/tests/functional/repl/doc-floatedIn.expected b/tests/functional/repl/doc-floatedIn.expected index d3f1c3f65..82bb80b95 100644 --- a/tests/functional/repl/doc-floatedIn.expected +++ b/tests/functional/repl/doc-floatedIn.expected @@ -9,5 +9,3 @@ Function `floatedIn`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:16:5 This also works. - -nix-repl> diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected index 268cac05e..ab5c95639 100644 --- a/tests/functional/repl/doc-lambda-flavors.expected +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -27,5 +27,3 @@ Function `strictPost`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:41:53 Here's another way to do this - -nix-repl> diff --git a/tests/functional/repl/doc-measurement.expected b/tests/functional/repl/doc-measurement.expected index 14ae11e80..555cac9a2 100644 --- a/tests/functional/repl/doc-measurement.expected +++ b/tests/functional/repl/doc-measurement.expected @@ -9,5 +9,3 @@ Function `measurement`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:13:17 👈 precisely this wide 👉 - -nix-repl> diff --git a/tests/functional/repl/doc-multiply.expected b/tests/functional/repl/doc-multiply.expected index 5cefda516..21523e24c 100644 --- a/tests/functional/repl/doc-multiply.expected +++ b/tests/functional/repl/doc-multiply.expected @@ -15,5 +15,3 @@ Perform *arithmetic* multiplication. It's kind of like repeated **addition**, ve multiply 2 3 => 6 ``` - -nix-repl> diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected index 0872750a1..0db5505d7 100644 --- a/tests/functional/repl/doc-unambiguous.expected +++ b/tests/functional/repl/doc-unambiguous.expected @@ -9,5 +9,3 @@ Function `unambiguous`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:24:5 Very close - -nix-repl> diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index f09aec88d..311855dae 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -35,5 +35,3 @@ nix-repl> twoDeepList [ [ ... ] ] - -nix-repl> From 6e680a664421927cd7d99f956a1e8c4b5bdc7db6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 01:01:14 +0200 Subject: [PATCH 659/910] tests/functional/repl: Improve failure reporting --- tests/functional/repl.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index a149916ac..706e0f5db 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -311,7 +311,10 @@ for test in $(cd "$testDir/repl"; echo *.in); do in="$testDir/repl/$test.in" actual="$testDir/repl/$test.actual" expected="$testDir/repl/$test.expected" - (cd "$testDir/repl"; set +x; runRepl 2>&1) < "$in" > "$actual" + (cd "$testDir/repl"; set +x; runRepl 2>&1) < "$in" > "$actual" || { + echo "FAIL: $test (exit code $?)" >&2 + badExitCode=1 + } diffAndAcceptInner "$test" "$actual" "$expected" done From 7d4d34a27d8d95e8a63d3b3940bd0ee7b623ff82 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 01:02:21 +0200 Subject: [PATCH 660/910] eval-gc.cc: Fix warning --- src/libexpr/eval-gc.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 73ab809e5..1bc8cd28f 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -84,7 +84,9 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) { void *& sp = *sp_ptr; auto pthread_id = reinterpret_cast(_pthread_id); +# ifndef __APPLE__ pthread_attr_t pattr; +# endif size_t osStackSize; void * osStackLow; void * osStackBase; From 0bd2d363750db35926460cbc4c3e62dcb1d09bf5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 12:53:37 +0200 Subject: [PATCH 661/910] Document renderMarkdownToTerminal --- src/libcmd/markdown.hh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libcmd/markdown.hh b/src/libcmd/markdown.hh index a04d32a4f..164f99b98 100644 --- a/src/libcmd/markdown.hh +++ b/src/libcmd/markdown.hh @@ -5,6 +5,13 @@ namespace nix { +/** + * Render the given Markdown text to the terminal. + * + * If Nix is compiled without Markdown support, this function will return the input text as-is. + * + * The renderer takes into account the terminal width, and wraps text accordingly. + */ std::string renderMarkdownToTerminal(std::string_view markdown); } From e48e0cbab0eed9b9174c27d3f3ddfa1afff5186b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 12:54:40 +0200 Subject: [PATCH 662/910] markdown.hh: Improve includes --- src/libcmd/markdown.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcmd/markdown.hh b/src/libcmd/markdown.hh index 164f99b98..66db1736c 100644 --- a/src/libcmd/markdown.hh +++ b/src/libcmd/markdown.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "types.hh" +#include namespace nix { From 907b0a371abe92b3314612faea0b3d68e048c156 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:02:40 +0200 Subject: [PATCH 663/910] Rename osStackLow -> osStackLimit This is in accordance with ARM's naming convention. "Low" is confusing, because it could refer to either the cold end of the stack as an abstract data type, or a low address. These are different places, because the stack grows down through the address space. --- src/libexpr/eval-gc.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 73ab809e5..75368c5ac 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -86,12 +86,13 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) auto pthread_id = reinterpret_cast(_pthread_id); pthread_attr_t pattr; size_t osStackSize; - void * osStackLow; + // The low address of the stack, which grows down. + void * osStackLimit; void * osStackBase; # ifdef __APPLE__ osStackSize = pthread_get_stacksize_np(pthread_id); - osStackLow = pthread_get_stackaddr_np(pthread_id); + osStackLimit = pthread_get_stackaddr_np(pthread_id); # else if (pthread_attr_init(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); @@ -110,18 +111,18 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) # else # error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" # endif - if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { + if (pthread_attr_getstack(&pattr, &osStackLimit, &osStackSize)) { throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); } if (pthread_attr_destroy(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); } # endif - osStackBase = (char *) osStackLow + osStackSize; + osStackBase = (char *) osStackLimit + osStackSize; // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. - if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack - sp = osStackLow; + if (sp >= osStackBase || sp < osStackLimit) { // lo is outside the os stack + sp = osStackLimit; } } From 68693276f9c5829489132c1f5a142717da84f44a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:27:26 +0200 Subject: [PATCH 664/910] Update fixupBoehmStackPointer doc lo might have made sense in the bdwgc code, maybe?, but not here. --- src/libexpr/eval-gc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 75368c5ac..ba19cd74e 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -121,7 +121,7 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) osStackBase = (char *) osStackLimit + osStackSize; // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. - if (sp >= osStackBase || sp < osStackLimit) { // lo is outside the os stack + if (sp >= osStackBase || sp < osStackLimit) { // sp is outside the os stack sp = osStackLimit; } } From a16df88252a34160fabe7c2710ba11ca51d9fee1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:18:50 +0200 Subject: [PATCH 665/910] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9?narHash=sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm%2B504Ch3sNKLd8%3D' (2023-01-17) → 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33?narHash=sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U%3D' (2023-10-04) • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/9126214d0a59633752a136528f5f3b9aa8565b7d?narHash=sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm%2BGpZNw%3D' (2024-04-01) → 'github:hercules-ci/flake-parts/9227223f6d922fee3c7b190b2cc238a99527bbb7?narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D' (2024-07-03) • Updated input 'libgit2': 'github:libgit2/libgit2/45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5?narHash=sha256-oX4Z3S9WtJlwvj0uH9HlYcWv%2Bx1hqp8mhXl7HsLu2f0%3D' (2023-10-18) → 'github:libgit2/libgit2/503b66cf00ad7dca940148529f60b1a409ccc462?narHash=sha256-tDUQi%2Bs8sxJ30SmUH7Ln9WmDz5jGatlgKumjwi7KnCo%3D' (2024-07-17) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/be3ca229c85e978880babdeda9748b14e6aa008f?narHash=sha256-L61BXz7n/yNzOeZ3FqlnUmxj4145JOVeq9fvQTQzbNM%3D' (2024-07-21) → 'github:NixOS/nixpkgs/d0907b75146a0ccc1ec0d6c3db287ec287588ef6?narHash=sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/%2BWqrnGEw%3D' (2024-07-24) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/40e6053ecb65fcbf12863338a6dcefb3f55f1bf8?narHash=sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y%3D' (2024-04-12) → 'github:cachix/pre-commit-hooks.nix/f451c19376071a90d8c58ab1a953c6e9840527fd?narHash=sha256-6FPUl7HVtvRHCCBQne7Ylp4p%2BdpP3P/OYuzjztZ4s70%3D' (2024-07-15) • Removed input 'pre-commit-hooks/flake-utils' --- flake.lock | 46 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/flake.lock b/flake.lock index 1d59439da..932c5fcf5 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1719994518, + "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", "type": "github" }, "original": { @@ -36,29 +36,14 @@ "type": "github" } }, - "flake-utils": { - "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "libgit2": { "flake": false, "locked": { - "lastModified": 1697646580, - "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "lastModified": 1721209236, + "narHash": "sha256-tDUQi+s8sxJ30SmUH7Ln9WmDz5jGatlgKumjwi7KnCo=", "owner": "libgit2", "repo": "libgit2", - "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "rev": "503b66cf00ad7dca940148529f60b1a409ccc462", "type": "github" }, "original": { @@ -69,11 +54,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1721560568, - "narHash": "sha256-L61BXz7n/yNzOeZ3FqlnUmxj4145JOVeq9fvQTQzbNM=", + "lastModified": 1721821769, + "narHash": "sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/+WqrnGEw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "be3ca229c85e978880babdeda9748b14e6aa008f", + "rev": "d0907b75146a0ccc1ec0d6c3db287ec287588ef6", "type": "github" }, "original": { @@ -118,7 +103,6 @@ "pre-commit-hooks": { "inputs": { "flake-compat": [], - "flake-utils": "flake-utils", "gitignore": [], "nixpkgs": [ "nixpkgs" @@ -128,11 +112,11 @@ ] }, "locked": { - "lastModified": 1712897695, - "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "lastModified": 1721042469, + "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", "type": "github" }, "original": { From 650f1894020c586360f30fa4e728fe9eea76d114 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:24:10 +0200 Subject: [PATCH 666/910] flake.nix: Pin libgit2 to a release --- flake.lock | 7 ++++--- flake.nix | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 932c5fcf5..96c35b088 100644 --- a/flake.lock +++ b/flake.lock @@ -39,15 +39,16 @@ "libgit2": { "flake": false, "locked": { - "lastModified": 1721209236, - "narHash": "sha256-tDUQi+s8sxJ30SmUH7Ln9WmDz5jGatlgKumjwi7KnCo=", + "lastModified": 1715853528, + "narHash": "sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY=", "owner": "libgit2", "repo": "libgit2", - "rev": "503b66cf00ad7dca940148529f60b1a409ccc462", + "rev": "36f7e21ad757a3dacc58cf7944329da6bc1d6e96", "type": "github" }, "original": { "owner": "libgit2", + "ref": "v1.8.1", "repo": "libgit2", "type": "github" } diff --git a/flake.nix b/flake.nix index ff2c8ecfa..45c493c6f 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,7 @@ inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; - inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; + inputs.libgit2 = { url = "github:libgit2/libgit2/v1.8.1"; flake = false; }; # dev tooling inputs.flake-parts.url = "github:hercules-ci/flake-parts"; From f9a23c8d2180e644afe930c07680eba9e360d9c5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:30:28 +0200 Subject: [PATCH 667/910] flake.nix: Remove outdated comment --- flake.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/flake.nix b/flake.nix index 45c493c6f..bfa4c409a 100644 --- a/flake.nix +++ b/flake.nix @@ -1,8 +1,6 @@ { description = "The purely functional package manager"; - # TODO switch to nixos-23.11-small - # https://nixpk.gs/pr-tracker.html?pr=291954 inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; From c316f1557d9c71a0a6864fa6774a913a52748023 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:30:56 +0200 Subject: [PATCH 668/910] flake: Switch to nixos-24.05 channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/d0907b75146a0ccc1ec0d6c3db287ec287588ef6?narHash=sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/%2BWqrnGEw%3D' (2024-07-24) → 'github:NixOS/nixpkgs/63d37ccd2d178d54e7fb691d7ec76000740ea24a?narHash=sha256-7cCC8%2BTdq1%2B3OPyc3%2BgVo9dzUNkNIQfwSDJ2HSi2u3o%3D' (2024-07-21) --- flake.lock | 8 ++++---- flake.nix | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index 96c35b088..d1b54eb87 100644 --- a/flake.lock +++ b/flake.lock @@ -55,16 +55,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1721821769, - "narHash": "sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/+WqrnGEw=", + "lastModified": 1721548954, + "narHash": "sha256-7cCC8+Tdq1+3OPyc3+gVo9dzUNkNIQfwSDJ2HSi2u3o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d0907b75146a0ccc1ec0d6c3db287ec287588ef6", + "rev": "63d37ccd2d178d54e7fb691d7ec76000740ea24a", "type": "github" }, "original": { "owner": "NixOS", - "ref": "release-24.05", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index bfa4c409a..e6af87723 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { description = "The purely functional package manager"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; From 171ef75218ec1bc0cdfe7b1b49796452d1d6bea6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:55:57 +0200 Subject: [PATCH 669/910] Rename pre-commit-hooks -> git-hooks-nix Following the upstream rename --- flake.lock | 54 ++++++++++++++++++------------------ flake.nix | 10 +++---- maintainers/flake-module.nix | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/flake.lock b/flake.lock index d1b54eb87..2ac413a69 100644 --- a/flake.lock +++ b/flake.lock @@ -36,6 +36,31 @@ "type": "github" } }, + "git-hooks-nix": { + "inputs": { + "flake-compat": [], + "gitignore": [], + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1721042469, + "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "libgit2": { "flake": false, "locked": { @@ -101,40 +126,15 @@ "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": [], - "gitignore": [], - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-stable": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1721042469, - "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "flake-compat": "flake-compat", "flake-parts": "flake-parts", + "git-hooks-nix": "git-hooks-nix", "libgit2": "libgit2", "nixpkgs": "nixpkgs", "nixpkgs-23-11": "nixpkgs-23-11", - "nixpkgs-regression": "nixpkgs-regression", - "pre-commit-hooks": "pre-commit-hooks" + "nixpkgs-regression": "nixpkgs-regression" } } }, diff --git a/flake.nix b/flake.nix index e6af87723..2384c2974 100644 --- a/flake.nix +++ b/flake.nix @@ -9,14 +9,14 @@ # dev tooling inputs.flake-parts.url = "github:hercules-ci/flake-parts"; - inputs.pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix"; + inputs.git-hooks-nix.url = "github:cachix/git-hooks.nix"; # work around https://github.com/NixOS/nix/issues/7730 inputs.flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; - inputs.pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs"; - inputs.pre-commit-hooks.inputs.nixpkgs-stable.follows = "nixpkgs"; + inputs.git-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; + inputs.git-hooks-nix.inputs.nixpkgs-stable.follows = "nixpkgs"; # work around 7730 and https://github.com/NixOS/nix/issues/7807 - inputs.pre-commit-hooks.inputs.flake-compat.follows = ""; - inputs.pre-commit-hooks.inputs.gitignore.follows = ""; + inputs.git-hooks-nix.inputs.flake-compat.follows = ""; + inputs.git-hooks-nix.inputs.gitignore.follows = ""; outputs = inputs@{ self, nixpkgs, nixpkgs-regression, libgit2, ... }: diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index b5c7bfd53..be91df536 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -2,7 +2,7 @@ { imports = [ - inputs.pre-commit-hooks.flakeModule + inputs.git-hooks-nix.flakeModule ]; perSystem = { config, pkgs, ... }: { From 3be7c0037eb4728e201b49572230d563cd2ac096 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Jul 2024 15:48:19 +0200 Subject: [PATCH 670/910] WorkerProto: Support fine-grained protocol feature negotiation Currently, the worker protocol has a version number that we increment whenever we change something in the protocol. However, this can cause a collision between Nix PRs / forks that make protocol changes (e.g. PR #9857 increments the version, which could collide with another PR). So instead, the client and daemon now exchange a set of protocol features (such as `auth-forwarding`). They will use the intersection of the sets of features, i.e. the features they both support. Note that protocol features are completely distinct from `ExperimentalFeature`s. --- src/libstore/daemon.cc | 11 ++--- src/libstore/remote-store.cc | 10 ++++- src/libstore/worker-protocol-connection.cc | 51 +++++++++++++++++++--- src/libstore/worker-protocol-connection.hh | 27 ++++++++++-- src/libstore/worker-protocol.hh | 8 +++- tests/unit/libstore/worker-protocol.cc | 49 ++++++++++++++++----- 6 files changed, 127 insertions(+), 29 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 6533b2f58..94f00cfb6 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1025,19 +1025,20 @@ void processConnection( #endif /* Exchange the greeting. */ - WorkerProto::Version clientVersion = + auto [protoVersion, features] = WorkerProto::BasicServerConnection::handshake( - to, from, PROTOCOL_VERSION); + to, from, PROTOCOL_VERSION, WorkerProto::allFeatures); - if (clientVersion < 0x10a) + if (protoVersion < 0x10a) throw Error("the Nix client version is too old"); WorkerProto::BasicServerConnection conn; conn.to = std::move(to); conn.from = std::move(from); - conn.protoVersion = clientVersion; + conn.protoVersion = protoVersion; + conn.features = features; - auto tunnelLogger = new TunnelLogger(conn.to, clientVersion); + auto tunnelLogger = new TunnelLogger(conn.to, protoVersion); auto prevLogger = nix::logger; // FIXME if (!recursive) diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index ebb0864c5..555936c18 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -73,8 +73,11 @@ void RemoteStore::initConnection(Connection & conn) StringSink saved; TeeSource tee(conn.from, saved); try { - conn.protoVersion = WorkerProto::BasicClientConnection::handshake( - conn.to, tee, PROTOCOL_VERSION); + auto [protoVersion, features] = WorkerProto::BasicClientConnection::handshake( + conn.to, tee, PROTOCOL_VERSION, + WorkerProto::allFeatures); + conn.protoVersion = protoVersion; + conn.features = features; } catch (SerialisationError & e) { /* In case the other side is waiting for our input, close it. */ @@ -88,6 +91,9 @@ void RemoteStore::initConnection(Connection & conn) static_cast(conn) = conn.postHandshake(*this); + for (auto & feature : conn.features) + debug("negotiated feature '%s'", feature); + auto ex = conn.processStderrReturn(); if (ex) std::rethrow_exception(ex); } diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc index 93d13d48e..a47dbb689 100644 --- a/src/libstore/worker-protocol-connection.cc +++ b/src/libstore/worker-protocol-connection.cc @@ -5,6 +5,8 @@ namespace nix { +const std::set WorkerProto::allFeatures{}; + WorkerProto::BasicClientConnection::~BasicClientConnection() { try { @@ -137,8 +139,21 @@ void WorkerProto::BasicClientConnection::processStderr(bool * daemonException, S } } -WorkerProto::Version -WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +static std::set +intersectFeatures(const std::set & a, const std::set & b) +{ + std::set res; + for (auto & x : a) + if (b.contains(x)) + res.insert(x); + return res; +} + +std::tuple> WorkerProto::BasicClientConnection::handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures) { to << WORKER_MAGIC_1 << localVersion; to.flush(); @@ -153,11 +168,24 @@ WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, if (GET_PROTOCOL_MINOR(daemonVersion) < 10) throw Error("the Nix daemon version is too old"); - return std::min(daemonVersion, localVersion); + auto protoVersion = std::min(daemonVersion, localVersion); + + /* Exchange features. */ + std::set daemonFeatures; + if (GET_PROTOCOL_MINOR(protoVersion) >= 38) { + to << supportedFeatures; + to.flush(); + daemonFeatures = readStrings>(from); + } + + return {protoVersion, intersectFeatures(daemonFeatures, supportedFeatures)}; } -WorkerProto::Version -WorkerProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +std::tuple> WorkerProto::BasicServerConnection::handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures) { unsigned int magic = readInt(from); if (magic != WORKER_MAGIC_1) @@ -165,7 +193,18 @@ WorkerProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, to << WORKER_MAGIC_2 << localVersion; to.flush(); auto clientVersion = readInt(from); - return std::min(clientVersion, localVersion); + + auto protoVersion = std::min(clientVersion, localVersion); + + /* Exchange features. */ + std::set clientFeatures; + if (GET_PROTOCOL_MINOR(protoVersion) >= 38) { + clientFeatures = readStrings>(from); + to << supportedFeatures; + to.flush(); + } + + return {protoVersion, intersectFeatures(clientFeatures, supportedFeatures)}; } WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandshake(const StoreDirConfig & store) diff --git a/src/libstore/worker-protocol-connection.hh b/src/libstore/worker-protocol-connection.hh index 38287d08e..9c96195b5 100644 --- a/src/libstore/worker-protocol-connection.hh +++ b/src/libstore/worker-protocol-connection.hh @@ -23,6 +23,11 @@ struct WorkerProto::BasicConnection */ WorkerProto::Version protoVersion; + /** + * The set of features that both sides support. + */ + std::set features; + /** * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the * factored out serve protocol serializers with a @@ -72,8 +77,8 @@ struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection /** * Establishes connection, negotiating version. * - * @return the version provided by the other side of the - * connection. + * @return the minimum version supported by both sides and the set + * of protocol features supported by both sides. * * @param to Taken by reference to allow for various error handling * mechanisms. @@ -82,8 +87,15 @@ struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection * handling mechanisms. * * @param localVersion Our version which is sent over + * + * @param features The protocol features that we support */ - static Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + // FIXME: this should probably be a constructor. + static std::tuple> handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures); /** * After calling handshake, must call this to exchange some basic @@ -138,8 +150,15 @@ struct WorkerProto::BasicServerConnection : WorkerProto::BasicConnection * handling mechanisms. * * @param localVersion Our version which is sent over + * + * @param features The protocol features that we support */ - static WorkerProto::Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + // FIXME: this should probably be a constructor. + static std::tuple> handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures); /** * After calling handshake, must call this to exchange some basic diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 9fc16d015..c356fa1bf 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -11,7 +11,9 @@ namespace nix { #define WORKER_MAGIC_1 0x6e697863 #define WORKER_MAGIC_2 0x6478696f -#define PROTOCOL_VERSION (1 << 8 | 37) +/* Note: you generally shouldn't change the protocol version. Define a + new `WorkerProto::Feature` instead. */ +#define PROTOCOL_VERSION (1 << 8 | 38) #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) @@ -131,6 +133,10 @@ struct WorkerProto { WorkerProto::Serialise::write(store, conn, t); } + + using Feature = std::string; + + static const std::set allFeatures; }; enum struct WorkerProto::Op : uint64_t diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index c15120010..bbea9ed75 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -658,15 +658,15 @@ TEST_F(WorkerProtoTest, handshake_log) FdSink out { toServer.writeSide.get() }; FdSource in0 { toClient.readSide.get() }; TeeSource in { in0, toClientLog }; - clientResult = WorkerProto::BasicClientConnection::handshake( - out, in, defaultVersion); + clientResult = std::get<0>(WorkerProto::BasicClientConnection::handshake( + out, in, defaultVersion, {})); }); { FdSink out { toClient.writeSide.get() }; FdSource in { toServer.readSide.get() }; WorkerProto::BasicServerConnection::handshake( - out, in, defaultVersion); + out, in, defaultVersion, {}); }; thread.join(); @@ -675,6 +675,33 @@ TEST_F(WorkerProtoTest, handshake_log) }); } +TEST_F(WorkerProtoTest, handshake_features) +{ + Pipe toClient, toServer; + toClient.create(); + toServer.create(); + + std::tuple> clientResult; + + auto clientThread = std::thread([&]() { + FdSink out { toServer.writeSide.get() }; + FdSource in { toClient.readSide.get() }; + clientResult = WorkerProto::BasicClientConnection::handshake( + out, in, 123, {"bar", "aap", "mies", "xyzzy"}); + }); + + FdSink out { toClient.writeSide.get() }; + FdSource in { toServer.readSide.get() }; + auto daemonResult = WorkerProto::BasicServerConnection::handshake( + out, in, 456, {"foo", "bar", "xyzzy"}); + + clientThread.join(); + + EXPECT_EQ(clientResult, daemonResult); + EXPECT_EQ(std::get<0>(clientResult), 123); + EXPECT_EQ(std::get<1>(clientResult), std::set({"bar", "xyzzy"})); +} + /// Has to be a `BufferedSink` for handshake. struct NullBufferedSink : BufferedSink { void writeUnbuffered(std::string_view data) override { } @@ -686,8 +713,8 @@ TEST_F(WorkerProtoTest, handshake_client_replay) NullBufferedSink nullSink; StringSource in { toClientLog }; - auto clientResult = WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion); + auto clientResult = std::get<0>(WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion, {})); EXPECT_EQ(clientResult, defaultVersion); }); @@ -705,13 +732,13 @@ TEST_F(WorkerProtoTest, handshake_client_truncated_replay_throws) if (len < 8) { EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), EndOfFile); } else { // Not sure why cannot keep on checking for `EndOfFile`. EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), Error); } } @@ -734,17 +761,17 @@ TEST_F(WorkerProtoTest, handshake_client_corrupted_throws) // magic bytes don't match EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), Error); } else if (idx < 8 || idx >= 12) { // Number out of bounds EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), SerialisationError); } else { - auto ver = WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion); + auto ver = std::get<0>(WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion, {})); // `std::min` of this and the other version saves us EXPECT_EQ(ver, defaultVersion); } From 0194f815870ed875b791d60335be920de249a510 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Jul 2024 15:10:47 +0200 Subject: [PATCH 671/910] addToStore(): Do evaluation on the main stack This hopefully avoids the need for all our Boehm GC coroutine workarounds, since the GC roots will be on the main stack of the thread. Fixes #11141. --- src/libstore/local-store.cc | 1 + src/libstore/store-api.cc | 16 +++++++++------- src/libutil/serialise.cc | 8 ++++---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 819cee345..956a4453d 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1245,6 +1245,7 @@ StorePath LocalStore::addToStoreFromDump( }); try { got = source.read(dumpBuffer.get() + oldSize, want); + if (!got) break; } catch (EndOfFile &) { inMemory = true; break; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 2c4dee518..5d6109bae 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -210,14 +210,16 @@ StorePath Store::addToStore( fsm = FileSerialisationMethod::NixArchive; break; } - auto source = sinkToSource([&](Sink & sink) { - dumpPath(path, sink, fsm, filter); + std::optional storePath; + auto sink = sourceToSink([&](Source & source) { + LengthSource lengthSource(source); + storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); + if (lengthSource.total >= settings.warnLargePathThreshold) + warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total)); }); - LengthSource lengthSource(*source); - auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); - if (lengthSource.total >= settings.warnLargePathThreshold) - warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total)); - return storePath; + dumpPath(path, *sink, fsm, filter); + sink->finish(); + return storePath.value(); } void Store::addMultipleToStore( diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 36b99905a..0f2febaed 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -243,11 +243,11 @@ std::unique_ptr sourceToSink(std::function fun) if (!coro) { CoroutineContext ctx; coro = coro_t::push_type(VirtualStackAllocator{}, [&](coro_t::pull_type & yield) { - LambdaSource source([&](char *out, size_t out_len) { + LambdaSource source([&](char * out, size_t out_len) { if (cur.empty()) { yield(); if (yield.get()) { - return (size_t)0; + return (size_t) 0; } } @@ -271,12 +271,12 @@ std::unique_ptr sourceToSink(std::function fun) void finish() override { if (!coro) return; - if (!*coro) abort(); + //if (!*coro) abort(); { CoroutineContext ctx; (*coro)(true); } - if (*coro) abort(); + //if (*coro) abort(); } }; From 609df83c011ad74c20cd806e00d59f6ed4a580b5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Jul 2024 15:18:58 +0200 Subject: [PATCH 672/910] sourceToSink(): Throw EndOfFile --- src/libstore/local-store.cc | 1 - src/libutil/serialise.cc | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 956a4453d..819cee345 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1245,7 +1245,6 @@ StorePath LocalStore::addToStoreFromDump( }); try { got = source.read(dumpBuffer.get() + oldSize, want); - if (!got) break; } catch (EndOfFile &) { inMemory = true; break; diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 0f2febaed..d203a01bf 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -246,9 +246,8 @@ std::unique_ptr sourceToSink(std::function fun) LambdaSource source([&](char * out, size_t out_len) { if (cur.empty()) { yield(); - if (yield.get()) { - return (size_t) 0; - } + if (yield.get()) + throw EndOfFile("coroutine has finished"); } size_t n = std::min(cur.size(), out_len); @@ -271,12 +270,12 @@ std::unique_ptr sourceToSink(std::function fun) void finish() override { if (!coro) return; - //if (!*coro) abort(); + if (!*coro) abort(); { CoroutineContext ctx; (*coro)(true); } - //if (*coro) abort(); + if (*coro) abort(); } }; From ca0f7db843d2c58bb5ca7b45f2b2aa57273e5f7e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Jul 2024 15:28:21 +0200 Subject: [PATCH 673/910] Remove hacks to support evaluation from coroutines Since we're not doing this anymore. --- .../src/installation/prerequisites-source.md | 2 - src/libexpr/eval-gc.cc | 132 ------------------ src/libutil/serialise.cc | 58 +------- src/libutil/serialise.hh | 23 --- 4 files changed, 2 insertions(+), 213 deletions(-) diff --git a/doc/manual/src/installation/prerequisites-source.md b/doc/manual/src/installation/prerequisites-source.md index 4aafa6d27..c346a0a4b 100644 --- a/doc/manual/src/installation/prerequisites-source.md +++ b/doc/manual/src/installation/prerequisites-source.md @@ -39,8 +39,6 @@ `pkgconfig` and the Boehm garbage collector, and pass the flag `--enable-gc` to `configure`. - For `bdw-gc` <= 8.2.4 Nix needs a [small patch](https://github.com/NixOS/nix/blob/ac4d2e7b857acdfeac35ac8a592bdecee2d29838/boehmgc-traceable_allocator-public.diff) to be applied. - - The `boost` library of version 1.66.0 or higher. It can be obtained from the official web site . diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index ba19cd74e..4bf48194e 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -30,120 +30,6 @@ static void * oomHandler(size_t requested) throw std::bad_alloc(); } -class BoehmGCStackAllocator : public StackAllocator -{ - boost::coroutines2::protected_fixedsize_stack stack{ - // We allocate 8 MB, the default max stack size on NixOS. - // A smaller stack might be quicker to allocate but reduces the stack - // depth available for source filter expressions etc. - std::max(boost::context::stack_traits::default_size(), static_cast(8 * 1024 * 1024))}; - - // This is specific to boost::coroutines2::protected_fixedsize_stack. - // The stack protection page is included in sctx.size, so we have to - // subtract one page size from the stack size. - std::size_t pfss_usable_stack_size(boost::context::stack_context & sctx) - { - return sctx.size - boost::context::stack_traits::page_size(); - } - -public: - boost::context::stack_context allocate() override - { - auto sctx = stack.allocate(); - - // Stacks generally start at a high address and grow to lower addresses. - // Architectures that do the opposite are rare; in fact so rare that - // boost_routine does not implement it. - // So we subtract the stack size. - GC_add_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); - return sctx; - } - - void deallocate(boost::context::stack_context sctx) override - { - GC_remove_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); - stack.deallocate(sctx); - } -}; - -static BoehmGCStackAllocator boehmGCStackAllocator; - -/** - * When a thread goes into a coroutine, we lose its original sp until - * control flow returns to the thread. - * While in the coroutine, the sp points outside the thread stack, - * so we can detect this and push the entire thread stack instead, - * as an approximation. - * The coroutine's stack is covered by `BoehmGCStackAllocator`. - * This is not an optimal solution, because the garbage is scanned when a - * coroutine is active, for both the coroutine and the original thread stack. - * However, the implementation is quite lean, and usually we don't have active - * coroutines during evaluation, so this is acceptable. - */ -void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) -{ - void *& sp = *sp_ptr; - auto pthread_id = reinterpret_cast(_pthread_id); - pthread_attr_t pattr; - size_t osStackSize; - // The low address of the stack, which grows down. - void * osStackLimit; - void * osStackBase; - -# ifdef __APPLE__ - osStackSize = pthread_get_stacksize_np(pthread_id); - osStackLimit = pthread_get_stackaddr_np(pthread_id); -# else - if (pthread_attr_init(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); - } -# ifdef HAVE_PTHREAD_GETATTR_NP - if (pthread_getattr_np(pthread_id, &pattr)) { - throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); - } -# elif HAVE_PTHREAD_ATTR_GET_NP - if (!pthread_attr_init(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); - } - if (!pthread_attr_get_np(pthread_id, &pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed"); - } -# else -# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" -# endif - if (pthread_attr_getstack(&pattr, &osStackLimit, &osStackSize)) { - throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); - } - if (pthread_attr_destroy(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); - } -# endif - osStackBase = (char *) osStackLimit + osStackSize; - // NOTE: We assume the stack grows down, as it does on all architectures we support. - // Architectures that grow the stack up are rare. - if (sp >= osStackBase || sp < osStackLimit) { // sp is outside the os stack - sp = osStackLimit; - } -} - -/* 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(); - }; -}; - static inline void initGCReal() { /* Initialise the Boehm garbage collector. */ @@ -164,24 +50,6 @@ static inline void initGCReal() GC_set_oom_fn(oomHandler); - StackAllocator::defaultAllocator = &boehmGCStackAllocator; - -// TODO: Remove __APPLE__ condition. -// Comment suggests an implementation that works on darwin and windows -// https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 -# if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__) - GC_set_sp_corrector(&fixupBoehmStackPointer); - - if (!GC_get_sp_corrector()) { - printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); - /* Used to disable GC when entering coroutines on macOS */ - create_coro_gc_hook = []() -> std::shared_ptr { return std::make_shared(); }; - } -# else -# warning \ - "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later." -# 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/libutil/serialise.cc b/src/libutil/serialise.cc index d203a01bf..cd52fbfb4 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -171,55 +171,6 @@ size_t StringSource::read(char * data, size_t len) #error Coroutines are broken in this version of Boost! #endif -/* A concrete datatype allow virtual dispatch of stack allocation methods. */ -struct VirtualStackAllocator { - StackAllocator *allocator = StackAllocator::defaultAllocator; - - boost::context::stack_context allocate() { - return allocator->allocate(); - } - - void deallocate(boost::context::stack_context sctx) { - allocator->deallocate(sctx); - } -}; - - -/* This class reifies the default boost coroutine stack allocation strategy with - a virtual interface. */ -class DefaultStackAllocator : public StackAllocator { - boost::coroutines2::default_stack stack; - - boost::context::stack_context allocate() { - return stack.allocate(); - } - - void deallocate(boost::context::stack_context sctx) { - stack.deallocate(sctx); - } -}; - -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 @@ -241,8 +192,7 @@ std::unique_ptr sourceToSink(std::function fun) cur = in; if (!coro) { - CoroutineContext ctx; - coro = coro_t::push_type(VirtualStackAllocator{}, [&](coro_t::pull_type & yield) { + coro = coro_t::push_type([&](coro_t::pull_type & yield) { LambdaSource source([&](char * out, size_t out_len) { if (cur.empty()) { yield(); @@ -262,7 +212,6 @@ std::unique_ptr sourceToSink(std::function fun) if (!*coro) { abort(); } if (!cur.empty()) { - CoroutineContext ctx; (*coro)(false); } } @@ -272,7 +221,6 @@ std::unique_ptr sourceToSink(std::function fun) if (!coro) return; if (!*coro) abort(); { - CoroutineContext ctx; (*coro)(true); } if (*coro) abort(); @@ -306,8 +254,7 @@ std::unique_ptr sinkToSource( size_t read(char * data, size_t len) override { if (!coro) { - CoroutineContext ctx; - coro = coro_t::pull_type(VirtualStackAllocator{}, [&](coro_t::push_type & yield) { + coro = coro_t::pull_type([&](coro_t::push_type & yield) { LambdaSink sink([&](std::string_view data) { if (!data.empty()) yield(std::string(data)); }); @@ -319,7 +266,6 @@ std::unique_ptr sinkToSource( if (pos == cur.size()) { if (!cur.empty()) { - CoroutineContext ctx; (*coro)(); } cur = coro->get(); diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 8137db5f4..a795bb088 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -557,27 +557,4 @@ struct FramedSink : nix::BufferedSink }; }; -/** - * Stack allocation strategy for sinkToSource. - * Mutable to avoid a boehm gc dependency in libutil. - * - * boost::context doesn't provide a virtual class, so we define our own. - */ -struct StackAllocator { - virtual boost::context::stack_context allocate() = 0; - virtual void deallocate(boost::context::stack_context sctx) = 0; - - /** - * The stack allocator to use in sinkToSource and potentially elsewhere. - * It is reassigned by the initGC() method in libexpr. - */ - 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)(); - - } From b8684eb44723ad023e74a50c446f77ef56031f18 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Jul 2024 20:03:29 +0200 Subject: [PATCH 674/910] sourceToSink(): Fix abort --- src/libutil/serialise.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index cd52fbfb4..8fbfe248c 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -218,12 +218,8 @@ std::unique_ptr sourceToSink(std::function fun) void finish() override { - if (!coro) return; - if (!*coro) abort(); - { + if (coro && *coro) (*coro)(true); - } - if (*coro) abort(); } }; From 3172e88af544ca53a54930ccf3f580b15141f01c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 20 Jul 2024 22:46:09 +0200 Subject: [PATCH 675/910] Make abort() call sites log first --- src/libcmd/repl.cc | 2 +- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval.cc | 8 +++---- src/libexpr/nixexpr.cc | 4 ++-- src/libexpr/primops.cc | 2 +- src/libexpr/print-ambiguous.cc | 2 +- src/libexpr/print.cc | 4 ++-- src/libexpr/symbol-table.hh | 3 ++- src/libexpr/value.hh | 2 +- src/libfetchers/attrs.cc | 4 ++-- src/libmain/loggers.cc | 2 +- .../build/drv-output-substitution-goal.hh | 2 +- src/libstore/build/goal.hh | 4 ++-- src/libstore/build/substitution-goal.hh | 2 +- src/libstore/build/worker.cc | 4 ++-- src/libstore/daemon.cc | 2 +- src/libstore/gc.cc | 2 +- src/libstore/globals.cc | 2 +- src/libstore/nar-info-disk-cache.cc | 4 ++-- .../unix/build/local-derivation-goal.cc | 2 +- src/libstore/unix/pathlocks.cc | 2 +- src/libutil/chunked-vector.hh | 4 +++- src/libutil/error.cc | 12 ++++++++++ src/libutil/error.hh | 24 +++++++++++++++++++ src/libutil/file-content-address.cc | 2 +- src/libutil/fs-sink.cc | 2 +- src/libutil/git.cc | 2 +- src/libutil/hash.cc | 2 +- src/libutil/logging.cc | 2 +- src/libutil/serialise.cc | 8 +++---- src/libutil/sync.hh | 4 +++- src/libutil/unix/monitor-fd.hh | 4 +++- src/libutil/unix/processes.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-store/nix-store.cc | 2 +- 35 files changed, 88 insertions(+), 45 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index b5d0816dd..bf0d820c2 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -217,7 +217,7 @@ ReplExitStatus NixRepl::mainLoop() case ProcessLineResult::PromptAgain: break; default: - abort(); + unreachable(); } } catch (ParseError & e) { if (e.msg().find("unexpected end of file") != std::string::npos) { diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index d61d93630..2f67260c5 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -134,7 +134,7 @@ std::pair findPackageFilename(EvalState & state, Value & v return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno}; } catch (std::invalid_argument & e) { fail(); - abort(); + unreachable(); } } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b192f9b4b..32bc68e6d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -149,7 +149,7 @@ std::string_view showType(ValueType type, bool withArticle) case nFloat: return WA("a", "float"); case nThunk: return WA("a", "thunk"); } - abort(); + unreachable(); } @@ -771,7 +771,7 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & case ReplExitStatus::Continue: break; default: - abort(); + unreachable(); } } } @@ -1140,7 +1140,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx po void Expr::eval(EvalState & state, Env & env, Value & v) { - abort(); + unreachable(); } @@ -1573,7 +1573,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & .withFrame(*fun.payload.lambda.env, lambda) .debugThrow(); } - abort(); // can't happen + unreachable(); } } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 6c6769cfd..dbc74faf9 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -25,7 +25,7 @@ std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol) void Expr::show(const SymbolTable & symbols, std::ostream & str) const { - abort(); + unreachable(); } void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const @@ -271,7 +271,7 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) void Expr::bindVars(EvalState & es, const std::shared_ptr & env) { - abort(); + unreachable(); } void ExprInt::bindVars(EvalState & es, const std::shared_ptr & env) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5a373a43b..0b3b19b57 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -426,7 +426,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val t = args[0]->external()->typeOf(); break; case nFloat: t = "float"; break; - case nThunk: abort(); + case nThunk: unreachable(); } v.mkString(t); } diff --git a/src/libexpr/print-ambiguous.cc b/src/libexpr/print-ambiguous.cc index 5d55b45da..a40c98643 100644 --- a/src/libexpr/print-ambiguous.cc +++ b/src/libexpr/print-ambiguous.cc @@ -94,7 +94,7 @@ void printAmbiguous( break; default: printError("Nix evaluator internal error: printAmbiguous: invalid value type"); - abort(); + unreachable(); } } diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index bc17d6bfe..4d1a6868c 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -475,7 +475,7 @@ private: else output << "primop"; } else { - abort(); + unreachable(); } output << "»"; @@ -504,7 +504,7 @@ private: if (options.ansiColors) output << ANSI_NORMAL; } else { - abort(); + unreachable(); } } diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index c7a3563b0..dee7369e8 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -7,6 +7,7 @@ #include "types.hh" #include "chunked-vector.hh" +#include "error.hh" namespace nix { @@ -113,7 +114,7 @@ public: SymbolStr operator[](Symbol s) const { if (s.id == 0 || s.id > store.size()) - abort(); + unreachable(); return SymbolStr(store[s.id - 1]); } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 1f4d72d39..fdc6c84c4 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -285,7 +285,7 @@ public: if (invalidIsThunk) return nThunk; else - abort(); + unreachable(); } inline void finishValue(InternalType newType, Payload newPayload) diff --git a/src/libfetchers/attrs.cc b/src/libfetchers/attrs.cc index b788c5948..25d04cdc9 100644 --- a/src/libfetchers/attrs.cc +++ b/src/libfetchers/attrs.cc @@ -33,7 +33,7 @@ nlohmann::json attrsToJSON(const Attrs & attrs) json[attr.first] = *v; } else if (auto v = std::get_if>(&attr.second)) { json[attr.first] = v->t; - } else abort(); + } else unreachable(); } return json; } @@ -99,7 +99,7 @@ std::map attrsToQuery(const Attrs & attrs) query.insert_or_assign(attr.first, *v); } else if (auto v = std::get_if>(&attr.second)) { query.insert_or_assign(attr.first, v->t ? "1" : "0"); - } else abort(); + } else unreachable(); } return query; } diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index 9829859de..a4e0530c8 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -36,7 +36,7 @@ Logger * makeDefaultLogger() { return logger; } default: - abort(); + unreachable(); } } diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh index 807054926..8c60d0198 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -36,7 +36,7 @@ public: Co init() override; Co realisationFetched(std::shared_ptr outputInfo, nix::ref sub); - void timedOut(Error && ex) override { abort(); }; + void timedOut(Error && ex) override { unreachable(); }; std::string key() override; diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh index 162c392d0..9c6a40c84 100644 --- a/src/libstore/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -400,12 +400,12 @@ public: virtual void handleChildOutput(Descriptor fd, std::string_view data) { - abort(); + unreachable(); } virtual void handleEOF(Descriptor fd) { - abort(); + unreachable(); } void trace(std::string_view s); diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index 86e4f5423..c1de45379 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -50,7 +50,7 @@ public: PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); ~PathSubstitutionGoal(); - void timedOut(Error && ex) override { abort(); }; + void timedOut(Error && ex) override { unreachable(); }; /** * We prepend "a$" to the key name to ensure substitution goals diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 7fc41b121..ab0ba67b5 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -216,7 +216,7 @@ void Worker::childStarted(GoalPtr goal, const std::set std::string BaseSetting::to_string() const if (value == smEnabled) return "true"; else if (value == smRelaxed) return "relaxed"; else if (value == smDisabled) return "false"; - else abort(); + else unreachable(); } template<> void BaseSetting::convertToArg(Args & args, const std::string & category) diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 288f618d5..83e63794e 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -164,7 +164,7 @@ public: Cache & getCache(State & state, const std::string & uri) { auto i = state.caches.find(uri); - if (i == state.caches.end()) abort(); + if (i == state.caches.end()) unreachable(); return i->second; } @@ -211,7 +211,7 @@ public: { auto r(state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority)); - if (!r.next()) { abort(); } + if (!r.next()) { unreachable(); } ret.id = (int) r.getInt(0); } diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 0dd102200..ac5b4dd0b 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -165,7 +165,7 @@ void LocalDerivationGoal::killSandbox(bool getStats) buildResult.cpuSystem = stats.cpuSystem; } #else - abort(); + unreachable(); #endif } diff --git a/src/libstore/unix/pathlocks.cc b/src/libstore/unix/pathlocks.cc index af21319a7..1ec4579ec 100644 --- a/src/libstore/unix/pathlocks.cc +++ b/src/libstore/unix/pathlocks.cc @@ -45,7 +45,7 @@ bool lockFile(Descriptor desc, LockType lockType, bool wait) if (lockType == ltRead) type = LOCK_SH; else if (lockType == ltWrite) type = LOCK_EX; else if (lockType == ltNone) type = LOCK_UN; - else abort(); + else unreachable(); if (wait) { while (flock(desc, type) != 0) { diff --git a/src/libutil/chunked-vector.hh b/src/libutil/chunked-vector.hh index d914e2542..4709679a6 100644 --- a/src/libutil/chunked-vector.hh +++ b/src/libutil/chunked-vector.hh @@ -6,6 +6,8 @@ #include #include +#include "error.hh" + namespace nix { /** @@ -30,7 +32,7 @@ private: auto & addChunk() { if (size_ >= std::numeric_limits::max() - ChunkSize) - abort(); + unreachable(); chunks.emplace_back(); chunks.back().reserve(ChunkSize); return chunks.back(); diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 33c391963..b1858911a 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -430,4 +430,16 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s return out; } +void panic(std::string_view msg) +{ + printError(msg); + printError("This was a fatal error, aborting."); + abort(); +} + +void panic(const char * file, int line, const char * func) +{ + panic(std::string("Unexpected condition in ") + func + " at " + file + ":" + std::to_string(line)); +} + } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index d7fe902d6..572a1baf7 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -273,4 +273,28 @@ using NativeSysError = */ void throwExceptionSelfCheck(); +/** + * Print a message and abort(). + * + * @note: This assumes that the logger is operational + */ +[[noreturn]] +void panic(std::string_view msg); + +/** + * Print a basic error message with source position and abort(). + * Use the unreachable macro to call this. + * + * @note: This assumes that the logger is operational + */ +[[noreturn]] +void panic(const char * file, int line, const char * func); + +/** + * Print a basic error message with source position and abort(). + * + * @note: This assumes that the logger is operational + */ +#define unreachable() (panic(__FILE__, __LINE__, __func__)) + } diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 438dac7da..86378dd67 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -63,7 +63,7 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method) case FileIngestionMethod::Git: return "git"; default: - abort(); + unreachable(); } } diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 3246e0902..f15324d0a 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -53,7 +53,7 @@ void copyRecursive( throw Error("file '%1%' has an unsupported type", from); default: - abort(); + unreachable(); } } diff --git a/src/libutil/git.cc b/src/libutil/git.cc index a6968a43e..af91fa643 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -201,7 +201,7 @@ std::optional convertMode(SourceAccessor::Type type) case SourceAccessor::tRegular: return Mode::Regular; case SourceAccessor::tDirectory: return Mode::Directory; case SourceAccessor::tMisc: return std::nullopt; - default: abort(); + default: unreachable(); } } diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 35b913e42..ab2a8695d 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -25,7 +25,7 @@ static size_t regularHashSize(HashAlgorithm type) { case HashAlgorithm::SHA256: return sha256HashSize; case HashAlgorithm::SHA512: return sha512HashSize; } - abort(); + unreachable(); } diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 55751b4cf..29427f2f6 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -189,7 +189,7 @@ struct JSONLogger : Logger { else if (f.type == Logger::Field::tString) arr.push_back(f.s); else - abort(); + unreachable(); } void write(const nlohmann::json & json) diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 36b99905a..ee4351545 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -260,7 +260,7 @@ std::unique_ptr sourceToSink(std::function fun) }); } - if (!*coro) { abort(); } + if (!*coro) { unreachable(); } if (!cur.empty()) { CoroutineContext ctx; @@ -271,12 +271,12 @@ std::unique_ptr sourceToSink(std::function fun) void finish() override { if (!coro) return; - if (!*coro) abort(); + if (!*coro) unreachable(); { CoroutineContext ctx; (*coro)(true); } - if (*coro) abort(); + if (*coro) unreachable(); } }; @@ -316,7 +316,7 @@ std::unique_ptr sinkToSource( }); } - if (!*coro) { eof(); abort(); } + if (!*coro) { eof(); unreachable(); } if (pos == cur.size()) { if (!cur.empty()) { diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index 20dd6ee52..c1b699ffc 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -7,6 +7,8 @@ #include #include +#include "error.hh" + namespace nix { /** @@ -47,7 +49,7 @@ public: friend SyncBase; Lock(SyncBase * s) : s(s), lk(s->mutex) { } public: - Lock(Lock && l) : s(l.s) { abort(); } + Lock(Lock && l) : s(l.s) { unreachable(); } Lock(const Lock & l) = delete; ~Lock() { } diff --git a/src/libutil/unix/monitor-fd.hh b/src/libutil/unix/monitor-fd.hh index 103894de9..b6610feff 100644 --- a/src/libutil/unix/monitor-fd.hh +++ b/src/libutil/unix/monitor-fd.hh @@ -40,7 +40,9 @@ public: #endif ; auto count = poll(fds, 1, -1); - if (count == -1) abort(); // can't happen + if (count == -1) + unreachable(); + /* This shouldn't happen, but can on macOS due to a bug. See rdar://37550628. diff --git a/src/libutil/unix/processes.cc b/src/libutil/unix/processes.cc index 1af559a21..c5ce74acc 100644 --- a/src/libutil/unix/processes.cc +++ b/src/libutil/unix/processes.cc @@ -182,7 +182,7 @@ static pid_t doFork(bool allowVfork, ChildWrapperFunction & fun) #endif if (pid != 0) return pid; fun(); - abort(); + unreachable(); } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 5e170c99d..40c200542 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1159,7 +1159,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) case cvEqual: ch = '='; break; case cvGreater: ch = '<'; break; case cvUnavail: ch = '-'; break; - default: abort(); + default: unreachable(); } if (xmlOutput) { diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index f073074e8..b4de42ba1 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -480,7 +480,7 @@ static void opQuery(Strings opFlags, Strings opArgs) } default: - abort(); + unreachable(); } } From e086d5d899aaefdce8cc7a509cb2971be9732b2b Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Wed, 24 Jul 2024 13:17:28 -0400 Subject: [PATCH 676/910] libexpr: experimental pipe operators --- doc/manual/rl-next/pipe-operators.md | 28 ++++++++++ doc/manual/src/language/operators.md | 37 ++++++++++++- src/libexpr/lexer.l | 14 +++++ src/libexpr/parser.y | 30 ++++++++--- src/libutil/experimental-features.cc | 10 +++- src/libutil/experimental-features.hh | 1 + .../lang/eval-fail-pipe-operators.err.exp | 5 ++ .../lang/eval-fail-pipe-operators.nix | 1 + tests/unit/libexpr/main.cc | 3 ++ tests/unit/libexpr/trivial.cc | 54 +++++++++++++++++++ 10 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 doc/manual/rl-next/pipe-operators.md create mode 100644 tests/functional/lang/eval-fail-pipe-operators.err.exp create mode 100644 tests/functional/lang/eval-fail-pipe-operators.nix diff --git a/doc/manual/rl-next/pipe-operators.md b/doc/manual/rl-next/pipe-operators.md new file mode 100644 index 000000000..b4cbe30e3 --- /dev/null +++ b/doc/manual/rl-next/pipe-operators.md @@ -0,0 +1,28 @@ +--- +synopsis: "Add `pipe-operators` experimental feature" +prs: +- 11131 +--- + +This is a draft implementation of [RFC 0148](https://github.com/NixOS/rfcs/pull/148). + +The `pipe-operators` experimental feature adds [`<|` and `|>` operators][pipe operators] to the Nix language. +*a* `|>` *b* is equivalent to the function application *b* *a*, and +*a* `<|` *b* is equivalent to the function application *a* *b*. + +For example: + +``` +nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 +9 + +nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 +7 +``` + +`<|` and `|>` are right and left associative, respectively, and have lower precedence than any other operator. +These properties may change in future releases. + +See [the RFC](https://github.com/NixOS/rfcs/pull/148) for more examples and rationale. + +[pipe operators]: @docroot@/language/operators.md#pipe-operators diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 9660a764d..a1e28349b 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -26,13 +26,17 @@ | Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 | | Logical disjunction (`OR`) | *bool* \|\| *bool* | left | 13 | | [Logical implication] | *bool* `->` *bool* | right | 14 | +| [Pipe operator] (experimental) | *expr* `\|>` *func* | left | 15 | +| [Pipe operator] (experimental) | *func* `<\|` *expr* | right | 15 | [string]: ./types.md#type-string [path]: ./types.md#type-path -[number]: ./types.md#type-float +[number]: ./types.md#type-float [list]: ./types.md#list [attribute set]: ./types.md#attribute-set + + ## Attribute selection > **Syntax** @@ -182,3 +186,34 @@ Equivalent to `!`*b1* `||` *b2*. [Logical implication]: #logical-implication +## Pipe operators + +- *a* `|>` *b* is equivalent to *b* *a* +- *a* `<|` *b* is equivalent to *a* *b* + +> **Example** +> +> ``` +> nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 +> 9 +> +> nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 +> 7 +> ``` + +> **Warning** +> +> This syntax is part of an +> [experimental feature](@docroot@/contributing/experimental-features.md) +> and may change in future releases. +> +> To use this syntax, make sure the +> [`pipe-operators` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-pipe-operators) +> is enabled. +> For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): +> +> ``` +> extra-experimental-features = pipe-operators +> ``` + +[Pipe operator]: #pipe-operators diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 58401be8e..eb1825b7c 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -67,6 +67,14 @@ static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) return {result, size_t(t - result)}; } +static void requireExperimentalFeature(const ExperimentalFeature & feature, const Pos & pos) +{ + if (!experimentalFeatureSettings.isEnabled(feature)) + throw ParseError(ErrorInfo{ + .msg = HintFmt("experimental Nix feature '%1%' is disabled; add '--extra-experimental-features %1%' to enable it", showExperimentalFeature(feature)), + .pos = pos, + }); +} } @@ -119,6 +127,12 @@ or { return OR_KW; } \-\> { return IMPL; } \/\/ { return UPDATE; } \+\+ { return CONCAT; } +\<\| { requireExperimentalFeature(Xp::PipeOperators, state->positions[CUR_POS]); + return PIPE_FROM; + } +\|\> { requireExperimentalFeature(Xp::PipeOperators, state->positions[CUR_POS]); + return PIPE_INTO; + } {ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; } {INT} { errno = 0; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 8ea176b24..9ad41c148 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -99,6 +99,14 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P } } +static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) { + if (auto e2 = dynamic_cast(fn)) { + e2->args.push_back(arg); + return fn; + } + return new ExprCall(pos, fn, {arg}); +} + %} @@ -123,6 +131,7 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P %type start expr expr_function expr_if expr_op %type expr_select expr_simple expr_app +%type expr_pipe_from expr_pipe_into %type expr_list %type binds %type formals @@ -140,6 +149,7 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P %token PATH HPATH SPATH PATH_END %token URI %token IF THEN ELSE ASSERT WITH LET IN_KW REC INHERIT EQ NEQ AND OR IMPL OR_KW +%token PIPE_FROM PIPE_INTO /* <| and |> */ %token DOLLAR_CURLY /* == ${ */ %token IND_STRING_OPEN IND_STRING_CLOSE %token ELLIPSIS @@ -206,9 +216,21 @@ expr_function expr_if : IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); } + | expr_pipe_from + | expr_pipe_into | expr_op ; +expr_pipe_from + : expr_op PIPE_FROM expr_pipe_from { $$ = makeCall(state->at(@2), $1, $3); } + | expr_op PIPE_FROM expr_op { $$ = makeCall(state->at(@2), $1, $3); } + ; + +expr_pipe_into + : expr_pipe_into PIPE_INTO expr_op { $$ = makeCall(state->at(@2), $3, $1); } + | expr_op PIPE_INTO expr_op { $$ = makeCall(state->at(@2), $3, $1); } + ; + expr_op : '!' expr_op %prec NOT { $$ = new ExprOpNot($2); } | '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(state->s.sub), {new ExprInt(0), $2}); } @@ -233,13 +255,7 @@ expr_op ; expr_app - : expr_app expr_select { - if (auto e2 = dynamic_cast($1)) { - e2->args.push_back($2); - $$ = $1; - } else - $$ = new ExprCall(CUR_POS, $1, {$2}); - } + : expr_app expr_select { $$ = makeCall(CUR_POS, $1, $2); } | expr_select ; diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 1c080e372..a0c955816 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -24,7 +24,7 @@ struct ExperimentalFeatureDetails * feature, we either have no issue at all if few features are not added * at the end of the list, or a proper merge conflict if they are. */ -constexpr size_t numXpFeatures = 1 + static_cast(Xp::VerifiedFetches); +constexpr size_t numXpFeatures = 1 + static_cast(Xp::PipeOperators); constexpr std::array xpFeatureDetails = {{ { @@ -294,6 +294,14 @@ constexpr std::array xpFeatureDetails )", .trackingUrl = "https://github.com/NixOS/nix/milestone/48", }, + { + .tag = Xp::PipeOperators, + .name = "pipe-operators", + .description = R"( + Add `|>` and `<|` operators to the Nix language. + )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/55", + }, }}; static_assert( diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index 6ffbc0c10..e65e51280 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -35,6 +35,7 @@ enum struct ExperimentalFeature ConfigurableImpureEnv, MountedSSHStore, VerifiedFetches, + PipeOperators, }; /** diff --git a/tests/functional/lang/eval-fail-pipe-operators.err.exp b/tests/functional/lang/eval-fail-pipe-operators.err.exp new file mode 100644 index 000000000..49f3fa8ad --- /dev/null +++ b/tests/functional/lang/eval-fail-pipe-operators.err.exp @@ -0,0 +1,5 @@ +error: experimental Nix feature 'pipe-operators' is disabled; add '--extra-experimental-features pipe-operators' to enable it + at /pwd/lang/eval-fail-pipe-operators.nix:1:3: + 1| 1 |> 2 + | ^ + 2| diff --git a/tests/functional/lang/eval-fail-pipe-operators.nix b/tests/functional/lang/eval-fail-pipe-operators.nix new file mode 100644 index 000000000..433e0fd7f --- /dev/null +++ b/tests/functional/lang/eval-fail-pipe-operators.nix @@ -0,0 +1 @@ +1 |> 2 diff --git a/tests/unit/libexpr/main.cc b/tests/unit/libexpr/main.cc index cf7fcf5a3..e3412d9ef 100644 --- a/tests/unit/libexpr/main.cc +++ b/tests/unit/libexpr/main.cc @@ -34,6 +34,9 @@ int main (int argc, char **argv) { setEnv("_NIX_TEST_NO_SANDBOX", "1"); #endif + // For pipe operator tests in trivial.cc + experimentalFeatureSettings.set("experimental-features", "pipe-operators"); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/unit/libexpr/trivial.cc b/tests/unit/libexpr/trivial.cc index 61ea71a0f..e455a571b 100644 --- a/tests/unit/libexpr/trivial.cc +++ b/tests/unit/libexpr/trivial.cc @@ -182,6 +182,60 @@ namespace nix { ASSERT_THAT(v, IsIntEq(15)); } + TEST_F(TrivialExpressionTest, forwardPipe) { + auto v = eval("1 |> builtins.add 2 |> builtins.mul 3"); + ASSERT_THAT(v, IsIntEq(9)); + } + + TEST_F(TrivialExpressionTest, backwardPipe) { + auto v = eval("builtins.add 1 <| builtins.mul 2 <| 3"); + ASSERT_THAT(v, IsIntEq(7)); + } + + TEST_F(TrivialExpressionTest, forwardPipeEvaluationOrder) { + auto v = eval("1 |> null |> (x: 2)"); + ASSERT_THAT(v, IsIntEq(2)); + } + + TEST_F(TrivialExpressionTest, backwardPipeEvaluationOrder) { + auto v = eval("(x: 1) <| null <| 2"); + ASSERT_THAT(v, IsIntEq(1)); + } + + TEST_F(TrivialExpressionTest, differentPipeOperatorsDoNotAssociate) { + ASSERT_THROW(eval("(x: 1) <| 2 |> (x: 3)"), ParseError); + } + + TEST_F(TrivialExpressionTest, differentPipeOperatorsParensLeft) { + auto v = eval("((x: 1) <| 2) |> (x: 3)"); + ASSERT_THAT(v, IsIntEq(3)); + } + + TEST_F(TrivialExpressionTest, differentPipeOperatorsParensRight) { + auto v = eval("(x: 1) <| (2 |> (x: 3))"); + ASSERT_THAT(v, IsIntEq(1)); + } + + TEST_F(TrivialExpressionTest, forwardPipeLowestPrecedence) { + auto v = eval("false -> true |> (x: !x)"); + ASSERT_THAT(v, IsFalse()); + } + + TEST_F(TrivialExpressionTest, backwardPipeLowestPrecedence) { + auto v = eval("(x: !x) <| false -> true"); + ASSERT_THAT(v, IsFalse()); + } + + TEST_F(TrivialExpressionTest, forwardPipeStrongerThanElse) { + auto v = eval("if true then 1 else 2 |> 3"); + ASSERT_THAT(v, IsIntEq(1)); + } + + TEST_F(TrivialExpressionTest, backwardPipeStrongerThanElse) { + auto v = eval("if true then 1 else 2 <| 3"); + ASSERT_THAT(v, IsIntEq(1)); + } + TEST_F(TrivialExpressionTest, bindOr) { auto v = eval("{ or = 1; }"); ASSERT_THAT(v, IsAttrsOfSize(1)); From 459ee005633eb8094c82b6b6d9bff1df2732b893 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 19:22:53 +0200 Subject: [PATCH 677/910] Render the release notes when building the manual from dev shell --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 2384c2974..fc6d16169 100644 --- a/flake.nix +++ b/flake.nix @@ -333,6 +333,7 @@ ++ [ pkgs.buildPackages.cmake pkgs.buildPackages.shellcheck + pkgs.buildPackages.changelog-d modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) From 4bfc96f376ff0e0cd5fba4b36d7afcd4abcee020 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 19:21:34 +0200 Subject: [PATCH 678/910] Fix and update release notes --- doc/manual/rl-next/shebang-relative.md | 2 +- doc/manual/rl-next/zzz-other.md | 50 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 doc/manual/rl-next/zzz-other.md diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index c887a598a..d12c0f8dc 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -8,7 +8,7 @@ issues: --- -Relative [path](@docroot@/language/values.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary?highlight=base%20directory#gloss-base-directory). +Relative [path](@docroot@/language/types.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary.md?highlight=base%20directory#gloss-base-directory). Previously they were resolved relative to the current working directory. For example, consider the following script in `~/myproject/say-hi`: diff --git a/doc/manual/rl-next/zzz-other.md b/doc/manual/rl-next/zzz-other.md new file mode 100644 index 000000000..f3721bd38 --- /dev/null +++ b/doc/manual/rl-next/zzz-other.md @@ -0,0 +1,50 @@ +--- +synopsis: Other changes +--- + +- [#9063](https://github.com/NixOS/nix/pull/9063): introduce `libnixflake` and move flakes-specific code there (Nix is internally composed of a set of libraries, and this better reflects the architecture wrt flakes) +- [#10852](https://github.com/NixOS/nix/pull/10852): make Nix commands respond better to interruption - Ctrl+C in the terminal +- [#10853](https://github.com/NixOS/nix/pull/10853): fix docs of `builtins.importNative` +- [#10858](https://github.com/NixOS/nix/pull/10858): `flake check`: Recognize well known `homeModule`/`homeModules` attribute +- [#10865](https://github.com/NixOS/nix/pull/10865): Update dependencies to Nixpkgs 24.05 (when using the `nix` flake), and support bdwgc 8.2.6 ([#11141](https://github.com/NixOS/nix/issues/11141), [#10880](https://github.com/NixOS/nix/pull/10880)) +- [#10883](https://github.com/NixOS/nix/pull/10883): Use `TMP` instead of `XDG_RUNTIME_DIR` +- [#10994](https://github.com/NixOS/nix/pull/10994): fix minor bug in elided item counts when printing values lazily +- [#10988](https://github.com/NixOS/nix/pull/10988): restore `commit-lockfile-summary` alias +- [#10941](https://github.com/NixOS/nix/pull/10941): invalid derivation name now causes an actionable error message +- Changes to the interaction between Nix's I/O coroutines and the garbage collector: +- [#10878](https://github.com/NixOS/nix/pull/10878): allow `ipc-sysv*` in the Darwin build sandbox +- [#11031](https://github.com/NixOS/nix/pull/11031): fix Darwin build sandbox +- [#10907](https://github.com/NixOS/nix/pull/10907): use opaque struct instead of `void *` in the C API +- [#10947](https://github.com/NixOS/nix/issues/10947): fix evaluation cache accidentally persisting disallowed IFD errors +- [#11020](https://github.com/NixOS/nix/pull/11020): enable fetch and eval caching for tarballs +- [#11041](https://github.com/NixOS/nix/pull/11041): add discovered attribute paths to the `--show-trace` error trace in `nix-build`, `nix-env`, OfBorg, and other callers of `getDerivations` +- [#11056](https://github.com/NixOS/nix/pull/11056): `s3` store now uses system defined proxy settings +- [#11077](https://github.com/NixOS/nix/pull/11077): support hardlinks in tarballs +- [#11100](https://github.com/NixOS/nix/pull/11100): pretty print values consistently regardless of prior thunk state +- [#11086](https://github.com/NixOS/nix/pull/11086): fix loss of evaluation cache additions in `nix env run`, `nix shell`, `nix develop`, and `nix fmt` +- [#11149](https://github.com/NixOS/nix/pull/11149): report GC time and number of GC cycles in `NIX_SHOW_STATS=1` report +- [#11142](https://github.com/NixOS/nix/pull/11142): aliased options can now also be passed as flags, just like their "normal" counterparts, e.g. `--build-max-jobs` now works +- [#11043](https://github.com/NixOS/nix/pull/11043): `assert a == b; e` now reports some detail about why `a` and `b` are different when they are +- [#11159](https://github.com/NixOS/nix/pull/11159): don't crash a nix-daemon worker process when the client disconnects +- Stability improvements and fixes [#10861](https://github.com/NixOS/nix/pull/10861), [#10865](https://github.com/NixOS/nix/pull/10865), [#10918](https://github.com/NixOS/nix/pull/10918), [#10916](https://github.com/NixOS/nix/pull/10916), [#10884](https://github.com/NixOS/nix/pull/10884), [#10943](https://github.com/NixOS/nix/pull/10943), [#11019](https://github.com/NixOS/nix/pull/11019), [#11122](https://github.com/NixOS/nix/pull/11122), [#11117](https://github.com/NixOS/nix/pull/11117) +- User documentation improvements [#10888](https://github.com/NixOS/nix/pull/10888), [#10966](https://github.com/NixOS/nix/pull/10966), [#10974](https://github.com/NixOS/nix/pull/10974), [#10997](https://github.com/NixOS/nix/pull/10997), [#11013](https://github.com/NixOS/nix/pull/11013), [#11059](https://github.com/NixOS/nix/pull/11059), [#11119](https://github.com/NixOS/nix/pull/11119), [#11116](https://github.com/NixOS/nix/pull/11116), [#11061](https://github.com/NixOS/nix/pull/11061), [#11102](https://github.com/NixOS/nix/pull/11102) +- BSD support: [#10896](https://github.com/NixOS/nix/pull/10896) [#11022](https://github.com/NixOS/nix/pull/11022) [#11156](https://github.com/NixOS/nix/pull/11156) +- Windows support: [#10769](https://github.com/NixOS/nix/pull/10769), [#10975](https://github.com/NixOS/nix/pull/10975) [#11153](https://github.com/NixOS/nix/pull/11153) +- Portability: [#7048](https://github.com/NixOS/nix/pull/7048) [#11090](https://github.com/NixOS/nix/pull/11090) +- Installer improvements [#10902](https://github.com/NixOS/nix/pull/10902) +- Performance improvements [#10853](https://github.com/NixOS/nix/pull/10853), [#10854](https://github.com/NixOS/nix/pull/10854), [#11082](https://github.com/NixOS/nix/pull/11082), [#11092](https://github.com/NixOS/nix/pull/11092), [#11113](https://github.com/NixOS/nix/pull/11113) + +Contributor experience improvements: + +Use Meson to build Nix (nearing completion) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10967](https://github.com/NixOS/nix/pull/10967) [#10963](https://github.com/NixOS/nix/pull/10963) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11064](https://github.com/NixOS/nix/pull/11064) [#11060](https://github.com/NixOS/nix/pull/11060) [#11155](https://github.com/NixOS/nix/pull/11155) +- Testing improvements [#10864](https://github.com/NixOS/nix/pull/10864), [#10903](https://github.com/NixOS/nix/pull/10903), [#10874](https://github.com/NixOS/nix/pull/10874), [#10922](https://github.com/NixOS/nix/pull/10922), [#11006](https://github.com/NixOS/nix/pull/11006), [#11110](https://github.com/NixOS/nix/pull/11110), [#10931](https://github.com/NixOS/nix/pull/10931), [#11123](https://github.com/NixOS/nix/pull/11123) + - [#10603](https://github.com/NixOS/nix/pull/10603): We now evaluate a set of flakes in CI + - [#10922](https://github.com/NixOS/nix/pull/10922): The functional test suite is now run in both in the build sandbox and in a NixOS environment +- CI improvements [#10929](https://github.com/NixOS/nix/pull/10929) [#10999](https://github.com/NixOS/nix/pull/10999) [#11009](https://github.com/NixOS/nix/pull/11009) [#11065](https://github.com/NixOS/nix/pull/11065) [#11071](https://github.com/NixOS/nix/pull/11071) +- Contributor documentation improvements [#10869](https://github.com/NixOS/nix/pull/10869), [#9871](https://github.com/NixOS/nix/pull/9871), [#10960](https://github.com/NixOS/nix/pull/10960), [#11147](https://github.com/NixOS/nix/pull/11147) +- Error message improvements: [#11050](https://github.com/NixOS/nix/pull/11050) [#11154](https://github.com/NixOS/nix/pull/11154) +- Cleaning up the Settings system (`nix.conf` and related architectural cleanups): [#10913](https://github.com/NixOS/nix/pull/10913), [#10951](https://github.com/NixOS/nix/pull/10951), [#11007](https://github.com/NixOS/nix/pull/11007), [#11108](https://github.com/NixOS/nix/pull/11108), [#11014](https://github.com/NixOS/nix/pull/11014), [#11109](https://github.com/NixOS/nix/pull/11109), [#11112](https://github.com/NixOS/nix/pull/11112) +- Other cleanups and refactors [#10857](https://github.com/NixOS/nix/pull/10857) [#10935](https://github.com/NixOS/nix/pull/10935) [#10873](https://github.com/NixOS/nix/pull/10873) [#10745](https://github.com/NixOS/nix/pull/10745) [#10961](https://github.com/NixOS/nix/pull/10961) [#10962](https://github.com/NixOS/nix/pull/10962) [#10972](https://github.com/NixOS/nix/pull/10972) [#11018](https://github.com/NixOS/nix/pull/11018) [#11035](https://github.com/NixOS/nix/pull/11035) [#11037](https://github.com/NixOS/nix/pull/11037) [#11081](https://github.com/NixOS/nix/pull/11081) [#11089](https://github.com/NixOS/nix/pull/11089) [#11093](https://github.com/NixOS/nix/pull/11093) [#11114](https://github.com/NixOS/nix/pull/11114) [#11103](https://github.com/NixOS/nix/pull/11103) [#11126](https://github.com/NixOS/nix/pull/11126) [#11125](https://github.com/NixOS/nix/pull/11125) [#11120](https://github.com/NixOS/nix/pull/11120) +- Scheduler/builder refactoring [#11005](https://github.com/NixOS/nix/pull/11005) +- [#11011](https://github.com/NixOS/nix/pull/11011): enable `-Werror=unused-result` + From caf4e98f0c0a0a7178538ee8ef7b199d2a655aac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:10:41 +0200 Subject: [PATCH 679/910] Log download durations --- src/libstore/filetransfer.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index cbbb0fe7a..e7dae333b 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -71,7 +71,10 @@ struct curlFileTransfer : public FileTransfer curl_off_t writtenToSink = 0; + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + inline static const std::set successfulStatuses {200, 201, 204, 206, 304, 0 /* other protocol */}; + /* Get the HTTP status code, or 0 for other protocols. */ long getHTTPStatus() { @@ -373,10 +376,14 @@ struct curlFileTransfer : public FileTransfer void finish(CURLcode code) { + auto finishTime = std::chrono::steady_clock::now(); + auto httpStatus = getHTTPStatus(); - debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes", - request.verb(), request.uri, code, httpStatus, result.bodySize); + debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes, duration = %.2f s", + request.verb(), request.uri, code, httpStatus, result.bodySize, + std::chrono::duration_cast(finishTime - startTime).count() / 1000.0f + ); appendCurrentUrl(); From 8ffea0a018874e60584eabeb620ec3495873c30d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:10:45 +0200 Subject: [PATCH 680/910] Add 'download-buffer-size' setting We are piping curl downloads into `unpackTarfileToSink()`, but the latter is typically slower than the former if you're on a fast connection. So the download could appear unnecessarily slow. (There is even a risk that if the Git import is *really* slow for whatever reason, the TCP connection could time out.) So let's make the download buffer bigger by default - 64 MiB is big enough for the Nixpkgs tarball. Perhaps in the future, we could have an unlimited buffer that spills data to disk beyond a certain threshold, but that's probably overkill. --- src/libstore/filetransfer.cc | 2 +- src/libstore/filetransfer.hh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index e7dae333b..f48c2e22d 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -858,7 +858,7 @@ void FileTransfer::download( buffer). We don't wait forever to prevent stalling the download thread. (Hopefully sleeping will throttle the sender.) */ - if (state->data.size() > 1024 * 1024) { + if (state->data.size() > fileTransferSettings.downloadBufferSize) { debug("download buffer is full; going to sleep"); state.wait_for(state->request, std::chrono::seconds(10)); } diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh index 1f5b4ab93..d836ab2c4 100644 --- a/src/libstore/filetransfer.hh +++ b/src/libstore/filetransfer.hh @@ -47,6 +47,12 @@ struct FileTransferSettings : Config Setting tries{this, 5, "download-attempts", "How often Nix will attempt to download a file before giving up."}; + + Setting downloadBufferSize{this, 64 * 1024 * 1024, "download-buffer-size", + R"( + The size of Nix's internal download buffer during `curl` transfers. If data is + not processed quickly enough to exceed the size of this buffer, downloads may stall. + )"}; }; extern FileTransferSettings fileTransferSettings; From f6a9a71b38b25d6c9fb0b9a7fbf0eccf99fa5520 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:14:31 +0200 Subject: [PATCH 681/910] Warn if the download buffer is full --- src/libstore/filetransfer.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index f48c2e22d..5ea8b6f96 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -860,6 +860,8 @@ void FileTransfer::download( sender.) */ if (state->data.size() > fileTransferSettings.downloadBufferSize) { debug("download buffer is full; going to sleep"); + static bool haveWarned = false; + warnOnce(haveWarned, "download buffer is full; consider increasing the 'download-buffer-size' setting"); state.wait_for(state->request, std::chrono::seconds(10)); } From 01839b525c5e80d7f67f2808f7f7fb478ddf1ba0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:22:26 +0200 Subject: [PATCH 682/910] Show when we're unpacking an archive into the Git cache This happens in parallel with the download (which starts later), so you only see this message when the download has finished but the import hasn't. --- src/libfetchers/github.cc | 5 +++++ src/libfetchers/tarball.cc | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 2968d2df2..5710b94d5 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -254,10 +254,15 @@ struct GitArchiveInputScheme : InputScheme getFileTransfer()->download(std::move(req), sink); }); + auto act = std::make_unique(*logger, lvlInfo, actUnknown, + fmt("unpacking '%s' into the Git cache", input.to_string())); + TarArchive archive { *source }; auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); + act.reset(); + TarballInfo tarballInfo { .treeHash = parseSink->sync(), .lastModified = lastModified diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 55db3eafb..5837cf8a4 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -143,6 +143,9 @@ DownloadTarballResult downloadTarball( // TODO: fall back to cached value if download fails. + auto act = std::make_unique(*logger, lvlInfo, actUnknown, + fmt("unpacking '%s' into the Git cache", url)); + AutoDelete cleanupTemp; /* Note: if the download is cached, `importTarball()` will receive @@ -167,6 +170,8 @@ DownloadTarballResult downloadTarball( auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); + act.reset(); + auto res(_res->lock()); Attrs infoAttrs; From e0620213146b1a581533302a2fab54af21d49685 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 24 Jul 2024 23:17:15 +0200 Subject: [PATCH 683/910] fix `NIX_PATH` for real (#11079) * fix NIX_PATH overriding - test restricted evaluation - test precedence for setting the search path Co-authored-by: Robert Hensing Co-authored-by: John Ericson --- src/libexpr/eval-gc.cc | 8 ++++ src/libexpr/eval-settings.cc | 7 +--- src/libexpr/eval-settings.hh | 4 +- src/libexpr/eval.cc | 17 +++++++-- src/libutil/config.cc | 14 ++++++- tests/functional/nix_path.sh | 70 ++++++++++++++++++++++++++++++++++ tests/functional/restricted.sh | 3 ++ 7 files changed, 111 insertions(+), 12 deletions(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index ba19cd74e..d82ed1534 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -1,5 +1,7 @@ #include "error.hh" #include "environment-variables.hh" +#include "eval-settings.hh" +#include "config-global.hh" #include "serialise.hh" #include "eval-gc.hh" @@ -230,6 +232,12 @@ void initGC() gcCyclesAfterInit = GC_get_gc_no(); #endif + // NIX_PATH must override the regular setting + // See the comment in applyConfig + if (auto nixPathEnv = getEnv("NIX_PATH")) { + globalConfig.set("nix-path", concatStringsSep(" ", EvalSettings::parseNixPath(nixPathEnv.value()))); + } + gcInitialised = true; } diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index eb5761638..2846eccbc 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -8,7 +8,7 @@ namespace nix { /* Very hacky way to parse $NIX_PATH, which is colon-separated, but can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */ -static Strings parseNixPath(const std::string & s) +Strings EvalSettings::parseNixPath(const std::string & s) { Strings res; @@ -48,10 +48,7 @@ EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lo : readOnlyMode{readOnlyMode} , lookupPathHooks{lookupPathHooks} { - auto var = getEnv("NIX_PATH"); - if (var) nixPath = parseNixPath(*var); - - var = getEnv("NIX_ABORT_ON_WARN"); + auto var = getEnv("NIX_ABORT_ON_WARN"); if (var && (var == "1" || var == "yes" || var == "true")) builtinsAbortOnWarn = true; } diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 89a42caba..8f48b53a5 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -47,6 +47,8 @@ struct EvalSettings : Config static bool isPseudoUrl(std::string_view s); + static Strings parseNixPath(const std::string & s); + static std::string resolvePseudoUrl(std::string_view url); LookupPathHooks lookupPathHooks; @@ -71,7 +73,7 @@ struct EvalSettings : Config )"}; Setting nixPath{ - this, getDefaultNixPath(), "nix-path", + this, {}, "nix-path", R"( List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution. This setting determines the value of diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b192f9b4b..9a6b6c9e8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -215,7 +215,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( - const LookupPath & _lookupPath, + const LookupPath & lookupPathFromArguments, ref store, const fetchers::Settings & fetchSettings, const EvalSettings & settings, @@ -331,12 +331,21 @@ EvalState::EvalState( vStringSymlink.mkString("symlink"); vStringUnknown.mkString("unknown"); - /* Initialise the Nix expression search path. */ + /* Construct the Nix expression search path. */ + assert(lookupPath.elements.empty()); if (!settings.pureEval) { - for (auto & i : _lookupPath.elements) + for (auto & i : lookupPathFromArguments.elements) { lookupPath.elements.emplace_back(LookupPath::Elem {i}); - for (auto & i : settings.nixPath.get()) + } + /* $NIX_PATH overriding regular settings is implemented as a hack in `initGC()` */ + for (auto & i : settings.nixPath.get()) { lookupPath.elements.emplace_back(LookupPath::Elem::parse(i)); + } + if (!settings.restrictEval) { + for (auto & i : EvalSettings::getDefaultNixPath()) { + lookupPath.elements.emplace_back(LookupPath::Elem::parse(i)); + } + } } /* Allow access to all paths in the search path. */ diff --git a/src/libutil/config.cc b/src/libutil/config.cc index b39948261..ca8480304 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -1,6 +1,7 @@ #include "config.hh" #include "args.hh" #include "abstract-setting-to-json.hh" +#include "environment-variables.hh" #include "experimental-features.hh" #include "util.hh" #include "file-system.hh" @@ -170,9 +171,18 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string set(name, value); // Then apply other settings - for (const auto & [name, value] : parsedContents) - if (name != "experimental-features" && name != "extra-experimental-features") + // XXX: NIX_PATH must override the regular setting! This is done in `initGC()` + // Environment variables overriding settings should probably be part of the Config mechanism, + // but at the time of writing it's not worth building that for just one thing + for (const auto & [name, value] : parsedContents) { + if (name != "experimental-features" && name != "extra-experimental-features") { + if ((name == "nix-path" || name == "extra-nix-path") + && getEnv("NIX_PATH").has_value()) { + continue; + } set(name, value); + } + } } void Config::resetOverridden() diff --git a/tests/functional/nix_path.sh b/tests/functional/nix_path.sh index e6a2193f3..7e6a0458d 100755 --- a/tests/functional/nix_path.sh +++ b/tests/functional/nix_path.sh @@ -14,3 +14,73 @@ nix-instantiate --eval -E '' --restrict-eval [[ $(nix-instantiate --find-file by-absolute-path/simple.nix) = $PWD/simple.nix ]] [[ $(nix-instantiate --find-file by-relative-path/simple.nix) = $PWD/simple.nix ]] + +# this is the human-readable specification for the following test cases of interactions between various ways of specifying NIX_PATH. +# TODO: the actual tests are incomplete and too manual. +# there should be 43 of them, since the table has 9 rows and columns, and 2 interactions are meaningless +# ideally they would work off the table programmatically. +# +# | precedence | hard-coded | nix-path in file | extra-nix-path in file | nix-path in env | extra-nix-path in env | NIX_PATH | nix-path | extra-nix-path | -I | +# |------------------------|------------|------------------|------------------------|-----------------|-----------------------|-----------|-----------|-----------------|-----------------| +# | hard-coded | x | ^override | ^append | ^override | ^append | ^override | ^override | ^append | ^append | +# | nix-path in file | | last wins | ^append | ^override | ^append | ^override | ^override | ^append | ^append | +# | extra-nix-path in file | | | append in order | ^override | ^append | ^override | ^override | ^append | ^append | +# | nix-path in env | | | | last wins | ^append | ^override | ^override | ^append | ^append | +# | extra-nix-path in env | | | | | append in order | ^override | ^override | ^append | ^append | +# | NIX_PATH | | | | | | x | ^override | ^append | ^append | +# | nix-path | | | | | | | last wins | ^append | ^append | +# | extra-nix-path | | | | | | | | append in order | append in order | +# | -I | | | | | | | | | append in order | + +unset NIX_PATH + +mkdir -p $TEST_ROOT/{from-nix-path-file,from-NIX_PATH,from-nix-path,from-extra-nix-path,from-I} +for i in from-nix-path-file from-NIX_PATH from-nix-path from-extra-nix-path from-I; do + touch $TEST_ROOT/$i/only-$i.nix +done + +# finding something that's not in any of the default paths fails +( ! $(nix-instantiate --find-file test) ) + +echo "nix-path = test=$TEST_ROOT/from-nix-path-file" >> "$test_nix_conf" + +# Use nix.conf in absence of NIX_PATH +[[ $(nix-instantiate --find-file test) = $TEST_ROOT/from-nix-path-file ]] + +# NIX_PATH overrides nix.conf +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --find-file test) = $TEST_ROOT/from-NIX_PATH ]] +# if NIX_PATH does not have the desired entry, it fails +(! NIX_PATH=test=$TEST_ROOT nix-instantiate --find-file test/only-from-nix-path-file.nix) + +# -I extends nix.conf +[[ $(nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +# if -I does not have the desired entry, the value from nix.conf is used +[[ $(nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-nix-path-file.nix) = $TEST_ROOT/from-nix-path-file/only-from-nix-path-file.nix ]] + +# -I extends NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +# if -I does not have the desired entry, the value from NIX_PATH is used +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-NIX_PATH.nix) = $TEST_ROOT/from-NIX_PATH/only-from-NIX_PATH.nix ]] + +# --extra-nix-path extends NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test/only-from-extra-nix-path.nix) = $TEST_ROOT/from-extra-nix-path/only-from-extra-nix-path.nix ]] +# if --extra-nix-path does not have the desired entry, the value from NIX_PATH is used +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test/only-from-NIX_PATH.nix) = $TEST_ROOT/from-NIX_PATH/only-from-NIX_PATH.nix ]] + +# --nix-path overrides NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test) = $TEST_ROOT/from-nix-path ]] +# if --nix-path does not have the desired entry, it fails +(! NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test/only-from-NIX_PATH.nix) + +# --nix-path overrides nix.conf +[[ $(nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test) = $TEST_ROOT/from-nix-path ]] +(! nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test/only-from-nix-path-file.nix) + +# --extra-nix-path extends nix.conf +[[ $(nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test/only-from-extra-nix-path.nix) = $TEST_ROOT/from-extra-nix-path/only-from-extra-nix-path.nix ]] +# if --extra-nix-path does not have the desired entry, it is taken from nix.conf +[[ $(nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test) = $TEST_ROOT/from-nix-path-file ]] + +# -I extends --nix-path +[[ $(nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +[[ $(nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path -I test=$TEST_ROOT/from-I --find-file test/only-from-nix-path.nix) = $TEST_ROOT/from-nix-path/only-from-nix-path.nix ]] diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh index 915d973b0..591367e9f 100755 --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -10,6 +10,9 @@ nix-instantiate --restrict-eval --eval -E '1 + 2' nix-instantiate --restrict-eval ./simple.nix -I src=. nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh +# no default NIX_PATH +(unset NIX_PATH; ! nix-instantiate --restrict-eval --find-file .) + (! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix') nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../.. From dba1142c01111f1f98cc89aa850d5df0c012a907 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 25 Jul 2024 03:45:34 +0200 Subject: [PATCH 684/910] docs: add identifiers (#11174) * docs: add identifiers * clarify attribute set notation and add examples * add definition of names Co-authored-by: Ryan Hendrickson --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/language/identifiers.md | 50 ++++++++++++++++ doc/manual/src/language/operators.md | 6 -- doc/manual/src/language/syntax.md | 83 +++++++++++++++++++------- 4 files changed, 112 insertions(+), 28 deletions(-) create mode 100644 doc/manual/src/language/identifiers.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index a6a2101e9..b4dd277e3 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -28,6 +28,7 @@ - [Data Types](language/types.md) - [String context](language/string-context.md) - [Syntax and semantics](language/syntax.md) + - [Identifiers](language/identifiers.md) - [Scoping rules](language/scope.md) - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) diff --git a/doc/manual/src/language/identifiers.md b/doc/manual/src/language/identifiers.md new file mode 100644 index 000000000..c9e981da6 --- /dev/null +++ b/doc/manual/src/language/identifiers.md @@ -0,0 +1,50 @@ +# Identifiers + +An *identifier* is an [ASCII](https://en.wikipedia.org/wiki/ASCII) character sequence that: +- Starts with a letter (`a-z`, `A-Z`) or underscore (`_`) +- Can contain any number of: + - Letters (`a-z`, `A-Z`) + - Digits (`0-9`) + - Underscores (`_`) + - Apostrophes (`'`) + - Hyphens (`-`) +- Is not one of the [keywords](#keywords) + +> **Syntax** +> +> *identifier* ~ `[A-Za-z_][A-Za-z0-9_'-]*` + +# Names + +A name can be an [identifier](#identifier) or a [string literal](./syntax.md#string-literal). + +> **Syntax** +> +> *name* → *identifier* | *string* + +Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting attributes). + +# Keywords + +These keywords are reserved and cannot be used as [identifiers](#identifiers): + +- [`assert`](./syntax.md#assertions) +- [`else`][if] +- [`if`][if] +- [`in`][let] +- [`inherit`](./syntax.md#inheriting-attributes) +- [`let`][let] +- [`or`](./operators.md#attribute-selection) (see note) +- [`rec`](./syntax.md#recursive-sets) +- [`then`][if] +- [`with`](./syntax.md#with-expressions) + +[if]: ./syntax.md#conditionals +[let]: ./syntax.md#let-expressions + +> **Note** +> +> The Nix language evaluator currently allows `or` to be used as a name in some contexts, for backwards compatibility reasons. +> Users are advised not to rely on this. +> +> There are long-standing issues with how `or` is parsed as a name, which can't be resolved without making a breaking change to the language. diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 9660a764d..d2476c413 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -42,12 +42,6 @@ Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*. If the attribute doesn’t exist, return the *expr* after `or` if provided, otherwise abort evaluation. -An attribute path is a dot-separated list of [attribute names](./types.md#attribute-set). - -> **Syntax** -> -> *attrpath* = *name* [ `.` *name* ]... - [Attribute selection]: #attribute-selection ## Has attribute diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md index b0779ea95..6108bacd6 100644 --- a/doc/manual/src/language/syntax.md +++ b/doc/manual/src/language/syntax.md @@ -247,37 +247,76 @@ Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#built ## Attribute Set {#attrs-literal} -An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). +An attribute set is a collection of name-value-pairs called *attributes*. -An attribute name can be an identifier or a [string](#string). -An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`). +Attribute sets are written enclosed in curly brackets (`{ }`). +Attribute names and attribute values are separated by an equal sign (`=`). +Each value can be an arbitrary expression, terminated by a semicolon (`;`) + +An attribute name is a string without context, and is denoted by a [name] (an [identifier](./identifiers.md#identifiers) or [string literal](#string-literal)). + +[name]: ./identifiers.md#names > **Syntax** > -> *name* = *identifier* | *string* \ -> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*` - -Names and values are separated by an equal sign (`=`). -Each value is an arbitrary expression terminated by a semicolon (`;`). - -> **Syntax** -> -> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}` +> *attrset* → `{` { *name* `=` *expr* `;` } `}` Attributes can appear in any order. -An attribute name may only occur once. +An attribute name may only occur once in each attribute set. -Example: +> **Example** +> +> This defines an attribute set with attributes named: +> - `x` with the value `123`, an integer +> - `text` with the value `"Hello"`, a string +> - `y` where the value is the result of applying the function `f` to the attribute set `{ bla = 456; }` +> +> ```nix +> { +> x = 123; +> text = "Hello"; +> y = f { bla = 456; }; +> } +> ``` -```nix -{ - x = 123; - text = "Hello"; - y = f { bla = 456; }; -} -``` +Attributes in nested attribute sets can be written using *attribute paths*. -This defines a set with attributes named `x`, `text`, `y`. +> **Syntax** +> +> *attrset* → `{` { *attrpath* `=` *expr* `;` } `}` + +An attribute path is a dot-separated list of [names][name]. + +> **Syntax** +> +> *attrpath* = *name* { `.` *name* } + + + +> **Example** +> +> ```nix +> { a.b.c = 1; a.b.d = 2; } +> ``` +> +> { +> a = { +> b = { +> c = 1; +> d = 2; +> }; +> }; +> } + +Attribute names can also be set implicitly by using the [`inherit` keyword](#inheriting-attributes). + +> **Example** +> +> ```nix +> { inherit (builtins) true; } +> ``` +> +> { true = true; } Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection). From f4915af71c9a72a0519c90088abb3a36c265ca95 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 24 Jul 2024 22:25:08 -0400 Subject: [PATCH 685/910] Put flake functional tests in their own group This is a nice thing to have, and it made it easier to work on the Meson-ifcation of these functional tests too. --- Makefile | 1 + tests/functional/flakes/local.mk | 24 ++++++++++++++++++++++++ tests/functional/local.mk | 21 --------------------- 3 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 tests/functional/flakes/local.mk diff --git a/Makefile b/Makefile index bb64a104e..d3ea8513d 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ tests/functional/local.mk \ + tests/functional/flakes/local.mk \ tests/functional/ca/local.mk \ tests/functional/git-hashing/local.mk \ tests/functional/dyn-drv/local.mk \ diff --git a/tests/functional/flakes/local.mk b/tests/functional/flakes/local.mk new file mode 100644 index 000000000..71e50ad07 --- /dev/null +++ b/tests/functional/flakes/local.mk @@ -0,0 +1,24 @@ +flake-tests := \ + $(d)/flakes.sh \ + $(d)/develop.sh \ + $(d)/edit.sh \ + $(d)/run.sh \ + $(d)/mercurial.sh \ + $(d)/circular.sh \ + $(d)/init.sh \ + $(d)/inputs.sh \ + $(d)/follow-paths.sh \ + $(d)/bundle.sh \ + $(d)/check.sh \ + $(d)/unlocked-override.sh \ + $(d)/absolute-paths.sh \ + $(d)/absolute-attr-paths.sh \ + $(d)/build-paths.sh \ + $(d)/flake-in-submodule.sh \ + $(d)/prefetch.sh \ + $(d)/eval-cache.sh \ + $(d)/search-root.sh \ + $(d)/config.sh \ + $(d)/show.sh + +install-tests-groups += flake diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 797002e92..8b4945cac 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -1,23 +1,5 @@ nix_tests = \ test-infra.sh \ - flakes/flakes.sh \ - flakes/develop.sh \ - flakes/edit.sh \ - flakes/run.sh \ - flakes/mercurial.sh \ - flakes/circular.sh \ - flakes/init.sh \ - flakes/inputs.sh \ - flakes/follow-paths.sh \ - flakes/bundle.sh \ - flakes/check.sh \ - flakes/unlocked-override.sh \ - flakes/absolute-paths.sh \ - flakes/absolute-attr-paths.sh \ - flakes/build-paths.sh \ - flakes/flake-in-submodule.sh \ - flakes/prefetch.sh \ - flakes/eval-cache.sh \ gc.sh \ nix-collect-garbage-d.sh \ remote-store.sh \ @@ -61,7 +43,6 @@ nix_tests = \ restricted.sh \ fetchGitSubmodules.sh \ fetchGitVerification.sh \ - flakes/search-root.sh \ readfile-context.sh \ nix-channel.sh \ recursive.sh \ @@ -102,7 +83,6 @@ nix_tests = \ nix-copy-ssh-ng.sh \ post-hook.sh \ function-trace.sh \ - flakes/config.sh \ fmt.sh \ eval-store.sh \ why-depends.sh \ @@ -125,7 +105,6 @@ nix_tests = \ store-info.sh \ fetchClosure.sh \ completions.sh \ - flakes/show.sh \ impure-derivations.sh \ path-from-hash-part.sh \ path-info.sh \ From dcbe2453f536464a4747c20a057df3ea93c0c400 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 24 Jul 2024 22:36:43 -0400 Subject: [PATCH 686/910] Change skipped test error code from 99 to 77 Meson uses a venerable GNU convention described in https://www.gnu.org/software/automake/manual/html_node/Scripts_002dbased-Testsuites.html in which: > When no test protocol is in use, an exit status of 0 from a test > script will denote a success, an exit status of 77 a skipped test, an > exit status of 99 a hard error, and any other exit status will denote > a failure. 77 is thus what we want, not 99. --- mk/run-test.sh | 2 +- tests/functional/common/vars-and-functions.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mk/run-test.sh b/mk/run-test.sh index 543c845e1..7f9f1d5f8 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -28,7 +28,7 @@ run_test if [[ "$status" = 0 ]]; then echo "$post_run_msg [${green}PASS$normal]" -elif [[ "$status" = 99 ]]; then +elif [[ "$status" = 77 ]]; then echo "$post_run_msg [${yellow}SKIP$normal]" else echo "$post_run_msg [${red}FAIL$normal]" diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 4316a30d5..6a0988f12 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -190,7 +190,7 @@ isDaemonNewer () { skipTest () { echo "$1, skipping this test..." >&2 - exit 99 + exit 77 } TODO_NixOS() { From a2fed6db9e6ec0ce8441d16bec0752b420dd49e9 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 25 Jul 2024 04:53:06 +0200 Subject: [PATCH 687/910] manual: Contributing -> Development, Hacking -> Building (#9014) * manual: Contributing -> Development, Hacking -> Building what's currently called "hacking" are really instructions for setting up a development environment and compiling from source. we have a contribution guide in the repo (which rightly focuses on GitHub workflows), and the material in the manual is more about working on the code itself. since we'd otherwise have three headings that amount to "Building Nix", this change also moves the "classic Nix" instructions to the top. we may want to reorganise this in the future, and bring contributor-oriented information closer to the code, but for now let's stick to more accurate names to ease navigation. --- CONTRIBUTING.md | 8 +- README.md | 7 +- doc/manual/generate-builtins.nix | 2 +- doc/manual/generate-manpage.nix | 2 +- doc/manual/generate-settings.nix | 4 +- doc/manual/generate-store-info.nix | 4 +- doc/manual/generate-xp-features-shortlist.nix | 2 +- doc/manual/local.mk | 8 +- doc/manual/redirects.js | 11 +- doc/manual/src/SUMMARY.md.in | 17 +- doc/manual/src/_redirects | 10 +- doc/manual/src/c-api.md | 2 +- .../src/command-ref/experimental-commands.md | 2 +- .../src/command-ref/nix-store/realise.md | 2 +- .../hacking.md => development/building.md} | 181 +++++------------- .../cli-guideline.md | 0 doc/manual/src/development/contributing.md | 79 ++++++++ .../src/{contributing => development}/cxx.md | 0 .../documentation.md | 2 +- .../experimental-features.md | 0 .../{contributing => development}/index.md | 2 +- .../json-guideline.md | 0 .../{contributing => development}/testing.md | 0 doc/manual/src/glossary.md | 4 +- .../src/installation/installing-binary.md | 2 +- .../src/language/advanced-attributes.md | 10 +- doc/manual/src/protocols/derivation-aterm.md | 4 +- doc/manual/src/protocols/json/derivation.md | 2 +- .../src/protocols/json/store-object-info.md | 2 +- doc/manual/src/release-notes/rl-2.18.md | 4 +- doc/manual/src/release-notes/rl-2.19.md | 6 +- doc/manual/src/release-notes/rl-2.23.md | 4 +- doc/manual/src/release-notes/rl-2.4.md | 2 +- .../file-system-object/content-address.md | 2 +- .../src/store/store-object/content-address.md | 2 +- src/libexpr/eval-settings.hh | 2 +- src/libexpr/primops/fetchTree.cc | 10 +- src/libstore/globals.hh | 6 +- src/libutil/config.hh | 2 +- src/nix/nix.md | 6 +- 40 files changed, 214 insertions(+), 201 deletions(-) rename doc/manual/src/{contributing/hacking.md => development/building.md} (74%) rename doc/manual/src/{contributing => development}/cli-guideline.md (100%) create mode 100644 doc/manual/src/development/contributing.md rename doc/manual/src/{contributing => development}/cxx.md (100%) rename doc/manual/src/{contributing => development}/documentation.md (99%) rename doc/manual/src/{contributing => development}/experimental-features.md (100%) rename doc/manual/src/{contributing => development}/index.md (77%) rename doc/manual/src/{contributing => development}/json-guideline.md (100%) rename doc/manual/src/{contributing => development}/testing.md (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 38f5d43b7..12423366a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,9 +41,9 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). There are many open pull requests that might already do what you intend to work on. You can use [labels](https://github.com/NixOS/nix/labels) to filter for relevant topics. -3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests. +3. Check the [Nix reference manual](https://nix.dev/manual/nix/development/development/building.html) for information on building Nix and running its tests. - For contributions to the command line interface, please check the [CLI guidelines](https://nixos.org/manual/nix/unstable/contributing/cli-guideline.html). + For contributions to the command line interface, please check the [CLI guidelines](https://nix.dev/manual/nix/development/development/cli-guideline.html). 4. Make your change! @@ -69,7 +69,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). - [ ] API documentation in header files - [ ] Code and comments are self-explanatory - [ ] Commit message explains **why** the change was made - - [ ] New feature or incompatible change: [add a release note](https://nixos.org/manual/nix/stable/contributing/hacking#add-a-release-note) + - [ ] New feature or incompatible change: [add a release note](https://nix.dev/manual/nix/development/development/contributing.html#add-a-release-note) 7. If you need additional feedback or help to getting pull request into shape, ask other contributors using [@mentions](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#mentioning-people-and-teams). @@ -78,7 +78,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). The Nix reference manual is hosted on https://nixos.org/manual/nix. The underlying source files are located in [`doc/manual/src`](./doc/manual/src). For small changes you can [use GitHub to edit these files](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files) -For larger changes see the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html). +For larger changes see the [Nix reference manual](https://nix.dev/manual/nix/development/development/contributing.html). ## Getting help diff --git a/README.md b/README.md index 931a60bba..ab647e53b 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,18 @@ [![Test](https://github.com/NixOS/nix/workflows/Test/badge.svg)](https://github.com/NixOS/nix/actions) Nix is a powerful package manager for Linux and other Unix systems that makes package -management reliable and reproducible. Please refer to the [Nix manual](https://nixos.org/nix/manual) +management reliable and reproducible. Please refer to the [Nix manual](https://nix.dev/reference/nix-manual) for more details. ## Installation and first steps Visit [nix.dev](https://nix.dev) for [installation instructions](https://nix.dev/tutorials/install-nix) and [beginner tutorials](https://nix.dev/tutorials/first-steps). -Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual). +Full reference documentation can be found in the [Nix manual](https://nix.dev/reference/nix-manual). ## Building and developing -See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to - set up a development environment and build Nix from source. +Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/development/building.html). ## Contributing diff --git a/doc/manual/generate-builtins.nix b/doc/manual/generate-builtins.nix index 13de6c397..37ed12a43 100644 --- a/doc/manual/generate-builtins.nix +++ b/doc/manual/generate-builtins.nix @@ -12,7 +12,7 @@ let experimentalNotice = optionalString (experimental-feature != null) '' > **Note** > - > This function is only available if the [`${experimental-feature}` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-${experimental-feature}) is enabled. + > This function is only available if the [`${experimental-feature}` experimental feature](@docroot@/development/experimental-features.md#xp-feature-${experimental-feature}) is enabled. > > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 90eaa1a73..791bfd2c7 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -38,7 +38,7 @@ let result = '' > **Warning** \ > This program is - > [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) + > [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command) > and its interface is subject to change. # Name diff --git a/doc/manual/generate-settings.nix b/doc/manual/generate-settings.nix index 504cda362..93a8e093e 100644 --- a/doc/manual/generate-settings.nix +++ b/doc/manual/generate-settings.nix @@ -33,10 +33,10 @@ let > **Warning** > > This setting is part of an - > [experimental feature](@docroot@/contributing/experimental-features.md). + > [experimental feature](@docroot@/development/experimental-features.md). > > To change this setting, make sure the - > [`${experimentalFeature}` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}) + > [`${experimentalFeature}` experimental feature](@docroot@/development/experimental-features.md#xp-feature-${experimentalFeature}) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > diff --git a/doc/manual/generate-store-info.nix b/doc/manual/generate-store-info.nix index c311c3c39..cc3704124 100644 --- a/doc/manual/generate-store-info.nix +++ b/doc/manual/generate-store-info.nix @@ -32,10 +32,10 @@ let > **Warning** > > This store is part of an - > [experimental feature](@docroot@/contributing/experimental-features.md). + > [experimental feature](@docroot@/development/experimental-features.md). > > To use this store, make sure the - > [`${experimentalFeature}` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}) + > [`${experimentalFeature}` experimental feature](@docroot@/development/experimental-features.md#xp-feature-${experimentalFeature}) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > diff --git a/doc/manual/generate-xp-features-shortlist.nix b/doc/manual/generate-xp-features-shortlist.nix index ec09f4b75..eb735ba5f 100644 --- a/doc/manual/generate-xp-features-shortlist.nix +++ b/doc/manual/generate-xp-features-shortlist.nix @@ -4,6 +4,6 @@ with import ; let showExperimentalFeature = name: doc: '' - - [`${name}`](@docroot@/contributing/experimental-features.md#xp-feature-${name}) + - [`${name}`](@docroot@/development/experimental-features.md#xp-feature-${name}) ''; in xps: indent " " (concatStrings (attrValues (mapAttrs showExperimentalFeature xps))) diff --git a/doc/manual/local.mk b/doc/manual/local.mk index 0cec52885..fcc50f460 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -95,7 +95,7 @@ $(d)/nix-profiles.5: $(d)/src/command-ref/files/profiles.md $(trace-gen) lowdown -sT man --nroff-nolinks -M section=5 $^.tmp -o $@ @rm $^.tmp -$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/SUMMARY-rl-next.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md +$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/SUMMARY-rl-next.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/development/experimental-feature-descriptions.md @cp $< $@ @$(call process-includes,$@,$@) @@ -124,7 +124,7 @@ $(d)/conf-file.json: $(doc_nix) $(trace-gen) $(dummy-env) $(doc_nix) config show --json --experimental-features nix-command > $@.tmp @mv $@.tmp $@ -$(d)/src/contributing/experimental-feature-descriptions.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(doc_nix) +$(d)/src/development/experimental-feature-descriptions.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(doc_nix) @rm -rf $@ $@.tmp $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-xp-features.nix (builtins.fromJSON (builtins.readFile $<))' @mv $@.tmp $@ @@ -207,11 +207,11 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli done @touch $@ -# the `! -name 'contributing.md'` filter excludes the one place where +# the `! -name 'documentation.md'` filter excludes the one place where # `@docroot@` is to be preserved for documenting the mechanism # FIXME: maybe contributing guides should live right next to the code # instead of in the manual -$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg +$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/development/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg $(trace-gen) \ tmp="$$(mktemp -d)"; \ cp -r doc/manual "$$tmp"; \ diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 0f9f91b03..beef6ef4a 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -143,7 +143,7 @@ const redirects = { "opt-timeout": "command-ref/opt-common.html#opt-timeout", "sec-common-options": "command-ref/opt-common.html", "ch-utilities": "command-ref/utilities.html", - "chap-hacking": "contributing/hacking.html", + "chap-hacking": "development/building.html", "adv-attr-allowSubstitutes": "language/advanced-attributes.html#adv-attr-allowSubstitutes", "adv-attr-allowedReferences": "language/advanced-attributes.html#adv-attr-allowedReferences", "adv-attr-allowedRequisites": "language/advanced-attributes.html#adv-attr-allowedRequisites", @@ -350,7 +350,7 @@ const redirects = { "macos": "uninstall.html#macos", "uninstalling": "uninstall.html", }, - "contributing/hacking.html": { + "development/building.html": { "nix-with-flakes": "#building-nix-with-flakes", "classic-nix": "#building-nix", "running-tests": "testing.html#running-tests", @@ -361,7 +361,12 @@ const redirects = { "installer-tests": "testing.html#installer-tests", "one-time-setup": "testing.html#one-time-setup", "using-the-ci-generated-installer-for-manual-testing": "testing.html#using-the-ci-generated-installer-for-manual-testing", - "characterization-testing": "#characterisation-testing-unit", + "characterization-testing": "testing.html#characterisation-testing-unit", + "add-a-release-note": "contributing.html#add-a-release-note", + "add-an-entry": "contributing.html#add-an-entry", + "build-process": "contributing.html#build-process", + "reverting": "contributing.html#reverting", + "branches": "contributing.html#branches", }, "glossary.html": { "gloss-local-store": "store/types/local-store.html", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index b4dd277e3..3918faeb2 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -116,14 +116,15 @@ - [Derivation "ATerm" file format](protocols/derivation-aterm.md) - [C API](c-api.md) - [Glossary](glossary.md) -- [Contributing](contributing/index.md) - - [Hacking](contributing/hacking.md) - - [Testing](contributing/testing.md) - - [Documentation](contributing/documentation.md) - - [Experimental Features](contributing/experimental-features.md) - - [CLI guideline](contributing/cli-guideline.md) - - [JSON guideline](contributing/json-guideline.md) - - [C++ style guide](contributing/cxx.md) +- [Development](development/index.md) + - [Building](development/building.md) + - [Testing](development/testing.md) + - [Documentation](development/documentation.md) + - [CLI guideline](development/cli-guideline.md) + - [JSON guideline](development/json-guideline.md) + - [C++ style guide](development/cxx.md) + - [Experimental Features](development/experimental-features.md) + - [Contributing](development/contributing.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} - [Release 2.23 (2024-06-03)](release-notes/rl-2.23.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index 578c48f06..07b3130f9 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -20,7 +20,15 @@ /command-ref/command-ref /command-ref 301! -/contributing/contributing /contributing 301! +/contributing/contributing /development 301! +/contributing /development 301! +/contributing/hacking /development/building 301! +/contributing/testing /development/testing 301! +/contributing/documentation /development/documentation 301! +/contributing/experimental-features /development/experimental-features 301! +/contributing/cli-guideline /development/cli-guideline 301! +/contributing/json-guideline /development/json-guideline 301! +/contributing/cxx /development/cxx 301! /expressions/expression-language /language/ 301! /expressions/language-constructs /language/constructs 301! diff --git a/doc/manual/src/c-api.md b/doc/manual/src/c-api.md index 29df0b644..0cdd83832 100644 --- a/doc/manual/src/c-api.md +++ b/doc/manual/src/c-api.md @@ -10,7 +10,7 @@ See: - [Matrix Room *Nix Bindings*](https://matrix.to/#/#nix-bindings:nixos.org) for discussion and questions. - [Stabilisation Milestone](https://github.com/NixOS/nix/milestone/52) - [Other C API PRs and issues](https://github.com/NixOS/nix/labels/c%20api) -- [Contributing C API Documentation](contributing/documentation.md#c-api-documentation), including how to build it locally. +- [Contributing C API Documentation](development/documentation.md#c-api-documentation), including how to build it locally. [Getting Started]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs [Index]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs/globals.html diff --git a/doc/manual/src/command-ref/experimental-commands.md b/doc/manual/src/command-ref/experimental-commands.md index 286ddc6d6..1190729a2 100644 --- a/doc/manual/src/command-ref/experimental-commands.md +++ b/doc/manual/src/command-ref/experimental-commands.md @@ -1,6 +1,6 @@ # Experimental Commands -This section lists [experimental commands](@docroot@/contributing/experimental-features.md#xp-feature-nix-command). +This section lists [experimental commands](@docroot@/development/experimental-features.md#xp-feature-nix-command). > **Warning** > diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index e30b351a4..a899758df 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -32,7 +32,7 @@ If no substitutes are available and no store derivation is given, realisation fa [store objects]: @docroot@/store/store-object.md [closure]: @docroot@/glossary.md#gloss-closure [substituters]: @docroot@/command-ref/conf-file.md#conf-substituters -[content-addressed derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations +[content-addressed derivations]: @docroot@/development/experimental-features.md#xp-feature-ca-derivations [Nix database]: @docroot@/glossary.md#gloss-nix-database The resulting paths are printed on standard output. diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/development/building.md similarity index 74% rename from doc/manual/src/contributing/hacking.md rename to doc/manual/src/development/building.md index c128515e9..5a5fb3368 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/development/building.md @@ -1,24 +1,67 @@ -# Hacking +# Building Nix -This section provides some notes on how to hack on Nix. To get the -latest version of Nix from GitHub: +This section provides some notes on how to start hacking on Nix. +To get the latest version of Nix from GitHub: ```console $ git clone https://github.com/NixOS/nix.git $ cd nix ``` -The following instructions assume you already have some version of Nix installed locally, so that you can use it to set up the development environment. If you don't have it installed, follow the [installation instructions]. +> **Note** +> +> The following instructions assume you already have some version of Nix installed locally, so that you can use it to set up the development environment. +> If you don't have it installed, follow the [installation instructions](../installation/index.md). -[installation instructions]: ../installation/index.md + +To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found: + +```console +$ nix-shell +``` + +To get a shell with one of the other [supported compilation environments](#compilation-environments): + +```console +$ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages +``` + +> **Note** +> +> You can use `native-ccacheStdenvPackages` to drastically improve rebuild time. +> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`. + +To build Nix itself in this shell: + +```console +[nix-shell]$ autoreconfPhase +[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out +[nix-shell]$ make -j $NIX_BUILD_CORES +``` + +To install it in `$(pwd)/outputs` and test it: + +```console +[nix-shell]$ make install +[nix-shell]$ make installcheck -j $NIX_BUILD_CORES +[nix-shell]$ ./outputs/out/bin/nix --version +nix (Nix) 2.12 +``` + +To build a release version of Nix for the current operating system and CPU architecture: + +```console +$ nix-build +``` + +You can also build Nix for one of the [supported platforms](#platforms). ## Building Nix with flakes This section assumes you are using Nix with the [`flakes`] and [`nix-command`] experimental features enabled. -See the [Building Nix](#building-nix) section for equivalent instructions using stable Nix interfaces. -[`flakes`]: @docroot@/contributing/experimental-features.md#xp-feature-flakes -[`nix-command`]: @docroot@/contributing/experimental-features.md#xp-nix-command +[`flakes`]: @docroot@/development/experimental-features.md#xp-feature-flakes +[`nix-command`]: @docroot@/development/experimental-features.md#xp-nix-command To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found: @@ -67,50 +110,6 @@ $ nix build You can also build Nix for one of the [supported platforms](#platforms). -## Building Nix - -To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found: - -```console -$ nix-shell -``` - -To get a shell with one of the other [supported compilation environments](#compilation-environments): - -```console -$ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages -``` - -> **Note** -> -> You can use `native-ccacheStdenvPackages` to drastically improve rebuild time. -> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`. - -To build Nix itself in this shell: - -```console -[nix-shell]$ autoreconfPhase -[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out -[nix-shell]$ make -j $NIX_BUILD_CORES -``` - -To install it in `$(pwd)/outputs` and test it: - -```console -[nix-shell]$ make install -[nix-shell]$ make installcheck -j $NIX_BUILD_CORES -[nix-shell]$ ./outputs/out/bin/nix --version -nix (Nix) 2.12 -``` - -To build a release version of Nix for the current operating system and CPU architecture: - -```console -$ nix-build -``` - -You can also build Nix for one of the [supported platforms](#platforms). - ## Makefile variables You may need `profiledir=$out/etc/profile.d` and `sysconfdir=$out/etc` to run `make install`. @@ -294,81 +293,3 @@ If it fails, run `git add --patch` to approve the suggestions _and commit again_ To refresh pre-commit hook's config file, do the following: 1. Exit the development shell and start it again by running `nix develop`. 2. If you also use the pre-commit hook, also run `pre-commit-hooks-install` again. - -## Add a release note - -`doc/manual/rl-next` contains release notes entries for all unreleased changes. - -User-visible changes should come with a release note. - -### Add an entry - -Here's what a complete entry looks like. The file name is not incorporated in the document. - -``` ---- -synopsis: Basically a title -issues: 1234 -prs: 1238 ---- - -Here's one or more paragraphs that describe the change. - -- It's markdown -- Add references to the manual using @docroot@ -``` - -Significant changes should add the following header, which moves them to the top. - -``` -significance: significant -``` - - -See also the [format documentation](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog). - -### Build process - -Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`. - -## Branches - -- [`master`](https://github.com/NixOS/nix/commits/master) - - The main development branch. All changes are approved and merged here. - When developing a change, create a branch based on the latest `master`. - - Maintainers try to [keep it in a release-worthy state](#reverting). - -- [`maintenance-*.*`](https://github.com/NixOS/nix/branches/all?query=maintenance) - - These branches are the subject of backports only, and are - also [kept](#reverting) in a release-worthy state. - - See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) - -- [`latest-release`](https://github.com/NixOS/nix/tree/latest-release) - - The latest patch release of the latest minor version. - - See [`maintainers/release-process.md`](https://github.com/NixOS/nix/blob/master/maintainers/release-process.md) - -- [`backport-*-to-*`](https://github.com/NixOS/nix/branches/all?query=backport) - - Generally branches created by the backport action. - - See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) - -- [_other_](https://github.com/NixOS/nix/branches/all) - - Branches that do not conform to the above patterns should be feature branches. - -## Reverting - -If a change turns out to be merged by mistake, or contain a regression, it may be reverted. -A revert is not a rejection of the contribution, but merely part of an effective development process. -It makes sure that development keeps running smoothly, with minimal uncertainty, and less overhead. -If maintainers have to worry too much about avoiding reverts, they would not be able to merge as much. -By embracing reverts as a good part of the development process, everyone wins. - -However, taking a step back may be frustrating, so maintainers will be extra supportive on the next try. diff --git a/doc/manual/src/contributing/cli-guideline.md b/doc/manual/src/development/cli-guideline.md similarity index 100% rename from doc/manual/src/contributing/cli-guideline.md rename to doc/manual/src/development/cli-guideline.md diff --git a/doc/manual/src/development/contributing.md b/doc/manual/src/development/contributing.md new file mode 100644 index 000000000..7de7489dc --- /dev/null +++ b/doc/manual/src/development/contributing.md @@ -0,0 +1,79 @@ +# Contributing + +## Add a release note + +`doc/manual/rl-next` contains release notes entries for all unreleased changes. + +User-visible changes should come with a release note. + +### Add an entry + +Here's what a complete entry looks like. The file name is not incorporated in the document. + +``` +--- +synopsis: Basically a title +issues: 1234 +prs: 1238 +--- + +Here's one or more paragraphs that describe the change. + +- It's markdown +- Add references to the manual using @docroot@ +``` + +Significant changes should add the following header, which moves them to the top. + +``` +significance: significant +``` + + +See also the [format documentation](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog). + +### Build process + +Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`. + +## Branches + +- [`master`](https://github.com/NixOS/nix/commits/master) + + The main development branch. All changes are approved and merged here. + When developing a change, create a branch based on the latest `master`. + + Maintainers try to [keep it in a release-worthy state](#reverting). + +- [`maintenance-*.*`](https://github.com/NixOS/nix/branches/all?query=maintenance) + + These branches are the subject of backports only, and are + also [kept](#reverting) in a release-worthy state. + + See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) + +- [`latest-release`](https://github.com/NixOS/nix/tree/latest-release) + + The latest patch release of the latest minor version. + + See [`maintainers/release-process.md`](https://github.com/NixOS/nix/blob/master/maintainers/release-process.md) + +- [`backport-*-to-*`](https://github.com/NixOS/nix/branches/all?query=backport) + + Generally branches created by the backport action. + + See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) + +- [_other_](https://github.com/NixOS/nix/branches/all) + + Branches that do not conform to the above patterns should be feature branches. + +## Reverting + +If a change turns out to be merged by mistake, or contain a regression, it may be reverted. +A revert is not a rejection of the contribution, but merely part of an effective development process. +It makes sure that development keeps running smoothly, with minimal uncertainty, and less overhead. +If maintainers have to worry too much about avoiding reverts, they would not be able to merge as much. +By embracing reverts as a good part of the development process, everyone wins. + +However, taking a step back may be frustrating, so maintainers will be extra supportive on the next try. diff --git a/doc/manual/src/contributing/cxx.md b/doc/manual/src/development/cxx.md similarity index 100% rename from doc/manual/src/contributing/cxx.md rename to doc/manual/src/development/cxx.md diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/development/documentation.md similarity index 99% rename from doc/manual/src/contributing/documentation.md rename to doc/manual/src/development/documentation.md index a14ecedd6..63f574ab7 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/development/documentation.md @@ -24,7 +24,7 @@ nix build .#^doc and open `./result-doc/share/doc/nix/manual/index.html`. -To build the manual incrementally, [enter the development shell](./hacking.md) and run: +To build the manual incrementally, [enter the development shell](./building.md) and run: ```console make manual-html-open -j $NIX_BUILD_CORES diff --git a/doc/manual/src/contributing/experimental-features.md b/doc/manual/src/development/experimental-features.md similarity index 100% rename from doc/manual/src/contributing/experimental-features.md rename to doc/manual/src/development/experimental-features.md diff --git a/doc/manual/src/contributing/index.md b/doc/manual/src/development/index.md similarity index 77% rename from doc/manual/src/contributing/index.md rename to doc/manual/src/development/index.md index 4d55c17a4..6403c3e66 100644 --- a/doc/manual/src/contributing/index.md +++ b/doc/manual/src/development/index.md @@ -5,4 +5,4 @@ Check the [contributing guide](https://github.com/NixOS/nix/blob/master/CONTRIBU This chapter is a collection of guides for making changes to the code and documentation. -If you're not sure where to start, try to [compile Nix from source](./hacking.md) and consider [making improvements to documentation](./documentation.md). +If you're not sure where to start, try to [compile Nix from source](./building.md) and consider [making improvements to documentation](./documentation.md). diff --git a/doc/manual/src/contributing/json-guideline.md b/doc/manual/src/development/json-guideline.md similarity index 100% rename from doc/manual/src/contributing/json-guideline.md rename to doc/manual/src/development/json-guideline.md diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/development/testing.md similarity index 100% rename from doc/manual/src/contributing/testing.md rename to doc/manual/src/development/testing.md diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index f65ada63a..877c4668b 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -168,7 +168,7 @@ - [impure derivation]{#gloss-impure-derivation} - [An experimental feature](#@docroot@/contributing/experimental-features.md#xp-feature-impure-derivations) that allows derivations to be explicitly marked as impure, + [An experimental feature](#@docroot@/development/experimental-features.md#xp-feature-impure-derivations) that allows derivations to be explicitly marked as impure, so that they are always rebuilt, and their outputs not reused by subsequent calls to realise them. - [Nix database]{#gloss-nix-database} @@ -353,7 +353,7 @@ Not yet stabilized functionality guarded by named experimental feature flags. These flags are enabled or disabled with the [`experimental-features`](./command-ref/conf-file.html#conf-experimental-features) setting. - See the contribution guide on the [purpose and lifecycle of experimental feaures](@docroot@/contributing/experimental-features.md). + See the contribution guide on the [purpose and lifecycle of experimental feaures](@docroot@/development/experimental-features.md). [Nix language]: ./language/index.md diff --git a/doc/manual/src/installation/installing-binary.md b/doc/manual/src/installation/installing-binary.md index 385008d8c..6a168ff3d 100644 --- a/doc/manual/src/installation/installing-binary.md +++ b/doc/manual/src/installation/installing-binary.md @@ -77,7 +77,7 @@ $ su root # Installing from a binary tarball You can also download a binary tarball that contains Nix and all its dependencies: -- Choose a [version](https://releases.nixos.org/?prefix=nix/) and [system type](../contributing/hacking.md#platforms) +- Choose a [version](https://releases.nixos.org/?prefix=nix/) and [system type](../development/building.md#platforms) - Download and unpack the tarball - Run the installer diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index e916c7360..51b83fc8a 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -113,7 +113,7 @@ Derivations can declare some infrequently used optional attributes. > `nix-build`. If the [`configurable-impure-env` experimental - feature](@docroot@/contributing/experimental-features.md#xp-feature-configurable-impure-env) + feature](@docroot@/development/experimental-features.md#xp-feature-configurable-impure-env) is enabled, these environment variables can also be controlled through the [`impure-env`](@docroot@/command-ref/conf-file.md#conf-impure-env) @@ -226,7 +226,7 @@ Derivations can declare some infrequently used optional attributes. - [`__contentAddressed`]{#adv-attr-__contentAddressed} > **Warning** - > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md). + > This attribute is part of an [experimental feature](@docroot@/development/experimental-features.md). > > To use this attribute, you must enable the > [`ca-derivations`][xp-feature-ca-derivations] experimental feature. @@ -370,6 +370,6 @@ Derivations can declare some infrequently used optional attributes. ensures that the derivation can only be built on a machine with the `kvm` feature. -[xp-feature-ca-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations -[xp-feature-dynamic-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations -[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing +[xp-feature-ca-derivations]: @docroot@/development/experimental-features.md#xp-feature-ca-derivations +[xp-feature-dynamic-derivations]: @docroot@/development/experimental-features.md#xp-feature-dynamic-derivations +[xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/protocols/derivation-aterm.md b/doc/manual/src/protocols/derivation-aterm.md index e58b602a3..1ba757ae0 100644 --- a/doc/manual/src/protocols/derivation-aterm.md +++ b/doc/manual/src/protocols/derivation-aterm.md @@ -14,6 +14,6 @@ Derivations are serialised in one of the following formats: DrvWithVersion(, ...) ``` - The only `version-string`s that are in use today are for [experimental features](@docroot@/contributing/experimental-features.md): + The only `version-string`s that are in use today are for [experimental features](@docroot@/development/experimental-features.md): - - `"xp-dyn-drv"` for the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. + - `"xp-dyn-drv"` for the [`dynamic-derivations`](@docroot@/development/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. diff --git a/doc/manual/src/protocols/json/derivation.md b/doc/manual/src/protocols/json/derivation.md index f881dd703..2f85340d6 100644 --- a/doc/manual/src/protocols/json/derivation.md +++ b/doc/manual/src/protocols/json/derivation.md @@ -3,7 +3,7 @@ > **Warning** > > This JSON format is currently -> [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) +> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command) > and subject to change. The JSON serialization of a diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index 9f647a96c..6b4f48437 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -3,7 +3,7 @@ > **Warning** > > This JSON format is currently -> [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) +> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command) > and subject to change. Info about a [store object]. diff --git a/doc/manual/src/release-notes/rl-2.18.md b/doc/manual/src/release-notes/rl-2.18.md index 4bbc52b50..eb26fc9e7 100644 --- a/doc/manual/src/release-notes/rl-2.18.md +++ b/doc/manual/src/release-notes/rl-2.18.md @@ -13,7 +13,7 @@ - The `discard-references` feature has been stabilized. This means that the - [unsafeDiscardReferences](@docroot@/contributing/experimental-features.md#xp-feature-discard-references) + [unsafeDiscardReferences](@docroot@/development/experimental-features.md#xp-feature-discard-references) attribute is no longer guarded by an experimental flag and can be used freely. @@ -21,7 +21,7 @@ This only affects `nix-build --json` when "building" non-derivation things like fetched sources, which is a no-op. - A new builtin [`outputOf`](@docroot@/language/builtins.md#builtins-outputOf) has been added. - It is part of the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. + It is part of the [`dynamic-derivations`](@docroot@/development/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. - Flake follow paths at depths greater than 2 are now handled correctly, preventing "follows a non-existent input" errors. diff --git a/doc/manual/src/release-notes/rl-2.19.md b/doc/manual/src/release-notes/rl-2.19.md index ba6eb9c64..e2e2f85cc 100644 --- a/doc/manual/src/release-notes/rl-2.19.md +++ b/doc/manual/src/release-notes/rl-2.19.md @@ -17,8 +17,8 @@ - `nix-shell` shebang lines now support single-quoted arguments. -- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/contributing/experimental-features.md#xp-fetch-tree). - This allows stabilising it independently of the rest of what is encompassed by [`flakes`](@docroot@/contributing/experimental-features.md#xp-fetch-tree). +- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/development/experimental-features.md#xp-fetch-tree). + This allows stabilising it independently of the rest of what is encompassed by [`flakes`](@docroot@/development/experimental-features.md#xp-fetch-tree). - The interface for creating and updating lock files has been overhauled: @@ -33,7 +33,7 @@ - The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables. They are superceded by `nix flake update`. -- Commit signature verification for the [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit) is added as the new [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). +- Commit signature verification for the [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit) is added as the new [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - [`nix path-info --json`](@docroot@/command-ref/new-cli/nix3-path-info.md) (experimental) now returns a JSON map rather than JSON list. diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index 3c59b8583..ac842fdc0 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -14,7 +14,7 @@ - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) - The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline.md#returning-future-proof-json). + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/development/cli-guideline.md#returning-future-proof-json). In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. @@ -89,7 +89,7 @@ This makes records of this sort more self-describing, and easier to consume programmatically. We will follow this design principle going forward; - the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. + the [JSON guidelines](@docroot@/development/json-guideline.md) in the contributing section have been updated accordingly. - Large path warnings [#10661](https://github.com/NixOS/nix/pull/10661) diff --git a/doc/manual/src/release-notes/rl-2.4.md b/doc/manual/src/release-notes/rl-2.4.md index 8b566fc7b..1201e53b6 100644 --- a/doc/manual/src/release-notes/rl-2.4.md +++ b/doc/manual/src/release-notes/rl-2.4.md @@ -23,7 +23,7 @@ more than 2800 commits from 195 contributors since release 2.3. * The **`nix` command** has seen a lot of work and is now almost at feature parity with the old command-line interface (the `nix-*` commands). It aims to be [more modern, consistent and pleasant to - use](../contributing/cli-guideline.md) than the old CLI. It is still + use](../development/cli-guideline.md) than the old CLI. It is still marked as experimental but its interface should not change much anymore in future releases. diff --git a/doc/manual/src/store/file-system-object/content-address.md b/doc/manual/src/store/file-system-object/content-address.md index 1c63c52eb..410d7fb7c 100644 --- a/doc/manual/src/store/file-system-object/content-address.md +++ b/doc/manual/src/store/file-system-object/content-address.md @@ -82,4 +82,4 @@ In the future, we may support a Git-like hash for such file system objects, or w [file system object]: ../file-system-object.md [store object]: ../store-object.md -[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing +[xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/store/store-object/content-address.md b/doc/manual/src/store/store-object/content-address.md index f6f982035..02dce2836 100644 --- a/doc/manual/src/store/store-object/content-address.md +++ b/doc/manual/src/store/store-object/content-address.md @@ -92,4 +92,4 @@ becomes more widespread, this restriction will be revisited. [fso-ca]: ../file-system-object/content-address.md [sp-spec]: @docroot@/protocols/store-path.md -[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing +[xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 8f48b53a5..30a8c5c58 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -65,7 +65,7 @@ struct EvalSettings : Config extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); ``` - The [Nix C++ API documentation](@docroot@/contributing/documentation.md#api-documentation) has more details on evaluator internals. + The [Nix C++ API documentation](@docroot@/development/documentation.md#api-documentation) has more details on evaluator internals. - `builtins.exec` *arguments* diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 333e486fd..e59d7fe67 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -383,7 +383,7 @@ static RegisterPrimOp primop_fetchTree({ - `"mercurial"` *input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references). - The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-flakes) to be enabled. + The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled. > **Example** > @@ -670,12 +670,12 @@ static RegisterPrimOp primop_fetchGit({ Whether to check `rev` for a signature matching `publicKey` or `publicKeys`. If `verifyCommit` is enabled, then `fetchGit` cannot use a local repository with uncommitted changes. - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - `publicKey` The public key against which `rev` is verified if `verifyCommit` is enabled. - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - `keytype` (default: `"ssh-ed25519"`) @@ -687,7 +687,7 @@ static RegisterPrimOp primop_fetchGit({ - `"ssh-ed25519"` - `"ssh-ed25519-sk"` - `"ssh-rsa"` - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - `publicKeys` @@ -701,7 +701,7 @@ static RegisterPrimOp primop_fetchGit({ } ``` - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). Here are some examples of how to use `fetchGit`. diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 30d7537bd..8760c9d14 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -286,7 +286,7 @@ public: For backward compatibility, `ssh://` may be omitted. The hostname may be an alias defined in `~/.ssh/config`. - 2. A comma-separated list of [Nix system types](@docroot@/contributing/hacking.md#system-type). + 2. A comma-separated list of [Nix system types](@docroot@/development/building.md#system-type). If omitted, this defaults to the local platform type. > **Example** @@ -866,13 +866,13 @@ public: - `ca-derivations` - Included by default if the [`ca-derivations` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) is enabled. + Included by default if the [`ca-derivations` experimental feature](@docroot@/development/experimental-features.md#xp-feature-ca-derivations) is enabled. This system feature is implicitly required by derivations with the [`__contentAddressed` attribute](@docroot@/language/advanced-attributes.md#adv-attr-__contentAddressed). - `recursive-nix` - Included by default if the [`recursive-nix` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-recursive-nix) is enabled. + Included by default if the [`recursive-nix` experimental feature](@docroot@/development/experimental-features.md#xp-feature-recursive-nix) is enabled. - `uid-range` diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 1952ba1b8..c0c59ac68 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -393,7 +393,7 @@ struct ExperimentalFeatureSettings : Config { {{#include experimental-features-shortlist.md}} - Experimental features are [further documented in the manual](@docroot@/contributing/experimental-features.md). + Experimental features are [further documented in the manual](@docroot@/development/experimental-features.md). )"}; /** diff --git a/src/nix/nix.md b/src/nix/nix.md index 4464bef37..f958ce09a 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -50,7 +50,7 @@ manual](https://nixos.org/manual/nix/stable/). > **Warning** \ > Installables are part of the unstable -> [`nix-command` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-nix-command), +> [`nix-command` experimental feature](@docroot@/development/experimental-features.md#xp-feature-nix-command), > and subject to change without notice. Many `nix` subcommands operate on one or more *installables*. @@ -70,9 +70,9 @@ That is, Nix will operate on the default flake output attribute of the flake in > **Warning** \ > Flake output attribute installables depend on both the -> [`flakes`](@docroot@/contributing/experimental-features.md#xp-feature-flakes) +> [`flakes`](@docroot@/development/experimental-features.md#xp-feature-flakes) > and -> [`nix-command`](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) +> [`nix-command`](@docroot@/development/experimental-features.md#xp-feature-nix-command) > experimental features, and subject to change without notice. Example: `nixpkgs#hello` From 3b49f7a1439bc75cefddb0fe0031824f3708da83 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 24 Jul 2024 23:12:39 -0400 Subject: [PATCH 688/910] Deduplicate our many `package.nix` a bit (#11175) - They should all be built in parallel - They should all use strict deps by default --- packaging/dependencies.nix | 27 ++++++++++++++++++------- src/external-api-docs/package.nix | 4 ---- src/internal-api-docs/package.nix | 4 ---- src/libcmd/package.nix | 6 ------ src/libexpr-c/package.nix | 4 ---- src/libexpr/package.nix | 4 ---- src/libfetchers/package.nix | 6 ------ src/libflake/package.nix | 6 ------ src/libmain-c/package.nix | 4 ---- src/libmain/package.nix | 6 ------ src/libstore-c/package.nix | 4 ---- src/libstore/package.nix | 4 ---- src/libutil-c/package.nix | 4 ---- src/libutil/package.nix | 4 ---- src/nix/package.nix | 6 ------ src/perl/package.nix | 2 +- tests/unit/libexpr-support/package.nix | 4 ---- tests/unit/libexpr/package.nix | 4 ---- tests/unit/libfetchers/package.nix | 4 ---- tests/unit/libflake/package.nix | 4 ---- tests/unit/libstore-support/package.nix | 4 ---- tests/unit/libstore/package.nix | 4 ---- tests/unit/libutil-support/package.nix | 4 ---- tests/unit/libutil/package.nix | 4 ---- 24 files changed, 21 insertions(+), 106 deletions(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index f09ca5d18..b77e1d14f 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -63,9 +63,16 @@ let # Work around weird `--as-needed` linker behavior with BSD, see # https://github.com/mesonbuild/meson/issues/3593 - bsdNoLinkAsNeeded = finalAttrs: prevAttrs: lib.optionalAttrs stdenv.hostPlatform.isBSD { - mesonFlags = [ (lib.mesonBool "b_asneeded" false) ] ++ prevAttrs.mesonFlags or []; - }; + bsdNoLinkAsNeeded = finalAttrs: prevAttrs: + lib.optionalAttrs stdenv.hostPlatform.isBSD { + mesonFlags = [ (lib.mesonBool "b_asneeded" false) ] ++ prevAttrs.mesonFlags or []; + }; + + miscGoodPractice = finalAttrs: prevAttrs: + { + strictDeps = prevAttrs.strictDeps or true; + enableParallelBuilding = true; + }; in scope: { @@ -136,8 +143,14 @@ scope: { inherit resolvePath filesetToSource; - mkMesonDerivation = f: stdenv.mkDerivation - (lib.extends - (lib.composeExtensions bsdNoLinkAsNeeded localSourceLayer) - f); + mkMesonDerivation = f: let + exts = [ + miscGoodPractice + bsdNoLinkAsNeeded + localSourceLayer + ]; + in stdenv.mkDerivation + (lib.extends + (lib.foldr lib.composeExtensions (_: _: {}) exts) + f); } diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index da136bbe1..743b3e9b7 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -53,10 +53,6 @@ mkMesonDerivation (finalAttrs: { echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; - enableParallelBuilding = true; - - strictDeps = true; - meta = { platforms = lib.platforms.all; }; diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index f2077dcaf..07ca6d4d9 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -48,10 +48,6 @@ mkMesonDerivation (finalAttrs: { echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; - enableParallelBuilding = true; - - strictDeps = true; - meta = { platforms = lib.platforms.all; }; diff --git a/src/libcmd/package.nix b/src/libcmd/package.nix index ec3aa4660..cde494901 100644 --- a/src/libcmd/package.nix +++ b/src/libcmd/package.nix @@ -93,14 +93,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 0b895437b..eb42195a4 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -63,12 +63,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 704456c96..4d10079ff 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -102,12 +102,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index b4abb144b..9b5d8bff7 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -67,14 +67,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libflake/package.nix b/src/libflake/package.nix index af6f5da94..851adf07e 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -67,14 +67,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libmain-c/package.nix b/src/libmain-c/package.nix index 478e34a85..ce6f67300 100644 --- a/src/libmain-c/package.nix +++ b/src/libmain-c/package.nix @@ -68,12 +68,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libmain/package.nix b/src/libmain/package.nix index bbd97ec3e..47513dbdc 100644 --- a/src/libmain/package.nix +++ b/src/libmain/package.nix @@ -62,14 +62,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index fc34c1bda..e4f372236 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -64,12 +64,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 0a2ace91e..02ff4194a 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -101,12 +101,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index 53451998d..ccfafd4d3 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -62,12 +62,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 28d7d8f0e..4ce1a75b0 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -88,12 +88,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/nix/package.nix b/src/nix/package.nix index fe83c6969..ef7265458 100644 --- a/src/nix/package.nix +++ b/src/nix/package.nix @@ -84,8 +84,6 @@ mkMesonDerivation (finalAttrs: { ] ); - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja @@ -114,12 +112,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/perl/package.nix b/src/perl/package.nix index 26856e631..0b9343fba 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -73,5 +73,5 @@ perl.pkgs.toPerlModule (mkMesonDerivation (finalAttrs: { "--print-errorlogs" ]; - enableParallelBuilding = true; + strictDeps = false; })) diff --git a/tests/unit/libexpr-support/package.nix b/tests/unit/libexpr-support/package.nix index 0c966c55a..f53aa842f 100644 --- a/tests/unit/libexpr-support/package.nix +++ b/tests/unit/libexpr-support/package.nix @@ -66,12 +66,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 015e3fbc6..e70ed7836 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -71,12 +71,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index cf75f68e5..ad512f562 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -69,12 +69,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index d2c9fdb89..0d63d2ff7 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -69,12 +69,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libstore-support/package.nix b/tests/unit/libstore-support/package.nix index cb15cdd5f..f512db3ee 100644 --- a/tests/unit/libstore-support/package.nix +++ b/tests/unit/libstore-support/package.nix @@ -66,12 +66,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index 39bf77585..7560a5b79 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -73,12 +73,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix index fdecdec72..1665804cb 100644 --- a/tests/unit/libutil-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -64,12 +64,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index c7827e74f..2fce5bfa8 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -70,12 +70,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { From 8a7e31362ad0b232f4de098573370f6994834397 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 05:57:06 +0200 Subject: [PATCH 689/910] rl-next: Add credit --- doc/manual/rl-next/drop-vendored-toml11.md | 2 ++ doc/manual/rl-next/harden-user-sandboxing.md | 5 +++++ doc/manual/rl-next/nix-shell-looks-for-shell-nix.md | 2 ++ doc/manual/rl-next/repl-doc-renders-doc-comments.md | 4 +++- doc/manual/rl-next/shebang-relative.md | 2 ++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/manual/rl-next/drop-vendored-toml11.md b/doc/manual/rl-next/drop-vendored-toml11.md index d1feeb703..8dd786c44 100644 --- a/doc/manual/rl-next/drop-vendored-toml11.md +++ b/doc/manual/rl-next/drop-vendored-toml11.md @@ -4,3 +4,5 @@ synopsis: Stop vendoring toml11 We don't apply any patches to it, and vendoring it locks users into bugs (it hasn't been updated since its introduction in late 2021). + +Author: [**Winter (@winterqt)**](https://github.com/winterqt) diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md index a647acf25..ff81c9cb1 100644 --- a/doc/manual/rl-next/harden-user-sandboxing.md +++ b/doc/manual/rl-next/harden-user-sandboxing.md @@ -5,3 +5,8 @@ issues: --- The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. + +This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. + +Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) +Co-authors: [**@edolstra**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md index 99be4148b..b9e4b3fb3 100644 --- a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -26,3 +26,5 @@ This also applies to `nix-shell` shebang scripts. Consider the following example This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. + +Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md index 05023697c..fa241ebc1 100644 --- a/doc/manual/rl-next/repl-doc-renders-doc-comments.md +++ b/doc/manual/rl-next/repl-doc-renders-doc-comments.md @@ -48,6 +48,8 @@ Known limitations: - It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. - Some extensions to markdown are not yet supported, as you can see in the example above. -We'd like to acknowledge Yingchi Long for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as @sternenseemann and Johannes Kirschbauer for their contributions, proposals, and their work on [RFC 145]. +We'd like to acknowledge [Yingchi Long (@inclyc)](https://github.com/inclyc) for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as [@sternenseemann](https://github.com/sternenseemann) and [Johannes Kirschbauer (@hsjobeki)](https://github.com/hsjobeki) for their contributions, proposals, and their work on [RFC 145]. + +Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) [RFC 145]: https://github.com/NixOS/rfcs/pull/145 diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index d12c0f8dc..dd96bf203 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -60,3 +60,5 @@ Example: #!nix -c bash hello ``` + +Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) From 7275d68d3b28115e2e2096aef7e6025292d4d58e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 05:57:53 +0200 Subject: [PATCH 690/910] rl-next: Add top 10 by +1 reactions on PRs We should use a metric that weighs the related issues. Counterbalancing time doesn't make much sense to me. If it's around for longer, the fix will be relevant to more people. --- .../10564-attrcursor-remove-forceerrors.md | 9 ++++++ ...03-run-the-flake-regressions-test-suite.md | 8 +++++ ...unit-prefixes-in-configuration-settings.md | 10 ++++++ ...ild-show-all-fod-errors-with-keep-going.md | 10 ++++++ doc/manual/rl-next/10855-meson.md | 31 +++++++++++++++++++ .../11086-eval-cache-fix-cache-regressions.md | 14 +++++++++ .../rl-next/9063-introduce-libnixflake.md | 12 +++++++ 7 files changed, 94 insertions(+) create mode 100644 doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md create mode 100644 doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md create mode 100644 doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md create mode 100644 doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md create mode 100644 doc/manual/rl-next/10855-meson.md create mode 100644 doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md create mode 100644 doc/manual/rl-next/9063-introduce-libnixflake.md diff --git a/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md b/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md new file mode 100644 index 000000000..864a55b51 --- /dev/null +++ b/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md @@ -0,0 +1,9 @@ +--- +synopsis: "Solve `cached failure of attribute X`" +prs: 10564 +issues: 10513 9165 +--- + +This eliminates all "cached failure of attribute X" messages by forcing evaluation of the original value when needed to show the exception to the user. This enhancement improves error reporting by providing the underlying message and stack trace. + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md b/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md new file mode 100644 index 000000000..42864323c --- /dev/null +++ b/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md @@ -0,0 +1,8 @@ +--- +synopsis: "Run the flake regressions test suite" +prs: 10603 +--- + +This update introduces a GitHub action to run a subset of the [flake regressions test suite](https://github.com/NixOS/flake-regressions), which includes 259 flakes with their expected evaluation results. Currently, the action runs the first 25 flakes due to the full test suite's extensive runtime. A manually triggered action may be implemented later to run the entire test suite. + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md b/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md new file mode 100644 index 000000000..2caca9a81 --- /dev/null +++ b/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md @@ -0,0 +1,10 @@ +--- +synopsis: "Support unit prefixes in configuration settings" +prs: 10668 +--- + +Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/opt-common.md#opt-min-free) to set the minimum free space to 1 gigabyte. + +This enhancement was extracted from [#7851](https://github.com/NixOS/nix/pull/7851) and is also useful for PR [#10661](https://github.com/NixOS/nix/pull/10661). + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md new file mode 100644 index 000000000..5c2797be8 --- /dev/null +++ b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md @@ -0,0 +1,10 @@ +--- +synopsis: "nix3-build: show all FOD errors with `--keep-going`" +prs: 10734 +--- + +The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior of `nix build`. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. + +Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92) + +[`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) diff --git a/doc/manual/rl-next/10855-meson.md b/doc/manual/rl-next/10855-meson.md new file mode 100644 index 000000000..0ab71390f --- /dev/null +++ b/doc/manual/rl-next/10855-meson.md @@ -0,0 +1,31 @@ +--- +synopsis: "Build with Meson" +prs: +- 10378 +- 10855 +- 10904 +- 10908 +- 10914 +- 10933 +- 10936 +- 10954 +- 10955 +- 10967 +- 10963 +- 10973 +- 11034 +- 11054 +- 11055 +- 11064 +- 11060 +- 11155 +issues: +- 2503 +--- + +These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. + +Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). + +Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) +Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) diff --git a/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md b/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md new file mode 100644 index 000000000..8a348a9ad --- /dev/null +++ b/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md @@ -0,0 +1,14 @@ +--- +synopsis: "Eval cache: fix cache regressions" +prs: 11086 +issues: 10570 +--- + +This update addresses two bugs in the evaluation cache system: + +1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. +2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. + +These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. + +Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) diff --git a/doc/manual/rl-next/9063-introduce-libnixflake.md b/doc/manual/rl-next/9063-introduce-libnixflake.md new file mode 100644 index 000000000..fd3645446 --- /dev/null +++ b/doc/manual/rl-next/9063-introduce-libnixflake.md @@ -0,0 +1,12 @@ +--- +synopsis: "Introduce `libnixflake`" +prs: 9063 +--- + +A new library, `libnixflake`, has been introduced to better separate the Flakes layer within Nix. This change refactors the codebase to encapsulate Flakes-specific functionality within its own library. + +See the commits in the pull request for detailed changes, with the only significant code modifications happening in the initial commit. + +This change was alluded to in [RFC 134](https://github.com/nixos/rfcs/blob/master/rfcs/0134-nix-store-layer.md) and is a step towards a more modular and maintainable codebase. + +Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) From e92dd06a7b5ff00e8908e9a7b5699de56d55e8d6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Jul 2024 00:00:52 -0400 Subject: [PATCH 691/910] build-remote: Cope with long store URLs by falling back on hashing I hit this in the Meson port of the functional tests, because the use of standalone build directories. --- src/build-remote/build-remote.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index a0a404e57..8482b742d 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -264,7 +264,20 @@ connected: auto inputs = readStrings(source); auto wantedOutputs = readStrings(source); - AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri.render()) + ".upload-lock", true); + AutoCloseFD uploadLock; + { + auto setUpdateLock = [&](auto && fileName){ + uploadLock = openLockFile(currentLoad + "/" + escapeUri(fileName) + ".upload-lock", true); + }; + try { + setUpdateLock(storeUri.render()); + } catch (SysError & e) { + if (e.errNo != ENAMETOOLONG) throw; + // Try again hashing the store URL so we have a shorter path + auto h = hashString(HashAlgorithm::MD5, storeUri.render()); + setUpdateLock(h.to_string(HashFormat::Base64, false)); + } + } { Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri.render())); From b711fcbef9701ae1fd11839aa911e7737f8a23cd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 06:00:59 +0200 Subject: [PATCH 692/910] rl-next: Drop zzz-other. Number soup. --- doc/manual/rl-next/zzz-other.md | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 doc/manual/rl-next/zzz-other.md diff --git a/doc/manual/rl-next/zzz-other.md b/doc/manual/rl-next/zzz-other.md deleted file mode 100644 index f3721bd38..000000000 --- a/doc/manual/rl-next/zzz-other.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -synopsis: Other changes ---- - -- [#9063](https://github.com/NixOS/nix/pull/9063): introduce `libnixflake` and move flakes-specific code there (Nix is internally composed of a set of libraries, and this better reflects the architecture wrt flakes) -- [#10852](https://github.com/NixOS/nix/pull/10852): make Nix commands respond better to interruption - Ctrl+C in the terminal -- [#10853](https://github.com/NixOS/nix/pull/10853): fix docs of `builtins.importNative` -- [#10858](https://github.com/NixOS/nix/pull/10858): `flake check`: Recognize well known `homeModule`/`homeModules` attribute -- [#10865](https://github.com/NixOS/nix/pull/10865): Update dependencies to Nixpkgs 24.05 (when using the `nix` flake), and support bdwgc 8.2.6 ([#11141](https://github.com/NixOS/nix/issues/11141), [#10880](https://github.com/NixOS/nix/pull/10880)) -- [#10883](https://github.com/NixOS/nix/pull/10883): Use `TMP` instead of `XDG_RUNTIME_DIR` -- [#10994](https://github.com/NixOS/nix/pull/10994): fix minor bug in elided item counts when printing values lazily -- [#10988](https://github.com/NixOS/nix/pull/10988): restore `commit-lockfile-summary` alias -- [#10941](https://github.com/NixOS/nix/pull/10941): invalid derivation name now causes an actionable error message -- Changes to the interaction between Nix's I/O coroutines and the garbage collector: -- [#10878](https://github.com/NixOS/nix/pull/10878): allow `ipc-sysv*` in the Darwin build sandbox -- [#11031](https://github.com/NixOS/nix/pull/11031): fix Darwin build sandbox -- [#10907](https://github.com/NixOS/nix/pull/10907): use opaque struct instead of `void *` in the C API -- [#10947](https://github.com/NixOS/nix/issues/10947): fix evaluation cache accidentally persisting disallowed IFD errors -- [#11020](https://github.com/NixOS/nix/pull/11020): enable fetch and eval caching for tarballs -- [#11041](https://github.com/NixOS/nix/pull/11041): add discovered attribute paths to the `--show-trace` error trace in `nix-build`, `nix-env`, OfBorg, and other callers of `getDerivations` -- [#11056](https://github.com/NixOS/nix/pull/11056): `s3` store now uses system defined proxy settings -- [#11077](https://github.com/NixOS/nix/pull/11077): support hardlinks in tarballs -- [#11100](https://github.com/NixOS/nix/pull/11100): pretty print values consistently regardless of prior thunk state -- [#11086](https://github.com/NixOS/nix/pull/11086): fix loss of evaluation cache additions in `nix env run`, `nix shell`, `nix develop`, and `nix fmt` -- [#11149](https://github.com/NixOS/nix/pull/11149): report GC time and number of GC cycles in `NIX_SHOW_STATS=1` report -- [#11142](https://github.com/NixOS/nix/pull/11142): aliased options can now also be passed as flags, just like their "normal" counterparts, e.g. `--build-max-jobs` now works -- [#11043](https://github.com/NixOS/nix/pull/11043): `assert a == b; e` now reports some detail about why `a` and `b` are different when they are -- [#11159](https://github.com/NixOS/nix/pull/11159): don't crash a nix-daemon worker process when the client disconnects -- Stability improvements and fixes [#10861](https://github.com/NixOS/nix/pull/10861), [#10865](https://github.com/NixOS/nix/pull/10865), [#10918](https://github.com/NixOS/nix/pull/10918), [#10916](https://github.com/NixOS/nix/pull/10916), [#10884](https://github.com/NixOS/nix/pull/10884), [#10943](https://github.com/NixOS/nix/pull/10943), [#11019](https://github.com/NixOS/nix/pull/11019), [#11122](https://github.com/NixOS/nix/pull/11122), [#11117](https://github.com/NixOS/nix/pull/11117) -- User documentation improvements [#10888](https://github.com/NixOS/nix/pull/10888), [#10966](https://github.com/NixOS/nix/pull/10966), [#10974](https://github.com/NixOS/nix/pull/10974), [#10997](https://github.com/NixOS/nix/pull/10997), [#11013](https://github.com/NixOS/nix/pull/11013), [#11059](https://github.com/NixOS/nix/pull/11059), [#11119](https://github.com/NixOS/nix/pull/11119), [#11116](https://github.com/NixOS/nix/pull/11116), [#11061](https://github.com/NixOS/nix/pull/11061), [#11102](https://github.com/NixOS/nix/pull/11102) -- BSD support: [#10896](https://github.com/NixOS/nix/pull/10896) [#11022](https://github.com/NixOS/nix/pull/11022) [#11156](https://github.com/NixOS/nix/pull/11156) -- Windows support: [#10769](https://github.com/NixOS/nix/pull/10769), [#10975](https://github.com/NixOS/nix/pull/10975) [#11153](https://github.com/NixOS/nix/pull/11153) -- Portability: [#7048](https://github.com/NixOS/nix/pull/7048) [#11090](https://github.com/NixOS/nix/pull/11090) -- Installer improvements [#10902](https://github.com/NixOS/nix/pull/10902) -- Performance improvements [#10853](https://github.com/NixOS/nix/pull/10853), [#10854](https://github.com/NixOS/nix/pull/10854), [#11082](https://github.com/NixOS/nix/pull/11082), [#11092](https://github.com/NixOS/nix/pull/11092), [#11113](https://github.com/NixOS/nix/pull/11113) - -Contributor experience improvements: - -Use Meson to build Nix (nearing completion) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10967](https://github.com/NixOS/nix/pull/10967) [#10963](https://github.com/NixOS/nix/pull/10963) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11064](https://github.com/NixOS/nix/pull/11064) [#11060](https://github.com/NixOS/nix/pull/11060) [#11155](https://github.com/NixOS/nix/pull/11155) -- Testing improvements [#10864](https://github.com/NixOS/nix/pull/10864), [#10903](https://github.com/NixOS/nix/pull/10903), [#10874](https://github.com/NixOS/nix/pull/10874), [#10922](https://github.com/NixOS/nix/pull/10922), [#11006](https://github.com/NixOS/nix/pull/11006), [#11110](https://github.com/NixOS/nix/pull/11110), [#10931](https://github.com/NixOS/nix/pull/10931), [#11123](https://github.com/NixOS/nix/pull/11123) - - [#10603](https://github.com/NixOS/nix/pull/10603): We now evaluate a set of flakes in CI - - [#10922](https://github.com/NixOS/nix/pull/10922): The functional test suite is now run in both in the build sandbox and in a NixOS environment -- CI improvements [#10929](https://github.com/NixOS/nix/pull/10929) [#10999](https://github.com/NixOS/nix/pull/10999) [#11009](https://github.com/NixOS/nix/pull/11009) [#11065](https://github.com/NixOS/nix/pull/11065) [#11071](https://github.com/NixOS/nix/pull/11071) -- Contributor documentation improvements [#10869](https://github.com/NixOS/nix/pull/10869), [#9871](https://github.com/NixOS/nix/pull/9871), [#10960](https://github.com/NixOS/nix/pull/10960), [#11147](https://github.com/NixOS/nix/pull/11147) -- Error message improvements: [#11050](https://github.com/NixOS/nix/pull/11050) [#11154](https://github.com/NixOS/nix/pull/11154) -- Cleaning up the Settings system (`nix.conf` and related architectural cleanups): [#10913](https://github.com/NixOS/nix/pull/10913), [#10951](https://github.com/NixOS/nix/pull/10951), [#11007](https://github.com/NixOS/nix/pull/11007), [#11108](https://github.com/NixOS/nix/pull/11108), [#11014](https://github.com/NixOS/nix/pull/11014), [#11109](https://github.com/NixOS/nix/pull/11109), [#11112](https://github.com/NixOS/nix/pull/11112) -- Other cleanups and refactors [#10857](https://github.com/NixOS/nix/pull/10857) [#10935](https://github.com/NixOS/nix/pull/10935) [#10873](https://github.com/NixOS/nix/pull/10873) [#10745](https://github.com/NixOS/nix/pull/10745) [#10961](https://github.com/NixOS/nix/pull/10961) [#10962](https://github.com/NixOS/nix/pull/10962) [#10972](https://github.com/NixOS/nix/pull/10972) [#11018](https://github.com/NixOS/nix/pull/11018) [#11035](https://github.com/NixOS/nix/pull/11035) [#11037](https://github.com/NixOS/nix/pull/11037) [#11081](https://github.com/NixOS/nix/pull/11081) [#11089](https://github.com/NixOS/nix/pull/11089) [#11093](https://github.com/NixOS/nix/pull/11093) [#11114](https://github.com/NixOS/nix/pull/11114) [#11103](https://github.com/NixOS/nix/pull/11103) [#11126](https://github.com/NixOS/nix/pull/11126) [#11125](https://github.com/NixOS/nix/pull/11125) [#11120](https://github.com/NixOS/nix/pull/11120) -- Scheduler/builder refactoring [#11005](https://github.com/NixOS/nix/pull/11005) -- [#11011](https://github.com/NixOS/nix/pull/11011): enable `-Werror=unused-result` - From 1ae573831756ac08fd30eb9cfd90692b1c06ed3b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Jul 2024 00:02:43 -0400 Subject: [PATCH 693/910] Fix some warnings I think they came from the last Nixpkgs bump. --- src/libexpr/primops/fromTOML.cc | 4 ++++ src/libstore/s3-binary-cache-store.cc | 3 +++ src/libstore/s3.hh | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 6c7d303e8..70755f9e0 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -2,7 +2,11 @@ #include "eval-inline.hh" #include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch-enum" #include +#pragma GCC diagnostic pop namespace nix { diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 92ab47cd6..1082657bb 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -10,6 +10,8 @@ #include "compression.hh" #include "filetransfer.hh" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch-enum" #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#pragma GCC diagnostic pop using namespace Aws::Transfer; diff --git a/src/libstore/s3.hh b/src/libstore/s3.hh index f0aeb3bed..18de115ae 100644 --- a/src/libstore/s3.hh +++ b/src/libstore/s3.hh @@ -8,7 +8,7 @@ #include #include -namespace Aws { namespace Client { class ClientConfiguration; } } +namespace Aws { namespace Client { struct ClientConfiguration; } } namespace Aws { namespace S3 { class S3Client; } } namespace nix { From 6c38bc09526eab2ad66083e339f0c10cf7ce813a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 07:34:08 +0200 Subject: [PATCH 694/910] {src/perl,build-utils-meson/diagnostics}: sort cflags This makes them easier to copy between places. --- build-utils-meson/diagnostics/meson.build | 6 +++--- src/perl/meson.build | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index 2b79f6566..4548f93ad 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -1,11 +1,11 @@ add_project_arguments( - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', + '-Wdeprecated-copy', '-Werror=switch', '-Werror=switch-enum', '-Werror=unused-result', - '-Wdeprecated-copy', '-Wignored-qualifiers', + '-Wimplicit-fallthrough', + '-Wno-deprecated-declarations', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked # at ~1% overhead in `nix search`. # diff --git a/src/perl/meson.build b/src/perl/meson.build index 5fe7e1e28..02e0e68e5 100644 --- a/src/perl/meson.build +++ b/src/perl/meson.build @@ -23,21 +23,21 @@ nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) # set error arguments #------------------------------------------------- error_args = [ - '-Werror=unused-result', '-Wdeprecated-copy', '-Wdeprecated-declarations', + '-Werror=unused-result', '-Wignored-qualifiers', - '-Wno-pedantic', - '-Wno-non-virtual-dtor', - '-Wno-unused-parameter', - '-Wno-variadic-macros', - '-Wno-missing-field-initializers', - '-Wno-unknown-warning-option', - '-Wno-unused-variable', - '-Wno-literal-suffix', - '-Wno-reserved-user-defined-literal', '-Wno-duplicate-decl-specifier', + '-Wno-literal-suffix', + '-Wno-missing-field-initializers', + '-Wno-non-virtual-dtor', + '-Wno-pedantic', '-Wno-pointer-bool-conversion', + '-Wno-reserved-user-defined-literal', + '-Wno-unknown-warning-option', + '-Wno-unused-parameter', + '-Wno-unused-variable', + '-Wno-variadic-macros', ] add_project_arguments( From 2c07ea8abbb2804b476b9df21a9c7c3c2e8dbb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 07:40:31 +0200 Subject: [PATCH 695/910] build-utils-meson: remove oudated meson build comment --- build-utils-meson/diagnostics/meson.build | 3 --- 1 file changed, 3 deletions(-) diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index 4548f93ad..e81f19eff 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -6,8 +6,5 @@ add_project_arguments( '-Wignored-qualifiers', '-Wimplicit-fallthrough', '-Wno-deprecated-declarations', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # language : 'cpp', ) From 63e50a4b56a2e21e854636a622941fd36d2e78da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 07:32:03 +0200 Subject: [PATCH 696/910] add werror=suggest-override Improves code readability by making overrides explicit. Inspired by lix code-base --- Makefile | 2 +- build-utils-meson/diagnostics/meson.build | 1 + src/libexpr/json-to-value.cc | 26 +++++++++---------- src/libexpr/nixexpr.hh | 4 +-- src/libstore/daemon.cc | 2 +- .../unix/build/local-derivation-goal.cc | 2 +- src/libutil/serialise.cc | 4 +-- src/libutil/serialise.hh | 6 ++--- src/perl/meson.build | 1 + 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index bb64a104e..ac76b1532 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -Werror=unused-result -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -Werror=unused-result -Werror=suggest-override -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index e81f19eff..30eedfc13 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -1,5 +1,6 @@ add_project_arguments( '-Wdeprecated-copy', + '-Werror=suggest-override', '-Werror=switch', '-Werror=switch-enum', '-Werror=unused-result', diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 20bee193f..33ab55ee9 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -80,42 +80,42 @@ class JSONSax : nlohmann::json_sax { public: JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {}; - bool null() + bool null() override { rs->value(state).mkNull(); rs->add(); return true; } - bool boolean(bool val) + bool boolean(bool val) override { rs->value(state).mkBool(val); rs->add(); return true; } - bool number_integer(number_integer_t val) + bool number_integer(number_integer_t val) override { rs->value(state).mkInt(val); rs->add(); return true; } - bool number_unsigned(number_unsigned_t val) + bool number_unsigned(number_unsigned_t val) override { rs->value(state).mkInt(val); rs->add(); return true; } - bool number_float(number_float_t val, const string_t & s) + bool number_float(number_float_t val, const string_t & s) override { rs->value(state).mkFloat(val); rs->add(); return true; } - bool string(string_t & val) + bool string(string_t & val) override { rs->value(state).mkString(val); rs->add(); @@ -123,7 +123,7 @@ public: } #if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8 - bool binary(binary_t&) + bool binary(binary_t&) override { // This function ought to be unreachable assert(false); @@ -131,35 +131,35 @@ public: } #endif - bool start_object(std::size_t len) + bool start_object(std::size_t len) override { rs = std::make_unique(std::move(rs)); return true; } - bool key(string_t & name) + bool key(string_t & name) override { dynamic_cast(rs.get())->key(name, state); return true; } - bool end_object() { + bool end_object() override { rs = rs->resolve(state); rs->add(); return true; } - bool end_array() { + bool end_array() override { return end_object(); } - bool start_array(size_t len) { + bool start_array(size_t len) override { rs = std::make_unique(std::move(rs), len != std::numeric_limits::max() ? len : 128); return true; } - bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) { + bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) override { throw JSONParseError("%s", ex.what()); } }; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 1bcc962c5..3279e3d48 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -186,7 +186,7 @@ struct ExprInheritFrom : ExprVar this->fromWith = nullptr; } - void bindVars(EvalState & es, const std::shared_ptr & env); + void bindVars(EvalState & es, const std::shared_ptr & env) override; }; struct ExprSelect : Expr @@ -203,7 +203,7 @@ struct ExprSelect : Expr * * @param[out] v The attribute set that should contain the last attribute name (if it exists). * @return The last attribute name in `attrPath` - * + * * @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not exist. */ Symbol evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 6533b2f58..f28f92fce 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -167,7 +167,7 @@ struct TunnelSink : Sink { Sink & to; TunnelSink(Sink & to) : to(to) { } - void operator () (std::string_view data) + void operator () (std::string_view data) override { to << STDERR_WRITE; writeString(data, to); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 0dd102200..d30caaf51 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1258,7 +1258,7 @@ bool LocalDerivationGoal::isAllowed(const DerivedPath & req) struct RestrictedStoreConfig : virtual LocalFSStoreConfig { using LocalFSStoreConfig::LocalFSStoreConfig; - const std::string name() { return "Restricted Store"; } + const std::string name() override { return "Restricted Store"; } }; /* A wrapper around LocalStore that only allows building/querying of diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 36b99905a..1be7fa37a 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -190,11 +190,11 @@ struct VirtualStackAllocator { class DefaultStackAllocator : public StackAllocator { boost::coroutines2::default_stack stack; - boost::context::stack_context allocate() { + boost::context::stack_context allocate() override { return stack.allocate(); } - void deallocate(boost::context::stack_context sctx) { + void deallocate(boost::context::stack_context sctx) override { stack.deallocate(sctx); } }; diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 8137db5f4..c7290dcef 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -204,7 +204,7 @@ struct TeeSink : Sink { Sink & sink1, & sink2; TeeSink(Sink & sink1, Sink & sink2) : sink1(sink1), sink2(sink2) { } - virtual void operator () (std::string_view data) + virtual void operator () (std::string_view data) override { sink1(data); sink2(data); @@ -221,7 +221,7 @@ struct TeeSource : Source Sink & sink; TeeSource(Source & orig, Sink & sink) : orig(orig), sink(sink) { } - size_t read(char * data, size_t len) + size_t read(char * data, size_t len) override { size_t n = orig.read(data, len); sink({data, n}); @@ -238,7 +238,7 @@ struct SizedSource : Source size_t remain; SizedSource(Source & orig, size_t size) : orig(orig), remain(size) { } - size_t read(char * data, size_t len) + size_t read(char * data, size_t len) override { if (this->remain <= 0) { throw EndOfFile("sized: unexpected end-of-file"); diff --git a/src/perl/meson.build b/src/perl/meson.build index 02e0e68e5..dcb6a68a4 100644 --- a/src/perl/meson.build +++ b/src/perl/meson.build @@ -25,6 +25,7 @@ nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) error_args = [ '-Wdeprecated-copy', '-Wdeprecated-declarations', + '-Werror=suggest-override', '-Werror=unused-result', '-Wignored-qualifiers', '-Wno-duplicate-decl-specifier', From 9b5ce9acc29a98bedf0530d5611f25f57eae6197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 09:21:31 +0200 Subject: [PATCH 697/910] build-remote: only allocate storeUri once also it's probably not much overhead compared to the networking stuff it, but it's less code at least. --- src/build-remote/build-remote.cc | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 8482b742d..82ad7d862 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -101,7 +101,7 @@ static int main_build_remote(int argc, char * * argv) } std::optional drvPath; - StoreReference storeUri; + std::string storeUri; while (true) { @@ -234,17 +234,16 @@ static int main_build_remote(int argc, char * * argv) lock = -1; try { + storeUri = bestMachine->storeUri.render(); - Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", storeUri)); sshStore = bestMachine->openStore(); sshStore->connect(); - storeUri = bestMachine->storeUri; - } catch (std::exception & e) { auto msg = chomp(drainFD(5, false)); printError("cannot build on '%s': %s%s", - bestMachine->storeUri.render(), e.what(), + storeUri, e.what(), msg.empty() ? "" : ": " + msg); bestMachine->enabled = false; continue; @@ -259,7 +258,7 @@ connected: assert(sshStore); - std::cerr << "# accept\n" << storeUri.render() << "\n"; + std::cerr << "# accept\n" << storeUri << "\n"; auto inputs = readStrings(source); auto wantedOutputs = readStrings(source); @@ -270,17 +269,17 @@ connected: uploadLock = openLockFile(currentLoad + "/" + escapeUri(fileName) + ".upload-lock", true); }; try { - setUpdateLock(storeUri.render()); + setUpdateLock(storeUri); } catch (SysError & e) { if (e.errNo != ENAMETOOLONG) throw; // Try again hashing the store URL so we have a shorter path - auto h = hashString(HashAlgorithm::MD5, storeUri.render()); + auto h = hashString(HashAlgorithm::MD5, storeUri); setUpdateLock(h.to_string(HashFormat::Base64, false)); } } { - Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri)); auto old = signal(SIGALRM, handleAlarm); alarm(15 * 60); @@ -293,7 +292,7 @@ connected: auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute; { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri)); copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute); } @@ -331,7 +330,7 @@ connected: optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv); auto & result = *optResult; if (!result.success()) - throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri.render(), result.errorMsg); + throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg); } else { copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute); auto res = sshStore->buildPathsWithResults({ @@ -374,7 +373,7 @@ connected: } if (!missingPaths.empty()) { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri)); if (auto localStore = store.dynamic_pointer_cast()) for (auto & path : missingPaths) localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */ From baa28159d316e2c506fee8a82f66726c9305f68a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 15:38:02 +0200 Subject: [PATCH 698/910] Update tests/functional/test-infra.sh Co-authored-by: John Ericson --- tests/functional/test-infra.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 1dab069fb..d02a11b46 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -111,7 +111,7 @@ unset res # `grepQuiet` does not allow newlines in its arguments, because grep quietly # treats them as multiple queries. -( echo foo; echo bar; ) | expectStderr -101 grepQuiet $'foo\nbar' \ +{ echo foo; echo bar; } | expectStderr -101 grepQuiet $'foo\nbar' \ | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grepQuiet: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' # We took the blue pill and woke up in a world where `grep` is moderately safe. From f0fe1d880ded3b5c1e6d44ad0cee9105a50ebd65 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 15:39:15 +0200 Subject: [PATCH 699/910] Update doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Thalheim --- .../10734-nix3-build-show-all-fod-errors-with-keep-going.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md index 5c2797be8..e4e2f797c 100644 --- a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md +++ b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md @@ -5,6 +5,6 @@ prs: 10734 The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior of `nix build`. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. -Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92) +Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) [`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) From 55a654abfd0f9a0d8e70b9d2dec410888a3e76db Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 17:53:17 +0200 Subject: [PATCH 700/910] Make panic() and unreachable() robust Plus one or two tweaks. --- src/libutil/error.cc | 28 +++++++++++++++++++++++++--- src/libutil/error.hh | 8 ++------ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index b1858911a..ccd008c7c 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -1,3 +1,5 @@ +#include + #include "error.hh" #include "environment-variables.hh" #include "signals.hh" @@ -430,16 +432,36 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s return out; } +/** Write to stderr in a robust and minimal way, considering that the process + * may be in a bad state. + */ +static void writeErr(std::string_view buf) +{ + while (!buf.empty()) { + auto n = write(STDERR_FILENO, buf.data(), buf.size()); + if (n < 0) { + if (errno == EINTR) continue; + abort(); + } + buf = buf.substr(n); + } +} + void panic(std::string_view msg) { - printError(msg); - printError("This was a fatal error, aborting."); + writeErr("\n\n" ANSI_RED "terminating due to unexpected unrecoverable internal error: " ANSI_NORMAL ); + writeErr(msg); + writeErr("\n"); abort(); } void panic(const char * file, int line, const char * func) { - panic(std::string("Unexpected condition in ") + func + " at " + file + ":" + std::to_string(line)); + char buf[512]; + int n = snprintf(buf, sizeof(buf), "Unexpected condition in %s at %s:%d", func, file, line); + if (n < 0) + panic("Unexpected condition and could not format error message"); + panic(std::string_view(buf, std::min(static_cast(sizeof(buf)), n))); } } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 572a1baf7..58d902622 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -275,17 +275,13 @@ void throwExceptionSelfCheck(); /** * Print a message and abort(). - * - * @note: This assumes that the logger is operational */ [[noreturn]] void panic(std::string_view msg); /** * Print a basic error message with source position and abort(). - * Use the unreachable macro to call this. - * - * @note: This assumes that the logger is operational + * Use the unreachable() macro to call this. */ [[noreturn]] void panic(const char * file, int line, const char * func); @@ -295,6 +291,6 @@ void panic(const char * file, int line, const char * func); * * @note: This assumes that the logger is operational */ -#define unreachable() (panic(__FILE__, __LINE__, __func__)) +#define unreachable() (::nix::panic(__FILE__, __LINE__, __func__)) } From 6e178cd899dc9e25e8da6a018f7cfdb233b762ea Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Thu, 25 Jul 2024 11:38:45 -0700 Subject: [PATCH 701/910] Fix reference to experimental features docs Arose because https://github.com/NixOS/nix/pull/9014 merged before https://github.com/NixOS/nix/pull/11131, but the latter did not rebase / merge against the latest master. --- doc/manual/src/language/operators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index e96a28988..e1c020781 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -198,11 +198,11 @@ Equivalent to `!`*b1* `||` *b2*. > **Warning** > > This syntax is part of an -> [experimental feature](@docroot@/contributing/experimental-features.md) +> [experimental feature](@docroot@/development/experimental-features.md) > and may change in future releases. > > To use this syntax, make sure the -> [`pipe-operators` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-pipe-operators) +> [`pipe-operators` experimental feature](@docroot@/development/experimental-features.md#xp-feature-pipe-operators) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > From 90459e60dcfedae27bd7da5615fe1aac25cc4c4e Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Thu, 25 Jul 2024 11:38:45 -0700 Subject: [PATCH 702/910] Fix reference to experimental features docs Arose because https://github.com/NixOS/nix/pull/9014 merged before https://github.com/NixOS/nix/pull/11131, but the latter did not rebase / merge against the latest master. --- doc/manual/src/language/operators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index e96a28988..e1c020781 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -198,11 +198,11 @@ Equivalent to `!`*b1* `||` *b2*. > **Warning** > > This syntax is part of an -> [experimental feature](@docroot@/contributing/experimental-features.md) +> [experimental feature](@docroot@/development/experimental-features.md) > and may change in future releases. > > To use this syntax, make sure the -> [`pipe-operators` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-pipe-operators) +> [`pipe-operators` experimental feature](@docroot@/development/experimental-features.md#xp-feature-pipe-operators) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > From 492715c0bb589f0df963cb9902a9f46230104d44 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Tue, 18 Jun 2024 00:54:05 -0700 Subject: [PATCH 703/910] diff-closures: fix a use after free Found by looking for interesting asan reports from the test suite. What happened here is that name got overwritten, but it was what actually held the backing memory for the thing it got overwritten by, which was a by-reference value coming out of std::regex. Due to absurd reasons I cannot seem to use a string_view iterator here, so I just copy the string with a longer lifetime instead. idk lol ==3796364==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000014c61 at pc 0x74843523bf1d bp 0x7ffc68351330 sp 0x7ffc68350af0 READ of size 3 at 0x503000014c61 thread T0 0 0x74843523bf1c in __asan_memcpy (/nix/store/mzhqknx2mc94jdz4n320hn1lml86398y-clang-wrapper-17.0.6/resource-root/lib/linux/libclang_rt.asan-x86_64.so+0x159f1c) 1 0x6403cf6cbff4 in std::char_traits::copy(char*, char const*, unsigned long) /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/char_traits.h:445:33 <...> 7 0x6403cf6cbff4 in std::__cxx11::sub_match<__gnu_cxx::__normal_iterator, std::allocator>>>::str() const /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/regex.h:966:6 8 0x6403cf6cbff4 in std::__cxx11::sub_match<__gnu_cxx::__normal_iterator, std::allocator>>>::operator std::__cxx11::basic_string, std::allocator>() const /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/regex.h:955:16 9 0x6403cf6cbff4 in nix::getClosureInfo[abi:cxx11](nix::ref, nix::StorePath const&) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:37:26 10 0x6403cf6cd70c in nix::printClosureDiff(nix::ref, nix::StorePath const&, nix::StorePath const&, std::basic_string_view>) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:54:25 11 0x6403cf873331 in CmdProfileDiffClosures::run(nix::ref) /home/jade/lix/lix2/build/src/nix/profile.cc:479:17 <...> 0x503000014c61 is located 17 bytes inside of 21-byte region [0x503000014c50,0x503000014c65) freed by thread T0 here: 0 0x748435250470 in operator delete(void*) (/nix/store/mzhqknx2mc94jdz4n320hn1lml86398y-clang-wrapper-17.0.6/resource-root/lib/linux/libclang_rt.asan-x86_64.so+0x16e470) <...> 6 0x6403cf6cbda2 in std::__cxx11::basic_string, std::allocator>::~basic_string() /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/basic_string.h:792:9 7 0x6403cf6cbda2 in nix::getClosureInfo[abi:cxx11](nix::ref, nix::StorePath const&) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:36:13 8 0x6403cf6cd70c in nix::printClosureDiff(nix::ref, nix::StorePath const&, nix::StorePath const&, std::basic_string_view>) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:54:25 <...> previously allocated by thread T0 here: 0 0x74843524fa38 in operator new(unsigned long) (/nix/store/mzhqknx2mc94jdz4n320hn1lml86398y-clang-wrapper-17.0.6/resource-root/lib/linux/libclang_rt.asan-x86_64.so+0x16da38) <...> 9 0x6403cf6cb68c in std::__cxx11::basic_string, std::allocator>::basic_string>, void>(std::basic_string_view> const&, std::allocator const&) /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/basic_string.h:784:4 10 0x6403cf6cb68c in nix::getClosureInfo[abi:cxx11](nix::ref, nix::StorePath const&) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:33:21 11 0x6403cf6cd70c in nix::printClosureDiff(nix::ref, nix::StorePath const&, nix::StorePath const&, std::basic_string_view>) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:54:25 12 0x6403cf873331 in CmdProfileDiffClosures::run(nix::ref) /home/jade/lix/lix2/build/src/nix/profile.cc:479:17 <...> (cherry-picked from https://git.lix.systems/lix-project/lix/commit/b9b1bbd22fc3b34a4d8e2090f0d033f742c32ee0) --- src/nix/diff-closures.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 46c94b211..4e12dc60a 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -31,9 +31,18 @@ GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) version suffixes like "unstable"). */ static std::regex regex("(.*)-([a-z]+|lib32|lib64)"); std::smatch match; - std::string name(path.name()); + std::string name{path.name()}; + // Used to keep name alive through being potentially overwritten below + // (to not invalidate the references from the regex result) + // + // n.b. cannot be just path.name().{begin,end}() since that returns const + // char *, which does not, for some reason, convert as required on + // libstdc++. Seems like a libstdc++ bug or standard bug to me... we + // can afford the allocation in any case. + const std::string origName{path.name()}; std::string outputName; - if (std::regex_match(name, match, regex)) { + + if (std::regex_match(origName, match, regex)) { name = match[1]; outputName = match[2]; } From 07aeedd37e2e6c2b2d48fd5cf59917adc241f949 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Tue, 23 Jul 2024 21:45:30 +0200 Subject: [PATCH 704/910] diff-closures: remove gratuitous copy This was done originally because std::smatch does not accept `const char *` as iterators. However, this was because we should have been using std::cmatch instead. (cherry picked from commit https://git.lix.systems/lix-project/lix/commit/12a5838d11f790d36e2ac626e8916a2c19bcb80b) --- src/nix/diff-closures.cc | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 4e12dc60a..2bc7fe82b 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -25,24 +25,17 @@ GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) GroupedPaths groupedPaths; - for (auto & path : closure) { + for (auto const & path : closure) { /* Strip the output name. Unfortunately this is ambiguous (we can't distinguish between output names like "bin" and version suffixes like "unstable"). */ static std::regex regex("(.*)-([a-z]+|lib32|lib64)"); - std::smatch match; + std::cmatch match; std::string name{path.name()}; - // Used to keep name alive through being potentially overwritten below - // (to not invalidate the references from the regex result) - // - // n.b. cannot be just path.name().{begin,end}() since that returns const - // char *, which does not, for some reason, convert as required on - // libstdc++. Seems like a libstdc++ bug or standard bug to me... we - // can afford the allocation in any case. - const std::string origName{path.name()}; + std::string_view const origName = path.name(); std::string outputName; - if (std::regex_match(origName, match, regex)) { + if (std::regex_match(origName.begin(), origName.end(), match, regex)) { name = match[1]; outputName = match[2]; } From 429a197d246782e286fe7d9f6fad0b9daf2941ec Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Sat, 20 Jul 2024 12:04:25 -0400 Subject: [PATCH 705/910] parser.y: use names where I'll be refactoring --- src/libexpr/parser.y | 72 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 9ad41c148..256244a75 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -180,22 +180,22 @@ expr_function $$ = me; SET_DOC_POS(me, @1); } - | '{' formals '}' ':' expr_function - { auto me = new ExprLambda(CUR_POS, state->validateFormals($2), $5); + | '{' formals '}' ':' expr_function[body] + { auto me = new ExprLambda(CUR_POS, state->validateFormals($formals), $body); $$ = me; SET_DOC_POS(me, @1); } - | '{' formals '}' '@' ID ':' expr_function + | '{' formals '}' '@' ID ':' expr_function[body] { - auto arg = state->symbols.create($5); - auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7); + auto arg = state->symbols.create($ID); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formals, CUR_POS, arg), $body); $$ = me; SET_DOC_POS(me, @1); } - | ID '@' '{' formals '}' ':' expr_function + | ID '@' '{' formals '}' ':' expr_function[body] { - auto arg = state->symbols.create($1); - auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7); + auto arg = state->symbols.create($ID); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formals, CUR_POS, arg), $body); $$ = me; SET_DOC_POS(me, @1); } @@ -364,50 +364,50 @@ ind_string_parts ; binds - : binds attrpath '=' expr ';' { - $$ = $1; + : binds[accum] attrpath '=' expr ';' { + $$ = $accum; - auto pos = state->at(@2); - auto exprPos = state->at(@4); + auto pos = state->at(@attrpath); + auto exprPos = state->at(@expr); { auto it = state->lexerState.positionToDocComment.find(pos); if (it != state->lexerState.positionToDocComment.end()) { - $4->setDocComment(it->second); + $expr->setDocComment(it->second); state->lexerState.positionToDocComment.emplace(exprPos, it->second); } } - state->addAttr($$, std::move(*$2), $4, pos); - delete $2; + state->addAttr($$, std::move(*$attrpath), $expr, pos); + delete $attrpath; } - | binds INHERIT attrs ';' - { $$ = $1; - for (auto & [i, iPos] : *$3) { - if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); - $$->attrs.emplace( + | binds[accum] INHERIT attrs ';' + { $$ = $accum; + for (auto & [i, iPos] : *$attrs) { + if ($accum->attrs.find(i.symbol) != $accum->attrs.end()) + state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos); + $accum->attrs.emplace( i.symbol, ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited)); } - delete $3; + delete $attrs; } - | binds INHERIT '(' expr ')' attrs ';' - { $$ = $1; - if (!$$->inheritFromExprs) - $$->inheritFromExprs = std::make_unique>(); - $$->inheritFromExprs->push_back($4); - auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1); - for (auto & [i, iPos] : *$6) { - if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); - $$->attrs.emplace( + | binds[accum] INHERIT '(' expr ')' attrs ';' + { $$ = $accum; + if (!$accum->inheritFromExprs) + $accum->inheritFromExprs = std::make_unique>(); + $accum->inheritFromExprs->push_back($expr); + auto from = new nix::ExprInheritFrom(state->at(@expr), $accum->inheritFromExprs->size() - 1); + for (auto & [i, iPos] : *$attrs) { + if ($accum->attrs.find(i.symbol) != $accum->attrs.end()) + state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos); + $accum->attrs.emplace( i.symbol, ExprAttrs::AttrDef( new ExprSelect(iPos, from, i.symbol), iPos, ExprAttrs::AttrDef::Kind::InheritedFrom)); } - delete $6; + delete $attrs; } | { $$ = new ExprAttrs(state->at(@0)); } ; @@ -468,10 +468,10 @@ expr_list ; formals - : formal ',' formals - { $$ = $3; $$->formals.emplace_back(*$1); delete $1; } + : formal ',' formals[accum] + { $$ = $accum; $$->formals.emplace_back(*$formal); delete $formal; } | formal - { $$ = new Formals; $$->formals.emplace_back(*$1); $$->ellipsis = false; delete $1; } + { $$ = new Formals; $$->formals.emplace_back(*$formal); $$->ellipsis = false; delete $formal; } | { $$ = new Formals; $$->ellipsis = false; } | ELLIPSIS From b0a8430e851790ebac4e2324084bd6d86b995316 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Sat, 20 Jul 2024 12:04:25 -0400 Subject: [PATCH 706/910] parser.y: move attr doc setting into addAttr --- src/libexpr/parser-state.hh | 11 +++++++++-- src/libexpr/parser.y | 13 +------------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 4bb5c9204..c23ef32a5 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -86,7 +86,7 @@ struct ParserState void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); - void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos); + void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc); Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); Expr * stripIndentation(const PosIdx pos, std::vector>> && es); @@ -110,11 +110,12 @@ inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx pre }); } -inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos) +inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc) { AttrPath::iterator i; // All attrpaths have at least one attr assert(!attrPath.empty()); + auto pos = at(loc); // Checking attrPath validity. // =========================== for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) { @@ -179,6 +180,12 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * } else { attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); } + + auto it = lexerState.positionToDocComment.find(pos); + if (it != lexerState.positionToDocComment.end()) { + e->setDocComment(it->second); + lexerState.positionToDocComment.emplace(at(exprLoc), it->second); + } } inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 256244a75..6386747f5 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -366,18 +366,7 @@ ind_string_parts binds : binds[accum] attrpath '=' expr ';' { $$ = $accum; - - auto pos = state->at(@attrpath); - auto exprPos = state->at(@expr); - { - auto it = state->lexerState.positionToDocComment.find(pos); - if (it != state->lexerState.positionToDocComment.end()) { - $expr->setDocComment(it->second); - state->lexerState.positionToDocComment.emplace(exprPos, it->second); - } - } - - state->addAttr($$, std::move(*$attrpath), $expr, pos); + state->addAttr($$, std::move(*$attrpath), @attrpath, $expr, @expr); delete $attrpath; } | binds[accum] INHERIT attrs ';' From 6e3b9e6a4de7430c8b130a7c87d7f5df68cf6c86 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Sat, 20 Jul 2024 12:04:25 -0400 Subject: [PATCH 707/910] parser.y: eliminate conflicts --- src/libexpr/parser.y | 61 ++++++++++++------- .../lang/parse-fail-undef-var-2.err.exp | 2 +- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 6386747f5..2069931e1 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -8,8 +8,8 @@ %parse-param { nix::ParserState * state } %lex-param { void * scanner } %lex-param { nix::ParserState * state } -%expect 1 -%expect-rr 1 +%expect 0 +%expect-rr 0 %code requires { @@ -133,8 +133,8 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) { %type expr_select expr_simple expr_app %type expr_pipe_from expr_pipe_into %type expr_list -%type binds -%type formals +%type binds binds1 +%type formals formal_set %type formal %type attrpath %type attrs @@ -180,22 +180,22 @@ expr_function $$ = me; SET_DOC_POS(me, @1); } - | '{' formals '}' ':' expr_function[body] - { auto me = new ExprLambda(CUR_POS, state->validateFormals($formals), $body); + | formal_set ':' expr_function[body] + { auto me = new ExprLambda(CUR_POS, state->validateFormals($formal_set), $body); $$ = me; SET_DOC_POS(me, @1); } - | '{' formals '}' '@' ID ':' expr_function[body] + | formal_set '@' ID ':' expr_function[body] { auto arg = state->symbols.create($ID); - auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formals, CUR_POS, arg), $body); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formal_set, CUR_POS, arg), $body); $$ = me; SET_DOC_POS(me, @1); } - | ID '@' '{' formals '}' ':' expr_function[body] + | ID '@' formal_set ':' expr_function[body] { auto arg = state->symbols.create($ID); - auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formals, CUR_POS, arg), $body); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formal_set, CUR_POS, arg), $body); $$ = me; SET_DOC_POS(me, @1); } @@ -311,11 +311,13 @@ expr_simple /* Let expressions `let {..., body = ...}' are just desugared into `(rec {..., body = ...}).body'. */ | LET '{' binds '}' - { $3->recursive = true; $$ = new ExprSelect(noPos, $3, state->s.body); } + { $3->recursive = true; $3->pos = CUR_POS; $$ = new ExprSelect(noPos, $3, state->s.body); } | REC '{' binds '}' - { $3->recursive = true; $$ = $3; } - | '{' binds '}' - { $$ = $2; } + { $3->recursive = true; $3->pos = CUR_POS; $$ = $3; } + | '{' binds1 '}' + { $2->pos = CUR_POS; $$ = $2; } + | '{' '}' + { $$ = new ExprAttrs(CUR_POS); } | '[' expr_list ']' { $$ = $2; } ; @@ -364,8 +366,13 @@ ind_string_parts ; binds - : binds[accum] attrpath '=' expr ';' { - $$ = $accum; + : binds1 + | { $$ = new ExprAttrs; } + ; + +binds1 + : binds1[accum] attrpath '=' expr ';' + { $$ = $accum; state->addAttr($$, std::move(*$attrpath), @attrpath, $expr, @expr); delete $attrpath; } @@ -398,7 +405,11 @@ binds } delete $attrs; } - | { $$ = new ExprAttrs(state->at(@0)); } + | attrpath '=' expr ';' + { $$ = new ExprAttrs; + state->addAttr($$, std::move(*$attrpath), @attrpath, $expr, @expr); + delete $attrpath; + } ; attrs @@ -456,15 +467,19 @@ expr_list | { $$ = new ExprList; } ; +formal_set + : '{' formals ',' ELLIPSIS '}' { $$ = $formals; $$->ellipsis = true; } + | '{' ELLIPSIS '}' { $$ = new Formals; $$->ellipsis = true; } + | '{' formals ',' '}' { $$ = $formals; $$->ellipsis = false; } + | '{' formals '}' { $$ = $formals; $$->ellipsis = false; } + | '{' '}' { $$ = new Formals; $$->ellipsis = false; } + ; + formals - : formal ',' formals[accum] + : formals[accum] ',' formal { $$ = $accum; $$->formals.emplace_back(*$formal); delete $formal; } | formal - { $$ = new Formals; $$->formals.emplace_back(*$formal); $$->ellipsis = false; delete $formal; } - | - { $$ = new Formals; $$->ellipsis = false; } - | ELLIPSIS - { $$ = new Formals; $$->ellipsis = true; } + { $$ = new Formals; $$->formals.emplace_back(*$formal); delete $formal; } ; formal diff --git a/tests/functional/lang/parse-fail-undef-var-2.err.exp b/tests/functional/lang/parse-fail-undef-var-2.err.exp index 393c454dd..96e87b2aa 100644 --- a/tests/functional/lang/parse-fail-undef-var-2.err.exp +++ b/tests/functional/lang/parse-fail-undef-var-2.err.exp @@ -1,4 +1,4 @@ -error: syntax error, unexpected ':', expecting '}' +error: syntax error, unexpected ':', expecting '}' or ',' at «stdin»:3:13: 2| 3| f = {x, y : ["baz" "bar" z "bat"]}: x + y; From 18db46a6cb72acaae748833b09b428a7794bd9c8 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Mon, 22 Jul 2024 11:36:09 -0400 Subject: [PATCH 708/910] parser.y: GLR -> LALR --- src/libexpr/parser-state.hh | 1 + src/libexpr/parser.y | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index c23ef32a5..8ad0d9ad7 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -20,6 +20,7 @@ struct StringToken operator std::string_view() const { return {p, l}; } }; +// This type must be trivially copyable; see YYLTYPE_IS_TRIVIAL in parser.y. struct ParserLocation { int beginOffset; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 2069931e1..f2ccca7fc 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -1,4 +1,4 @@ -%glr-parser +%define api.location.type { ::nix::ParserLocation } %define api.pure %locations %define parse.error verbose @@ -9,7 +9,6 @@ %lex-param { void * scanner } %lex-param { nix::ParserState * state } %expect 0 -%expect-rr 0 %code requires { @@ -27,7 +26,17 @@ #include "eval-settings.hh" #include "parser-state.hh" -#define YYLTYPE ::nix::ParserLocation +// Bison seems to have difficulty growing the parser stack when using C++ with +// a custom location type. This undocumented macro tells Bison that our +// location type is "trivially copyable" in C++-ese, so it is safe to use the +// same memcpy macro it uses to grow the stack that it uses with its own +// default location type. Without this, we get "error: memory exhausted" when +// parsing some large Nix files. Our other options are to increase the initial +// stack size (200 by default) to be as large as we ever want to support (so +// that growing the stack is unnecessary), or redefine the stack-relocation +// macro ourselves (which is also undocumented). +#define YYLTYPE_IS_TRIVIAL 1 + #define YY_DECL int yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParserState * state) @@ -77,7 +86,7 @@ YY_DECL; using namespace nix; -#define CUR_POS state->at(*yylocp) +#define CUR_POS state->at(yyloc) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error) From 2141a52ca3e150079bce8bc12a0a992b16c046f1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 15:38:44 +0200 Subject: [PATCH 709/910] nix repl: Remove unnecessary call to evalString This crashes with the multithreaded evaluator, which checks against attempts to finish an already finished value. --- src/libcmd/repl.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index b5d0816dd..f5e836f8c 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -644,9 +644,6 @@ ProcessLineResult NixRepl::processLine(std::string line) fallbackPos = attr->pos; fallbackDoc = state->getDocCommentForPos(fallbackPos); } - - } else { - evalString(arg, v); } evalString(arg, v); From 6d843ce9fec37e854e518db19ce880895f7de2ac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 Jun 2024 19:32:46 +0200 Subject: [PATCH 710/910] Provide std::hash --- src/libexpr/json-to-value.cc | 2 +- src/libexpr/symbol-table.hh | 11 +++++++++++ src/libexpr/value.hh | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 33ab55ee9..21074bdd8 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -42,7 +42,7 @@ class JSONSax : nlohmann::json_sax { auto attrs2 = state.buildBindings(attrs.size()); for (auto & i : attrs) attrs2.insert(i.first, i.second); - parent->value(state).mkAttrs(attrs2.alreadySorted()); + parent->value(state).mkAttrs(attrs2); return std::move(parent); } void add() override { v = nullptr; } diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index c7a3563b0..8f7257e01 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -69,6 +69,8 @@ public: auto operator<=>(const Symbol other) const { return id <=> other.id; } bool operator==(const Symbol other) const { return id == other.id; } + + friend class std::hash; }; /** @@ -132,3 +134,12 @@ public: }; } + +template<> +struct std::hash +{ + std::size_t operator()(const nix::Symbol & s) const noexcept + { + return std::hash{}(s.id); + } +}; diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 1f4d72d39..bfe526db3 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -494,11 +494,11 @@ void Value::mkBlackhole() #if HAVE_BOEHMGC typedef std::vector> ValueVector; -typedef std::map, traceable_allocator>> ValueMap; +typedef std::unordered_map, std::equal_to, traceable_allocator>> ValueMap; typedef std::map, traceable_allocator>> ValueVectorMap; #else typedef std::vector ValueVector; -typedef std::map ValueMap; +typedef std::unordered_map ValueMap; typedef std::map ValueVectorMap; #endif From ce663d75e36f1bb41c11a9d666d5b649328bee92 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Jan 2024 15:05:20 +0100 Subject: [PATCH 711/910] LRUCache: Mark size() as const --- src/libutil/lru-cache.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/lru-cache.hh b/src/libutil/lru-cache.hh index 0e19517ed..6e14cac35 100644 --- a/src/libutil/lru-cache.hh +++ b/src/libutil/lru-cache.hh @@ -89,7 +89,7 @@ public: return i->second.second; } - size_t size() + size_t size() const { return data.size(); } From ea46264bd35d479ca60b3f725d31f0735863281d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 16:14:03 +0200 Subject: [PATCH 712/910] Store: Use SharedSync for state --- src/libstore/store-api.cc | 2 +- src/libstore/store-api.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 2c4dee518..21fa2939d 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -920,7 +920,7 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor const Store::Stats & Store::getStats() { { - auto state_(state.lock()); + auto state_(state.read()); stats.pathInfoCacheSize = state_->pathInfoCache.size(); } return stats; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 7d5f533c5..8288cfdf0 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -201,7 +201,7 @@ protected: LRUCache pathInfoCache; }; - Sync state; + SharedSync state; std::shared_ptr diskCache; From d9ba2a1634865b2d7306b97902ce5accb89a9bb2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 19:06:49 +0200 Subject: [PATCH 713/910] Fix error message --- src/libfetchers/git-utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index ecc71ae47..1a64acc50 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -131,7 +131,7 @@ T peelObject(git_repository * repo, git_object * obj, git_object_t type) T obj2; if (git_object_peel((git_object * *) (typename T::pointer *) Setter(obj2), obj, type)) { auto err = git_error_last(); - throw Error("peeling Git object '%s': %s", git_object_id(obj), err->message); + throw Error("peeling Git object '%s': %s", *git_object_id(obj), err->message); } return obj2; } From 06b686b62dae4db86bb4c449c3418e271dff1b68 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 20:24:58 +0200 Subject: [PATCH 714/910] Handle tarballs that don't consist of a single top-level directory Fixes #4785 (top-level directories are no longer merged into one). Fixes #10983 (top-level non-directories are no longer discarded). --- src/libfetchers/git-utils.cc | 77 ++++++++++++++++++++++++++---------- tests/functional/tarball.sh | 25 ++++++++++++ 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 1a64acc50..032d8e0bd 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -126,7 +126,7 @@ Object lookupObject(git_repository * repo, const git_oid & oid, git_object_t typ } template -T peelObject(git_repository * repo, git_object * obj, git_object_t type) +T peelObject(git_object * obj, git_object_t type) { T obj2; if (git_object_peel((git_object * *) (typename T::pointer *) Setter(obj2), obj, type)) { @@ -136,6 +136,29 @@ T peelObject(git_repository * repo, git_object * obj, git_object_t type) return obj2; } +template +T dupObject(typename T::pointer obj) +{ + T obj2; + if (git_object_dup((git_object * *) (typename T::pointer *) Setter(obj2), (git_object *) obj)) + throw Error("duplicating object '%s': %s", *git_object_id((git_object *) obj), git_error_last()->message); + return obj2; +} + +/** + * Peel the specified object (i.e. follow tag and commit objects) to + * either a blob or a tree. + */ +static Object peelToTreeOrBlob(git_object * obj) +{ + /* git_object_peel() doesn't handle blob objects, so handle those + specially. */ + if (git_object_type(obj) == GIT_OBJECT_BLOB) + return dupObject(obj); + else + return peelObject(obj, GIT_OBJECT_TREE); +} + struct GitRepoImpl : GitRepo, std::enable_shared_from_this { /** Location of the repository on disk. */ @@ -166,7 +189,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this std::unordered_set done; std::queue todo; - todo.push(peelObject(*this, lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT)); + todo.push(peelObject(lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT)); while (auto commit = pop(todo)) { if (!done.insert(*git_commit_id(commit->get())).second) continue; @@ -184,7 +207,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this uint64_t getLastModified(const Hash & rev) override { - auto commit = peelObject(*this, lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT); + auto commit = peelObject(lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT); return git_commit_time(commit.get()); } @@ -476,11 +499,11 @@ ref GitRepo::openRepo(const std::filesystem::path & path, bool create, struct GitSourceAccessor : SourceAccessor { ref repo; - Tree root; + Object root; GitSourceAccessor(ref repo_, const Hash & rev) : repo(repo_) - , root(peelObject(*repo, lookupObject(*repo, hashToOID(rev)).get(), GIT_OBJECT_TREE)) + , root(peelToTreeOrBlob(lookupObject(*repo, hashToOID(rev)).get())) { } @@ -506,7 +529,7 @@ struct GitSourceAccessor : SourceAccessor std::optional maybeLstat(const CanonPath & path) override { if (path.isRoot()) - return Stat { .type = tDirectory }; + return Stat { .type = git_object_type(root.get()) == GIT_OBJECT_TREE ? tDirectory : tRegular }; auto entry = lookup(path); if (!entry) @@ -616,10 +639,10 @@ struct GitSourceAccessor : SourceAccessor std::optional lookupTree(const CanonPath & path) { if (path.isRoot()) { - Tree tree; - if (git_tree_dup(Setter(tree), root.get())) - throw Error("duplicating directory '%s': %s", showPath(path), git_error_last()->message); - return tree; + if (git_object_type(root.get()) == GIT_OBJECT_TREE) + return dupObject((git_tree *) &*root); + else + return std::nullopt; } auto entry = lookup(path); @@ -646,10 +669,10 @@ struct GitSourceAccessor : SourceAccessor std::variant getTree(const CanonPath & path) { if (path.isRoot()) { - Tree tree; - if (git_tree_dup(Setter(tree), root.get())) - throw Error("duplicating directory '%s': %s", showPath(path), git_error_last()->message); - return tree; + if (git_object_type(root.get()) == GIT_OBJECT_TREE) + return dupObject((git_tree *) &*root); + else + throw Error("Git root object '%s' is not a directory", *git_object_id(root.get())); } auto entry = need(path); @@ -669,6 +692,9 @@ struct GitSourceAccessor : SourceAccessor Blob getBlob(const CanonPath & path, bool expectSymlink) { + if (!expectSymlink && git_object_type(root.get()) == GIT_OBJECT_BLOB) + return dupObject((git_blob *) &*root); + auto notExpected = [&]() { throw Error( @@ -782,8 +808,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink std::vector pendingDirs; - size_t componentsToStrip = 1; - void pushBuilder(std::string name) { git_treebuilder * b; @@ -839,9 +863,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink { std::span pathComponents2{pathComponents}; - if (pathComponents2.size() <= componentsToStrip) return false; - pathComponents2 = pathComponents2.subspan(componentsToStrip); - updateBuilders( isDir ? pathComponents2 @@ -964,11 +985,27 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink git_tree_entry_filemode(entry)); } - Hash sync() override { + Hash sync() override + { updateBuilders({}); auto [oid, _name] = popBuilder(); + /* If the root directory contains a single entry that is a + directory or a non-executable regular file, return that as + the top-level object. We don't do this for executables + because they don't have a tree hash in the Git object + model. */ + auto _tree = lookupObject(*repo, oid, GIT_OBJECT_TREE); + auto tree = (const git_tree *) &*_tree; + + if (git_tree_entrycount(tree) == 1) { + auto entry = git_tree_entry_byindex(tree, 0); + auto mode = git_tree_entry_filemode(entry); + if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) + oid = *git_tree_entry_id(entry); + } + return toHash(oid); } }; diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index ab357ac78..6799831ce 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -83,3 +83,28 @@ path="$(nix flake prefetch --json "tarball+file://$(pwd)/tree.tar.gz" | jq -r .s [[ $(cat "$path/a/zzz") = bar ]] [[ $(cat "$path/c/aap") = bar ]] [[ $(cat "$path/fnord") = bar ]] + +# Test a tarball that has multiple top-level directories. +rm -rf "$TEST_ROOT/tar_root" +mkdir -p "$TEST_ROOT/tar_root" "$TEST_ROOT/tar_root/foo" "$TEST_ROOT/tar_root/bar" +tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . +path="$(nix flake prefetch --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" +[[ -d "$path/foo" ]] +[[ -d "$path/bar" ]] + +# Test a tarball that has a single non-executable regular file. +rm -rf "$TEST_ROOT/tar_root" +mkdir -p "$TEST_ROOT/tar_root" +echo bar > "$TEST_ROOT/tar_root/foo" +tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . +path="$(nix flake prefetch --refresh --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" +[[ $(cat "$path") = bar ]] + +# Test a tarball that has a single executable regular file. +rm -rf "$TEST_ROOT/tar_root" +mkdir -p "$TEST_ROOT/tar_root" +echo bar > "$TEST_ROOT/tar_root/foo" +chmod +x "$TEST_ROOT/tar_root/foo" +tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . +path="$(nix flake prefetch --refresh --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" +[[ $(cat "$path/foo") = bar ]] From b88950ec777ab55a5e924c0b3758bad06a6f0f33 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 20:34:04 +0200 Subject: [PATCH 715/910] Update fetchTree docs --- src/libexpr/primops/fetchTree.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index e59d7fe67..9a3b81bc9 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -559,11 +559,11 @@ static RegisterPrimOp primop_fetchTarball({ .doc = R"( Download the specified URL, unpack it and return the path of the unpacked tree. The file must be a tape archive (`.tar`) compressed - with `gzip`, `bzip2` or `xz`. The top-level path component of the - files in the tarball is removed, so it is best if the tarball - contains a single directory at top level. The typical use of the - function is to obtain external Nix expression dependencies, such as - a particular version of Nixpkgs, e.g. + with `gzip`, `bzip2` or `xz`. If the tarball consists of a + single directory or non-executable file, then the top-level path + component of the files in the tarball is removed. The typical + use of the function is to obtain external Nix expression + dependencies, such as a particular version of Nixpkgs, e.g. ```nix with import (fetchTarball https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz) {}; From 5e83c0427f701df33a37a7993945d7db99f6267e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 20:46:07 +0200 Subject: [PATCH 716/910] Fix test --- tests/unit/libfetchers/git-utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index d3547ec6a..f0d38d50c 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -103,7 +103,7 @@ TEST_F(GitUtilsTest, sink_hardlink) sink->createHardlink(CanonPath("foo-1.1/link"), CanonPath("hello")); FAIL() << "Expected an exception"; } catch (const nix::Error & e) { - ASSERT_THAT(e.msg(), testing::HasSubstr("invalid hard link target")); + ASSERT_THAT(e.msg(), testing::HasSubstr("cannot find hard link target")); ASSERT_THAT(e.msg(), testing::HasSubstr("/hello")); ASSERT_THAT(e.msg(), testing::HasSubstr("foo-1.1/link")); } From 6af40f488a26fda5de55e16c6ba950c6f99762ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 01:38:18 +0200 Subject: [PATCH 717/910] Rename SyncBase::read() -> readLock() Make it explicit so it's clear what it's about when I and other contributors read its call sites. --- src/libstore/store-api.cc | 2 +- src/libutil/posix-source-accessor.cc | 2 +- src/libutil/sync.hh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 21fa2939d..b3e5ad014 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -920,7 +920,7 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor const Store::Stats & Store::getStats() { { - auto state_(state.read()); + auto state_(state.readLock()); stats.pathInfoCacheSize = state_->pathInfoCache.size(); } return stats; diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 35047f89e..2b1a485d5 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -97,7 +97,7 @@ std::optional PosixSourceAccessor::cachedLstat(const CanonPath & pa Path absPath = makeAbsPath(path).string(); { - auto cache(_cache.read()); + auto cache(_cache.readLock()); auto i = cache->find(absPath); if (i != cache->end()) return i->second; } diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index c1b699ffc..d340f3d97 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -106,7 +106,7 @@ public: * Acquire read access to the inner value. When using * `std::shared_mutex`, this will use a shared lock. */ - ReadLock read() const { return ReadLock(const_cast(this)); } + ReadLock readLock() const { return ReadLock(const_cast(this)); } }; template From 22f943bb1fe3feefdf4e469270de79406a46f5c3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 02:10:31 +0200 Subject: [PATCH 718/910] dependencies: Centralize aws-sdk-cpp and sync with Nixpkgs By syncing with Nixpkgs, we reuse the same derivation, which is generally a good idea, and has the benefit that it is transitively a channel blocker. Changes: - https://github.com/NixOS/nixpkgs/pull/163313 (SuperSandro2000) > nix: disable big-parallel for aws-sdk-cpp > aws-sdk-cpp only takes ~1m52s on a 4 core machine under 50% load > which does not justify the requirement on big parallel. > Tested with `nix-build -A nixVersions.nix_2_6.aws-sdk-cpp`. > I can finally build nix without requiring a big-parallel machine. - https://github.com/NixOS/nixpkgs/pull/227506 (Artturin) > nix: use [ ] instead null to empty requiredSystemFeatures > fixes 'error: value is null while a list was expected' with 'nixpkgs.hostPlatform.gcc.arch = "x86_64";' --- package.nix | 5 +---- packaging/dependencies.nix | 9 +++++++++ src/libstore/package.nix | 5 +---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package.nix b/package.nix index c3e565399..2499d4370 100644 --- a/package.nix +++ b/package.nix @@ -237,10 +237,7 @@ in { ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) - (aws-sdk-cpp.override { - apis = ["s3" "transfer"]; - customMemoryManagement = false; - }) + aws-sdk-cpp ; propagatedBuildInputs = [ diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index b77e1d14f..e0737593f 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -79,6 +79,15 @@ scope: { inherit stdenv versionSuffix; version = lib.fileContents ../.version + versionSuffix; + aws-sdk-cpp = (pkgs.aws-sdk-cpp.override { + apis = [ "s3" "transfer" ]; + customMemoryManagement = false; + }).overrideAttrs { + # only a stripped down version is built, which takes a lot less resources + # to build, so we don't need a "big-parallel" machine. + requiredSystemFeatures = [ ]; + }; + libseccomp = pkgs.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; src = pkgs.fetchurl { diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 02ff4194a..4582ba0d2 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -66,10 +66,7 @@ mkMesonDerivation (finalAttrs: { ] ++ lib.optional stdenv.hostPlatform.isLinux libseccomp # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) - (aws-sdk-cpp.override { - apis = ["s3" "transfer"]; - customMemoryManagement = false; - }) + aws-sdk-cpp ; propagatedBuildInputs = [ From 17b5d404457018ab1634fbbc38211607682ee37a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 02:39:55 +0200 Subject: [PATCH 719/910] package.nix: Empty build inputs if not doBuild --- package.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/package.nix b/package.nix index 2499d4370..a7c8923e8 100644 --- a/package.nix +++ b/package.nix @@ -216,7 +216,8 @@ in { ] ++ lib.optional stdenv.hostPlatform.isStatic unixtools.hexdump ; - buildInputs = lib.optionals doBuild [ + buildInputs = lib.optionals doBuild ( + [ brotli bzip2 curl @@ -238,12 +239,13 @@ in { # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) aws-sdk-cpp - ; + ); - propagatedBuildInputs = [ + propagatedBuildInputs = lib.optionals doBuild ([ boost nlohmann_json - ] ++ lib.optional enableGC boehmgc; + ] ++ lib.optional enableGC boehmgc + ); dontBuild = !attrs.doBuild; doCheck = attrs.doCheck; From f4464873f514a1aa05ca43178dfca6e1c978f0c9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 13:01:56 +0200 Subject: [PATCH 720/910] tests/nixos/remote-builds: Print hello world to stderr Trying to learn more about enigmatic spurious hang at https://hydra.nixos.org/build/267517233/nixlog/8 - builder1 seems to have started properly - ssh connection and session are established - ssh client doesn't exit or client.succeed does not return for some reason. Seeing the stdout on the console might give a tiny bit more info. --- tests/nixos/remote-builds.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/nixos/remote-builds.nix b/tests/nixos/remote-builds.nix index 1661203ec..8813832f5 100644 --- a/tests/nixos/remote-builds.nix +++ b/tests/nixos/remote-builds.nix @@ -104,7 +104,10 @@ in builder.succeed("mkdir -p -m 700 /root/.ssh") builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys") builder.wait_for_unit("sshd") - client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'") + client.succeed(f""" + ssh -o StrictHostKeyChecking=no {builder.name} \ + 'echo hello world on $(hostname)' >&2 + """) # Perform a build and check that it was performed on the builder. out = client.succeed( From 7c5a0b06a4a7908560bca273049e561c0f8193b2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 13:08:30 +0200 Subject: [PATCH 721/910] tests/nixos/remote-builds: Wait for multi-user This should make the test more robust, considering the strange hang in https://hydra.nixos.org/build/267517233/nixlog/8 `builder` seems to have reached `multi-user.target` before the SSH connection was established, but this seems to be coincidental. This does tell us that enforcing this has a minimal cost in terms of runtime. Waiting for `multi-user.target` on the client is honestly paranoid, but flaky tests are very bad for productivity. --- tests/nixos/remote-builds.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/nixos/remote-builds.nix b/tests/nixos/remote-builds.nix index 8813832f5..8ddf6ad02 100644 --- a/tests/nixos/remote-builds.nix +++ b/tests/nixos/remote-builds.nix @@ -104,6 +104,11 @@ in builder.succeed("mkdir -p -m 700 /root/.ssh") builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys") builder.wait_for_unit("sshd") + # Make sure the builder can handle our login correctly + builder.wait_for_unit("multi-user.target") + # Make sure there's no funny business on the client either + # (should not be necessary, but we have reason to be careful) + client.wait_for_unit("multi-user.target") client.succeed(f""" ssh -o StrictHostKeyChecking=no {builder.name} \ 'echo hello world on $(hostname)' >&2 From cc5b8cdc858467a759405ff8bba074bb9464b3b8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 13:42:03 +0200 Subject: [PATCH 722/910] buildNoTests: Disable unit tests This seems to have been the intent all along. The odd combination of unit tests, but no functional tests caused a build error where some data for the unit test was source-filtered out. Apparently. It's unclear to me why that happened, so I'm proposing this alternate "fix" to get the buildNoTests to pass. It would be nice to test more configurations, but this mode of building is on the way out anyway, so let's just make it pass and see what configurations make sense to test as part of the meson migration. --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 2384c2974..674c5ae8c 100644 --- a/flake.nix +++ b/flake.nix @@ -143,6 +143,7 @@ nix_noTests = final.nix.override { doInstallCheck = false; + doCheck = false; }; # See https://github.com/NixOS/nixpkgs/pull/214409 From aa2b1d10e29b3839760f02b1e30e70d70cb44ed2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 14:58:57 +0200 Subject: [PATCH 723/910] Update doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md --- .../10734-nix3-build-show-all-fod-errors-with-keep-going.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md index e4e2f797c..1d623e952 100644 --- a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md +++ b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md @@ -3,7 +3,7 @@ synopsis: "nix3-build: show all FOD errors with `--keep-going`" prs: 10734 --- -The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior of `nix build`. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. +The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) From e0198c513accf7f3b481ca71ff482840f294a1da Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sun, 28 Jul 2024 11:40:16 +0200 Subject: [PATCH 724/910] libcmd: do not compile editline helpers when building w/ readline The internal "completionCallback" and "listPossibleCallback" helpers are used only when building with editline; hence, do not build then when using readline, matching their usage in "ReadlineLikeInteracter::init()". --- src/libcmd/repl-interacter.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 76fe38780..187af46ea 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -35,6 +35,7 @@ void sigintHandler(int signo) static detail::ReplCompleterMixin * curRepl; // ugly +#ifndef USE_READLINE static char * completionCallback(char * s, int * match) { auto possible = curRepl->completePrefix(s); @@ -101,6 +102,7 @@ static int listPossibleCallback(char * s, char *** avp) return ac; } +#endif ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl) { From 1b47748e5a839c714864d51c9ec1ce86ea353456 Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Sun, 28 Jul 2024 13:08:33 +0300 Subject: [PATCH 725/910] libstore: return ENOTSUP for getxattr functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change updates the seccomp profile to return ENOTSUP for getxattr functions family. This reflects the behavior of filesystems that don’t support extended attributes (or have an option to disable them), e.g. ext2. The current behavior is confusing for some programs because we can read extended attributes, but only get to know that they are not supported when setting them. In addition to that, ACLs on Linux are implemented via extended attributes internally and if we don’t return ENOTSUP, acl library converts file mode to ACL. https://git.savannah.nongnu.org/cgit/acl.git/tree/libacl/acl_get_file.c?id=d9bb1759d4dad2f28a6dcc8c1742ff75d16dd10d#n69 --- src/libstore/unix/build/local-derivation-goal.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 364e9a148..d3482df17 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1702,10 +1702,13 @@ void setupSeccomp() throw SysError("unable to add seccomp rule"); } - /* Prevent builders from creating EAs or ACLs. Not all filesystems + /* Prevent builders from using EAs or ACLs. Not all filesystems support these, and they're not allowed in the Nix store because they're not representable in the NAR serialisation. */ - if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 || + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(getxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lgetxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fgetxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 || seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 || seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) throw SysError("unable to add seccomp rule"); From 933f2c086a96d8ce358cc787cf06c50a07263e9e Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sun, 28 Jul 2024 15:34:48 +0200 Subject: [PATCH 726/910] docs: fix link to building instructions (#11207) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab647e53b..021e54a3b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Full reference documentation can be found in the [Nix manual](https://nix.dev/re ## Building and developing -Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/development/building.html). +Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/building.html). ## Contributing From 96e06b2b06266684d5d74f5f809bdd82ebeb7b04 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sun, 28 Jul 2024 11:31:28 +0200 Subject: [PATCH 727/910] libutil: remove template id from constructors This is not allowed in C++20, and GCC 14 warns about it: ../src/libutil/ref.hh:26:20: warning: template-id not allowed for constructor in C++20 [-Wtemplate-id-cdtor] 26 | explicit ref(const std::shared_ptr & p) | ^ ../src/libutil/ref.hh:26:20: note: remove the '< >' ../src/libutil/ref.hh:33:21: warning: template-id not allowed for constructor in C++20 [-Wtemplate-id-cdtor] 33 | explicit ref(T * p) | ^ ../src/libutil/ref.hh:33:21: note: remove the '< >' --- src/libutil/ref.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 016fdd74a..3d0e64ab4 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -23,14 +23,14 @@ public: : p(r.p) { } - explicit ref(const std::shared_ptr & p) + explicit ref(const std::shared_ptr & p) : p(p) { if (!p) throw std::invalid_argument("null pointer cast to ref"); } - explicit ref(T * p) + explicit ref(T * p) : p(p) { if (!p) From c34077578ea48af7534f4dd2aaeef39cb64d9814 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sun, 28 Jul 2024 17:33:24 +0200 Subject: [PATCH 728/910] libutil: fix/improve includes in current-process.cc - move from a __linux__ block to a !_WIN32 block: this matches what the actual code does, using getrlimit() & setrlimit() in !_WIN32 blocks - drop , which is not portable, and it is not used --- src/libutil/current-process.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index 6ca48220d..c2b1ac500 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -15,13 +15,12 @@ #if __linux__ # include -# include # include "cgroup.hh" # include "namespaces.hh" #endif #ifndef _WIN32 -# include +# include #endif namespace nix { From e0012b97abb4c97ccf7fb20299d7b62dd906e89d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 14:26:25 +0200 Subject: [PATCH 729/910] Split tarball-specific logic from GitFileSystemObjectSink --- src/libfetchers/git-utils.cc | 33 ++++++++++++++++------------- src/libfetchers/git-utils.hh | 11 ++++++++++ src/libfetchers/github.cc | 5 +++-- src/libfetchers/tarball.cc | 6 ++++-- tests/unit/libfetchers/git-utils.cc | 2 +- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 032d8e0bd..c2d4fe6aa 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -486,6 +486,24 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this return narHash; } + + Hash dereferenceSingletonDirectory(const Hash & oid_) override + { + auto oid = hashToOID(oid_); + + /* If the root directory contains */ + auto _tree = lookupObject(*this, oid, GIT_OBJECT_TREE); + auto tree = (const git_tree *) &*_tree; + + if (git_tree_entrycount(tree) == 1) { + auto entry = git_tree_entry_byindex(tree, 0); + auto mode = git_tree_entry_filemode(entry); + if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) + oid = *git_tree_entry_id(entry); + } + + return toHash(oid); + } }; ref GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare) @@ -991,21 +1009,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink auto [oid, _name] = popBuilder(); - /* If the root directory contains a single entry that is a - directory or a non-executable regular file, return that as - the top-level object. We don't do this for executables - because they don't have a tree hash in the Git object - model. */ - auto _tree = lookupObject(*repo, oid, GIT_OBJECT_TREE); - auto tree = (const git_tree *) &*_tree; - - if (git_tree_entrycount(tree) == 1) { - auto entry = git_tree_entry_byindex(tree, 0); - auto mode = git_tree_entry_filemode(entry); - if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) - oid = *git_tree_entry_id(entry); - } - return toHash(oid); } }; diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 495916f62..644f22a07 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -98,6 +98,17 @@ struct GitRepo * serialisation. This is memoised on-disk. */ virtual Hash treeHashToNarHash(const Hash & treeHash) = 0; + + /** + * If the specified Git object is a directory with a single entry + * that is a directory or a non-executable regular file, return + * the ID of that object. + * + * Note: We don't do this for executable files because they don't + * have a tree hash in the Git object model that distinguishes + * them from non-executable files. + */ + virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0; }; ref getTarballCache(); diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 2968d2df2..a2ac9247a 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -255,11 +255,12 @@ struct GitArchiveInputScheme : InputScheme }); TarArchive archive { *source }; - auto parseSink = getTarballCache()->getFileSystemObjectSink(); + auto tarballCache = getTarballCache(); + auto parseSink = tarballCache->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); TarballInfo tarballInfo { - .treeHash = parseSink->sync(), + .treeHash = tarballCache->dereferenceSingletonDirectory(parseSink->sync()), .lastModified = lastModified }; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 55db3eafb..b09f628a4 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -164,7 +164,8 @@ DownloadTarballResult downloadTarball( TarArchive{path}; }) : TarArchive{*source}; - auto parseSink = getTarballCache()->getFileSystemObjectSink(); + auto tarballCache = getTarballCache(); + auto parseSink = tarballCache->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); auto res(_res->lock()); @@ -177,7 +178,8 @@ DownloadTarballResult downloadTarball( infoAttrs = cached->value; } else { infoAttrs.insert_or_assign("etag", res->etag); - infoAttrs.insert_or_assign("treeHash", parseSink->sync().gitRev()); + infoAttrs.insert_or_assign("treeHash", + tarballCache->dereferenceSingletonDirectory(parseSink->sync()).gitRev()); infoAttrs.insert_or_assign("lastModified", uint64_t(lastModified)); if (res->immutableUrl) infoAttrs.insert_or_assign("immutableUrl", *res->immutableUrl); diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index f0d38d50c..de5110cc3 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -77,7 +77,7 @@ TEST_F(GitUtilsTest, sink_basic) // sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello")); - auto result = sink->sync(); + auto result = repo->dereferenceSingletonDirectory(sink->sync()); auto accessor = repo->getAccessor(result, false); auto entries = accessor->readDirectory(CanonPath::root); ASSERT_EQ(entries.size(), 5); From 7c18b4d0600bfb8ad4159712025f1dcb790aa36f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 14:34:02 +0200 Subject: [PATCH 730/910] Don't dereference top-level regular files Since this yielded an empty directory as far back as Nix 2.3, we don't really need special handling for executables vs non-executables. --- src/libfetchers/git-utils.cc | 3 +-- src/libfetchers/git-utils.hh | 7 +------ tests/functional/tarball.sh | 10 +--------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index c2d4fe6aa..114aa4ec0 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -491,14 +491,13 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto oid = hashToOID(oid_); - /* If the root directory contains */ auto _tree = lookupObject(*this, oid, GIT_OBJECT_TREE); auto tree = (const git_tree *) &*_tree; if (git_tree_entrycount(tree) == 1) { auto entry = git_tree_entry_byindex(tree, 0); auto mode = git_tree_entry_filemode(entry); - if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) + if (mode == GIT_FILEMODE_TREE) oid = *git_tree_entry_id(entry); } diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 644f22a07..ca37f2e80 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -101,12 +101,7 @@ struct GitRepo /** * If the specified Git object is a directory with a single entry - * that is a directory or a non-executable regular file, return - * the ID of that object. - * - * Note: We don't do this for executable files because they don't - * have a tree hash in the Git object model that distinguishes - * them from non-executable files. + * that is a directory, return the ID of that object. */ virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0; }; diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index 6799831ce..4d8945625 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -92,15 +92,7 @@ path="$(nix flake prefetch --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .s [[ -d "$path/foo" ]] [[ -d "$path/bar" ]] -# Test a tarball that has a single non-executable regular file. -rm -rf "$TEST_ROOT/tar_root" -mkdir -p "$TEST_ROOT/tar_root" -echo bar > "$TEST_ROOT/tar_root/foo" -tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . -path="$(nix flake prefetch --refresh --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" -[[ $(cat "$path") = bar ]] - -# Test a tarball that has a single executable regular file. +# Test a tarball that has a single regular file. rm -rf "$TEST_ROOT/tar_root" mkdir -p "$TEST_ROOT/tar_root" echo bar > "$TEST_ROOT/tar_root/foo" From 71865dee2d2818a0955e784901de9c93a1f2baf7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 15:04:55 +0200 Subject: [PATCH 731/910] Fix fetchTarball docs --- src/libexpr/primops/fetchTree.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 9a3b81bc9..f79b6b7b8 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -560,10 +560,10 @@ static RegisterPrimOp primop_fetchTarball({ Download the specified URL, unpack it and return the path of the unpacked tree. The file must be a tape archive (`.tar`) compressed with `gzip`, `bzip2` or `xz`. If the tarball consists of a - single directory or non-executable file, then the top-level path - component of the files in the tarball is removed. The typical - use of the function is to obtain external Nix expression - dependencies, such as a particular version of Nixpkgs, e.g. + single directory, then the top-level path component of the files + in the tarball is removed. The typical use of the function is to + obtain external Nix expression dependencies, such as a + particular version of Nixpkgs, e.g. ```nix with import (fetchTarball https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz) {}; From e8bf2e74a5361033104670ab7148e6f8843073af Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 15:09:06 +0200 Subject: [PATCH 732/910] Add release note --- doc/manual/rl-next/tarball-fixes.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 doc/manual/rl-next/tarball-fixes.md diff --git a/doc/manual/rl-next/tarball-fixes.md b/doc/manual/rl-next/tarball-fixes.md new file mode 100644 index 000000000..c938e9db6 --- /dev/null +++ b/doc/manual/rl-next/tarball-fixes.md @@ -0,0 +1,9 @@ +--- +synopsis: "Improve handling of tarballs that don't consist of a single top-level directory" +prs: +- 11195 +--- + +In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. The new behaviour is that *only* if the tarball consists of a single directory, the top-level path component of the files in the tarball is removed (similar to `tar`'s `--strip-components=1`). + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) From a3171cec541a8df3be19e1a5a60e7cd4ae978ef9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 15:12:01 +0200 Subject: [PATCH 733/910] Update src/libfetchers/git-utils.hh Co-authored-by: Robert Hensing --- src/libfetchers/git-utils.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index ca37f2e80..915252868 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -102,6 +102,7 @@ struct GitRepo /** * If the specified Git object is a directory with a single entry * that is a directory, return the ID of that object. + * Otherwise, return the passed ID unchanged. */ virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0; }; From 12717325ccb5afeb686c8ddd02f9c9b623021f86 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 29 Jul 2024 12:16:05 -0400 Subject: [PATCH 734/910] Make sure we use `-isystem` with Meson on some deps Otherwise we get warnings on external code. --- src/libexpr/meson.build | 8 +++++++- src/libexpr/primops/fromTOML.cc | 3 --- src/libstore/meson.build | 3 ++- src/libstore/s3-binary-cache-store.cc | 3 --- src/libutil/meson.build | 1 + 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index fa90e7b41..4d8a38b43 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -32,6 +32,7 @@ subdir('build-utils-meson/threads') boost = dependency( 'boost', modules : ['container', 'context'], + include_type: 'system', ) # boost is a public dependency, but not a pkg-config dependency unfortunately, so we # put in `deps_other`. @@ -55,7 +56,12 @@ if bdw_gc.found() endif configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) -toml11 = dependency('toml11', version : '>=3.7.0', method : 'cmake') +toml11 = dependency( + 'toml11', + version : '>=3.7.0', + method : 'cmake', + include_type: 'system', +) deps_other += toml11 config_h = configure_file( diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 70755f9e0..b4f1df7a8 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -3,10 +3,7 @@ #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch-enum" #include -#pragma GCC diagnostic pop namespace nix { diff --git a/src/libstore/meson.build b/src/libstore/meson.build index cb8110f3f..50b15e15d 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -73,6 +73,7 @@ subdir('build-utils-meson/threads') boost = dependency( 'boost', modules : ['container'], + include_type: 'system', ) # boost is a public dependency, but not a pkg-config dependency unfortunately, so we # put in `deps_other`. @@ -113,7 +114,7 @@ if aws_s3.found() '-laws-cpp-sdk-core', '-laws-crt-cpp', ], - ) + ).as_system('system') endif deps_other += aws_s3 diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 1082657bb..92ab47cd6 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -10,8 +10,6 @@ #include "compression.hh" #include "filetransfer.hh" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch-enum" #include #include #include @@ -27,7 +25,6 @@ #include #include #include -#pragma GCC diagnostic pop using namespace Aws::Transfer; diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 04c778c31..8552c4c9d 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -62,6 +62,7 @@ endif boost = dependency( 'boost', modules : ['context', 'coroutine'], + include_type: 'system', ) # boost is a public dependency, but not a pkg-config dependency unfortunately, so we # put in `deps_other`. From f380becffacaabcfbace6b00e640cdc7bd0bbf64 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 29 Jul 2024 23:25:31 +0200 Subject: [PATCH 735/910] Credit all contributors in release notes --- .../data/release-credits-email-to-handle.json | 51 +++++ .../data/release-credits-handle-to-name.json | 44 +++++ maintainers/release-credits | 176 ++++++++++++++++++ maintainers/release-notes | 7 + maintainers/release-process.md | 4 + 5 files changed, 282 insertions(+) create mode 100644 maintainers/data/release-credits-email-to-handle.json create mode 100644 maintainers/data/release-credits-handle-to-name.json create mode 100755 maintainers/release-credits diff --git a/maintainers/data/release-credits-email-to-handle.json b/maintainers/data/release-credits-email-to-handle.json new file mode 100644 index 000000000..573ec2b31 --- /dev/null +++ b/maintainers/data/release-credits-email-to-handle.json @@ -0,0 +1,51 @@ +{ + "bogus": "bogus", + "edolstra@gmail.com": "edolstra", + "roberth@users.noreply.github.com": "roberth", + "toscano.pino@tiscali.it": "pinotree", + "valentin@gagarin.work": "fricklerhandwerk", + "mr.trubach@icloud.com": "tie", + "robert@roberthensing.nl": "roberth", + "lix@jade.fyi": "lf-", + "cole.e.helbling@outlook.com": "cole-h", + "joerg@thalheim.io": "Mic92", + "John.Ericson@Obsidian.Systems": "Ericson2314", + "ryan.hendrickson@alum.mit.edu": "rhendric", + "67135060+poweredbypie@users.noreply.github.com": "poweredbypie", + "detroyejr@outlook.com": "detroyejr", + "silvan.mosberger@tweag.io": "infinisil", + "vcs@emily.moe": "emilazy", + "farid.m.zakaria@gmail.com": "fzakaria", + "22859658+RTUnreal@users.noreply.github.com": "RTUnreal", + "me@las.rs": "L-as", + "philip.taron@gmail.com": "philiptaron", + "root@goldstein.rs": "GoldsteinE", + "tomberek@users.noreply.github.com": "tomberek", + "lexi.mattick@neuralink.com": "kognise", + "andrew@johnandrewmarshall.com": "amarshall", + "contact@romain-neil.fr": "romain-neil", + "Mic92@users.noreply.github.com": "Mic92", + "valentin.gagarin@tweag.io": "fricklerhandwerk", + "siddhantk232@gmail.com": "siddhantk232", + "kn@openbsd.org": "klemensn", + "slyich@gmail.com": "trofi", + "theophane.hufschmitt@tweag.io": "thufschmitt", + "alicebob@lijzij.de": "alicebob", + "winter@winter.cafe": "winterqt", + "brian@brianmckenna.org": "puffnfresh", + "git@haenoe.party": "haenoe", + "peshogo@gmail.com": "pineapplehunter", + "poweredbypie@users.noreply.github.com": "poweredbypie", + "arthur200126@gmail.com": "Artoria2e5", + "tomberek@gmail.com": "tomberek", + "jaredbaur@fastmail.com": "jmbaur", + "andreas@rammhold.de": "andir", + "hamirmahal@gmail.com": "hamirmahal", + "git@JohnEricson.me": "Ericson2314", + "8763518+SkamDart@users.noreply.github.com": "SkamDart", + "kirillrdy@gmail.com": "kirillrdy", + "pennae@lix.systems": "pennae", + "delroth@gmail.com": "delroth", + "enno@nerdworks.de": "elohmeier", + "mjbauer95@gmail.com": "matthewbauer" +} \ No newline at end of file diff --git a/maintainers/data/release-credits-handle-to-name.json b/maintainers/data/release-credits-handle-to-name.json new file mode 100644 index 000000000..d68311dde --- /dev/null +++ b/maintainers/data/release-credits-handle-to-name.json @@ -0,0 +1,44 @@ +{ + "fzakaria": "Farid Zakaria", + "kognise": "Lexi Mattick", + "L-as": "Las Safin", + "haenoe": "HaeNoe", + "andir": "Andreas Rammhold", + "matthewbauer": "Matthew Bauer", + "emilazy": "Emily", + "pineapplehunter": "Shogo Takata", + "RTUnreal": null, + "jmbaur": "Jared Baur", + "Ericson2314": "John Ericson", + "pinotree": "Pino Toscano", + "tie": "Ivan Trubach", + "poweredbypie": null, + "fricklerhandwerk": "Valentin Gagarin", + "Mic92": "J\u00f6rg Thalheim", + "alicebob": "Harmen", + "elohmeier": "Enno Richter", + "delroth": "Pierre Bourdon", + "kirillrdy": null, + "thufschmitt": "Th\u00e9ophane Hufschmitt", + "detroyejr": "Jonathan De Troye", + "klemensn": "Klemens Nanni", + "tomberek": null, + "rhendric": "Ryan Hendrickson", + "philiptaron": "Philip Taron", + "puffnfresh": "Brian McKenna", + "lf-": "jade", + "romain-neil": "Romain Neil", + "hamirmahal": "Hamir Mahal", + "edolstra": "Eelco Dolstra", + "Artoria2e5": "Mingye Wang", + "SkamDart": "Cameron", + "roberth": "Robert Hensing", + "amarshall": "Andrew Marshall", + "trofi": "Sergei Trofimovich", + "cole-h": "Cole Helbling", + "infinisil": "Silvan Mosberger", + "siddhantk232": "Siddhant Kumar", + "winterqt": "Winter", + "GoldsteinE": "Max \u201cGoldstein\u201d Siling", + "pennae": null +} \ No newline at end of file diff --git a/maintainers/release-credits b/maintainers/release-credits new file mode 100755 index 000000000..8350c4e2a --- /dev/null +++ b/maintainers/release-credits @@ -0,0 +1,176 @@ +#!/usr/bin/env nix +#!nix develop --impure --expr +#!nix `` +#!nix let flake = builtins.getFlake ("git+file://" + toString ../.); +#!nix pkgs = flake.inputs.nixpkgs.legacyPackages.${builtins.currentSystem}; +#!nix in pkgs.mkShell { nativeBuildInputs = [ +#!nix (pkgs.python3.withPackages (ps: with ps; [ requests ])) +#!nix ]; } +#!nix `` --command python3 + +# This script lists out the contributors for a given release. +# It must be run from the root of the Nix repository. + +import os +import sys +import json +import requests + +github_token = os.environ.get("GITHUB_TOKEN") +if not github_token: + print("GITHUB_TOKEN is not set. If you hit the rate limit, set it", file=sys.stderr) + # Might be ok, as we have a cache. + # raise ValueError("GITHUB_TOKEN must be set") + +# 1. Read the current version in .version +version = os.environ.get("VERSION") +if not version: + version = open(".version").read().strip() + +print(f"Generating release credits for Nix {version}", file=sys.stderr) + +# 2. Compute previous version +vcomponents = version.split(".") +if len(vcomponents) >= 2: + prev_version = f"{vcomponents[0]}.{int(vcomponents[1])-1}.0" +else: + raise ValueError(".version must have at least two components") + +# For unreleased versions +endref = "HEAD" +# For older releases +# endref = version + +# 2. Find the merge base between the current version and the previous version +mergeBase = os.popen(f"git merge-base {prev_version} {endref}").read().strip() +print(f"Merge base between {prev_version} and {endref} is {mergeBase}", file=sys.stderr) + +# 3. Find the date of the merge base +mergeBaseDate = os.popen(f"git show -s --format=%ci {mergeBase}").read().strip()[0:10] +print(f"Merge base date is {mergeBaseDate}", file=sys.stderr) + +# 4. Get the commits between the merge base and the current version + +def get_commits(): + raw = os.popen(f"git log --pretty=format:'%H\t%an\t%ae' {mergeBase}..{endref}").read().strip() + lines = raw.split("\n") + return [ { "hash": items[0], "author": items[1], "email": items[2] } + for line in lines + for items in (line.split("\t"),) + ] + +def commits_to_first_commit_by_email(commits): + by_email = dict() + for commit in commits: + email = commit["email"] + if email not in by_email: + by_email[email] = commit + return by_email + + +samples = commits_to_first_commit_by_email(get_commits()) + +# For quick testing, only pick two samples from the dict +# samples = dict(list(samples.items())[:2]) + +# Query the GitHub API to get handle +def get_github_commit(commit): + url = f"https://api.github.com/repos/NixOS/nix/commits/{commit['hash']}" + headers = {'Authorization': f'token {github_token}'} + response = requests.get(url, headers=headers) + response.raise_for_status() + return response.json() + +class Cache: + def __init__(self, filename, require = True): + self.filename = filename + try: + with open(filename, "r") as f: + self.values = json.load(f) + except FileNotFoundError: + if require: + raise + self.values = dict() + def save(self): + with open(self.filename, "w") as f: + json.dump(self.values, f, indent=4) + print(f"Saved cache to {self.filename}", file=sys.stderr) + +# The email to handle cache maps email addresses to either +# - a handle (string) +# - None (if no handle was found) +email_to_handle_cache = Cache("maintainers/data/release-credits-email-to-handle.json") + +handles = set() +emails = dict() + +for sample in samples: + s = samples[sample] + email = s["email"] + if not email in email_to_handle_cache.values: + print(f"Querying GitHub API for {s['hash']}, to get handle for {s['email']}") + ghc = get_github_commit(samples[sample]) + gha = ghc["author"] + if gha and gha["login"]: + handle = gha["login"] + print(f"Handle: {handle}") + email_to_handle_cache.values[email] = handle + else: + print(f"Found no handle for {s['email']}") + email_to_handle_cache.values[email] = None + handle = email_to_handle_cache.values[email] + if handle is not None: + handles.add(handle) + else: + emails[email] = s["author"] + +# print(email_to_handle_cache.values) + +email_to_handle_cache.save() + +handle_to_name_cache = Cache("maintainers/data/release-credits-handle-to-name.json") + +print(f"Found {len(handles)} handles", file=sys.stderr) + +for handle in handles: + if not handle in handle_to_name_cache.values: + print(f"Querying GitHub API for {handle}, to get name", file=sys.stderr) + url = f"https://api.github.com/users/{handle}" + headers = {'Authorization': f'token {github_token}'} + response = requests.get(url, headers=headers) + response.raise_for_status() + user = response.json() + name = user["name"] + print(f"Name: {name}", file=sys.stderr) + handle_to_name_cache.values[handle] = name + +handle_to_name_cache.save() + +entries = list() + +for handle in handles: + name = handle_to_name_cache.values[handle] + if name is None: + # This way it looks more regular + name = handle + entries += [ f"- {name} [**(@{handle})**](https://github.com/{handle})" ] + +def shuffle(entries): + salt = os.urandom(16) + return sorted(entries, key=lambda x: hash((x, salt))) + +# Fair ordering is undecidable +entries = shuffle(entries) + +# For a sanity check, we could sort the entries by handle instead. +# entries = sorted(entries) + +print("") +print(f"This release was made possible by the following {len(entries)} contributors:") +print("") + +for entry in entries: + print(entry) + +for email in emails: + print(f"- {emails[email]}") diff --git a/maintainers/release-notes b/maintainers/release-notes index 0fca5abf2..b7b5731c8 100755 --- a/maintainers/release-notes +++ b/maintainers/release-notes @@ -151,6 +151,13 @@ section_title="Release $version_full ($DATE)" echo "# $section_title" echo changelog-d doc/manual/rl-next | sed -e 's/ *$//' + + if ! $IS_PATCH; then + echo + echo "# Contributors" + echo + VERSION=$version_full ./maintainers/release-credits + fi ) | tee -a $file log "Wrote $file" diff --git a/maintainers/release-process.md b/maintainers/release-process.md index da6886ea9..7a2b3c0a7 100644 --- a/maintainers/release-process.md +++ b/maintainers/release-process.md @@ -39,6 +39,10 @@ release: * Proof-read / edit / rearrange the release notes if needed. Breaking changes and highlights should go to the top. +* Run `maintainers/release-credits` to make sure the credits script works + and produces a sensible output. Some emails might not automatically map to + a GitHub handle. + * Push. ```console From 850e4779d6285feffd78dbf4c12eb203a3f10637 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Jul 2024 00:15:49 +0200 Subject: [PATCH 736/910] release-credits: Remove bots --- maintainers/release-credits | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/maintainers/release-credits b/maintainers/release-credits index 8350c4e2a..18641a07b 100755 --- a/maintainers/release-credits +++ b/maintainers/release-credits @@ -148,12 +148,19 @@ handle_to_name_cache.save() entries = list() +def is_bot(handle): + return handle.endswith("[bot]") + for handle in handles: name = handle_to_name_cache.values[handle] if name is None: # This way it looks more regular name = handle - entries += [ f"- {name} [**(@{handle})**](https://github.com/{handle})" ] + + # Note to bots: I'm only doing this because it would distract humans, and + # therefore delay your creation. I'm sorry; it's for your benefit. + if not is_bot(handle): + entries += [ f"- {name} [**(@{handle})**](https://github.com/{handle})" ] def shuffle(entries): salt = os.urandom(16) From d7f46cf28ea521eef0530a1a41af56a34dd89278 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Tue, 30 Jul 2024 05:29:32 +0200 Subject: [PATCH 737/910] makefiles: recognize GNU/Hurd Set HOST_HURD & HOST_UNIX for GNU/Hurd in the makefile-based build system; the latter variable is important as it will include all the commit Unix bits. --- mk/platform.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mk/platform.mk b/mk/platform.mk index fe960dedf..22c114a20 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -29,4 +29,8 @@ ifdef HOST_OS HOST_SOLARIS = 1 HOST_UNIX = 1 endif + ifeq ($(HOST_KERNEL), gnu) + HOST_HURD = 1 + HOST_UNIX = 1 + endif endif From 7442f4a16143fe69a71949f1edbbbcab6862ed23 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Tue, 30 Jul 2024 05:31:42 +0200 Subject: [PATCH 738/910] libutil: use /proc/self/exe on Hurd as well Rely on the Linux-compatible procfs available on the Hurd to get the path of the current executable. --- src/libutil/current-process.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index c2b1ac500..0bc46d746 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -137,7 +137,7 @@ std::optional getSelfExe() { static auto cached = []() -> std::optional { - #if __linux__ + #if __linux__ || __GNU__ return readLink("/proc/self/exe"); #elif __APPLE__ char buf[1024]; From a1ccf6061396f398526bb99518cfd3b9c432aeb1 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Tue, 30 Jul 2024 05:34:34 +0200 Subject: [PATCH 739/910] tests: define fallback PATH_MAX Few filesystem-related tests rely on PATH_MAX for buffers, and PATH_MAX is optional in POSIX (and not available on the Hurd). To make them build and pass, provide a fallback definition of PATH_MAX in case not available. Ideally speaking, the tests ought to not unconditionally rely on PATH_MAX, do alternative strategies (e.g. dynamically allocate buffers, expand them as needed, etc); OTOH this is test code, so it would be more work that what it would be worth, so IMHO the define fallback is good enough. --- tests/unit/libutil/tests.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index 8a3ca8561..2b73d323b 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -17,6 +17,10 @@ # define FS_ROOT FS_SEP #endif +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + namespace nix { /* ----------- tests for util.hh ------------------------------------------------*/ From ee86e7f361c55c8c7dc2e45c3868802af249aeff Mon Sep 17 00:00:00 2001 From: Corbin Simpson Date: Tue, 30 Jul 2024 05:51:47 -0700 Subject: [PATCH 740/910] doc/command-ref/nix-shell: Shebangs can occur anywhere (#11202) Co-authored-by: Valentin Gagarin --- doc/manual/src/command-ref/nix-shell.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index ddec30f5b..69a711bd5 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -297,3 +297,8 @@ with import {}; runCommand "dummy" { buildInputs = [ python pythonPackages.prettytable ]; } "" ``` + +The script's file name is passed as the first argument to the interpreter specified by the `-i` flag. + +Aside from the very first line, which is a directive to the operating system, the additional `#! nix-shell` lines do not need to be at the beginning of the file. +This allows wrapping them in block comments for languages where `#` does not start a comment, such as ECMAScript, Erlang, PHP, or Ruby. From f011cfd28dc359bb786990d94b63c2c482e1b8da Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Jul 2024 17:35:46 +0200 Subject: [PATCH 741/910] maintainers/release-*: Add mode line This lets various tools figure out the language more easily. --- maintainers/release-credits | 1 + maintainers/release-notes | 1 + 2 files changed, 2 insertions(+) diff --git a/maintainers/release-credits b/maintainers/release-credits index 18641a07b..7a5c87d7d 100755 --- a/maintainers/release-credits +++ b/maintainers/release-credits @@ -1,4 +1,5 @@ #!/usr/bin/env nix +# vim: set filetype=python: #!nix develop --impure --expr #!nix `` #!nix let flake = builtins.getFlake ("git+file://" + toString ../.); diff --git a/maintainers/release-notes b/maintainers/release-notes index b7b5731c8..c0c4ee734 100755 --- a/maintainers/release-notes +++ b/maintainers/release-notes @@ -1,4 +1,5 @@ #!/usr/bin/env nix +# vim: set filetype=bash: #!nix shell .#changelog-d --command bash # --- CONFIGURATION --- From ef8021744855754a12a4b0d343d902b2a8a29d8c Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 29 Jul 2024 10:42:43 -0400 Subject: [PATCH 742/910] fix: add flake headers --- src/libflake/flake/nix-flake.pc.in | 10 ++++++++++ src/libflake/local.mk | 5 +++++ src/nix/meson.build | 1 + 3 files changed, 16 insertions(+) create mode 100644 src/libflake/flake/nix-flake.pc.in diff --git a/src/libflake/flake/nix-flake.pc.in b/src/libflake/flake/nix-flake.pc.in new file mode 100644 index 000000000..10c52f5e9 --- /dev/null +++ b/src/libflake/flake/nix-flake.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix +Description: Nix Package Manager +Version: @PACKAGE_VERSION@ +Requires: nix-util nix-store nix-expr +Libs: -L${libdir} -lnixflake +Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libflake/local.mk b/src/libflake/local.mk index 2cceda2bf..5e604ef3a 100644 --- a/src/libflake/local.mk +++ b/src/libflake/local.mk @@ -15,3 +15,8 @@ libflake_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetcher libflake_LDFLAGS += $(THREAD_LDFLAGS) libflake_LIBS = libutil libstore libfetchers libexpr + +$(eval $(call install-file-in, $(buildprefix)$(d)/flake/nix-flake.pc, $(libdir)/pkgconfig, 0644)) + +$(foreach i, $(wildcard src/libflake/flake/*.hh), \ + $(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644))) diff --git a/src/nix/meson.build b/src/nix/meson.build index 53bb083a9..7405548de 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -20,6 +20,7 @@ deps_private_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-expr'), + dependency('nix-flake'), dependency('nix-fetchers'), dependency('nix-main'), dependency('nix-cmd'), From dd75711895057328b00f92353730e7f873eafd52 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Fri, 12 Jul 2024 14:56:30 +0200 Subject: [PATCH 743/910] Use std::strong_ordering for version comparison The actual motive here is the avoidance of integer overflow if we were to make these use checked NixInts and retain the subtraction. However, the actual *intent* of this code is a three-way comparison, which can be done with operator<=>, so we should just do *that* instead. Change-Id: I7f9a7da1f3176424b528af6d1b4f1591e4ab26bf --- src/libexpr/primops.cc | 3 ++- src/libstore/names.cc | 8 ++++---- src/libstore/names.hh | 2 +- src/nix-env/nix-env.cc | 22 +++++++++++----------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0b3b19b57..a23583f34 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4446,7 +4446,8 @@ static void prim_compareVersions(EvalState & state, const PosIdx pos, Value * * { auto version1 = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.compareVersions"); auto version2 = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.compareVersions"); - v.mkInt(compareVersions(version1, version2)); + auto result = compareVersions(version1, version2); + v.mkInt(result < 0 ? -1 : result > 0 ? 1 : 0); } static RegisterPrimOp primop_compareVersions({ diff --git a/src/libstore/names.cc b/src/libstore/names.cc index 277aabf0f..c0e1b1022 100644 --- a/src/libstore/names.cc +++ b/src/libstore/names.cc @@ -94,7 +94,7 @@ static bool componentsLT(const std::string_view c1, const std::string_view c2) } -int compareVersions(const std::string_view v1, const std::string_view v2) +std::strong_ordering compareVersions(const std::string_view v1, const std::string_view v2) { auto p1 = v1.begin(); auto p2 = v2.begin(); @@ -102,11 +102,11 @@ int compareVersions(const std::string_view v1, const std::string_view v2) while (p1 != v1.end() || p2 != v2.end()) { auto c1 = nextComponent(p1, v1.end()); auto c2 = nextComponent(p2, v2.end()); - if (componentsLT(c1, c2)) return -1; - else if (componentsLT(c2, c1)) return 1; + if (componentsLT(c1, c2)) return std::strong_ordering::less; + else if (componentsLT(c2, c1)) return std::strong_ordering::greater; } - return 0; + return std::strong_ordering::equal; } diff --git a/src/libstore/names.hh b/src/libstore/names.hh index d82b99bb4..a6909d545 100644 --- a/src/libstore/names.hh +++ b/src/libstore/names.hh @@ -30,7 +30,7 @@ typedef std::list DrvNames; std::string_view nextComponent(std::string_view::const_iterator & p, const std::string_view::const_iterator end); -int compareVersions(const std::string_view v1, const std::string_view v2); +std::strong_ordering compareVersions(const std::string_view v1, const std::string_view v2); DrvNames drvNamesFromArgs(const Strings & opArgs); } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 40c200542..ba2baccee 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -204,15 +204,15 @@ static void loadDerivations(EvalState & state, const SourcePath & nixExprPath, } -static long getPriority(EvalState & state, PackageInfo & drv) +static NixInt getPriority(EvalState & state, PackageInfo & drv) { - return drv.queryMetaInt("priority", 0); + return drv.queryMetaInt("priority", NixInt(0)); } -static long comparePriorities(EvalState & state, PackageInfo & drv1, PackageInfo & drv2) +static std::strong_ordering comparePriorities(EvalState & state, PackageInfo & drv1, PackageInfo & drv2) { - return getPriority(state, drv2) - getPriority(state, drv1); + return getPriority(state, drv2) <=> getPriority(state, drv1); } @@ -280,7 +280,7 @@ std::vector pickNewestOnly(EvalState & state, std::vector matches) auto & oneDrv = match.packageInfo; const auto drvName = DrvName { oneDrv.queryName() }; - long comparison = 1; + std::strong_ordering comparison = std::strong_ordering::greater; const auto itOther = newest.find(drvName.name); @@ -288,9 +288,9 @@ std::vector pickNewestOnly(EvalState & state, std::vector matches) auto & newestDrv = itOther->second.packageInfo; comparison = - oneDrv.querySystem() == newestDrv.querySystem() ? 0 : - oneDrv.querySystem() == settings.thisSystem ? 1 : - newestDrv.querySystem() == settings.thisSystem ? -1 : 0; + oneDrv.querySystem() == newestDrv.querySystem() ? std::strong_ordering::equal : + oneDrv.querySystem() == settings.thisSystem ? std::strong_ordering::greater : + newestDrv.querySystem() == settings.thisSystem ? std::strong_ordering::less : std::strong_ordering::equal; if (comparison == 0) comparison = comparePriorities(state, oneDrv, newestDrv); if (comparison == 0) @@ -625,13 +625,13 @@ static void upgradeDerivations(Globals & globals, continue; DrvName newName(j->queryName()); if (newName.name == drvName.name) { - int d = compareVersions(drvName.version, newName.version); + std::strong_ordering d = compareVersions(drvName.version, newName.version); if ((upgradeType == utLt && d < 0) || (upgradeType == utLeq && d <= 0) || (upgradeType == utEq && d == 0) || upgradeType == utAlways) { - long d2 = -1; + std::strong_ordering d2 = std::strong_ordering::less; if (bestElem != availElems.end()) { d2 = comparePriorities(*globals.state, *bestElem, *j); if (d2 == 0) d2 = compareVersions(bestVersion, newName.version); @@ -902,7 +902,7 @@ static VersionDiff compareVersionAgainstSet( for (auto & i : elems) { DrvName name2(i.queryName()); if (name.name == name2.name) { - int d = compareVersions(name.version, name2.version); + std::strong_ordering d = compareVersions(name.version, name2.version); if (d < 0) { diff = cvGreater; version = name2.version; From e28cb67d41643bf2073e46149e6f5d10d1fd0ef5 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Fri, 5 Jul 2024 15:52:55 +0200 Subject: [PATCH 744/910] libutil: add checked arithmetic tools This is in preparation for adding checked arithmetic to the evaluator. Change-Id: I6e115ce8f5411feda1706624977a4dcd5efd4d13 --- src/libutil/checked-arithmetic.hh | 182 ++++++++++++++++++ src/libutil/meson.build | 1 + .../tests/gtest-with-params.hh | 54 ++++++ tests/unit/libutil/checked-arithmetic.cc | 158 +++++++++++++++ 4 files changed, 395 insertions(+) create mode 100644 src/libutil/checked-arithmetic.hh create mode 100644 tests/unit/libutil-support/tests/gtest-with-params.hh create mode 100644 tests/unit/libutil/checked-arithmetic.cc diff --git a/src/libutil/checked-arithmetic.hh b/src/libutil/checked-arithmetic.hh new file mode 100644 index 000000000..55d6ad205 --- /dev/null +++ b/src/libutil/checked-arithmetic.hh @@ -0,0 +1,182 @@ +#pragma once +/** + * @file Checked arithmetic with classes that make it hard to accidentally make something an unchecked operation. + */ + +#include +#include // IWYU pragma: keep +#include +#include +#include +#include +#include + +namespace nix::checked { + +class DivideByZero : std::exception +{}; + +/** + * Numeric value enforcing checked arithmetic. Performing mathematical operations on such values will return a Result + * type which needs to be checked. + */ +template +struct Checked +{ + using Inner = T; + + // TODO: this must be a "trivial default constructor", which means it + // cannot set the value to NOT DO UB on uninit. + T value; + + Checked() = default; + explicit Checked(T const value) + : value{value} + { + } + Checked(Checked const & other) = default; + Checked(Checked && other) = default; + Checked & operator=(Checked const & other) = default; + + std::strong_ordering operator<=>(Checked const & other) const = default; + std::strong_ordering operator<=>(T const & other) const + { + return value <=> other; + } + + explicit operator T() const + { + return value; + } + + enum class OverflowKind { + NoOverflow, + Overflow, + DivByZero, + }; + + class Result + { + T value; + OverflowKind overflowed_; + + public: + Result(T value, bool overflowed) + : value{value} + , overflowed_{overflowed ? OverflowKind::Overflow : OverflowKind::NoOverflow} + { + } + Result(T value, OverflowKind overflowed) + : value{value} + , overflowed_{overflowed} + { + } + + bool operator==(Result other) const + { + return value == other.value && overflowed_ == other.overflowed_; + } + + std::optional valueChecked() const + { + if (overflowed_ != OverflowKind::NoOverflow) { + return std::nullopt; + } else { + return value; + } + } + + /** + * Returns the result as if the arithmetic were performed as wrapping arithmetic. + * + * \throws DivideByZero if the operation was a divide by zero. + */ + T valueWrapping() const + { + if (overflowed_ == OverflowKind::DivByZero) { + throw DivideByZero{}; + } + return value; + } + + bool overflowed() const + { + return overflowed_ == OverflowKind::Overflow; + } + + bool divideByZero() const + { + return overflowed_ == OverflowKind::DivByZero; + } + }; + + Result operator+(Checked const other) const + { + return (*this) + other.value; + } + Result operator+(T const other) const + { + T result; + bool overflowed = __builtin_add_overflow(value, other, &result); + return Result{result, overflowed}; + } + + Result operator-(Checked const other) const + { + return (*this) - other.value; + } + Result operator-(T const other) const + { + T result; + bool overflowed = __builtin_sub_overflow(value, other, &result); + return Result{result, overflowed}; + } + + Result operator*(Checked const other) const + { + return (*this) * other.value; + } + Result operator*(T const other) const + { + T result; + bool overflowed = __builtin_mul_overflow(value, other, &result); + return Result{result, overflowed}; + } + + Result operator/(Checked const other) const + { + return (*this) / other.value; + } + /** + * Performs a checked division. + * + * If the right hand side is zero, the result is marked as a DivByZero and + * valueWrapping will throw. + */ + Result operator/(T const other) const + { + constexpr T const minV = std::numeric_limits::min(); + + // It's only possible to overflow with signed division since doing so + // requires crossing the two's complement limits by MIN / -1 (since + // two's complement has one more in range in the negative direction + // than in the positive one). + if (std::is_signed() && (value == minV && other == -1)) { + return Result{minV, true}; + } else if (other == 0) { + return Result{0, OverflowKind::DivByZero}; + } else { + T result = value / other; + return Result{result, false}; + } + } +}; + +template +std::ostream & operator<<(std::ostream & ios, Checked v) +{ + ios << v.value; + return ios; +} + +} diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 8552c4c9d..e0299177f 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -171,6 +171,7 @@ headers = [config_h] + files( 'args/root.hh', 'callback.hh', 'canon-path.hh', + 'checked-arithmetic.hh', 'chunked-vector.hh', 'closure.hh', 'comparator.hh', diff --git a/tests/unit/libutil-support/tests/gtest-with-params.hh b/tests/unit/libutil-support/tests/gtest-with-params.hh new file mode 100644 index 000000000..323a083fe --- /dev/null +++ b/tests/unit/libutil-support/tests/gtest-with-params.hh @@ -0,0 +1,54 @@ +#pragma once +// SPDX-FileCopyrightText: 2014 Emil Eriksson +// +// SPDX-License-Identifier: BSD-2-Clause +// +// The lion's share of this code is copy pasted directly out of RapidCheck +// headers, so the copyright is set accordingly. +/** + * @file Implements the ability to run a RapidCheck test under gtest with changed + * test parameters such as the number of tests to run. This is useful for + * running very large numbers of the extremely cheap property tests. + */ + +#include +#include +#include + +namespace rc::detail { + +using MakeTestParams = TestParams (*)(); + +template +void checkGTestWith(Testable && testable, MakeTestParams makeTestParams) +{ + const auto testInfo = ::testing::UnitTest::GetInstance()->current_test_info(); + detail::TestMetadata metadata; + metadata.id = std::string(testInfo->test_case_name()) + "/" + std::string(testInfo->name()); + metadata.description = std::string(testInfo->name()); + + const auto result = checkTestable(std::forward(testable), metadata, makeTestParams()); + + if (result.template is()) { + const auto success = result.template get(); + if (!success.distribution.empty()) { + printResultMessage(result, std::cout); + std::cout << std::endl; + } + } else { + std::ostringstream ss; + printResultMessage(result, ss); + FAIL() << ss.str() << std::endl; + } +} +} + +#define RC_GTEST_PROP_WITH_PARAMS(TestCase, Name, MakeParams, ArgList) \ + void rapidCheck_propImpl_##TestCase##_##Name ArgList; \ + \ + TEST(TestCase, Name) \ + { \ + ::rc::detail::checkGTestWith(&rapidCheck_propImpl_##TestCase##_##Name, MakeParams); \ + } \ + \ + void rapidCheck_propImpl_##TestCase##_##Name ArgList diff --git a/tests/unit/libutil/checked-arithmetic.cc b/tests/unit/libutil/checked-arithmetic.cc new file mode 100644 index 000000000..75018660d --- /dev/null +++ b/tests/unit/libutil/checked-arithmetic.cc @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "tests/gtest-with-params.hh" + +namespace rc { +using namespace nix; + +template +struct Arbitrary> +{ + static Gen> arbitrary() + { + return gen::arbitrary(); + } +}; + +} + +namespace nix::checked { + +// Pointer to member function! Mildly gross. +template +using Oper = Checked::Result (Checked::*)(T const other) const; + +template +using ReferenceOper = T (*)(T a, T b); + +/** + * Checks that performing an operation that overflows into an inaccurate result + * has the desired behaviour. + * + * TBig is a type large enough to represent all results of TSmall operations. + */ +template +void checkType(TSmall a_, TSmall b, Oper oper, ReferenceOper reference) +{ + // Sufficient to fit all values + TBig referenceResult = reference(a_, b); + constexpr const TSmall minV = std::numeric_limits::min(); + constexpr const TSmall maxV = std::numeric_limits::max(); + + Checked a{a_}; + auto result = (a.*(oper))(b); + + // Just truncate it to get the in-range result + RC_ASSERT(result.valueWrapping() == static_cast(referenceResult)); + + if (referenceResult > maxV || referenceResult < minV) { + RC_ASSERT(result.overflowed()); + RC_ASSERT(!result.valueChecked().has_value()); + } else { + RC_ASSERT(!result.overflowed()); + RC_ASSERT(result.valueChecked().has_value()); + RC_ASSERT(*result.valueChecked() == referenceResult); + } +} + +/** + * Checks that performing an operation that overflows into an inaccurate result + * has the desired behaviour. + * + * TBig is a type large enough to represent all results of TSmall operations. + */ +template +void checkDivision(TSmall a_, TSmall b) +{ + // Sufficient to fit all values + constexpr const TSmall minV = std::numeric_limits::min(); + + Checked a{a_}; + auto result = a / b; + + if (std::is_signed() && a_ == minV && b == -1) { + // This is the only possible overflow condition + RC_ASSERT(result.valueWrapping() == minV); + RC_ASSERT(result.overflowed()); + } else if (b == 0) { + RC_ASSERT(result.divideByZero()); + RC_ASSERT_THROWS_AS(result.valueWrapping(), nix::checked::DivideByZero); + RC_ASSERT(result.valueChecked() == std::nullopt); + } else { + TBig referenceResult = a_ / b; + auto result_ = result.valueChecked(); + RC_ASSERT(result_.has_value()); + RC_ASSERT(*result_ == referenceResult); + RC_ASSERT(result.valueWrapping() == referenceResult); + } +} + +/** Creates parameters that perform a more adequate number of checks to validate + * extremely cheap tests such as arithmetic tests */ +static rc::detail::TestParams makeParams() +{ + auto const & conf = rc::detail::configuration(); + auto newParams = conf.testParams; + newParams.maxSuccess = 10000; + return newParams; +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, add_unsigned, makeParams, (uint16_t a, uint16_t b)) +{ + checkType(a, b, &Checked::operator+, [](int32_t a, int32_t b) { return a + b; }); +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, add_signed, makeParams, (int16_t a, int16_t b)) +{ + checkType(a, b, &Checked::operator+, [](int32_t a, int32_t b) { return a + b; }); +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, sub_unsigned, makeParams, (uint16_t a, uint16_t b)) +{ + checkType(a, b, &Checked::operator-, [](int32_t a, int32_t b) { return a - b; }); +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, sub_signed, makeParams, (int16_t a, int16_t b)) +{ + checkType(a, b, &Checked::operator-, [](int32_t a, int32_t b) { return a - b; }); +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, mul_unsigned, makeParams, (uint16_t a, uint16_t b)) +{ + checkType(a, b, &Checked::operator*, [](int64_t a, int64_t b) { return a * b; }); +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, mul_signed, makeParams, (int16_t a, int16_t b)) +{ + checkType(a, b, &Checked::operator*, [](int64_t a, int64_t b) { return a * b; }); +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, div_unsigned, makeParams, (uint16_t a, uint16_t b)) +{ + checkDivision(a, b); +} + +RC_GTEST_PROP_WITH_PARAMS(Checked, div_signed, makeParams, (int16_t a, int16_t b)) +{ + checkDivision(a, b); +} + +// Make absolutely sure that we check the special cases if the proptest +// generator does not come up with them. This one is especially important +// because it has very specific pairs required for the edge cases unlike the +// others. +TEST(Checked, div_signed_special_cases) +{ + checkDivision(std::numeric_limits::min(), -1); + checkDivision(std::numeric_limits::min(), 0); + checkDivision(0, 0); +} + +} From 7b6622d73317f0cd093cde96b5898b8af07abea7 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Fri, 12 Jul 2024 16:22:34 +0200 Subject: [PATCH 745/910] language: cleanly ban integer overflows This also bans various sneaking of negative numbers from the language into unsuspecting builtins as was exposed while auditing the consequences of changing the Nix language integer type to a newtype. It's unlikely that this change comprehensively ensures correctness when passing integers out of the Nix language and we should probably add a checked-narrowing function or something similar, but that's out of scope for the immediate change. During the development of this I found a few fun facts about the language: - You could overflow integers by converting from unsigned JSON values. - You could overflow unsigned integers by converting negative numbers into them when going into Nix config, into fetchTree, and into flake inputs. The flake inputs and Nix config cannot actually be tested properly since they both ban thunks, however, we put in checks anyway because it's possible these could somehow be used to do such shenanigans some other way. Note that Lix has banned Nix language integer overflows since the very first public beta, but threw a SIGILL about them because we run with -fsanitize=signed-overflow -fsanitize-undefined-trap-on-error in production builds. Since the Nix language uses signed integers, overflow was simply undefined behaviour, and since we defined that to trap, it did. Trapping on it was a bad UX, but we didn't even entirely notice that we had done this at all until it was reported as a bug a couple of months later (which is, to be fair, that flag working as intended), and it's got enough production time that, aside from code that is IMHO buggy (and which is, in any case, not in nixpkgs) such as https://git.lix.systems/lix-project/lix/issues/445, we don't think anyone doing anything reasonable actually depends on wrapping overflow. Even for weird use cases such as doing funny bit crimes, it doesn't make sense IMO to have wrapping behaviour, since two's complement arithmetic overflow behaviour is so *aggressively* not what you want for *any* kind of mathematics/algorithms. The Nix language exists for package management, a domain where bit crimes are already only dubiously in scope to begin with, and it makes a lot more sense for that domain for the integers to never lose precision, either by throwing errors if they would, or by being arbitrary-precision. Fixes: https://github.com/NixOS/nix/issues/10968 Original-CL: https://gerrit.lix.systems/c/lix/+/1596 Change-Id: I51f253840c4af2ea5422b8a420aa5fafbf8fae75 --- src/libcmd/installable-flake.cc | 4 +- src/libcmd/installable-value.hh | 2 +- src/libexpr-c/nix_api_value.cc | 2 +- src/libexpr/eval-cache.cc | 4 +- src/libexpr/eval.cc | 21 ++-- src/libexpr/get-drvs.cc | 4 +- src/libexpr/json-to-value.cc | 7 +- src/libexpr/lexer.l | 2 +- src/libexpr/nixexpr.hh | 1 + src/libexpr/primops.cc | 102 ++++++++++++------ src/libexpr/primops/fetchTree.cc | 12 ++- src/libexpr/value-to-json.cc | 2 +- src/libexpr/value.hh | 10 +- src/libflake/flake/flake.cc | 22 +++- src/libstore/globals.hh | 10 +- .../lang/eval-fail-fetchTree-negative.err.exp | 8 ++ .../lang/eval-fail-fetchTree-negative.nix | 5 + ...ake-ref-to-string-negative-integer.err.exp | 14 +++ ...l-flake-ref-to-string-negative-integer.nix | 7 ++ .../eval-fail-fromJSON-overflowing.err.exp | 8 ++ .../lang/eval-fail-fromJSON-overflowing.nix | 1 + .../lang/eval-fail-overflowing-add.err.exp | 6 ++ .../lang/eval-fail-overflowing-add.nix | 4 + .../lang/eval-fail-overflowing-div.err.exp | 23 ++++ .../lang/eval-fail-overflowing-div.nix | 7 ++ .../lang/eval-fail-overflowing-mul.err.exp | 16 +++ .../lang/eval-fail-overflowing-mul.nix | 3 + .../lang/eval-fail-overflowing-sub.err.exp | 9 ++ .../lang/eval-fail-overflowing-sub.nix | 4 + tests/unit/libexpr-support/tests/libexpr.hh | 2 +- 30 files changed, 260 insertions(+), 62 deletions(-) create mode 100644 tests/functional/lang/eval-fail-fetchTree-negative.err.exp create mode 100644 tests/functional/lang/eval-fail-fetchTree-negative.nix create mode 100644 tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.err.exp create mode 100644 tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.nix create mode 100644 tests/functional/lang/eval-fail-fromJSON-overflowing.err.exp create mode 100644 tests/functional/lang/eval-fail-fromJSON-overflowing.nix create mode 100644 tests/functional/lang/eval-fail-overflowing-add.err.exp create mode 100644 tests/functional/lang/eval-fail-overflowing-add.nix create mode 100644 tests/functional/lang/eval-fail-overflowing-div.err.exp create mode 100644 tests/functional/lang/eval-fail-overflowing-div.nix create mode 100644 tests/functional/lang/eval-fail-overflowing-mul.err.exp create mode 100644 tests/functional/lang/eval-fail-overflowing-mul.nix create mode 100644 tests/functional/lang/eval-fail-overflowing-sub.err.exp create mode 100644 tests/functional/lang/eval-fail-overflowing-sub.nix diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 8796ad5ba..6c9ee6748 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -104,12 +104,12 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths() auto drvPath = attr->forceDerivation(); - std::optional priority; + std::optional priority; if (attr->maybeGetAttr(state->sOutputSpecified)) { } else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) { if (auto aPriority = aMeta->maybeGetAttr("priority")) - priority = aPriority->getInt(); + priority = aPriority->getInt().value; } return {{ diff --git a/src/libcmd/installable-value.hh b/src/libcmd/installable-value.hh index 798cb5e1a..60207cd23 100644 --- a/src/libcmd/installable-value.hh +++ b/src/libcmd/installable-value.hh @@ -40,7 +40,7 @@ struct ExtraPathInfoValue : ExtraPathInfo /** * An optional priority for use with "build envs". See Package */ - std::optional priority; + std::optional priority; /** * The attribute path associated with this value. The idea is diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 845e87935..fa2a9cbe2 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -306,7 +306,7 @@ int64_t nix_get_int(nix_c_context * context, const nix_value * value) try { auto & v = check_value_in(value); assert(v.type() == nix::nInt); - return v.integer(); + return v.integer().value; } NIXC_CATCH_ERRS_RES(0); } diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 26352187e..95b484606 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -326,7 +326,7 @@ struct AttrDb case AttrType::Bool: return {{rowId, queryAttribute.getInt(2) != 0}}; case AttrType::Int: - return {{rowId, int_t{queryAttribute.getInt(2)}}}; + return {{rowId, int_t{NixInt{queryAttribute.getInt(2)}}}}; case AttrType::ListOfStrings: return {{rowId, tokenizeString>(queryAttribute.getStr(2), "\t")}}; case AttrType::Missing: @@ -469,7 +469,7 @@ Value & AttrCursor::forceValue() else if (v.type() == nBool) cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()}; else if (v.type() == nInt) - cachedValue = {root->db->setInt(getKey(), v.integer()), int_t{v.integer()}}; + cachedValue = {root->db->setInt(getKey(), v.integer().value), int_t{v.integer()}}; else if (v.type() == nAttrs) ; // FIXME: do something? else diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 746ccab2a..76c54936f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1979,7 +1979,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) NixStringContext context; std::vector s; size_t sSize = 0; - NixInt n = 0; + NixInt n{0}; NixFloat nf = 0; bool first = !forceString; @@ -2023,17 +2023,22 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) if (firstType == nInt) { if (vTmp.type() == nInt) { - n += vTmp.integer(); + auto newN = n + vTmp.integer(); + if (auto checked = newN.valueChecked(); checked.has_value()) { + n = NixInt(*checked); + } else { + state.error("integer overflow in adding %1% + %2%", n, vTmp.integer()).atPos(i_pos).debugThrow(); + } } else if (vTmp.type() == nFloat) { // Upgrade the type from int to float; firstType = nFloat; - nf = n; + nf = n.value; nf += vTmp.fpoint(); } else state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { - nf += vTmp.integer(); + nf += vTmp.integer().value; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint(); } else @@ -2158,7 +2163,7 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err try { forceValue(v, pos); if (v.type() == nInt) - return v.integer(); + return v.integer().value; else if (v.type() != nFloat) error( "expected a float but found %1%: %2%", @@ -2345,7 +2350,7 @@ BackedStringView EvalState::coerceToString( shell scripting convenience, just like `null'. */ if (v.type() == nBool && v.boolean()) return "1"; if (v.type() == nBool && !v.boolean()) return ""; - if (v.type() == nInt) return std::to_string(v.integer()); + if (v.type() == nInt) return std::to_string(v.integer().value); if (v.type() == nFloat) return std::to_string(v.fpoint()); if (v.type() == nNull) return ""; @@ -2728,9 +2733,9 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v // Special case type-compatibility between float and int if (v1.type() == nInt && v2.type() == nFloat) - return v1.integer() == v2.fpoint(); + return v1.integer().value == v2.fpoint(); if (v1.type() == nFloat && v2.type() == nInt) - return v1.fpoint() == v2.integer(); + return v1.fpoint() == v2.integer().value; // All other types are not compatible with each other. if (v1.type() != v2.type()) return false; diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 7041a3932..20963ec91 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -246,8 +246,8 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def) if (v->type() == nString) { /* Backwards compatibility with before we had support for integer meta fields. */ - if (auto n = string2Int(v->c_str())) - return *n; + if (auto n = string2Int(v->c_str())) + return NixInt{*n}; } return def; } diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 21074bdd8..9ac56541a 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -2,6 +2,7 @@ #include "value.hh" #include "eval.hh" +#include #include #include @@ -101,8 +102,12 @@ public: return true; } - bool number_unsigned(number_unsigned_t val) override + bool number_unsigned(number_unsigned_t val_) override { + if (val_ > std::numeric_limits::max()) { + throw Error("unsigned json number %1% outside of Nix integer range", val_); + } + NixInt::Inner val = val_; rs->value(state).mkInt(val); rs->add(); return true; diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index eb1825b7c..a7e44cb72 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -138,7 +138,7 @@ or { return OR_KW; } {INT} { errno = 0; std::optional numMay = string2Int(yytext); if (numMay.has_value()) { - yylval->n = *numMay; + yylval->n = NixInt{*numMay}; } else { throw ParseError(ErrorInfo{ .msg = HintFmt("invalid integer '%1%'", yytext), diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 3279e3d48..7868834f1 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -107,6 +107,7 @@ struct ExprInt : Expr { Value v; ExprInt(NixInt n) { v.mkInt(n); }; + ExprInt(NixInt::Inner n) { v.mkInt(n); }; Value * maybeThunk(EvalState & state, Env & env) override; COMMON_METHODS }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a23583f34..acc0d6c1c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -587,9 +587,9 @@ struct CompareValues { try { if (v1->type() == nFloat && v2->type() == nInt) - return v1->fpoint() < v2->integer(); + return v1->fpoint() < v2->integer().value; if (v1->type() == nInt && v2->type() == nFloat) - return v1->integer() < v2->fpoint(); + return v1->integer().value < v2->fpoint(); if (v1->type() != v2->type()) state.error("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow(); // Allow selecting a subset of enum values @@ -2677,13 +2677,13 @@ static struct LazyPosAcessors { PrimOp primop_lineOfPos{ .arity = 1, .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.positions[PosIdx(args[0]->integer())].line); + v.mkInt(state.positions[PosIdx(args[0]->integer().value)].line); } }; PrimOp primop_columnOfPos{ .arity = 1, .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.positions[PosIdx(args[0]->integer())].column); + v.mkInt(state.positions[PosIdx(args[0]->integer().value)].column); } }; @@ -3159,7 +3159,8 @@ static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Val /* Return the n-1'th element of a list. */ static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - elemAt(state, pos, *args[0], state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.elemAt"), v); + NixInt::Inner elem = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.elemAt").value; + elemAt(state, pos, *args[0], elem, v); } static RegisterPrimOp primop_elemAt({ @@ -3453,10 +3454,12 @@ static RegisterPrimOp primop_all({ static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - auto len = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.genList"); + auto len_ = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.genList").value; - if (len < 0) - state.error("cannot create list of size %1%", len).atPos(pos).debugThrow(); + if (len_ < 0) + state.error("cannot create list of size %1%", len_).atPos(pos).debugThrow(); + + size_t len = size_t(len_); // More strict than striclty (!) necessary, but acceptable // as evaluating map without accessing any values makes little sense. @@ -3713,9 +3716,17 @@ static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value if (args[0]->type() == nFloat || args[1]->type() == nFloat) v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first argument of the addition") + state.forceFloat(*args[1], pos, "while evaluating the second argument of the addition")); - else - v.mkInt( state.forceInt(*args[0], pos, "while evaluating the first argument of the addition") - + state.forceInt(*args[1], pos, "while evaluating the second argument of the addition")); + else { + auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument of the addition"); + auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument of the addition"); + + auto result_ = i1 + i2; + if (auto result = result_.valueChecked(); result.has_value()) { + v.mkInt(*result); + } else { + state.error("integer overflow in adding %1% + %2%", i1, i2).atPos(pos).debugThrow(); + } + } } static RegisterPrimOp primop_add({ @@ -3734,9 +3745,18 @@ static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value if (args[0]->type() == nFloat || args[1]->type() == nFloat) v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first argument of the subtraction") - state.forceFloat(*args[1], pos, "while evaluating the second argument of the subtraction")); - else - v.mkInt( state.forceInt(*args[0], pos, "while evaluating the first argument of the subtraction") - - state.forceInt(*args[1], pos, "while evaluating the second argument of the subtraction")); + else { + auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument of the subtraction"); + auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument of the subtraction"); + + auto result_ = i1 - i2; + + if (auto result = result_.valueChecked(); result.has_value()) { + v.mkInt(*result); + } else { + state.error("integer overflow in subtracting %1% - %2%", i1, i2).atPos(pos).debugThrow(); + } + } } static RegisterPrimOp primop_sub({ @@ -3755,9 +3775,18 @@ static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value if (args[0]->type() == nFloat || args[1]->type() == nFloat) v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first of the multiplication") * state.forceFloat(*args[1], pos, "while evaluating the second argument of the multiplication")); - else - v.mkInt( state.forceInt(*args[0], pos, "while evaluating the first argument of the multiplication") - * state.forceInt(*args[1], pos, "while evaluating the second argument of the multiplication")); + else { + auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument of the multiplication"); + auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument of the multiplication"); + + auto result_ = i1 * i2; + + if (auto result = result_.valueChecked(); result.has_value()) { + v.mkInt(*result); + } else { + state.error("integer overflow in multiplying %1% * %2%", i1, i2).atPos(pos).debugThrow(); + } + } } static RegisterPrimOp primop_mul({ @@ -3784,10 +3813,12 @@ static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value NixInt i1 = state.forceInt(*args[0], pos, "while evaluating the first operand of the division"); NixInt i2 = state.forceInt(*args[1], pos, "while evaluating the second operand of the division"); /* Avoid division overflow as it might raise SIGFPE. */ - if (i1 == std::numeric_limits::min() && i2 == -1) - state.error("overflow in integer division").atPos(pos).debugThrow(); - - v.mkInt(i1 / i2); + auto result_ = i1 / i2; + if (auto result = result_.valueChecked(); result.has_value()) { + v.mkInt(*result); + } else { + state.error("integer overflow in dividing %1% / %2%", i1, i2).atPos(pos).debugThrow(); + } } } @@ -3802,8 +3833,9 @@ static RegisterPrimOp primop_div({ static void prim_bitAnd(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitAnd") - & state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitAnd")); + auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitAnd"); + auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitAnd"); + v.mkInt(i1.value & i2.value); } static RegisterPrimOp primop_bitAnd({ @@ -3817,8 +3849,10 @@ static RegisterPrimOp primop_bitAnd({ static void prim_bitOr(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitOr") - | state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitOr")); + auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitOr"); + auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitOr"); + + v.mkInt(i1.value | i2.value); } static RegisterPrimOp primop_bitOr({ @@ -3832,8 +3866,10 @@ static RegisterPrimOp primop_bitOr({ static void prim_bitXor(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitXor") - ^ state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitXor")); + auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitXor"); + auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitXor"); + + v.mkInt(i1.value ^ i2.value); } static RegisterPrimOp primop_bitXor({ @@ -3913,13 +3949,19 @@ static RegisterPrimOp primop_toString({ non-negative. */ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring"); + NixInt::Inner start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring").value; if (start < 0) state.error("negative start position in 'substring'").atPos(pos).debugThrow(); - int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring"); + NixInt::Inner len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring").value; + + // Negative length may be idiomatically passed to builtins.substring to get + // the tail of the string. + if (len < 0) { + len = std::numeric_limits::max(); + } // Special-case on empty substring to avoid O(n) strlen // This allows for the use of empty substrings to efficently capture string context @@ -3962,7 +4004,7 @@ static void prim_stringLength(EvalState & state, const PosIdx pos, Value * * arg { NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.stringLength"); - v.mkInt(s->size()); + v.mkInt(NixInt::Inner(s->size())); } static RegisterPrimOp primop_stringLength({ diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index f79b6b7b8..78328701d 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -122,9 +122,15 @@ static void fetchTree( } else if (attr.value->type() == nBool) attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean()}); - else if (attr.value->type() == nInt) - attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer())); - else if (state.symbols[attr.name] == "publicKeys") { + else if (attr.value->type() == nInt) { + auto intValue = attr.value->integer().value; + + if (intValue < 0) { + state.error("negative value given for fetchTree attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow(); + } + + attrs.emplace(state.symbols[attr.name], uint64_t(intValue)); + } else if (state.symbols[attr.name] == "publicKeys") { experimentalFeatureSettings.require(Xp::VerifiedFetches); attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump()); } diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index f8cc05616..8044fe347 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -22,7 +22,7 @@ json printValueAsJSON(EvalState & state, bool strict, switch (v.type()) { case nInt: - out = v.integer(); + out = v.integer().value; break; case nBool: diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 257da1d2d..1510abfea 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -8,6 +8,7 @@ #include "value/context.hh" #include "source-path.hh" #include "print-options.hh" +#include "checked-arithmetic.hh" #if HAVE_BOEHMGC #include @@ -73,8 +74,8 @@ class EvalState; class XMLWriter; class Printer; -typedef int64_t NixInt; -typedef double NixFloat; +using NixInt = checked::Checked; +using NixFloat = double; /** * External values must descend from ExternalValueBase, so that @@ -304,6 +305,11 @@ public: return internalType != tUninitialized; } + inline void mkInt(NixInt::Inner n) + { + mkInt(NixInt{n}); + } + inline void mkInt(NixInt n) { finishValue(tInt, { .integer = n }); diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index 627dfe830..fd1183514 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -140,9 +140,16 @@ static FlakeInput parseFlakeInput(EvalState & state, case nBool: attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean() }); break; - case nInt: - attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer()); + case nInt: { + auto intValue = attr.value->integer().value; + + if (intValue < 0) { + state.error("negative value given for flake input attribute %1%: %2%", state.symbols[attr.name], intValue).debugThrow(); + } + + attrs.emplace(state.symbols[attr.name], uint64_t(intValue)); break; + } default: if (attr.name == state.symbols.create("publicKeys")) { experimentalFeatureSettings.require(Xp::VerifiedFetches); @@ -272,7 +279,7 @@ static Flake readFlake( else if (setting.value->type() == nInt) flake.config.settings.emplace( state.symbols[setting.name], - state.forceInt(*setting.value, setting.pos, "")); + state.forceInt(*setting.value, setting.pos, "").value); else if (setting.value->type() == nBool) flake.config.settings.emplace( state.symbols[setting.name], @@ -904,8 +911,13 @@ static void prim_flakeRefToString( for (const auto & attr : *args[0]->attrs()) { auto t = attr.value->type(); if (t == nInt) { - attrs.emplace(state.symbols[attr.name], - (uint64_t) attr.value->integer()); + auto intValue = attr.value->integer().value; + + if (intValue < 0) { + state.error("negative value given for flake ref attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow(); + } + + attrs.emplace(state.symbols[attr.name], uint64_t(intValue)); } else if (t == nBool) { attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean() }); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 8760c9d14..1ca9f02f5 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1131,7 +1131,10 @@ public: )"}; Setting maxFree{ - this, std::numeric_limits::max(), "max-free", + // n.b. this is deliberately int64 max rather than uint64 max because + // this goes through the Nix language JSON parser and thus needs to be + // representable in Nix language integers. + this, std::numeric_limits::max(), "max-free", R"( When a garbage collection is triggered by the `min-free` option, it stops as soon as `max-free` bytes are available. The default is @@ -1221,7 +1224,10 @@ public: Setting warnLargePathThreshold{ this, - std::numeric_limits::max(), + // n.b. this is deliberately int64 max rather than uint64 max because + // this goes through the Nix language JSON parser and thus needs to be + // representable in Nix language integers. + std::numeric_limits::max(), "warn-large-path-threshold", R"( Warn when copying a path larger than this number of bytes to the Nix store diff --git a/tests/functional/lang/eval-fail-fetchTree-negative.err.exp b/tests/functional/lang/eval-fail-fetchTree-negative.err.exp new file mode 100644 index 000000000..d9ba1f0b2 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchTree-negative.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fetchTree' builtin + at /pwd/lang/eval-fail-fetchTree-negative.nix:1:1: + 1| builtins.fetchTree { + | ^ + 2| type = "file"; + + error: negative value given for fetchTree attr owner: -1 diff --git a/tests/functional/lang/eval-fail-fetchTree-negative.nix b/tests/functional/lang/eval-fail-fetchTree-negative.nix new file mode 100644 index 000000000..90bcab5d8 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchTree-negative.nix @@ -0,0 +1,5 @@ +builtins.fetchTree { + type = "file"; + url = "file://eval-fail-fetchTree-negative.nix"; + owner = -1; +} diff --git a/tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.err.exp b/tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.err.exp new file mode 100644 index 000000000..25c8d7eaa --- /dev/null +++ b/tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.err.exp @@ -0,0 +1,14 @@ +error: + … while calling the 'seq' builtin + at /pwd/lang/eval-fail-flake-ref-to-string-negative-integer.nix:1:16: + 1| let n = -1; in builtins.seq n (builtins.flakeRefToString { + | ^ + 2| type = "github"; + + … while calling the 'flakeRefToString' builtin + at /pwd/lang/eval-fail-flake-ref-to-string-negative-integer.nix:1:32: + 1| let n = -1; in builtins.seq n (builtins.flakeRefToString { + | ^ + 2| type = "github"; + + error: negative value given for flake ref attr repo: -1 diff --git a/tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.nix b/tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.nix new file mode 100644 index 000000000..e0208eb25 --- /dev/null +++ b/tests/functional/lang/eval-fail-flake-ref-to-string-negative-integer.nix @@ -0,0 +1,7 @@ +let n = -1; in builtins.seq n (builtins.flakeRefToString { + type = "github"; + owner = "NixOS"; + repo = n; + ref = "23.05"; + dir = "lib"; +}) diff --git a/tests/functional/lang/eval-fail-fromJSON-overflowing.err.exp b/tests/functional/lang/eval-fail-fromJSON-overflowing.err.exp new file mode 100644 index 000000000..a39082b45 --- /dev/null +++ b/tests/functional/lang/eval-fail-fromJSON-overflowing.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fromJSON' builtin + at /pwd/lang/eval-fail-fromJSON-overflowing.nix:1:1: + 1| builtins.fromJSON ''{"attr": 18446744073709551615}'' + | ^ + 2| + + error: unsigned json number 18446744073709551615 outside of Nix integer range diff --git a/tests/functional/lang/eval-fail-fromJSON-overflowing.nix b/tests/functional/lang/eval-fail-fromJSON-overflowing.nix new file mode 100644 index 000000000..6dfbce3f6 --- /dev/null +++ b/tests/functional/lang/eval-fail-fromJSON-overflowing.nix @@ -0,0 +1 @@ +builtins.fromJSON ''{"attr": 18446744073709551615}'' diff --git a/tests/functional/lang/eval-fail-overflowing-add.err.exp b/tests/functional/lang/eval-fail-overflowing-add.err.exp new file mode 100644 index 000000000..6458cf1c9 --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-add.err.exp @@ -0,0 +1,6 @@ +error: integer overflow in adding 9223372036854775807 + 1 + at /pwd/lang/eval-fail-overflowing-add.nix:4:8: + 3| b = 1; + 4| in a + b + | ^ + 5| diff --git a/tests/functional/lang/eval-fail-overflowing-add.nix b/tests/functional/lang/eval-fail-overflowing-add.nix new file mode 100644 index 000000000..24258fc20 --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-add.nix @@ -0,0 +1,4 @@ +let + a = 9223372036854775807; + b = 1; +in a + b diff --git a/tests/functional/lang/eval-fail-overflowing-div.err.exp b/tests/functional/lang/eval-fail-overflowing-div.err.exp new file mode 100644 index 000000000..8ce07d4d6 --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-div.err.exp @@ -0,0 +1,23 @@ +error: + … while calling the 'seq' builtin + at /pwd/lang/eval-fail-overflowing-div.nix:7:4: + 6| b = -1; + 7| in builtins.seq intMin (builtins.seq b (intMin / b)) + | ^ + 8| + + … while calling the 'seq' builtin + at /pwd/lang/eval-fail-overflowing-div.nix:7:25: + 6| b = -1; + 7| in builtins.seq intMin (builtins.seq b (intMin / b)) + | ^ + 8| + + … while calling the 'div' builtin + at /pwd/lang/eval-fail-overflowing-div.nix:7:48: + 6| b = -1; + 7| in builtins.seq intMin (builtins.seq b (intMin / b)) + | ^ + 8| + + error: integer overflow in dividing -9223372036854775808 / -1 diff --git a/tests/functional/lang/eval-fail-overflowing-div.nix b/tests/functional/lang/eval-fail-overflowing-div.nix new file mode 100644 index 000000000..44fbe9d7e --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-div.nix @@ -0,0 +1,7 @@ +let + # lol, this has to be written as an expression like this because negative + # numbers use unary negation rather than parsing directly, and 2**63 is out + # of range + intMin = -9223372036854775807 - 1; + b = -1; +in builtins.seq intMin (builtins.seq b (intMin / b)) diff --git a/tests/functional/lang/eval-fail-overflowing-mul.err.exp b/tests/functional/lang/eval-fail-overflowing-mul.err.exp new file mode 100644 index 000000000..f42b39d4d --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-mul.err.exp @@ -0,0 +1,16 @@ +error: + … while calling the 'mul' builtin + at /pwd/lang/eval-fail-overflowing-mul.nix:3:10: + 2| a = 4294967297; + 3| in a * a * a + | ^ + 4| + + … while calling the 'mul' builtin + at /pwd/lang/eval-fail-overflowing-mul.nix:3:6: + 2| a = 4294967297; + 3| in a * a * a + | ^ + 4| + + error: integer overflow in multiplying 4294967297 * 4294967297 diff --git a/tests/functional/lang/eval-fail-overflowing-mul.nix b/tests/functional/lang/eval-fail-overflowing-mul.nix new file mode 100644 index 000000000..6081d9c7b --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-mul.nix @@ -0,0 +1,3 @@ +let + a = 4294967297; +in a * a * a diff --git a/tests/functional/lang/eval-fail-overflowing-sub.err.exp b/tests/functional/lang/eval-fail-overflowing-sub.err.exp new file mode 100644 index 000000000..66a3a03f8 --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-sub.err.exp @@ -0,0 +1,9 @@ +error: + … while calling the 'sub' builtin + at /pwd/lang/eval-fail-overflowing-sub.nix:4:6: + 3| b = 2; + 4| in a - b + | ^ + 5| + + error: integer overflow in subtracting -9223372036854775807 - 2 diff --git a/tests/functional/lang/eval-fail-overflowing-sub.nix b/tests/functional/lang/eval-fail-overflowing-sub.nix new file mode 100644 index 000000000..229b8c6d2 --- /dev/null +++ b/tests/functional/lang/eval-fail-overflowing-sub.nix @@ -0,0 +1,4 @@ +let + a = -9223372036854775807; + b = 2; +in a - b diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index 54a4a0e9e..045607e87 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -85,7 +85,7 @@ namespace nix { if (arg.type() != nInt) { return false; } - return arg.integer() == v; + return arg.integer().value == v; } MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) { From bf050d9e966534eeb92188da1a93ac459bed0f97 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Fri, 12 Jul 2024 18:20:14 +0200 Subject: [PATCH 746/910] docs: update to define integer overflow Change-Id: Ie8a1b31035f2d27a220e5df2e9e178ec3b39ee68 --- doc/manual/src/language/operators.md | 8 ++++++-- doc/manual/src/language/syntax.md | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index e1c020781..2c2dcc276 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -67,8 +67,12 @@ After evaluating *attrset* and *attrpath*, the computational complexity is O(log ## Arithmetic -Numbers are type-compatible: -Pure integer operations will always return integers, whereas any operation involving at least one floating point number return a floating point number. +Numbers will retain their type unless mixed with other numeric types: +Pure integer operations will always return integers, whereas any operation involving at least one floating point number returns a floating point number. + +Evaluation of the following numeric operations throws an evaluation error: +- Division by zero +- Integer overflow, that is, any operation yielding a result outside of the representable range of [Nix language integers](./syntax.md#number-literal) See also [Comparison] and [Equality]. diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md index 6108bacd6..f05935502 100644 --- a/doc/manual/src/language/syntax.md +++ b/doc/manual/src/language/syntax.md @@ -183,6 +183,13 @@ This section covers syntax and semantics of the Nix language. Numbers, which can be *integers* (like `123`) or *floating point* (like `123.43` or `.27e13`). + Integers in the Nix language are 64-bit [two's complement] signed integers, with a range of -9223372036854775808 to 9223372036854775807, inclusive. + + [two's complement]: https://en.wikipedia.org/wiki/Two%27s_complement + + Note that negative numeric literals are actually parsed as unary negation of positive numeric literals. + This means that the minimum integer `-9223372036854775808` cannot be written as-is as a literal, since the positive number `9223372036854775808` is one past the maximum range. + See [arithmetic] and [comparison] operators for semantics. [arithmetic]: ./operators.md#arithmetic From 5878b1475f1c50b42672ed85e7259e7d328920e9 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Thu, 25 Jul 2024 23:06:53 +0200 Subject: [PATCH 747/910] doc: release notes for banning integer overflow Change-Id: Ib75ab5b8b4d879035d7ee7678f9cd0c491a39c0a --- doc/manual/rl-next/ban-integer-overflow.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 doc/manual/rl-next/ban-integer-overflow.md diff --git a/doc/manual/rl-next/ban-integer-overflow.md b/doc/manual/rl-next/ban-integer-overflow.md new file mode 100644 index 000000000..49ecfe4c6 --- /dev/null +++ b/doc/manual/rl-next/ban-integer-overflow.md @@ -0,0 +1,21 @@ +--- +synopsis: Define integer overflow in the Nix language as an error +issues: [10968] +prs: [11188] +--- + +Previously, integer overflow in the Nix language invoked C++ level signed overflow, which was undefined behaviour, but *probably* manifested as wrapping around on overflow. + +Since prior to the public release of Lix, Lix had C++ signed overflow defined to crash the process and nobody noticed this having accidentally removed overflow from the Nix language for three months until it was caught by fiddling around. +Given the significant body of actual Nix code that has been evaluated by Lix in that time, it does not appear that nixpkgs or much of importance depends on integer overflow, so it is safe to turn into an error. + +Some other overflows were fixed: +- `builtins.fromJSON` of values greater than the maximum representable value in a signed 64-bit integer will generate an error. +- `nixConfig` in flakes will no longer accept negative values for configuration options. + +Integer overflow now looks like the following: + +``` +» nix eval --expr '9223372036854775807 + 1' +error: integer overflow in adding 9223372036854775807 + 1 +``` From db5bacb63772138036bdab4cb89a83bb52bbbcfa Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 31 Jul 2024 21:41:26 +0200 Subject: [PATCH 748/910] reword documentation on `nix-path` config option (#7772) * docs: unify documentation on search paths - put all the information on search path semantics into `builtins.findFile` - put all the information on determining the value of `builtins.nixPath` into the `nix-path` setting maybe `builtins.nixPath` is a better place for this, but those bits can still be moved around now that it's all next to each other. - link to the syntax page for lookup paths from all places that are concerned with it - add or clarify examples - add a test verifying a claim from documentation --- doc/manual/src/command-ref/env-common.md | 26 ++-- doc/manual/src/command-ref/opt-common.md | 9 +- src/libcmd/common-eval-args.cc | 70 +---------- src/libexpr/eval-settings.hh | 21 ++-- src/libexpr/primops.cc | 148 ++++++++++++++++++----- tests/functional/nix_path.sh | 16 +-- 6 files changed, 165 insertions(+), 125 deletions(-) diff --git a/doc/manual/src/command-ref/env-common.md b/doc/manual/src/command-ref/env-common.md index d3f5f9c14..0b5017882 100644 --- a/doc/manual/src/command-ref/env-common.md +++ b/doc/manual/src/command-ref/env-common.md @@ -9,22 +9,26 @@ Most Nix commands interpret the following environment variables: - [`NIX_PATH`](#env-NIX_PATH) - A colon-separated list of directories used to look up the location of Nix - expressions using [paths](@docroot@/language/types.md#type-path) - enclosed in angle brackets (i.e., ``), - e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the - [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). + A colon-separated list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). - If `NIX_PATH` is not set at all, Nix will fall back to the following list in [impure](@docroot@/command-ref/conf-file.md#conf-pure-eval) and [unrestricted](@docroot@/command-ref/conf-file.md#conf-restrict-eval) evaluation mode: + This environment variable overrides the value of the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path). - 1. `$HOME/.nix-defexpr/channels` - 2. `nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs` - 3. `/nix/var/nix/profiles/per-user/root/channels` + It can be extended using the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). + + > **Example** + > + > ```bash + > $ export NIX_PATH=`/home/eelco/Dev:nixos-config=/etc/nixos + > ``` If `NIX_PATH` is set to an empty string, resolving search paths will always fail. - For example, attempting to use `` will produce: - error: file 'nixpkgs' was not found in the Nix search path + > **Example** + > + > ```bash + > $ NIX_PATH= nix-instantiate --eval '' + > error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I) + > ``` - [`NIX_IGNORE_SYMLINK_STORE`](#env-NIX_IGNORE_SYMLINK_STORE) diff --git a/doc/manual/src/command-ref/opt-common.md b/doc/manual/src/command-ref/opt-common.md index a42909e2d..69a700207 100644 --- a/doc/manual/src/command-ref/opt-common.md +++ b/doc/manual/src/command-ref/opt-common.md @@ -37,7 +37,7 @@ Most Nix commands accept the following command-line options: Print even more informational messages. - `4` “Debug” - + Print debug information. - `5` “Vomit” @@ -187,11 +187,12 @@ Most Nix commands accept the following command-line options: For `nix-shell`, this option is commonly used to give you a shell in which you can build the packages returned by the expression. If you want to get a shell which contain the *built* packages ready for use, give your expression to the `nix-shell --packages ` convenience flag instead. -- [`-I`](#opt-I) *path* +- [`-I` / `--include`](#opt-I) *path* - Add an entry to the [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path). + Add an entry to the list of search paths used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). This option may be given multiple times. - Paths added through `-I` take precedence over [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH). + + Paths added through `-I` take precedence over the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path) and the [`NIX_PATH` environment variable](@docroot@/command-ref/env-common.md#env-NIX_PATH). - [`--option`](#opt-option) *name* *value* diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index decadd751..fcef92487 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -91,75 +91,11 @@ MixEvalArgs::MixEvalArgs() .longName = "include", .shortName = 'I', .description = R"( - Add *path* to the Nix search path. The Nix search path is - initialized from the colon-separated [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) environment - variable, and is used to look up the location of Nix expressions using [paths](@docroot@/language/types.md#type-path) enclosed in angle - brackets (i.e., ``). + Add *path* to search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md) - For instance, passing + This option may be given multiple times. - ``` - -I /home/eelco/Dev - -I /etc/nixos - ``` - - will cause Nix to look for paths relative to `/home/eelco/Dev` and - `/etc/nixos`, in that order. This is equivalent to setting the - `NIX_PATH` environment variable to - - ``` - /home/eelco/Dev:/etc/nixos - ``` - - It is also possible to match paths against a prefix. For example, - passing - - ``` - -I nixpkgs=/home/eelco/Dev/nixpkgs-branch - -I /etc/nixos - ``` - - will cause Nix to search for `` in - `/home/eelco/Dev/nixpkgs-branch/path` and `/etc/nixos/nixpkgs/path`. - - If a path in the Nix search path starts with `http://` or `https://`, - it is interpreted as the URL of a tarball that will be downloaded and - unpacked to a temporary location. The tarball must consist of a single - top-level directory. For example, passing - - ``` - -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/master.tar.gz - ``` - - tells Nix to download and use the current contents of the `master` - branch in the `nixpkgs` repository. - - The URLs of the tarballs from the official `nixos.org` channels - (see [the manual page for `nix-channel`](../nix-channel.md)) can be - abbreviated as `channel:`. For instance, the - following two flags are equivalent: - - ``` - -I nixpkgs=channel:nixos-21.05 - -I nixpkgs=https://nixos.org/channels/nixos-21.05/nixexprs.tar.xz - ``` - - You can also fetch source trees using [flake URLs](./nix3-flake.md#url-like-syntax) and add them to the - search path. For instance, - - ``` - -I nixpkgs=flake:nixpkgs - ``` - - specifies that the prefix `nixpkgs` shall refer to the source tree - downloaded from the `nixpkgs` entry in the flake registry. Similarly, - - ``` - -I nixpkgs=flake:github:NixOS/nixpkgs/nixos-22.05 - ``` - - makes `` refer to a particular branch of the - `NixOS/nixpkgs` repository on GitHub. + Paths added through `-I` take precedence over the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path) and the [`NIX_PATH` environment variable](@docroot@/command-ref/env-common.md#env-NIX_PATH). )", .category = category, .labels = {"path"}, diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 30a8c5c58..0cfc14c1b 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -79,19 +79,24 @@ struct EvalSettings : Config This setting determines the value of [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath) and can be used with [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile). - The default value is + - The configuration setting is overridden by the [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) + environment variable. + - `NIX_PATH` is overridden by [specifying the setting as the command line flag](@docroot@/command-ref/conf-file.md#command-line-flags) `--nix-path`. + - Any current value is extended by the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I) or `--extra-nix-path`. - ``` - $HOME/.nix-defexpr/channels - nixpkgs=$NIX_STATE_DIR/profiles/per-user/root/channels/nixpkgs - $NIX_STATE_DIR/profiles/per-user/root/channels - ``` + If the respective paths are accessible, the default values are: - It can be overridden with the [`NIX_PATH` environment variable](@docroot@/command-ref/env-common.md#env-NIX_PATH) or the [`-I` command line option](@docroot@/command-ref/opt-common.md#opt-I). + - `$HOME/.nix-defexpr/channels` + - `nixpkgs=$NIX_STATE_DIR/profiles/per-user/root/channels/nixpkgs` + - `$NIX_STATE_DIR/profiles/per-user/root/channels` + + See [`NIX_STATE_DIR`](@docroot@/command-ref/env-common.md#env-NIX_STATE_DIR) for details. > **Note** > - > If [pure evaluation](#conf-pure-eval) is enabled, `nixPath` evaluates to the empty list `[ ]`. + > If [restricted evaluation](@docroot@/command-ref/conf-file.md#conf-restrict-eval) is enabled, the default value is empty. + > + > If [pure evaluation](#conf-pure-eval) is enabled, `builtins.nixPath` *always* evaluates to the empty list `[ ]`. )", {}, false}; Setting currentSystem{ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0b3b19b57..7ceb84f0e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1843,34 +1843,6 @@ static RegisterPrimOp primop_findFile(PrimOp { .doc = R"( Find *lookup-path* in *search-path*. - A search path is represented list of [attribute sets](./types.md#attribute-set) with two attributes: - - `prefix` is a relative path. - - `path` denotes a file system location - The exact syntax depends on the command line interface. - - Examples of search path attribute sets: - - - ``` - { - prefix = "nixos-config"; - path = "/etc/nixos/configuration.nix"; - } - ``` - - - ``` - { - prefix = ""; - path = "/nix/var/nix/profiles/per-user/root/channels"; - } - ``` - - The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/types.md#type-path) of the match: - - - If *lookup-path* matches `prefix`, then the remainder of *lookup-path* (the "suffix") is searched for within the directory denoted by `path`. - Note that the `path` may need to be downloaded at this point to look inside. - - If the suffix is found inside that directory, then the entry is a match. - The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. - [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](#builtins-nixPath): ```nix @@ -1882,6 +1854,119 @@ static RegisterPrimOp primop_findFile(PrimOp { ```nix builtins.findFile builtins.nixPath "nixpkgs" ``` + + A search path is represented as a list of [attribute sets](./types.md#attribute-set) with two attributes: + - `prefix` is a relative path. + - `path` denotes a file system location + + Examples of search path attribute sets: + + - ``` + { + prefix = ""; + path = "/nix/var/nix/profiles/per-user/root/channels"; + } + ``` + - ``` + { + prefix = "nixos-config"; + path = "/etc/nixos/configuration.nix"; + } + ``` + - ``` + { + prefix = "nixpkgs"; + path = "https://github.com/NixOS/nixpkgs/tarballs/master"; + } + ``` + - ``` + { + prefix = "nixpkgs"; + path = "channel:nixpkgs-unstable"; + } + ``` + - ``` + { + prefix = "flake-compat"; + path = "flake:github:edolstra/flake-compat"; + } + ``` + + The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/types.md#type-path) of the match: + + - If a prefix of `lookup-path` matches `prefix`, then the remainder of *lookup-path* (the "suffix") is searched for within the directory denoted by `path`. + The contents of `path` may need to be downloaded at this point to look inside. + + - If the suffix is found inside that directory, then the entry is a match. + The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. + + > **Example** + > + > A *search-path* value + > + > ``` + > [ + > { + > prefix = ""; + > path = "/home/eelco/Dev"; + > } + > { + > prefix = "nixos-config"; + > path = "/etc/nixos"; + > } + > ] + > ``` + > + > and a *lookup-path* value `"nixos-config"` will cause Nix to try `/home/eelco/Dev/nixos-config` and `/etc/nixos` in that order and return the first path that exists. + + If `path` starts with `http://` or `https://`, it is interpreted as the URL of a tarball that will be downloaded and unpacked to a temporary location. + The tarball must consist of a single top-level directory. + + The URLs of the tarballs from the official `nixos.org` channels can be abbreviated as `channel:`. + See [documentation on `nix-channel`](@docroot@/command-ref/nix-channel.md) for details about channels. + + > **Example** + > + > These two search path entries are equivalent: + > + > - ``` + > { + > prefix = "nixpkgs"; + > path = "channel:nixpkgs-unstable"; + > } + > ``` + > - ``` + > { + > prefix = "nixpkgs"; + > path = "https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz"; + > } + > ``` + + Search paths can also point to source trees using [flake URLs](@docroot@/command-ref/new-cli/nix3-flake.md#url-like-syntax). + + + > **Example** + > + > The search path entry + > + > ``` + > { + > prefix = "nixpkgs"; + > path = "flake:nixpkgs"; + > } + > ``` + > specifies that the prefix `nixpkgs` shall refer to the source tree downloaded from the `nixpkgs` entry in the flake registry. + > + > Similarly + > + > ``` + > { + > prefix = "nixpkgs"; + > path = "flake:github:nixos/nixpkgs/nixos-22.05"; + > } + > ``` + > + > makes `` refer to a particular branch of the `NixOS/nixpkgs` repository on GitHub. )", .fun = prim_findFile, }); @@ -4731,6 +4816,13 @@ void EvalState::createBaseEnv() .doc = R"( The value of the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path): a list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). + > **Example** + > + > ```bash + > $ NIX_PATH= nix-instantiate --eval --expr "builtins.nixPath" -I foo=bar --no-pure-eval + > [ { path = "bar"; prefix = "foo"; } ] + > ``` + Lookup path expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.findFile`](./builtins.html#builtins-findFile): diff --git a/tests/functional/nix_path.sh b/tests/functional/nix_path.sh index 7e6a0458d..90cba1f0c 100755 --- a/tests/functional/nix_path.sh +++ b/tests/functional/nix_path.sh @@ -22,13 +22,13 @@ nix-instantiate --eval -E '' --restrict-eval # # | precedence | hard-coded | nix-path in file | extra-nix-path in file | nix-path in env | extra-nix-path in env | NIX_PATH | nix-path | extra-nix-path | -I | # |------------------------|------------|------------------|------------------------|-----------------|-----------------------|-----------|-----------|-----------------|-----------------| -# | hard-coded | x | ^override | ^append | ^override | ^append | ^override | ^override | ^append | ^append | -# | nix-path in file | | last wins | ^append | ^override | ^append | ^override | ^override | ^append | ^append | -# | extra-nix-path in file | | | append in order | ^override | ^append | ^override | ^override | ^append | ^append | -# | nix-path in env | | | | last wins | ^append | ^override | ^override | ^append | ^append | -# | extra-nix-path in env | | | | | append in order | ^override | ^override | ^append | ^append | -# | NIX_PATH | | | | | | x | ^override | ^append | ^append | -# | nix-path | | | | | | | last wins | ^append | ^append | +# | hard-coded | x | ^override | ^append | ^override | ^append | ^override | ^override | ^append | ^prepend | +# | nix-path in file | | last wins | ^append | ^override | ^append | ^override | ^override | ^append | ^prepend | +# | extra-nix-path in file | | | append in order | ^override | ^append | ^override | ^override | ^append | ^prepend | +# | nix-path in env | | | | last wins | ^append | ^override | ^override | ^append | ^prepend | +# | extra-nix-path in env | | | | | append in order | ^override | ^override | ^append | ^prepend | +# | NIX_PATH | | | | | | x | ^override | ^append | ^prepend | +# | nix-path | | | | | | | last wins | ^append | ^prepend | # | extra-nix-path | | | | | | | | append in order | append in order | # | -I | | | | | | | | | append in order | @@ -59,6 +59,8 @@ echo "nix-path = test=$TEST_ROOT/from-nix-path-file" >> "$test_nix_conf" # -I extends NIX_PATH [[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +# -I takes precedence over NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test) = $TEST_ROOT/from-I ]] # if -I does not have the desired entry, the value from NIX_PATH is used [[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-NIX_PATH.nix) = $TEST_ROOT/from-NIX_PATH/only-from-NIX_PATH.nix ]] From c952d933e574dbc17304b18b33a9afeed7ebd966 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 21:57:31 +0200 Subject: [PATCH 749/910] release notes: 2.24.0 --- .../10564-attrcursor-remove-forceerrors.md | 9 - ...03-run-the-flake-regressions-test-suite.md | 8 - ...unit-prefixes-in-configuration-settings.md | 10 - ...ild-show-all-fod-errors-with-keep-going.md | 10 - doc/manual/rl-next/10855-meson.md | 31 -- .../11086-eval-cache-fix-cache-regressions.md | 14 - .../rl-next/9063-introduce-libnixflake.md | 12 - doc/manual/rl-next/drop-vendored-toml11.md | 8 - doc/manual/rl-next/harden-user-sandboxing.md | 12 - .../rl-next/nix-shell-looks-for-shell-nix.md | 30 -- doc/manual/rl-next/pipe-operators.md | 28 -- .../rl-next/repl-doc-renders-doc-comments.md | 55 ---- doc/manual/rl-next/shebang-relative.md | 64 ---- doc/manual/rl-next/tarball-fixes.md | 9 - doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/release-notes/rl-2.24.md | 300 ++++++++++++++++++ 16 files changed, 301 insertions(+), 300 deletions(-) delete mode 100644 doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md delete mode 100644 doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md delete mode 100644 doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md delete mode 100644 doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md delete mode 100644 doc/manual/rl-next/10855-meson.md delete mode 100644 doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md delete mode 100644 doc/manual/rl-next/9063-introduce-libnixflake.md delete mode 100644 doc/manual/rl-next/drop-vendored-toml11.md delete mode 100644 doc/manual/rl-next/harden-user-sandboxing.md delete mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md delete mode 100644 doc/manual/rl-next/pipe-operators.md delete mode 100644 doc/manual/rl-next/repl-doc-renders-doc-comments.md delete mode 100644 doc/manual/rl-next/shebang-relative.md delete mode 100644 doc/manual/rl-next/tarball-fixes.md create mode 100644 doc/manual/src/release-notes/rl-2.24.md diff --git a/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md b/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md deleted file mode 100644 index 864a55b51..000000000 --- a/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: "Solve `cached failure of attribute X`" -prs: 10564 -issues: 10513 9165 ---- - -This eliminates all "cached failure of attribute X" messages by forcing evaluation of the original value when needed to show the exception to the user. This enhancement improves error reporting by providing the underlying message and stack trace. - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md b/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md deleted file mode 100644 index 42864323c..000000000 --- a/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: "Run the flake regressions test suite" -prs: 10603 ---- - -This update introduces a GitHub action to run a subset of the [flake regressions test suite](https://github.com/NixOS/flake-regressions), which includes 259 flakes with their expected evaluation results. Currently, the action runs the first 25 flakes due to the full test suite's extensive runtime. A manually triggered action may be implemented later to run the entire test suite. - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md b/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md deleted file mode 100644 index 2caca9a81..000000000 --- a/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: "Support unit prefixes in configuration settings" -prs: 10668 ---- - -Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/opt-common.md#opt-min-free) to set the minimum free space to 1 gigabyte. - -This enhancement was extracted from [#7851](https://github.com/NixOS/nix/pull/7851) and is also useful for PR [#10661](https://github.com/NixOS/nix/pull/10661). - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md deleted file mode 100644 index 1d623e952..000000000 --- a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: "nix3-build: show all FOD errors with `--keep-going`" -prs: 10734 ---- - -The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. - -Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) - -[`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) diff --git a/doc/manual/rl-next/10855-meson.md b/doc/manual/rl-next/10855-meson.md deleted file mode 100644 index 0ab71390f..000000000 --- a/doc/manual/rl-next/10855-meson.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -synopsis: "Build with Meson" -prs: -- 10378 -- 10855 -- 10904 -- 10908 -- 10914 -- 10933 -- 10936 -- 10954 -- 10955 -- 10967 -- 10963 -- 10973 -- 11034 -- 11054 -- 11055 -- 11064 -- 11060 -- 11155 -issues: -- 2503 ---- - -These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. - -Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). - -Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) -Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) diff --git a/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md b/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md deleted file mode 100644 index 8a348a9ad..000000000 --- a/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -synopsis: "Eval cache: fix cache regressions" -prs: 11086 -issues: 10570 ---- - -This update addresses two bugs in the evaluation cache system: - -1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. -2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. - -These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. - -Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) diff --git a/doc/manual/rl-next/9063-introduce-libnixflake.md b/doc/manual/rl-next/9063-introduce-libnixflake.md deleted file mode 100644 index fd3645446..000000000 --- a/doc/manual/rl-next/9063-introduce-libnixflake.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: "Introduce `libnixflake`" -prs: 9063 ---- - -A new library, `libnixflake`, has been introduced to better separate the Flakes layer within Nix. This change refactors the codebase to encapsulate Flakes-specific functionality within its own library. - -See the commits in the pull request for detailed changes, with the only significant code modifications happening in the initial commit. - -This change was alluded to in [RFC 134](https://github.com/nixos/rfcs/blob/master/rfcs/0134-nix-store-layer.md) and is a step towards a more modular and maintainable codebase. - -Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) diff --git a/doc/manual/rl-next/drop-vendored-toml11.md b/doc/manual/rl-next/drop-vendored-toml11.md deleted file mode 100644 index 8dd786c44..000000000 --- a/doc/manual/rl-next/drop-vendored-toml11.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: Stop vendoring toml11 ---- - -We don't apply any patches to it, and vendoring it locks users into -bugs (it hasn't been updated since its introduction in late 2021). - -Author: [**Winter (@winterqt)**](https://github.com/winterqt) diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md deleted file mode 100644 index ff81c9cb1..000000000 --- a/doc/manual/rl-next/harden-user-sandboxing.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: Harden the user sandboxing -significance: significant -issues: ---- - -The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. - -This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. - -Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) -Co-authors: [**@edolstra**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md deleted file mode 100644 index b9e4b3fb3..000000000 --- a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -synopsis: "`nix-shell ` looks for `shell.nix`" -significance: significant -issues: -- 496 -- 2279 -- 4529 -- 5431 -- 11053 -prs: -- 11057 ---- - -`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. - -Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. -This adjustment addresses a commonly reported problem. - -This also applies to `nix-shell` shebang scripts. Consider the following example: - -```shell -#!/usr/bin/env nix-shell -#!nix-shell -i bash -``` - -This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. - -The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. - -Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) diff --git a/doc/manual/rl-next/pipe-operators.md b/doc/manual/rl-next/pipe-operators.md deleted file mode 100644 index b4cbe30e3..000000000 --- a/doc/manual/rl-next/pipe-operators.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -synopsis: "Add `pipe-operators` experimental feature" -prs: -- 11131 ---- - -This is a draft implementation of [RFC 0148](https://github.com/NixOS/rfcs/pull/148). - -The `pipe-operators` experimental feature adds [`<|` and `|>` operators][pipe operators] to the Nix language. -*a* `|>` *b* is equivalent to the function application *b* *a*, and -*a* `<|` *b* is equivalent to the function application *a* *b*. - -For example: - -``` -nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 -9 - -nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 -7 -``` - -`<|` and `|>` are right and left associative, respectively, and have lower precedence than any other operator. -These properties may change in future releases. - -See [the RFC](https://github.com/NixOS/rfcs/pull/148) for more examples and rationale. - -[pipe operators]: @docroot@/language/operators.md#pipe-operators diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md deleted file mode 100644 index fa241ebc1..000000000 --- a/doc/manual/rl-next/repl-doc-renders-doc-comments.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -synopsis: "`nix-repl`'s `:doc` shows documentation comments" -significance: significant -issues: -- 3904 -- 10771 -prs: -- 1652 -- 9054 -- 11072 ---- - -`nix repl` has a `:doc` command that previously only rendered documentation for internally defined functions. -This feature has been extended to also render function documentation comments, in accordance with [RFC 145]. - -Example: - -``` -nix-repl> :doc lib.toFunction -Function toFunction - … defined at /home/user/h/nixpkgs/lib/trivial.nix:1072:5 - - Turns any non-callable values into constant functions. Returns - callable values as is. - -Inputs - - v - - : Any value - -Examples - - :::{.example} - -## lib.trivial.toFunction usage example - - | nix-repl> lib.toFunction 1 2 - | 1 - | - | nix-repl> lib.toFunction (x: x + 1) 2 - | 3 - - ::: -``` - -Known limitations: -- It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. -- Some extensions to markdown are not yet supported, as you can see in the example above. - -We'd like to acknowledge [Yingchi Long (@inclyc)](https://github.com/inclyc) for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as [@sternenseemann](https://github.com/sternenseemann) and [Johannes Kirschbauer (@hsjobeki)](https://github.com/hsjobeki) for their contributions, proposals, and their work on [RFC 145]. - -Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) - -[RFC 145]: https://github.com/NixOS/rfcs/pull/145 diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md deleted file mode 100644 index dd96bf203..000000000 --- a/doc/manual/rl-next/shebang-relative.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -synopsis: "`nix-shell` shebang uses relative path" -prs: -- 5088 -- 11058 -issues: -- 4232 ---- - - -Relative [path](@docroot@/language/types.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary.md?highlight=base%20directory#gloss-base-directory). -Previously they were resolved relative to the current working directory. - -For example, consider the following script in `~/myproject/say-hi`: - -```shell -#!/usr/bin/env nix-shell -#!nix-shell --expr 'import ./shell.nix' -#!nix-shell --arg toolset './greeting-tools.nix' -#!nix-shell -i bash -hello -``` - -Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: - -```console -[hostname:~]$ ./myproject/say-hi -error: - … while calling the 'import' builtin - at «string»:1:2: - 1| (import ./shell.nix) - | ^ - - error: path '/home/user/shell.nix' does not exist -``` - -Since this release, `nix-shell` resolves `shell.nix` relative to the script's location, and `~/myproject/shell.nix` is used. - -```console -$ ./myproject/say-hi -Hello, world! -``` - -**Opt-out** - -This is technically a breaking change, so we have added an option so you can adapt independently of your Nix update. -The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. -This option will be removed in a future release. - -**`nix` command shebang** - -The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. - -Example: - -```shell -#!/usr/bin/env nix -#!nix develop -#!nix --expr ``import ./shell.nix`` -#!nix -c bash -hello -``` - -Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) diff --git a/doc/manual/rl-next/tarball-fixes.md b/doc/manual/rl-next/tarball-fixes.md deleted file mode 100644 index c938e9db6..000000000 --- a/doc/manual/rl-next/tarball-fixes.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: "Improve handling of tarballs that don't consist of a single top-level directory" -prs: -- 11195 ---- - -In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. The new behaviour is that *only* if the tarball consists of a single directory, the top-level path component of the files in the tarball is removed (similar to `tar`'s `--strip-components=1`). - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 3918faeb2..8739599a0 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -127,6 +127,7 @@ - [Contributing](development/contributing.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} + - [Release 2.24 (2024-07-31)](release-notes/rl-2.24.md) - [Release 2.23 (2024-06-03)](release-notes/rl-2.23.md) - [Release 2.22 (2024-04-23)](release-notes/rl-2.22.md) - [Release 2.21 (2024-03-11)](release-notes/rl-2.21.md) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md new file mode 100644 index 000000000..c68072573 --- /dev/null +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -0,0 +1,300 @@ +# Release 2.24.0 (2024-07-31) + +### Significant changes + +- Harden the user sandboxing + + The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. + + This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. + + Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) + Co-authors: [**@edolstra**](https://github.com/edolstra) + +- `nix-shell ` looks for `shell.nix` [#496](https://github.com/NixOS/nix/issues/496) [#2279](https://github.com/NixOS/nix/issues/2279) [#4529](https://github.com/NixOS/nix/issues/4529) [#5431](https://github.com/NixOS/nix/issues/5431) [#11053](https://github.com/NixOS/nix/issues/11053) [#11057](https://github.com/NixOS/nix/pull/11057) + + `nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + + Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. + This adjustment addresses a commonly reported problem. + + This also applies to `nix-shell` shebang scripts. Consider the following example: + + ```shell + #!/usr/bin/env nix-shell + #!nix-shell -i bash + ``` + + This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + + The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. + + Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) + +- `nix-repl`'s `:doc` shows documentation comments [#3904](https://github.com/NixOS/nix/issues/3904) [#10771](https://github.com/NixOS/nix/issues/10771) [#1652](https://github.com/NixOS/nix/pull/1652) [#9054](https://github.com/NixOS/nix/pull/9054) [#11072](https://github.com/NixOS/nix/pull/11072) + + `nix repl` has a `:doc` command that previously only rendered documentation for internally defined functions. + This feature has been extended to also render function documentation comments, in accordance with [RFC 145]. + + Example: + + ``` + nix-repl> :doc lib.toFunction + Function toFunction + … defined at /home/user/h/nixpkgs/lib/trivial.nix:1072:5 + + Turns any non-callable values into constant functions. Returns + callable values as is. + + Inputs + + v + + : Any value + + Examples + + :::{.example} + + ## lib.trivial.toFunction usage example + + | nix-repl> lib.toFunction 1 2 + | 1 + | + | nix-repl> lib.toFunction (x: x + 1) 2 + | 3 + + ::: + ``` + + Known limitations: + - It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. + - Some extensions to markdown are not yet supported, as you can see in the example above. + + We'd like to acknowledge [Yingchi Long (@inclyc)](https://github.com/inclyc) for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as [@sternenseemann](https://github.com/sternenseemann) and [Johannes Kirschbauer (@hsjobeki)](https://github.com/hsjobeki) for their contributions, proposals, and their work on [RFC 145]. + + Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) + + [RFC 145]: https://github.com/NixOS/rfcs/pull/145 + +### Other changes + +- Solve `cached failure of attribute X` [#9165](https://github.com/NixOS/nix/issues/9165) [#10513](https://github.com/NixOS/nix/issues/10513) [#10564](https://github.com/NixOS/nix/pull/10564) + + This eliminates all "cached failure of attribute X" messages by forcing evaluation of the original value when needed to show the exception to the user. This enhancement improves error reporting by providing the underlying message and stack trace. + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Run the flake regressions test suite [#10603](https://github.com/NixOS/nix/pull/10603) + + This update introduces a GitHub action to run a subset of the [flake regressions test suite](https://github.com/NixOS/flake-regressions), which includes 259 flakes with their expected evaluation results. Currently, the action runs the first 25 flakes due to the full test suite's extensive runtime. A manually triggered action may be implemented later to run the entire test suite. + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Support unit prefixes in configuration settings [#10668](https://github.com/NixOS/nix/pull/10668) + + Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/opt-common.md#opt-min-free) to set the minimum free space to 1 gigabyte. + + This enhancement was extracted from [#7851](https://github.com/NixOS/nix/pull/7851) and is also useful for PR [#10661](https://github.com/NixOS/nix/pull/10661). + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- nix3-build: show all FOD errors with `--keep-going` [#10734](https://github.com/NixOS/nix/pull/10734) + + The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. + + Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) + + [`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) + +- Build with Meson [#2503](https://github.com/NixOS/nix/issues/2503) [#10378](https://github.com/NixOS/nix/pull/10378) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10963](https://github.com/NixOS/nix/pull/10963) [#10967](https://github.com/NixOS/nix/pull/10967) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11060](https://github.com/NixOS/nix/pull/11060) [#11064](https://github.com/NixOS/nix/pull/11064) [#11155](https://github.com/NixOS/nix/pull/11155) + + These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. + + Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). + + Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) + Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) + +- Eval cache: fix cache regressions [#10570](https://github.com/NixOS/nix/issues/10570) [#11086](https://github.com/NixOS/nix/pull/11086) + + This update addresses two bugs in the evaluation cache system: + + 1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. + 2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. + + These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. + + Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) + +- Introduce `libnixflake` [#9063](https://github.com/NixOS/nix/pull/9063) + + A new library, `libnixflake`, has been introduced to better separate the Flakes layer within Nix. This change refactors the codebase to encapsulate Flakes-specific functionality within its own library. + + See the commits in the pull request for detailed changes, with the only significant code modifications happening in the initial commit. + + This change was alluded to in [RFC 134](https://github.com/nixos/rfcs/blob/master/rfcs/0134-nix-store-layer.md) and is a step towards a more modular and maintainable codebase. + + Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) + +- CL options `--arg-from-file` and `--arg-from-stdin` [#9913](https://github.com/NixOS/nix/pull/9913) + + The `--debugger` now prints source location information, instead of the + pointers of source location information. Before: + + ``` + nix-repl> :bt + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + 0x600001522598 + ``` + + After: + + ``` + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + /nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27 + + 131| + 132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs; + | ^ + 133| in + ``` + +- Make `nix store gc` use the auto-GC policy [#7851](https://github.com/NixOS/nix/pull/7851) + + + +- Stop vendoring toml11 + + We don't apply any patches to it, and vendoring it locks users into + bugs (it hasn't been updated since its introduction in late 2021). + + Author: [**Winter (@winterqt)**](https://github.com/winterqt) + +- Rename hash format `base32` to `nix32` [#8678](https://github.com/NixOS/nix/pull/8678) + + Hash format `base32` was renamed to `nix32` since it used a special nix-specific character set for + [Base32](https://en.wikipedia.org/wiki/Base32). + + ## Deprecation: Use `nix32` instead of `base32` as `toHashFormat` + + For the builtin `convertHash`, the `toHashFormat` parameter now accepts the same hash formats as the `--to`/`--from` + parameters of the `nix hash conert` command: `"base16"`, `"nix32"`, `"base64"`, and `"sri"`. The former `"base32"` value + remains as a deprecated alias for `"base32"`. Please convert your code from: + + ```nix + builtins.convertHash { inherit hash hashAlgo; toHashFormat = "base32";} + ``` + + to + + ```nix + builtins.convertHash { inherit hash hashAlgo; toHashFormat = "nix32";} + ``` + +- Add `pipe-operators` experimental feature [#11131](https://github.com/NixOS/nix/pull/11131) + + This is a draft implementation of [RFC 0148](https://github.com/NixOS/rfcs/pull/148). + + The `pipe-operators` experimental feature adds [`<|` and `|>` operators][pipe operators] to the Nix language. + *a* `|>` *b* is equivalent to the function application *b* *a*, and + *a* `<|` *b* is equivalent to the function application *a* *b*. + + For example: + + ``` + nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 + 9 + + nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 + 7 + ``` + + `<|` and `|>` are right and left associative, respectively, and have lower precedence than any other operator. + These properties may change in future releases. + + See [the RFC](https://github.com/NixOS/rfcs/pull/148) for more examples and rationale. + + [pipe operators]: @docroot@/language/operators.md#pipe-operators + +- `nix-shell` shebang uses relative path [#4232](https://github.com/NixOS/nix/issues/4232) [#5088](https://github.com/NixOS/nix/pull/5088) [#11058](https://github.com/NixOS/nix/pull/11058) + + + Relative [path](@docroot@/language/types.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary.md?highlight=base%20directory#gloss-base-directory). + Previously they were resolved relative to the current working directory. + + For example, consider the following script in `~/myproject/say-hi`: + + ```shell + #!/usr/bin/env nix-shell + #!nix-shell --expr 'import ./shell.nix' + #!nix-shell --arg toolset './greeting-tools.nix' + #!nix-shell -i bash + hello + ``` + + Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: + + ```console + [hostname:~]$ ./myproject/say-hi + error: + … while calling the 'import' builtin + at «string»:1:2: + 1| (import ./shell.nix) + | ^ + + error: path '/home/user/shell.nix' does not exist + ``` + + Since this release, `nix-shell` resolves `shell.nix` relative to the script's location, and `~/myproject/shell.nix` is used. + + ```console + $ ./myproject/say-hi + Hello, world! + ``` + + **Opt-out** + + This is technically a breaking change, so we have added an option so you can adapt independently of your Nix update. + The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. + This option will be removed in a future release. + + **`nix` command shebang** + + The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. + + Example: + + ```shell + #!/usr/bin/env nix + #!nix develop + #!nix --expr ``import ./shell.nix`` + #!nix -c bash + hello + ``` + + Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) + +- Improve handling of tarballs that don't consist of a single top-level directory [#11195](https://github.com/NixOS/nix/pull/11195) + + In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. The new behaviour is that *only* if the tarball consists of a single directory, the top-level path component of the files in the tarball is removed (similar to `tar`'s `--strip-components=1`). + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Improve handling of tarballs that don't consist of a single top-level directory [#11195](https://github.com/NixOS/nix/pull/11195) + + In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Setting to warn about large paths [#10778](https://github.com/NixOS/nix/pull/10778) + + Nix can now warn when evaluation of a Nix expression causes a large + path to be copied to the Nix store. The threshold for this warning can + be configured using the `warn-large-path-threshold` setting, + e.g. `--warn-large-path-threshold 100M`. + + +# Contributors + +Querying GitHub API for ee86e7f361c55c8c7dc2e45c3868802af249aeff, to get handle for MostAwesomeDude@gmail.com From 733c816d3493459e0f7f899111b9ad19fe04147a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 31 Jul 2024 15:04:18 -0500 Subject: [PATCH 750/910] Small windows cross fixes (#11230) --- src/libexpr/eval.cc | 4 +++- src/libstore/gc.cc | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 746ccab2a..de5d85821 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2860,8 +2860,10 @@ void EvalState::printStatistics() topObj["cpuTime"] = cpuTime; #endif topObj["time"] = { +#ifndef _WIN32 // TODO implement {"cpu", cpuTime}, -#ifdef HAVE_BOEHMGC +#endif +#if HAVE_BOEHMGC {GC_is_incremental_mode() ? "gcNonIncremental" : "gc", gcFullOnlyTime}, {GC_is_incremental_mode() ? "gcNonIncrementalFraction" : "gcFraction", gcFullOnlyTime / cpuTime}, #endif diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index c865fddd7..1494712da 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -891,7 +891,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) void LocalStore::autoGC(bool sync) { -#ifdef HAVE_STATVFS +#if HAVE_STATVFS static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); auto getAvail = [this]() -> uint64_t { From 22ad0e653f9abff03de97fe928eaf13a99f7e8a1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 22:14:27 +0200 Subject: [PATCH 751/910] Edit release notes --- doc/manual/src/release-notes/rl-2.24.md | 42 +++++++------------------ 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md index c68072573..cb82b1def 100644 --- a/doc/manual/src/release-notes/rl-2.24.md +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -2,11 +2,11 @@ ### Significant changes -- Harden the user sandboxing +- Harden user sandboxing The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. - This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. + This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531). Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) Co-authors: [**@edolstra**](https://github.com/edolstra) @@ -99,7 +99,7 @@ Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) -- nix3-build: show all FOD errors with `--keep-going` [#10734](https://github.com/NixOS/nix/pull/10734) +- `nix build`: show all FOD errors with `--keep-going` [#10734](https://github.com/NixOS/nix/pull/10734) The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. @@ -109,22 +109,20 @@ - Build with Meson [#2503](https://github.com/NixOS/nix/issues/2503) [#10378](https://github.com/NixOS/nix/pull/10378) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10963](https://github.com/NixOS/nix/pull/10963) [#10967](https://github.com/NixOS/nix/pull/10967) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11060](https://github.com/NixOS/nix/pull/11060) [#11064](https://github.com/NixOS/nix/pull/11064) [#11155](https://github.com/NixOS/nix/pull/11155) - These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. + These changes aim to replace the use of autotools and `make` with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) -- Eval cache: fix cache regressions [#10570](https://github.com/NixOS/nix/issues/10570) [#11086](https://github.com/NixOS/nix/pull/11086) +- Evaluation cache: fix cache regressions [#10570](https://github.com/NixOS/nix/issues/10570) [#11086](https://github.com/NixOS/nix/pull/11086) This update addresses two bugs in the evaluation cache system: - 1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. + 1. Regression in #10570: The evaluation cache was not being persisted in `nix develop`. 2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. - These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. - Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) - Introduce `libnixflake` [#9063](https://github.com/NixOS/nix/pull/9063) @@ -137,9 +135,9 @@ Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) -- CL options `--arg-from-file` and `--arg-from-stdin` [#9913](https://github.com/NixOS/nix/pull/9913) +- CLI options `--arg-from-file` and `--arg-from-stdin` [#9913](https://github.com/NixOS/nix/pull/9913) - The `--debugger` now prints source location information, instead of the +- The `--debugger` now prints source location information, instead of the pointers of source location information. Before: ``` @@ -160,11 +158,7 @@ 133| in ``` -- Make `nix store gc` use the auto-GC policy [#7851](https://github.com/NixOS/nix/pull/7851) - - - -- Stop vendoring toml11 +- Stop vendoring `toml11` We don't apply any patches to it, and vendoring it locks users into bugs (it hasn't been updated since its introduction in late 2021). @@ -176,7 +170,7 @@ Hash format `base32` was renamed to `nix32` since it used a special nix-specific character set for [Base32](https://en.wikipedia.org/wiki/Base32). - ## Deprecation: Use `nix32` instead of `base32` as `toHashFormat` + **Deprecation**: Use `nix32` instead of `base32` as `toHashFormat` For the builtin `convertHash`, the `toHashFormat` parameter now accepts the same hash formats as the `--to`/`--from` parameters of the `nix hash conert` command: `"base16"`, `"nix32"`, `"base64"`, and `"sri"`. The former `"base32"` value @@ -233,7 +227,7 @@ hello ``` - Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: + Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory, such as the user's home directory in this example: ```console [hostname:~]$ ./myproject/say-hi @@ -259,20 +253,6 @@ The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. This option will be removed in a future release. - **`nix` command shebang** - - The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. - - Example: - - ```shell - #!/usr/bin/env nix - #!nix develop - #!nix --expr ``import ./shell.nix`` - #!nix -c bash - hello - ``` - Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) - Improve handling of tarballs that don't consist of a single top-level directory [#11195](https://github.com/NixOS/nix/pull/11195) From f136ec5290128470244006579d935653df355bdf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 22:16:44 +0200 Subject: [PATCH 752/910] Add contributors --- doc/manual/src/release-notes/rl-2.24.md | 46 ++++++++++++++++++- .../data/release-credits-email-to-handle.json | 3 +- .../data/release-credits-handle-to-name.json | 3 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md index cb82b1def..5479cd3b9 100644 --- a/doc/manual/src/release-notes/rl-2.24.md +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -277,4 +277,48 @@ # Contributors -Querying GitHub API for ee86e7f361c55c8c7dc2e45c3868802af249aeff, to get handle for MostAwesomeDude@gmail.com +This release was made possible by the following 43 contributors: + +- Andreas Rammhold [**(@andir)**](https://github.com/andir) +- Andrew Marshall [**(@amarshall)**](https://github.com/amarshall) +- Brian McKenna [**(@puffnfresh)**](https://github.com/puffnfresh) +- Cameron [**(@SkamDart)**](https://github.com/SkamDart) +- Cole Helbling [**(@cole-h)**](https://github.com/cole-h) +- Corbin Simpson [**(@MostAwesomeDude)**](https://github.com/MostAwesomeDude) +- Eelco Dolstra [**(@edolstra)**](https://github.com/edolstra) +- Emily [**(@emilazy)**](https://github.com/emilazy) +- Enno Richter [**(@elohmeier)**](https://github.com/elohmeier) +- Farid Zakaria [**(@fzakaria)**](https://github.com/fzakaria) +- HaeNoe [**(@haenoe)**](https://github.com/haenoe) +- Hamir Mahal [**(@hamirmahal)**](https://github.com/hamirmahal) +- Harmen [**(@alicebob)**](https://github.com/alicebob) +- Ivan Trubach [**(@tie)**](https://github.com/tie) +- Jared Baur [**(@jmbaur)**](https://github.com/jmbaur) +- John Ericson [**(@Ericson2314)**](https://github.com/Ericson2314) +- Jonathan De Troye [**(@detroyejr)**](https://github.com/detroyejr) +- Jörg Thalheim [**(@Mic92)**](https://github.com/Mic92) +- Klemens Nanni [**(@klemensn)**](https://github.com/klemensn) +- Las Safin [**(@L-as)**](https://github.com/L-as) +- Lexi Mattick [**(@kognise)**](https://github.com/kognise) +- Matthew Bauer [**(@matthewbauer)**](https://github.com/matthewbauer) +- Max “Goldstein” Siling [**(@GoldsteinE)**](https://github.com/GoldsteinE) +- Mingye Wang [**(@Artoria2e5)**](https://github.com/Artoria2e5) +- Philip Taron [**(@philiptaron)**](https://github.com/philiptaron) +- Pierre Bourdon [**(@delroth)**](https://github.com/delroth) +- Pino Toscano [**(@pinotree)**](https://github.com/pinotree) +- RTUnreal [**(@RTUnreal)**](https://github.com/RTUnreal) +- Robert Hensing [**(@roberth)**](https://github.com/roberth) +- Romain Neil [**(@romain-neil)**](https://github.com/romain-neil) +- Ryan Hendrickson [**(@rhendric)**](https://github.com/rhendric) +- Sergei Trofimovich [**(@trofi)**](https://github.com/trofi) +- Shogo Takata [**(@pineapplehunter)**](https://github.com/pineapplehunter) +- Siddhant Kumar [**(@siddhantk232)**](https://github.com/siddhantk232) +- Silvan Mosberger [**(@infinisil)**](https://github.com/infinisil) +- Théophane Hufschmitt [**(@thufschmitt)**](https://github.com/thufschmitt) +- Valentin Gagarin [**(@fricklerhandwerk)**](https://github.com/fricklerhandwerk) +- Winter [**(@winterqt)**](https://github.com/winterqt) +- jade [**(@lf-)**](https://github.com/lf-) +- kirillrdy [**(@kirillrdy)**](https://github.com/kirillrdy) +- pennae [**(@pennae)**](https://github.com/pennae) +- poweredbypie [**(@poweredbypie)**](https://github.com/poweredbypie) +- tomberek [**(@tomberek)**](https://github.com/tomberek) diff --git a/maintainers/data/release-credits-email-to-handle.json b/maintainers/data/release-credits-email-to-handle.json index 573ec2b31..cddc1a6e7 100644 --- a/maintainers/data/release-credits-email-to-handle.json +++ b/maintainers/data/release-credits-email-to-handle.json @@ -47,5 +47,6 @@ "pennae@lix.systems": "pennae", "delroth@gmail.com": "delroth", "enno@nerdworks.de": "elohmeier", - "mjbauer95@gmail.com": "matthewbauer" + "mjbauer95@gmail.com": "matthewbauer", + "MostAwesomeDude@gmail.com": "MostAwesomeDude" } \ No newline at end of file diff --git a/maintainers/data/release-credits-handle-to-name.json b/maintainers/data/release-credits-handle-to-name.json index d68311dde..abf9ed05b 100644 --- a/maintainers/data/release-credits-handle-to-name.json +++ b/maintainers/data/release-credits-handle-to-name.json @@ -40,5 +40,6 @@ "siddhantk232": "Siddhant Kumar", "winterqt": "Winter", "GoldsteinE": "Max \u201cGoldstein\u201d Siling", - "pennae": null + "pennae": null, + "MostAwesomeDude": "Corbin Simpson" } \ No newline at end of file From 8ff169715dde088a4c286b3379d3f548eafe3221 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Mon, 29 Apr 2024 08:15:16 -0600 Subject: [PATCH 753/910] docs: clarify how the different kinds of installables are selected Change-Id: I146736bb97ebe035e04be69ce9fb60a557e38c6c --- src/nix/nix.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nix/nix.md b/src/nix/nix.md index f958ce09a..34f73d032 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -59,9 +59,13 @@ These are command line arguments that represent something that can be realised i The following types of installable are supported by most commands: - [Flake output attribute](#flake-output-attribute) (experimental) + - This is the default - [Store path](#store-path) + - This is assumed if the argument is a Nix store path or a symlink to a Nix store path - [Nix file](#nix-file), optionally qualified by an attribute path + - Specified with `--file`/`-f` - [Nix expression](#nix-expression), optionally qualified by an attribute path + - Specified with `--expr`/`-E` For most commands, if no installable is specified, `.` is assumed. That is, Nix will operate on the default flake output attribute of the flake in the current directory. From cb5a5dd4f3064e3235fa908a44a5d074ef3c4205 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Mon, 29 Apr 2024 08:09:50 -0600 Subject: [PATCH 754/910] docs: clarify how ^ works for -E/-f installables We didn't even realize you *could* use this syntax with -E and -f, much less that the attribute path could be *empty*. Change-Id: Id1a6715609f3a76a5ce477bd43a7832effbbe07b --- src/nix/nix.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/nix/nix.md b/src/nix/nix.md index 34f73d032..56587d0b2 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -182,9 +182,10 @@ that contains programs, and a `dev` output that provides development artifacts like C/C++ header files. The outputs on which `nix` commands operate are determined as follows: -* You can explicitly specify the desired outputs using the syntax - *installable*`^`*output1*`,`*...*`,`*outputN*. For example, you can - obtain the `dev` and `static` outputs of the `glibc` package: +* You can explicitly specify the desired outputs using the syntax *installable*`^`*output1*`,`*...*`,`*outputN* — that is, a caret followed immediately by a comma-separated list of derivation outputs to select. + For installables specified as [Flake output attributes](#flake-output-attribute) or [Store paths](#store-path), the output is specified in the same argument: + + For example, you can obtain the `dev` and `static` outputs of the `glibc` package: ```console # nix build 'nixpkgs#glibc^dev,static' @@ -199,6 +200,19 @@ operate are determined as follows: … ``` + For `-e`/`--expr` and `-f`/`--file`, the derivation output is specified as part of the attribute path: + + ```console + $ nix build -f '' 'glibc^dev,static' + $ nix build --impure -E 'import { }' 'glibc^dev,static' + ``` + + This syntax is the same even if the actual attribute path is empty: + + ```console + $ nix build -E 'let pkgs = import { }; in pkgs.glibc' '^dev,static' + ``` + * You can also specify that *all* outputs should be used using the syntax *installable*`^*`. For example, the following shows the size of all outputs of the `glibc` package in the binary cache: From 794a50065b33cbaaaf1f6ff9dbf954bb5eedfc14 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 22:33:41 +0200 Subject: [PATCH 755/910] base32 -> nix32 --- doc/manual/src/release-notes/rl-2.24.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md index 5479cd3b9..5bcc1d79c 100644 --- a/doc/manual/src/release-notes/rl-2.24.md +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -174,7 +174,7 @@ For the builtin `convertHash`, the `toHashFormat` parameter now accepts the same hash formats as the `--to`/`--from` parameters of the `nix hash conert` command: `"base16"`, `"nix32"`, `"base64"`, and `"sri"`. The former `"base32"` value - remains as a deprecated alias for `"base32"`. Please convert your code from: + remains as a deprecated alias for `"nix32"`. Please convert your code from: ```nix builtins.convertHash { inherit hash hashAlgo; toHashFormat = "base32";} From 6ed67d35ed51905434eba7d4a167b43aa581478f Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Wed, 31 Jul 2024 17:39:43 -0400 Subject: [PATCH 756/910] docs: add variables; rework scope (#11062) Co-authored-by: Valentin Gagarin --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/language/identifiers.md | 2 +- doc/manual/src/language/scope.md | 32 ++++++++++++++++++-------- doc/manual/src/language/variables.md | 10 ++++++++ 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 doc/manual/src/language/variables.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 8739599a0..7661f5f62 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -28,6 +28,7 @@ - [Data Types](language/types.md) - [String context](language/string-context.md) - [Syntax and semantics](language/syntax.md) + - [Variables](language/variables.md) - [Identifiers](language/identifiers.md) - [Scoping rules](language/scope.md) - [String interpolation](language/string-interpolation.md) diff --git a/doc/manual/src/language/identifiers.md b/doc/manual/src/language/identifiers.md index c9e981da6..bd58a9b36 100644 --- a/doc/manual/src/language/identifiers.md +++ b/doc/manual/src/language/identifiers.md @@ -22,7 +22,7 @@ A name can be an [identifier](#identifier) or a [string literal](./syntax.md#str > > *name* → *identifier* | *string* -Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting attributes). +Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting-attributes). # Keywords diff --git a/doc/manual/src/language/scope.md b/doc/manual/src/language/scope.md index 5c6aed38d..96c7468eb 100644 --- a/doc/manual/src/language/scope.md +++ b/doc/manual/src/language/scope.md @@ -1,14 +1,28 @@ # Scoping rules -Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. +A *scope* in the Nix language is a dictionary keyed by [name](./identifiers.md#names), mapping each name to an expression and a *definition type*. +The definition type is either *explicit* or *implicit*. +Each entry in this dictionary is a *definition*. -* primary scope: explicitly-bound variables - * [`let`](./syntax.md#let-expressions) - * [`inherit`](./syntax.md#inheriting-attributes) - * [function](./syntax.md#functions) arguments +Explicit definitions are created by the following expressions: +- [let-expressions](syntax.md#let-expressions) +- [recursive attribute set literals](syntax.md#recursive-sets) (`rec`) +- [function literals](syntax.md#functions) -* secondary scope: implicitly-bound variables - * [`with`](./syntax.md#with-expressions) +Implicit definitions are only created by [with-expressions](./syntax.md#with-expressions). -Primary scope takes precedence over secondary scope. -See [`with`](./syntax.md#with-expressions) for a detailed example. +Every expression is *enclosed* by a scope. +The outermost expression is enclosed by the [built-in, global scope](./builtins.md), which contains only explicit definitions. +The respective definition types *extend* their enclosing scope by adding new definitions, or replacing existing ones with the same name. +An explicit definition can replace a definition of any type; an implicit definition can only replace another implicit definition. + +Each of the above expressions defines which of its subexpressions are enclosed by the extended scope. +In all other cases, the same scope that encloses an expression is the enclosing scope for its subexpressions. + +The Nix language is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope); +the value of a variable is determined only by the variable's enclosing scope, and not by the dynamic context in which the variable is evaluated. + +> **Note** +> +> Expressions entered into the [Nix REPL](@docroot@/command-ref/new-cli/nix3-repl.md) are enclosed by a scope that can be extended by command line arguments or previous REPL commands. +> These ways of extending scope are not, strictly speaking, part of the Nix language. diff --git a/doc/manual/src/language/variables.md b/doc/manual/src/language/variables.md new file mode 100644 index 000000000..af6aff8a2 --- /dev/null +++ b/doc/manual/src/language/variables.md @@ -0,0 +1,10 @@ +# Variables + +A *variable* is an [identifier](identifiers.md) used as an expression. + +> **Syntax** +> +> *expression* → *identifier* + +A variable must have the same name as a definition in the [scope](./scope.md) that encloses it. +The value of a variable is the value of the corresponding expression in the enclosing scope. From 9e8afc68e554c5c45b8e9f387d4fcf4ac0174a6a Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Wed, 31 Jul 2024 19:07:57 -0400 Subject: [PATCH 757/910] docs: add language/string-literals.md --- doc/manual/redirects.js | 1 + doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/language/identifiers.md | 2 +- .../src/language/string-interpolation.md | 4 + doc/manual/src/language/string-literals.md | 189 ++++++++++++++++++ doc/manual/src/language/syntax.md | 172 +--------------- doc/manual/src/language/types.md | 2 +- 7 files changed, 199 insertions(+), 172 deletions(-) create mode 100644 doc/manual/src/language/string-literals.md diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index beef6ef4a..cb8cd18fa 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -344,6 +344,7 @@ const redirects = { }, "language/syntax.html": { "scoping-rules": "scoping.html", + "string-literal": "string-literals.html", }, "installation/installing-binary.html": { "linux": "uninstall.html#linux", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 7661f5f62..eef7d189c 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -29,6 +29,7 @@ - [String context](language/string-context.md) - [Syntax and semantics](language/syntax.md) - [Variables](language/variables.md) + - [String literals](language/string-literals.md) - [Identifiers](language/identifiers.md) - [Scoping rules](language/scope.md) - [String interpolation](language/string-interpolation.md) diff --git a/doc/manual/src/language/identifiers.md b/doc/manual/src/language/identifiers.md index bd58a9b36..a4153b588 100644 --- a/doc/manual/src/language/identifiers.md +++ b/doc/manual/src/language/identifiers.md @@ -16,7 +16,7 @@ An *identifier* is an [ASCII](https://en.wikipedia.org/wiki/ASCII) character seq # Names -A name can be an [identifier](#identifier) or a [string literal](./syntax.md#string-literal). +A name can be an [identifier](#identifier) or a [string literal](string-literals.md). > **Syntax** > diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 1778bdfa0..27780dcbb 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -8,6 +8,10 @@ Such a construct is called *interpolated string*, and the expression inside is a [path]: ./types.md#type-path [attribute set]: ./types.md#attribute-set +> **Syntax** +> +> *interpolation_element* → `${` *expression* `}` + ## Examples ### String diff --git a/doc/manual/src/language/string-literals.md b/doc/manual/src/language/string-literals.md new file mode 100644 index 000000000..7605e8c61 --- /dev/null +++ b/doc/manual/src/language/string-literals.md @@ -0,0 +1,189 @@ +# String literals + +A *string literal* represents a [string](types.md#type-string) value. + +> **Syntax** +> +> *expression* → *string* +> +> *string* → `"` ( *string_char*\* [*interpolation_element*][string interpolation] )* *string_char*\* `"` +> +> *string* → `''` ( *indented_string_char*\* [*interpolation_element*][string interpolation] )* *indented_string_char*\* `''` +> +> *string* → *uri* +> +> *string_char* ~ `[^"$\\]|\$(?!\{)|\\.` +> +> *indented_string_char* ~ `[^$']|\$\$|\$(?!\{)|''[$']|''\\.|'(?!')` +> +> *uri* ~ `[A-Za-z][+\-.0-9A-Za-z]*:[!$%&'*+,\-./0-9:=?@A-Z_a-z~]+` + +Strings can be written in three ways. + +The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. +Strings can span multiple lines. +The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. + +[string interpolation]: ./string-interpolation.md + +The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): + +- Double quote (`"`) + +> **Example** +> +> ```nix +> "\"" +> ``` +> +> "\"" + +- Backslash (`\`) + +> **Example** +> +> ```nix +> "\\" +> ``` +> +> "\\" + +- Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" + +> **Example** +> +> ```nix +> "\${" +> ``` +> +> "\${" + +The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. + +A "double-dollar-curly" (`$${`) can be written literally. + +> **Example** +> +> ```nix +> "$${" +> ``` +> +> "$\${" + +String values are output on the terminal with Nix-specific escaping. +Strings written to files will contain the characters encoded by the escaping. + +The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: + +```nix +'' +This is the first line. +This is the second line. + This is the third line. +'' +``` + +This kind of string literal intelligently strips indentation from +the start of each line. To be precise, it strips from each line a +number of spaces equal to the minimal indentation of the string as a +whole (disregarding the indentation of empty lines). For instance, +the first and second line are indented two spaces, while the third +line is indented four spaces. Thus, two spaces are stripped from +each line, so the resulting string is + +```nix +"This is the first line.\nThis is the second line.\n This is the third line.\n" +``` + +> **Note** +> +> Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. + +> **Warning** +> +> Prefixed tab characters are not stripped. +> +> > **Example** +> > +> > The following indented string is prefixed with tabs: +> > +> > '' +> > all: +> > @echo hello +> > '' +> > +> > "\tall:\n\t\t@echo hello\n" + +Indented strings support [string interpolation]. + +The following must be escaped to represent them in an indented string: + +- `$` is escaped by prefixing it with two single quotes (`''`) + +> **Example** +> +> ```nix +> '' +> ''$ +> '' +> ``` +> +> "$\n" + +- `''` is escaped by prefixing it with one single quote (`'`) + +> **Example** +> +> ```nix +> '' +> ''' +> '' +> ``` +> +> "''\n" + +These special characters are escaped as follows: +- Linefeed (`\n`): `''\n` +- Carriage return (`\r`): `''\r` +- Tab (`\t`): `''\t` + +`''\` escapes any other character. + +A "double-dollar-curly" (`$${`) can be written literally. + +> **Example** +> +> ```nix +> '' +> $${ +> '' +> ``` +> +> "$\${\n" + +Indented strings are primarily useful in that they allow multi-line +string literals to follow the indentation of the enclosing Nix +expression, and that less escaping is typically necessary for +strings representing languages such as shell scripts and +configuration files because `''` is much less common than `"`. +Example: + +```nix +stdenv.mkDerivation { +... +postInstall = + '' + mkdir $out/bin $out/etc + cp foo $out/bin + echo "Hello World" > $out/etc/foo.conf + ${if enableBar then "cp bar $out/bin" else ""} + ''; +... +} +``` + +Finally, as a convenience, *URIs* as defined in appendix B of +[RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as +is*, without quotes. For instance, the string +`"http://example.org/foo.tar.bz2"` can also be written as +`http://example.org/foo.tar.bz2`. diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md index 6108bacd6..daf073aef 100644 --- a/doc/manual/src/language/syntax.md +++ b/doc/manual/src/language/syntax.md @@ -6,175 +6,7 @@ This section covers syntax and semantics of the Nix language. ### String {#string-literal} - *Strings* can be written in three ways. - - The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. - Strings can span multiple lines. - The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. - - [string interpolation]: ./string-interpolation.md - - The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): - - - Double quote (`"`) - - > **Example** - > - > ```nix - > "\"" - > ``` - > - > "\"" - - - Backslash (`\`) - - > **Example** - > - > ```nix - > "\\" - > ``` - > - > "\\" - - - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" - - > **Example** - > - > ```nix - > "\${" - > ``` - > - > "\${" - - The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. - - A "double-dollar-curly" (`$${`) can be written literally. - - > **Example** - > - > ```nix - > "$${" - > ``` - > - > "$\${" - - String values are output on the terminal with Nix-specific escaping. - Strings written to files will contain the characters encoded by the escaping. - - The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: - - ```nix - '' - This is the first line. - This is the second line. - This is the third line. - '' - ``` - - This kind of string literal intelligently strips indentation from - the start of each line. To be precise, it strips from each line a - number of spaces equal to the minimal indentation of the string as a - whole (disregarding the indentation of empty lines). For instance, - the first and second line are indented two spaces, while the third - line is indented four spaces. Thus, two spaces are stripped from - each line, so the resulting string is - - ```nix - "This is the first line.\nThis is the second line.\n This is the third line.\n" - ``` - - > **Note** - > - > Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. - - > **Warning** - > - > Prefixed tab characters are not stripped. - > - > > **Example** - > > - > > The following indented string is prefixed with tabs: - > > - > > '' - > > all: - > > @echo hello - > > '' - > > - > > "\tall:\n\t\t@echo hello\n" - - Indented strings support [string interpolation]. - - The following must be escaped to represent them in an indented string: - - - `$` is escaped by prefixing it with two single quotes (`''`) - - > **Example** - > - > ```nix - > '' - > ''$ - > '' - > ``` - > - > "$\n" - - - `''` is escaped by prefixing it with one single quote (`'`) - - > **Example** - > - > ```nix - > '' - > ''' - > '' - > ``` - > - > "''\n" - - These special characters are escaped as follows: - - Linefeed (`\n`): `''\n` - - Carriage return (`\r`): `''\r` - - Tab (`\t`): `''\t` - - `''\` escapes any other character. - - A "double-dollar-curly" (`$${`) can be written literally. - - > **Example** - > - > ```nix - > '' - > $${ - > '' - > ``` - > - > "$\${\n" - - Indented strings are primarily useful in that they allow multi-line - string literals to follow the indentation of the enclosing Nix - expression, and that less escaping is typically necessary for - strings representing languages such as shell scripts and - configuration files because `''` is much less common than `"`. - Example: - - ```nix - stdenv.mkDerivation { - ... - postInstall = - '' - mkdir $out/bin $out/etc - cp foo $out/bin - echo "Hello World" > $out/etc/foo.conf - ${if enableBar then "cp bar $out/bin" else ""} - ''; - ... - } - ``` - - Finally, as a convenience, *URIs* as defined in appendix B of - [RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as - is*, without quotes. For instance, the string - `"http://example.org/foo.tar.bz2"` can also be written as - `http://example.org/foo.tar.bz2`. +See [String literals](string-literals.md). ### Number {#number-literal} @@ -253,7 +85,7 @@ Attribute sets are written enclosed in curly brackets (`{ }`). Attribute names and attribute values are separated by an equal sign (`=`). Each value can be an arbitrary expression, terminated by a semicolon (`;`) -An attribute name is a string without context, and is denoted by a [name] (an [identifier](./identifiers.md#identifiers) or [string literal](#string-literal)). +An attribute name is a string without context, and is denoted by a [name] (an [identifier](./identifiers.md#identifiers) or [string literal](string-literals.md)). [name]: ./identifiers.md#names diff --git a/doc/manual/src/language/types.md b/doc/manual/src/language/types.md index 229756e6b..82184a8b0 100644 --- a/doc/manual/src/language/types.md +++ b/doc/manual/src/language/types.md @@ -45,7 +45,7 @@ The function [`builtins.isBool`](builtins.md#builtins-isBool) can be used to det A _string_ in the Nix language is an immutable, finite-length sequence of bytes, along with a [string context](string-context.md). Nix does not assume or support working natively with character encodings. -String values without string context can be expressed as [string literals](syntax.md#string-literal). +String values without string context can be expressed as [string literals](string-literals.md). The function [`builtins.isString`](builtins.md#builtins-isString) can be used to determine if a value is a string. ### Path {#type-path} From 17318bc70dd0a3712d0a2c94a180dd9f614c97b0 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Wed, 31 Jul 2024 19:22:17 -0400 Subject: [PATCH 758/910] docs: fix string literal example formatting --- doc/manual/src/language/string-literals.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/language/string-literals.md b/doc/manual/src/language/string-literals.md index 7605e8c61..8f4b75f3e 100644 --- a/doc/manual/src/language/string-literals.md +++ b/doc/manual/src/language/string-literals.md @@ -36,7 +36,7 @@ The following must be escaped to represent them within a string, by prefixing wi > "\"" > ``` > -> "\"" +> "\"" - Backslash (`\`) @@ -46,7 +46,7 @@ The following must be escaped to represent them within a string, by prefixing wi > "\\" > ``` > -> "\\" +> "\\" - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" @@ -56,7 +56,7 @@ The following must be escaped to represent them within a string, by prefixing wi > "\${" > ``` > -> "\${" +> "\${" The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. @@ -68,7 +68,7 @@ A "double-dollar-curly" (`$${`) can be written literally. > "$${" > ``` > -> "$\${" +> "$\${" String values are output on the terminal with Nix-specific escaping. Strings written to files will contain the characters encoded by the escaping. @@ -107,10 +107,11 @@ each line, so the resulting string is > > > > The following indented string is prefixed with tabs: > > -> > '' +> >
''
 > > 	all:
 > > 		@echo hello
 > > ''
+> > 
> > > > "\tall:\n\t\t@echo hello\n" From 617e711820762d27285eb8710ce42d52f6448acd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Aug 2024 10:41:42 +0200 Subject: [PATCH 759/910] 'build' is now 'build.nix' --- maintainers/upload-release.pl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 4c4e2bd6f..731988568 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -42,7 +42,7 @@ my $flakeUrl = $evalInfo->{flake}; my $flakeInfo = decode_json(`nix flake metadata --json "$flakeUrl"` or die) if $flakeUrl; my $nixRev = ($flakeInfo ? $flakeInfo->{revision} : $evalInfo->{jobsetevalinputs}->{nix}->{revision}) or die; -my $buildInfo = decode_json(fetch("$evalUrl/job/build.x86_64-linux", 'application/json')); +my $buildInfo = decode_json(fetch("$evalUrl/job/build.nix.x86_64-linux", 'application/json')); #print Dumper($buildInfo); my $releaseName = $buildInfo->{nixname}; @@ -91,7 +91,7 @@ sub getStorePath { sub copyManual { my $manual; eval { - $manual = getStorePath("build.x86_64-linux", "doc"); + $manual = getStorePath("build.nix.x86_64-linux", "doc"); }; if ($@) { warn "$@"; @@ -240,12 +240,12 @@ if ($haveDocker) { # Upload nix-fallback-paths.nix. write_file("$tmpDir/fallback-paths.nix", "{\n" . - " x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" . - " i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" . - " aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" . - " riscv64-linux = \"" . getStorePath("buildCross.riscv64-unknown-linux-gnu.x86_64-linux") . "\";\n" . - " x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" . - " aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" . + " x86_64-linux = \"" . getStorePath("build.nix.x86_64-linux") . "\";\n" . + " i686-linux = \"" . getStorePath("build.nix.i686-linux") . "\";\n" . + " aarch64-linux = \"" . getStorePath("build.nix.aarch64-linux") . "\";\n" . + " riscv64-linux = \"" . getStorePath("buildCross.nix.riscv64-unknown-linux-gnu.x86_64-linux") . "\";\n" . + " x86_64-darwin = \"" . getStorePath("build.nix.x86_64-darwin") . "\";\n" . + " aarch64-darwin = \"" . getStorePath("build.nix.aarch64-darwin") . "\";\n" . "}\n"); # Upload release files to S3. From 30aca6f243d8d5dcdbbfcf2a36bcdefa9d2aeeee Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Aug 2024 10:43:00 +0200 Subject: [PATCH 760/910] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index ad2261920..5c18f9195 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.24.0 +2.25.0 From b291b61089da1121145d92d6747ad2b18737ad9f Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Thu, 1 Aug 2024 05:14:49 -0400 Subject: [PATCH 761/910] docs: editorial quibbles (#11232) --- doc/manual/src/language/identifiers.md | 3 ++- doc/manual/src/language/scope.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/identifiers.md b/doc/manual/src/language/identifiers.md index bd58a9b36..861ee3e20 100644 --- a/doc/manual/src/language/identifiers.md +++ b/doc/manual/src/language/identifiers.md @@ -16,13 +16,14 @@ An *identifier* is an [ASCII](https://en.wikipedia.org/wiki/ASCII) character seq # Names -A name can be an [identifier](#identifier) or a [string literal](./syntax.md#string-literal). +A *name* can be written as an [identifier](#identifier) or a [string literal](./syntax.md#string-literal). > **Syntax** > > *name* → *identifier* | *string* Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting-attributes). +Two names are the same if they represent the same sequence of characters, regardless of whether they are written as identifiers or strings. # Keywords diff --git a/doc/manual/src/language/scope.md b/doc/manual/src/language/scope.md index 96c7468eb..9373324e2 100644 --- a/doc/manual/src/language/scope.md +++ b/doc/manual/src/language/scope.md @@ -13,7 +13,7 @@ Implicit definitions are only created by [with-expressions](./syntax.md#with-exp Every expression is *enclosed* by a scope. The outermost expression is enclosed by the [built-in, global scope](./builtins.md), which contains only explicit definitions. -The respective definition types *extend* their enclosing scope by adding new definitions, or replacing existing ones with the same name. +The expressions listed above *extend* their enclosing scope by adding new definitions, or replacing existing ones with the same name. An explicit definition can replace a definition of any type; an implicit definition can only replace another implicit definition. Each of the above expressions defines which of its subexpressions are enclosed by the extended scope. From 9b5b7b796341eca437fe08bb278c49dfbae2deaa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Aug 2024 16:51:57 +0200 Subject: [PATCH 762/910] Fix the S3 store It was failing with: error: AWS error fetching 'nix-cache-info': The specified bucket does not exist because `S3BinaryCacheStoreImpl` had a `bucketName` field that shadowed the inherited `bucketName from `S3BinaryCacheStoreConfig`. --- src/libstore/s3-binary-cache-store.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 92ab47cd6..21175b1eb 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -220,8 +220,6 @@ std::string S3BinaryCacheStoreConfig::doc() struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore { - std::string bucketName; - Stats stats; S3Helper s3Helper; From 739418504c4d2f28fb5f45151b1c83707c3571e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 2 Aug 2024 11:12:06 +0200 Subject: [PATCH 763/910] allow to c api with older c versions In the FFI world we have many tools that are not gcc/clang and therefore not always support the latest C standard. This fixes support with cffi i.e. used in https://github.com/tweag/python-nix --- src/libexpr-c/nix_api_expr.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index adf8b65b1..1764b49f3 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -14,6 +14,16 @@ #include "nix_api_util.h" #include +#ifndef __has_c_attribute +# define __has_c_attribute(x) 0 +#endif + +#if __has_c_attribute(deprecated) +# define NIX_DEPRECATED(msg) [[deprecated(msg)]] +#else +# define NIX_DEPRECATED(msg) +#endif + #ifdef __cplusplus extern "C" { #endif @@ -45,7 +55,7 @@ typedef struct EvalState EvalState; // nix::EvalState * @see nix_value_incref, nix_value_decref */ typedef struct nix_value nix_value; -[[deprecated("use nix_value instead")]] typedef nix_value Value; +NIX_DEPRECATED("use nix_value instead") typedef nix_value Value; // Function prototypes /** From 088fa815d324e9c6fddf2f0358ac9b64ba6d42c7 Mon Sep 17 00:00:00 2001 From: 0x5a4 <54070204+0x5a4@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:37:45 +0200 Subject: [PATCH 764/910] fix: bash mangles flake ref completion --- misc/bash/completion.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/misc/bash/completion.sh b/misc/bash/completion.sh index 9af695f5a..c4ba96cd3 100644 --- a/misc/bash/completion.sh +++ b/misc/bash/completion.sh @@ -12,9 +12,16 @@ function _complete_nix { elif [[ $completion == attrs ]]; then compopt -o nospace fi - else - COMPREPLY+=("$completion") + continue fi + + if [[ "${cur}" =~ "=" ]]; then + # drop everything up to the first =. if a = is included, bash assumes this to be + # an arg=value argument and the completion gets mangled (see #11208) + completion="${completion#*=}" + fi + + COMPREPLY+=("${completion}") done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}" 2>/dev/null) __ltrim_colon_completions "$cur" } From 5a6e28e1667386b6ce4310bb07ed196432d7a9ec Mon Sep 17 00:00:00 2001 From: a-kenji Date: Mon, 5 Aug 2024 01:03:21 +0200 Subject: [PATCH 765/910] docs: installable remove alternate expression flag (#11254) --- src/nix/nix.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nix/nix.md b/src/nix/nix.md index 56587d0b2..443757a05 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -65,7 +65,7 @@ The following types of installable are supported by most commands: - [Nix file](#nix-file), optionally qualified by an attribute path - Specified with `--file`/`-f` - [Nix expression](#nix-expression), optionally qualified by an attribute path - - Specified with `--expr`/`-E` + - Specified with `--expr` For most commands, if no installable is specified, `.` is assumed. That is, Nix will operate on the default flake output attribute of the flake in the current directory. @@ -200,17 +200,17 @@ operate are determined as follows: … ``` - For `-e`/`--expr` and `-f`/`--file`, the derivation output is specified as part of the attribute path: + For `--expr` and `-f`/`--file`, the derivation output is specified as part of the attribute path: ```console $ nix build -f '' 'glibc^dev,static' - $ nix build --impure -E 'import { }' 'glibc^dev,static' + $ nix build --impure --expr 'import { }' 'glibc^dev,static' ``` This syntax is the same even if the actual attribute path is empty: ```console - $ nix build -E 'let pkgs = import { }; in pkgs.glibc' '^dev,static' + $ nix build --impure --expr 'let pkgs = import { }; in pkgs.glibc' '^dev,static' ``` * You can also specify that *all* outputs should be used using the From 2950f9e18af1bd57b566b8c0b4df71022edb3b80 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 5 Aug 2024 11:38:38 +0200 Subject: [PATCH 766/910] Add a VM test for S3BinaryCacheStore Fixes #11238. --- tests/nixos/default.nix | 2 + tests/nixos/nix-copy-closure.nix | 2 +- tests/nixos/s3-binary-cache-store.nix | 63 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 tests/nixos/s3-binary-cache-store.nix diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index c0c7b42fd..66174c928 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -146,4 +146,6 @@ in functional_root = runNixOSTestFor "x86_64-linux" ./functional/as-root.nix; user-sandboxing = runNixOSTestFor "x86_64-linux" ./user-sandboxing; + + s3-binary-cache-store = runNixOSTestFor "x86_64-linux" ./s3-binary-cache-store.nix; } diff --git a/tests/nixos/nix-copy-closure.nix b/tests/nixos/nix-copy-closure.nix index 66cbfb033..b9daa0a1f 100644 --- a/tests/nixos/nix-copy-closure.nix +++ b/tests/nixos/nix-copy-closure.nix @@ -1,6 +1,6 @@ # Test ‘nix-copy-closure’. -{ lib, config, nixpkgs, hostPkgs, ... }: +{ lib, config, nixpkgs, ... }: let pkgs = config.nodes.client.nixpkgs.pkgs; diff --git a/tests/nixos/s3-binary-cache-store.nix b/tests/nixos/s3-binary-cache-store.nix new file mode 100644 index 000000000..015457968 --- /dev/null +++ b/tests/nixos/s3-binary-cache-store.nix @@ -0,0 +1,63 @@ +{ lib, config, nixpkgs, ... }: + +let + pkgs = config.nodes.client.nixpkgs.pkgs; + + pkgA = pkgs.cowsay; + + accessKey = "BKIKJAA5BMMU2RHO6IBB"; + secretKey = "V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12"; + env = "AWS_ACCESS_KEY_ID=${accessKey} AWS_SECRET_ACCESS_KEY=${secretKey}"; + + storeUrl = "s3://my-cache?endpoint=http://server:9000®ion=eu-west-1"; + +in { + name = "nix-copy-closure"; + + nodes = + { server = + { config, lib, pkgs, ... }: + { virtualisation.writableStore = true; + virtualisation.additionalPaths = [ pkgA ]; + environment.systemPackages = [ pkgs.minio-client ]; + nix.extraOptions = "experimental-features = nix-command"; + services.minio = { + enable = true; + region = "eu-west-1"; + rootCredentialsFile = pkgs.writeText "minio-credentials-full" '' + MINIO_ROOT_USER=${accessKey} + MINIO_ROOT_PASSWORD=${secretKey} + ''; + }; + networking.firewall.allowedTCPPorts = [ 9000 ]; + }; + + client = + { config, pkgs, ... }: + { virtualisation.writableStore = true; + nix.extraOptions = "experimental-features = nix-command"; + }; + }; + + testScript = { nodes }: '' + # fmt: off + start_all() + + # Create a binary cache. + server.wait_for_unit("minio") + + server.succeed("mc config host add minio http://localhost:9000 ${accessKey} ${secretKey} --api s3v4") + server.succeed("mc mb minio/my-cache") + + server.succeed("${env} nix copy --to '${storeUrl}' ${pkgA}") + + # Copy a package from the binary cache. + client.fail("nix path-info ${pkgA}") + + client.succeed("${env} nix store info --store '${storeUrl}' >&2") + + client.succeed("${env} nix copy --no-check-sigs --from '${storeUrl}' ${pkgA}") + + client.succeed("nix path-info ${pkgA}") + ''; +} From 547e808a7528e9705fb2fec8014113a483b2006d Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Thu, 27 Jun 2024 15:35:55 -0400 Subject: [PATCH 767/910] nix flake show: add the description if it exists --- src/nix/flake.cc | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3f9f8f99b..8eb88d708 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1243,25 +1243,30 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto showDerivation = [&]() { auto name = visitor.getAttr(state->sName)->getString(); + std::optional description; + if (auto aMeta = visitor.maybeGetAttr(state->sMeta)) { + if (auto aDescription = aMeta->maybeGetAttr(state->sDescription)) + description = aDescription->getString(); + } + if (json) { - std::optional description; - if (auto aMeta = visitor.maybeGetAttr(state->sMeta)) { - if (auto aDescription = aMeta->maybeGetAttr(state->sDescription)) - description = aDescription->getString(); - } j.emplace("type", "derivation"); j.emplace("name", name); if (description) j.emplace("description", *description); } else { - logger->cout("%s: %s '%s'", - headerPrefix, + auto type = attrPath.size() == 2 && attrPathS[0] == "devShell" ? "development environment" : attrPath.size() >= 2 && attrPathS[0] == "devShells" ? "development environment" : attrPath.size() == 3 && attrPathS[0] == "checks" ? "derivation" : attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" : - "package", - name); + "package"; + if (description) { + logger->cout("%s: %s '%s' - '%s'", headerPrefix, type, name, *description); + } + else { + logger->cout("%s: %s '%s'", headerPrefix, type, name); + } } }; From 07d0527c0c2154c0868e242fb1d0e8da34199eeb Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Mon, 8 Jul 2024 11:56:41 -0400 Subject: [PATCH 768/910] nix flake show: Only print up to the first new line if it exists. --- src/nix/flake.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8eb88d708..9c6469a08 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1262,7 +1262,11 @@ struct CmdFlakeShow : FlakeCommand, MixJSON attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" : "package"; if (description) { - logger->cout("%s: %s '%s' - '%s'", headerPrefix, type, name, *description); + // Handle new lines in descriptions. + auto index = description->find('\n'); + std::string_view sanitized_description(description->data(), index != std::string::npos ? index : description->size()); + + logger->cout("%s: %s '%s' - '%s'", headerPrefix, type, name, sanitized_description); } else { logger->cout("%s: %s '%s'", headerPrefix, type, name); From 59b6aafadb66fcc6b2b7a2a6a72680ef58f684c7 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Mon, 8 Jul 2024 15:25:17 -0400 Subject: [PATCH 769/910] add tests --- tests/functional/flakes/show.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh index 22e1f4193..2911790de 100755 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -87,3 +87,22 @@ assert show_output.legacyPackages.${builtins.currentSystem}.AAAAAASomeThingsFail assert show_output.legacyPackages.${builtins.currentSystem}.simple.name == "simple"; true ' + +cat >flake.nix< ./show-output.txt +test "$(awk -F '[:] ' '/aNoDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" +test "$(awk -F '[:] ' '/bOneLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'one line'" +test "$(awk -F '[:] ' '/cMultiLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'line one'" From f22cf1fd3851ebac8a0a2040428ba9ce92a209c6 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Mon, 8 Jul 2024 16:31:33 -0400 Subject: [PATCH 770/910] Handle long strings, embedded new lines and empty descriptions --- src/nix/flake.cc | 21 ++++++++++++++++----- tests/functional/flakes/show.sh | 8 +++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9c6469a08..89a1326fd 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1261,12 +1261,23 @@ struct CmdFlakeShow : FlakeCommand, MixJSON attrPath.size() == 3 && attrPathS[0] == "checks" ? "derivation" : attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" : "package"; - if (description) { - // Handle new lines in descriptions. - auto index = description->find('\n'); - std::string_view sanitized_description(description->data(), index != std::string::npos ? index : description->size()); + if (description && !description->empty()) { + // Trim the string and only display the first line of the description. + auto trimmed = nix::trim(*description); + auto newLinePos = trimmed.find('\n'); + auto length = newLinePos != std::string::npos ? newLinePos : trimmed.size(); - logger->cout("%s: %s '%s' - '%s'", headerPrefix, type, name, sanitized_description); + // If the string is too long then resize add ellipses + std::string desc; + if (length > 80) { + trimmed.resize(80); + desc = trimmed.append("..."); + } + else { + desc = trimmed.substr(0, length); + } + + logger->cout("%s: %s '%s' - '%s'", headerPrefix, type, name, desc); } else { logger->cout("%s: %s '%s'", headerPrefix, type, name); diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh index 2911790de..d60adb99f 100755 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -95,9 +95,13 @@ cat >flake.nix< ./show-output.txt test "$(awk -F '[:] ' '/aNoDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" test "$(awk -F '[:] ' '/bOneLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'one line'" test "$(awk -F '[:] ' '/cMultiLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'line one'" +test "$(awk -F '[:] ' '/dLongDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - '01234567890123456789012345678901234567890123456789012345678901234567890123456789...'" +test "$(awk -F '[:] ' '/eEmptyDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" From 930818bb1daf97d5751b67fc6399323b3557bb7a Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Thu, 18 Jul 2024 09:42:48 -0400 Subject: [PATCH 771/910] Account for total length of 80 --- src/nix/flake.cc | 4 ++-- tests/functional/flakes/show.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 89a1326fd..48bec08c1 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1269,8 +1269,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON // If the string is too long then resize add ellipses std::string desc; - if (length > 80) { - trimmed.resize(80); + if (length > 77) { + trimmed.resize(77); desc = trimmed.append("..."); } else { diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh index d60adb99f..3d91613ee 100755 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -110,5 +110,5 @@ nix flake show > ./show-output.txt test "$(awk -F '[:] ' '/aNoDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" test "$(awk -F '[:] ' '/bOneLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'one line'" test "$(awk -F '[:] ' '/cMultiLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'line one'" -test "$(awk -F '[:] ' '/dLongDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - '01234567890123456789012345678901234567890123456789012345678901234567890123456789...'" +test "$(awk -F '[:] ' '/dLongDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - '01234567890123456789012345678901234567890123456789012345678901234567890123456...'" test "$(awk -F '[:] ' '/eEmptyDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" From 9d2d4d11e6fad32d97cb3605614835a16c5b5cdf Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 5 Aug 2024 12:34:05 -0400 Subject: [PATCH 772/910] Split tests, organize more string functions The test split matches PR #8920, so the utility files and tests files are once again to 1-1. The string changes continues what was started in PR #11093. --- src/libexpr/eval-cache.cc | 2 + src/libutil/strings-inline.hh | 41 ++ src/libutil/strings.cc | 13 +- src/libutil/strings.hh | 28 ++ src/libutil/util.cc | 18 - src/libutil/util.hh | 44 +- tests/unit/libutil/file-system.cc | 258 +++++++++++ tests/unit/libutil/meson.build | 5 +- tests/unit/libutil/processes.cc | 17 + tests/unit/libutil/strings.cc | 151 +++++++ tests/unit/libutil/terminal.cc | 60 +++ tests/unit/libutil/tests.cc | 703 ------------------------------ tests/unit/libutil/util.cc | 385 ++++++++++++++++ 13 files changed, 959 insertions(+), 766 deletions(-) create mode 100644 tests/unit/libutil/file-system.cc create mode 100644 tests/unit/libutil/processes.cc create mode 100644 tests/unit/libutil/terminal.cc delete mode 100644 tests/unit/libutil/tests.cc create mode 100644 tests/unit/libutil/util.cc diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 26352187e..3d77c029e 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -4,6 +4,8 @@ #include "eval.hh" #include "eval-inline.hh" #include "store-api.hh" +// Need specialization involving `SymbolStr` just in this one module. +#include "strings-inline.hh" namespace nix::eval_cache { diff --git a/src/libutil/strings-inline.hh b/src/libutil/strings-inline.hh index 10c1b19e6..d254d486d 100644 --- a/src/libutil/strings-inline.hh +++ b/src/libutil/strings-inline.hh @@ -4,6 +4,21 @@ namespace nix { +template +C tokenizeString(std::string_view s, std::string_view separators) +{ + C result; + auto pos = s.find_first_not_of(separators, 0); + while (pos != s.npos) { + auto end = s.find_first_of(separators, pos + 1); + if (end == s.npos) + end = s.size(); + result.insert(result.end(), std::string(s, pos, end - pos)); + pos = s.find_first_not_of(separators, end); + } + return result; +} + template std::string concatStringsSep(const std::string_view sep, const C & ss) { @@ -28,4 +43,30 @@ std::string concatStringsSep(const std::string_view sep, const C & ss) return s; } +template +std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) +{ + size_t size = 0; + + // TODO? remove to make sure we don't rely on the empty item ignoring behavior, + // or just get rid of this function by understanding the remaining calls. + // for (auto & i : ss) { + // // Make sure we don't rely on the empty item ignoring behavior + // assert(!i.empty()); + // break; + // } + + // need a cast to string_view since this is also called with Symbols + for (const auto & s : ss) + size += sep.size() + std::string_view(s).size(); + std::string s; + s.reserve(size); + for (auto & i : ss) { + if (s.size() != 0) + s += sep; + s += i; + } + return s; +} + } // namespace nix diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc index 7ec618bf4..2bb7f8c0a 100644 --- a/src/libutil/strings.cc +++ b/src/libutil/strings.cc @@ -1,12 +1,15 @@ #include #include "strings-inline.hh" -#include "util.hh" namespace nix { -template std::string concatStringsSep(std::string_view, const Strings &); -template std::string concatStringsSep(std::string_view, const StringSet &); +template std::list tokenizeString(std::string_view s, std::string_view separators); +template std::set tokenizeString(std::string_view s, std::string_view separators); +template std::vector tokenizeString(std::string_view s, std::string_view separators); + +template std::string concatStringsSep(std::string_view, const std::list &); +template std::string concatStringsSep(std::string_view, const std::set &); template std::string concatStringsSep(std::string_view, const std::vector &); typedef std::string_view strings_2[2]; @@ -16,4 +19,8 @@ template std::string concatStringsSep(std::string_view, const strings_3 &); typedef std::string_view strings_4[4]; template std::string concatStringsSep(std::string_view, const strings_4 &); +template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::list &); +template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set &); +template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector &); + } // namespace nix diff --git a/src/libutil/strings.hh b/src/libutil/strings.hh index 3b112c409..6e991e490 100644 --- a/src/libutil/strings.hh +++ b/src/libutil/strings.hh @@ -8,6 +8,18 @@ namespace nix { +/** + * String tokenizer. + * + * See also `basicSplitString()`, which preserves empty strings between separators, as well as at the start and end. + */ +template +C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); + +extern template std::list tokenizeString(std::string_view s, std::string_view separators); +extern template std::set tokenizeString(std::string_view s, std::string_view separators); +extern template std::vector tokenizeString(std::string_view s, std::string_view separators); + /** * Concatenate the given strings with a separator between the elements. */ @@ -18,4 +30,20 @@ extern template std::string concatStringsSep(std::string_view, const std::list &); extern template std::string concatStringsSep(std::string_view, const std::vector &); +/** + * Ignore any empty strings at the start of the list, and then concatenate the + * given strings with a separator between the elements. + * + * @deprecated This function exists for historical reasons. You probably just + * want to use `concatStringsSep`. + */ +template +[[deprecated( + "Consider removing the empty string dropping behavior. If acceptable, use concatStringsSep instead.")]] std::string +dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss); + +extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::list &); +extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set &); +extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector &); + } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 698e181a1..460802be3 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -53,24 +53,6 @@ std::vector stringsToCharPtrs(const Strings & ss) ////////////////////////////////////////////////////////////////////// -template C tokenizeString(std::string_view s, std::string_view separators) -{ - C result; - auto pos = s.find_first_not_of(separators, 0); - while (pos != s.npos) { - auto end = s.find_first_of(separators, pos + 1); - if (end == s.npos) end = s.size(); - result.insert(result.end(), std::string(s, pos, end - pos)); - pos = s.find_first_not_of(separators, end); - } - return result; -} - -template Strings tokenizeString(std::string_view s, std::string_view separators); -template StringSet tokenizeString(std::string_view s, std::string_view separators); -template std::vector tokenizeString(std::string_view s, std::string_view separators); - - std::string chomp(std::string_view s) { size_t i = s.find_last_not_of(" \n\r\t"); diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 877d15279..25128a900 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -28,49 +28,11 @@ std::vector stringsToCharPtrs(const Strings & ss); MakeError(FormatError, Error); -/** - * String tokenizer. - */ -template C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); - - -/** - * Ignore any empty strings at the start of the list, and then concatenate the - * given strings with a separator between the elements. - * - * @deprecated This function exists for historical reasons. You probably just - * want to use `concatStringsSep`. - */ -template -[[deprecated("Consider removing the empty string dropping behavior. If acceptable, use concatStringsSep instead.")]] -std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) -{ - size_t size = 0; - - // TODO? remove to make sure we don't rely on the empty item ignoring behavior, - // or just get rid of this function by understanding the remaining calls. - // for (auto & i : ss) { - // // Make sure we don't rely on the empty item ignoring behavior - // assert(!i.empty()); - // break; - // } - - // need a cast to string_view since this is also called with Symbols - for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); - std::string s; - s.reserve(size); - for (auto & i : ss) { - if (s.size() != 0) s += sep; - s += i; - } - return s; -} - -template -auto concatStrings(Parts && ... parts) +template +auto concatStrings(Parts &&... parts) -> std::enable_if_t<(... && std::is_convertible_v), std::string> { - std::string_view views[sizeof...(parts)] = { parts... }; + std::string_view views[sizeof...(parts)] = {parts...}; return concatStringsSep({}, views); } diff --git a/tests/unit/libutil/file-system.cc b/tests/unit/libutil/file-system.cc new file mode 100644 index 000000000..cfddaae1c --- /dev/null +++ b/tests/unit/libutil/file-system.cc @@ -0,0 +1,258 @@ +#include "util.hh" +#include "types.hh" +#include "file-system.hh" +#include "processes.hh" +#include "terminal.hh" +#include "strings.hh" + +#include +#include +#include + +#include + +#ifdef _WIN32 +# define FS_SEP "\\" +# define FS_ROOT "C:" FS_SEP // Need a mounted one, C drive is likely +#else +# define FS_SEP "/" +# define FS_ROOT FS_SEP +#endif + +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + +namespace nix { + +/* ----------- tests for file-system.hh -------------------------------------*/ + +/* ---------------------------------------------------------------------------- + * absPath + * --------------------------------------------------------------------------*/ + +TEST(absPath, doesntChangeRoot) +{ + auto p = absPath(FS_ROOT); + + ASSERT_EQ(p, FS_ROOT); +} + +TEST(absPath, turnsEmptyPathIntoCWD) +{ + char cwd[PATH_MAX + 1]; + auto p = absPath(""); + + ASSERT_EQ(p, getcwd((char *) &cwd, PATH_MAX)); +} + +TEST(absPath, usesOptionalBasePathWhenGiven) +{ + char _cwd[PATH_MAX + 1]; + char * cwd = getcwd((char *) &_cwd, PATH_MAX); + + auto p = absPath("", cwd); + + ASSERT_EQ(p, cwd); +} + +TEST(absPath, isIdempotent) +{ + char _cwd[PATH_MAX + 1]; + char * cwd = getcwd((char *) &_cwd, PATH_MAX); + auto p1 = absPath(cwd); + auto p2 = absPath(p1); + + ASSERT_EQ(p1, p2); +} + +TEST(absPath, pathIsCanonicalised) +{ + auto path = FS_ROOT "some/path/with/trailing/dot/."; + auto p1 = absPath(path); + auto p2 = absPath(p1); + + ASSERT_EQ(p1, FS_ROOT "some" FS_SEP "path" FS_SEP "with" FS_SEP "trailing" FS_SEP "dot"); + ASSERT_EQ(p1, p2); +} + +/* ---------------------------------------------------------------------------- + * canonPath + * --------------------------------------------------------------------------*/ + +TEST(canonPath, removesTrailingSlashes) +{ + auto path = FS_ROOT "this/is/a/path//"; + auto p = canonPath(path); + + ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); +} + +TEST(canonPath, removesDots) +{ + auto path = FS_ROOT "this/./is/a/path/./"; + auto p = canonPath(path); + + ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); +} + +TEST(canonPath, removesDots2) +{ + auto path = FS_ROOT "this/a/../is/a////path/foo/.."; + auto p = canonPath(path); + + ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); +} + +TEST(canonPath, requiresAbsolutePath) +{ + ASSERT_ANY_THROW(canonPath(".")); + ASSERT_ANY_THROW(canonPath("..")); + ASSERT_ANY_THROW(canonPath("../")); + ASSERT_DEATH({ canonPath(""); }, "path != \"\""); +} + +/* ---------------------------------------------------------------------------- + * dirOf + * --------------------------------------------------------------------------*/ + +TEST(dirOf, returnsEmptyStringForRoot) +{ + auto p = dirOf("/"); + + ASSERT_EQ(p, "/"); +} + +TEST(dirOf, returnsFirstPathComponent) +{ + auto p1 = dirOf("/dir/"); + ASSERT_EQ(p1, "/dir"); + auto p2 = dirOf("/dir"); + ASSERT_EQ(p2, "/"); + auto p3 = dirOf("/dir/.."); + ASSERT_EQ(p3, "/dir"); + auto p4 = dirOf("/dir/../"); + ASSERT_EQ(p4, "/dir/.."); +} + +/* ---------------------------------------------------------------------------- + * baseNameOf + * --------------------------------------------------------------------------*/ + +TEST(baseNameOf, emptyPath) +{ + auto p1 = baseNameOf(""); + ASSERT_EQ(p1, ""); +} + +TEST(baseNameOf, pathOnRoot) +{ + auto p1 = baseNameOf("/dir"); + ASSERT_EQ(p1, "dir"); +} + +TEST(baseNameOf, relativePath) +{ + auto p1 = baseNameOf("dir/foo"); + ASSERT_EQ(p1, "foo"); +} + +TEST(baseNameOf, pathWithTrailingSlashRoot) +{ + auto p1 = baseNameOf("/"); + ASSERT_EQ(p1, ""); +} + +TEST(baseNameOf, trailingSlash) +{ + auto p1 = baseNameOf("/dir/"); + ASSERT_EQ(p1, "dir"); +} + +TEST(baseNameOf, trailingSlashes) +{ + auto p1 = baseNameOf("/dir//"); + ASSERT_EQ(p1, "dir"); +} + +TEST(baseNameOf, absoluteNothingSlashNothing) +{ + auto p1 = baseNameOf("//"); + ASSERT_EQ(p1, ""); +} + +/* ---------------------------------------------------------------------------- + * isInDir + * --------------------------------------------------------------------------*/ + +TEST(isInDir, trivialCase) +{ + auto p1 = isInDir("/foo/bar", "/foo"); + ASSERT_EQ(p1, true); +} + +TEST(isInDir, notInDir) +{ + auto p1 = isInDir("/zes/foo/bar", "/foo"); + ASSERT_EQ(p1, false); +} + +// XXX: hm, bug or feature? :) Looking at the implementation +// this might be problematic. +TEST(isInDir, emptyDir) +{ + auto p1 = isInDir("/zes/foo/bar", ""); + ASSERT_EQ(p1, true); +} + +/* ---------------------------------------------------------------------------- + * isDirOrInDir + * --------------------------------------------------------------------------*/ + +TEST(isDirOrInDir, trueForSameDirectory) +{ + ASSERT_EQ(isDirOrInDir("/nix", "/nix"), true); + ASSERT_EQ(isDirOrInDir("/", "/"), true); +} + +TEST(isDirOrInDir, trueForEmptyPaths) +{ + ASSERT_EQ(isDirOrInDir("", ""), true); +} + +TEST(isDirOrInDir, falseForDisjunctPaths) +{ + ASSERT_EQ(isDirOrInDir("/foo", "/bar"), false); +} + +TEST(isDirOrInDir, relativePaths) +{ + ASSERT_EQ(isDirOrInDir("/foo/..", "/foo"), true); +} + +// XXX: while it is possible to use "." or ".." in the +// first argument this doesn't seem to work in the second. +TEST(isDirOrInDir, DISABLED_shouldWork) +{ + ASSERT_EQ(isDirOrInDir("/foo/..", "/foo/."), true); +} + +/* ---------------------------------------------------------------------------- + * pathExists + * --------------------------------------------------------------------------*/ + +TEST(pathExists, rootExists) +{ + ASSERT_TRUE(pathExists(FS_ROOT)); +} + +TEST(pathExists, cwdExists) +{ + ASSERT_TRUE(pathExists(".")); +} + +TEST(pathExists, bogusPathDoesNotExist) +{ + ASSERT_FALSE(pathExists("/schnitzel/darmstadt/pommes")); +} +} diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index 7f024e6f2..f4f2ae7f9 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -61,11 +61,14 @@ sources = files( 'lru-cache.cc', 'nix_api_util.cc', 'pool.cc', + 'processes.cc', 'references.cc', 'spawn.cc', + 'strings.cc', 'suggestions.cc', - 'tests.cc', + 'terminal.cc', 'url.cc', + 'util.cc', 'xml-writer.cc', ) diff --git a/tests/unit/libutil/processes.cc b/tests/unit/libutil/processes.cc new file mode 100644 index 000000000..9033595e8 --- /dev/null +++ b/tests/unit/libutil/processes.cc @@ -0,0 +1,17 @@ +#include "processes.hh" + +#include + +namespace nix { + +/* ---------------------------------------------------------------------------- + * statusOk + * --------------------------------------------------------------------------*/ + +TEST(statusOk, zeroIsOk) +{ + ASSERT_EQ(statusOk(0), true); + ASSERT_EQ(statusOk(1), false); +} + +} // namespace nix diff --git a/tests/unit/libutil/strings.cc b/tests/unit/libutil/strings.cc index 47a20770e..0bd2fe0a5 100644 --- a/tests/unit/libutil/strings.cc +++ b/tests/unit/libutil/strings.cc @@ -80,4 +80,155 @@ TEST(concatStringsSep, buildSingleString) ASSERT_EQ(concatStringsSep(",", strings), "this"); } +/* ---------------------------------------------------------------------------- + * dropEmptyInitThenConcatStringsSep + * --------------------------------------------------------------------------*/ + +TEST(dropEmptyInitThenConcatStringsSep, empty) +{ + Strings strings; + + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), ""); +} + +TEST(dropEmptyInitThenConcatStringsSep, buildCommaSeparatedString) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); + + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this,is,great"); +} + +TEST(dropEmptyInitThenConcatStringsSep, buildStringWithEmptySeparator) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); + + ASSERT_EQ(dropEmptyInitThenConcatStringsSep("", strings), "thisisgreat"); +} + +TEST(dropEmptyInitThenConcatStringsSep, buildSingleString) +{ + Strings strings; + strings.push_back("this"); + strings.push_back(""); + + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this,"); +} + +TEST(dropEmptyInitThenConcatStringsSep, emptyStrings) +{ + Strings strings; + strings.push_back(""); + strings.push_back(""); + + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), ""); +} + +/* ---------------------------------------------------------------------------- + * tokenizeString + * --------------------------------------------------------------------------*/ + +TEST(tokenizeString, empty) +{ + Strings expected = {}; + + ASSERT_EQ(tokenizeString(""), expected); +} + +TEST(tokenizeString, oneSep) +{ + Strings expected = {}; + + ASSERT_EQ(tokenizeString(" "), expected); +} + +TEST(tokenizeString, twoSep) +{ + Strings expected = {}; + + ASSERT_EQ(tokenizeString(" \n"), expected); +} + +TEST(tokenizeString, tokenizeSpacesWithDefaults) +{ + auto s = "foo bar baz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s), expected); +} + +TEST(tokenizeString, tokenizeTabsWithDefaults) +{ + auto s = "foo\tbar\tbaz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s), expected); +} + +TEST(tokenizeString, tokenizeTabsSpacesWithDefaults) +{ + auto s = "foo\t bar\t baz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s), expected); +} + +TEST(tokenizeString, tokenizeTabsSpacesNewlineWithDefaults) +{ + auto s = "foo\t\n bar\t\n baz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s), expected); +} + +TEST(tokenizeString, tokenizeTabsSpacesNewlineRetWithDefaults) +{ + auto s = "foo\t\n\r bar\t\n\r baz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s), expected); + + auto s2 = "foo \t\n\r bar \t\n\r baz"; + Strings expected2 = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s2), expected2); +} + +TEST(tokenizeString, tokenizeWithCustomSep) +{ + auto s = "foo\n,bar\n,baz\n"; + Strings expected = {"foo\n", "bar\n", "baz\n"}; + + ASSERT_EQ(tokenizeString(s, ","), expected); +} + +TEST(tokenizeString, tokenizeSepAtStart) +{ + auto s = ",foo,bar,baz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s, ","), expected); +} + +TEST(tokenizeString, tokenizeSepAtEnd) +{ + auto s = "foo,bar,baz,"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(tokenizeString(s, ","), expected); +} + +TEST(tokenizeString, tokenizeSepEmpty) +{ + auto s = "foo,,baz"; + Strings expected = {"foo", "baz"}; + + ASSERT_EQ(tokenizeString(s, ","), expected); +} + } // namespace nix diff --git a/tests/unit/libutil/terminal.cc b/tests/unit/libutil/terminal.cc new file mode 100644 index 000000000..cdeb9fd94 --- /dev/null +++ b/tests/unit/libutil/terminal.cc @@ -0,0 +1,60 @@ +#include "util.hh" +#include "types.hh" +#include "terminal.hh" +#include "strings.hh" + +#include +#include + +#include + +namespace nix { + +/* ---------------------------------------------------------------------------- + * filterANSIEscapes + * --------------------------------------------------------------------------*/ + +TEST(filterANSIEscapes, emptyString) +{ + auto s = ""; + auto expected = ""; + + ASSERT_EQ(filterANSIEscapes(s), expected); +} + +TEST(filterANSIEscapes, doesntChangePrintableChars) +{ + auto s = "09 2q304ruyhr slk2-19024 kjsadh sar f"; + + ASSERT_EQ(filterANSIEscapes(s), s); +} + +TEST(filterANSIEscapes, filtersColorCodes) +{ + auto s = "\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m"; + + ASSERT_EQ(filterANSIEscapes(s, true, 2), " A"); + ASSERT_EQ(filterANSIEscapes(s, true, 3), " A "); + ASSERT_EQ(filterANSIEscapes(s, true, 4), " A "); + ASSERT_EQ(filterANSIEscapes(s, true, 5), " A B"); + ASSERT_EQ(filterANSIEscapes(s, true, 8), " A B C"); +} + +TEST(filterANSIEscapes, expandsTabs) +{ + auto s = "foo\tbar\tbaz"; + + ASSERT_EQ(filterANSIEscapes(s, true), "foo bar baz"); +} + +TEST(filterANSIEscapes, utf8) +{ + ASSERT_EQ(filterANSIEscapes("foobar", true, 5), "fooba"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 6), "fóóbär"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 5), "fóóbä"); + ASSERT_EQ(filterANSIEscapes("fóóbär", true, 3), "fóó"); + ASSERT_EQ(filterANSIEscapes("f€€bär", true, 4), "f€€b"); + ASSERT_EQ(filterANSIEscapes("f𐍈𐍈bär", true, 4), "f𐍈𐍈b"); +} + +} // namespace nix diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc deleted file mode 100644 index 2b73d323b..000000000 --- a/tests/unit/libutil/tests.cc +++ /dev/null @@ -1,703 +0,0 @@ -#include "util.hh" -#include "types.hh" -#include "file-system.hh" -#include "processes.hh" -#include "terminal.hh" - -#include -#include - -#include - -#ifdef _WIN32 -# define FS_SEP "\\" -# define FS_ROOT "C:" FS_SEP // Need a mounted one, C drive is likely -#else -# define FS_SEP "/" -# define FS_ROOT FS_SEP -#endif - -#ifndef PATH_MAX -# define PATH_MAX 4096 -#endif - -namespace nix { - -/* ----------- tests for util.hh ------------------------------------------------*/ - - /* ---------------------------------------------------------------------------- - * absPath - * --------------------------------------------------------------------------*/ - - TEST(absPath, doesntChangeRoot) { - auto p = absPath(FS_ROOT); - - ASSERT_EQ(p, FS_ROOT); - } - - - - - TEST(absPath, turnsEmptyPathIntoCWD) { - char cwd[PATH_MAX+1]; - auto p = absPath(""); - - ASSERT_EQ(p, getcwd((char*)&cwd, PATH_MAX)); - } - - TEST(absPath, usesOptionalBasePathWhenGiven) { - char _cwd[PATH_MAX+1]; - char* cwd = getcwd((char*)&_cwd, PATH_MAX); - - auto p = absPath("", cwd); - - ASSERT_EQ(p, cwd); - } - - TEST(absPath, isIdempotent) { - char _cwd[PATH_MAX+1]; - char* cwd = getcwd((char*)&_cwd, PATH_MAX); - auto p1 = absPath(cwd); - auto p2 = absPath(p1); - - ASSERT_EQ(p1, p2); - } - - - TEST(absPath, pathIsCanonicalised) { - auto path = FS_ROOT "some/path/with/trailing/dot/."; - auto p1 = absPath(path); - auto p2 = absPath(p1); - - ASSERT_EQ(p1, FS_ROOT "some" FS_SEP "path" FS_SEP "with" FS_SEP "trailing" FS_SEP "dot"); - ASSERT_EQ(p1, p2); - } - - /* ---------------------------------------------------------------------------- - * canonPath - * --------------------------------------------------------------------------*/ - - TEST(canonPath, removesTrailingSlashes) { - auto path = FS_ROOT "this/is/a/path//"; - auto p = canonPath(path); - - ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); - } - - TEST(canonPath, removesDots) { - auto path = FS_ROOT "this/./is/a/path/./"; - auto p = canonPath(path); - - ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); - } - - TEST(canonPath, removesDots2) { - auto path = FS_ROOT "this/a/../is/a////path/foo/.."; - auto p = canonPath(path); - - ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); - } - - TEST(canonPath, requiresAbsolutePath) { - ASSERT_ANY_THROW(canonPath(".")); - ASSERT_ANY_THROW(canonPath("..")); - ASSERT_ANY_THROW(canonPath("../")); - ASSERT_DEATH({ canonPath(""); }, "path != \"\""); - } - - /* ---------------------------------------------------------------------------- - * dirOf - * --------------------------------------------------------------------------*/ - - TEST(dirOf, returnsEmptyStringForRoot) { - auto p = dirOf("/"); - - ASSERT_EQ(p, "/"); - } - - TEST(dirOf, returnsFirstPathComponent) { - auto p1 = dirOf("/dir/"); - ASSERT_EQ(p1, "/dir"); - auto p2 = dirOf("/dir"); - ASSERT_EQ(p2, "/"); - auto p3 = dirOf("/dir/.."); - ASSERT_EQ(p3, "/dir"); - auto p4 = dirOf("/dir/../"); - ASSERT_EQ(p4, "/dir/.."); - } - - /* ---------------------------------------------------------------------------- - * baseNameOf - * --------------------------------------------------------------------------*/ - - TEST(baseNameOf, emptyPath) { - auto p1 = baseNameOf(""); - ASSERT_EQ(p1, ""); - } - - TEST(baseNameOf, pathOnRoot) { - auto p1 = baseNameOf("/dir"); - ASSERT_EQ(p1, "dir"); - } - - TEST(baseNameOf, relativePath) { - auto p1 = baseNameOf("dir/foo"); - ASSERT_EQ(p1, "foo"); - } - - TEST(baseNameOf, pathWithTrailingSlashRoot) { - auto p1 = baseNameOf("/"); - ASSERT_EQ(p1, ""); - } - - TEST(baseNameOf, trailingSlash) { - auto p1 = baseNameOf("/dir/"); - ASSERT_EQ(p1, "dir"); - } - - TEST(baseNameOf, trailingSlashes) { - auto p1 = baseNameOf("/dir//"); - ASSERT_EQ(p1, "dir"); - } - - TEST(baseNameOf, absoluteNothingSlashNothing) { - auto p1 = baseNameOf("//"); - ASSERT_EQ(p1, ""); - } - - /* ---------------------------------------------------------------------------- - * isInDir - * --------------------------------------------------------------------------*/ - - TEST(isInDir, trivialCase) { - auto p1 = isInDir("/foo/bar", "/foo"); - ASSERT_EQ(p1, true); - } - - TEST(isInDir, notInDir) { - auto p1 = isInDir("/zes/foo/bar", "/foo"); - ASSERT_EQ(p1, false); - } - - // XXX: hm, bug or feature? :) Looking at the implementation - // this might be problematic. - TEST(isInDir, emptyDir) { - auto p1 = isInDir("/zes/foo/bar", ""); - ASSERT_EQ(p1, true); - } - - /* ---------------------------------------------------------------------------- - * isDirOrInDir - * --------------------------------------------------------------------------*/ - - TEST(isDirOrInDir, trueForSameDirectory) { - ASSERT_EQ(isDirOrInDir("/nix", "/nix"), true); - ASSERT_EQ(isDirOrInDir("/", "/"), true); - } - - TEST(isDirOrInDir, trueForEmptyPaths) { - ASSERT_EQ(isDirOrInDir("", ""), true); - } - - TEST(isDirOrInDir, falseForDisjunctPaths) { - ASSERT_EQ(isDirOrInDir("/foo", "/bar"), false); - } - - TEST(isDirOrInDir, relativePaths) { - ASSERT_EQ(isDirOrInDir("/foo/..", "/foo"), true); - } - - // XXX: while it is possible to use "." or ".." in the - // first argument this doesn't seem to work in the second. - TEST(isDirOrInDir, DISABLED_shouldWork) { - ASSERT_EQ(isDirOrInDir("/foo/..", "/foo/."), true); - - } - - /* ---------------------------------------------------------------------------- - * pathExists - * --------------------------------------------------------------------------*/ - - TEST(pathExists, rootExists) { - ASSERT_TRUE(pathExists(FS_ROOT)); - } - - TEST(pathExists, cwdExists) { - ASSERT_TRUE(pathExists(".")); - } - - TEST(pathExists, bogusPathDoesNotExist) { - ASSERT_FALSE(pathExists("/schnitzel/darmstadt/pommes")); - } - - /* ---------------------------------------------------------------------------- - * dropEmptyInitThenConcatStringsSep - * --------------------------------------------------------------------------*/ - - TEST(dropEmptyInitThenConcatStringsSep, buildCommaSeparatedString) { - Strings strings; - strings.push_back("this"); - strings.push_back("is"); - strings.push_back("great"); - - ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this,is,great"); - } - - TEST(dropEmptyInitThenConcatStringsSep, buildStringWithEmptySeparator) { - Strings strings; - strings.push_back("this"); - strings.push_back("is"); - strings.push_back("great"); - - ASSERT_EQ(dropEmptyInitThenConcatStringsSep("", strings), "thisisgreat"); - } - - TEST(dropEmptyInitThenConcatStringsSep, buildSingleString) { - Strings strings; - strings.push_back("this"); - - ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this"); - } - - /* ---------------------------------------------------------------------------- - * hasPrefix - * --------------------------------------------------------------------------*/ - - TEST(hasPrefix, emptyStringHasNoPrefix) { - ASSERT_FALSE(hasPrefix("", "foo")); - } - - TEST(hasPrefix, emptyStringIsAlwaysPrefix) { - ASSERT_TRUE(hasPrefix("foo", "")); - ASSERT_TRUE(hasPrefix("jshjkfhsadf", "")); - } - - TEST(hasPrefix, trivialCase) { - ASSERT_TRUE(hasPrefix("foobar", "foo")); - } - - /* ---------------------------------------------------------------------------- - * hasSuffix - * --------------------------------------------------------------------------*/ - - TEST(hasSuffix, emptyStringHasNoSuffix) { - ASSERT_FALSE(hasSuffix("", "foo")); - } - - TEST(hasSuffix, trivialCase) { - ASSERT_TRUE(hasSuffix("foo", "foo")); - ASSERT_TRUE(hasSuffix("foobar", "bar")); - } - - /* ---------------------------------------------------------------------------- - * base64Encode - * --------------------------------------------------------------------------*/ - - TEST(base64Encode, emptyString) { - ASSERT_EQ(base64Encode(""), ""); - } - - TEST(base64Encode, encodesAString) { - ASSERT_EQ(base64Encode("quod erat demonstrandum"), "cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="); - } - - TEST(base64Encode, encodeAndDecode) { - auto s = "quod erat demonstrandum"; - auto encoded = base64Encode(s); - auto decoded = base64Decode(encoded); - - ASSERT_EQ(decoded, s); - } - - TEST(base64Encode, encodeAndDecodeNonPrintable) { - char s[256]; - std::iota(std::rbegin(s), std::rend(s), 0); - - auto encoded = base64Encode(s); - auto decoded = base64Decode(encoded); - - EXPECT_EQ(decoded.length(), 255); - ASSERT_EQ(decoded, s); - } - - /* ---------------------------------------------------------------------------- - * base64Decode - * --------------------------------------------------------------------------*/ - - TEST(base64Decode, emptyString) { - ASSERT_EQ(base64Decode(""), ""); - } - - TEST(base64Decode, decodeAString) { - ASSERT_EQ(base64Decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), "quod erat demonstrandum"); - } - - TEST(base64Decode, decodeThrowsOnInvalidChar) { - ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error); - } - - /* ---------------------------------------------------------------------------- - * getLine - * --------------------------------------------------------------------------*/ - - TEST(getLine, all) { - { - auto [line, rest] = getLine("foo\nbar\nxyzzy"); - ASSERT_EQ(line, "foo"); - ASSERT_EQ(rest, "bar\nxyzzy"); - } - - { - auto [line, rest] = getLine("foo\r\nbar\r\nxyzzy"); - ASSERT_EQ(line, "foo"); - ASSERT_EQ(rest, "bar\r\nxyzzy"); - } - - { - auto [line, rest] = getLine("foo\n"); - ASSERT_EQ(line, "foo"); - ASSERT_EQ(rest, ""); - } - - { - auto [line, rest] = getLine("foo"); - ASSERT_EQ(line, "foo"); - ASSERT_EQ(rest, ""); - } - - { - auto [line, rest] = getLine(""); - ASSERT_EQ(line, ""); - ASSERT_EQ(rest, ""); - } - } - - /* ---------------------------------------------------------------------------- - * toLower - * --------------------------------------------------------------------------*/ - - TEST(toLower, emptyString) { - ASSERT_EQ(toLower(""), ""); - } - - TEST(toLower, nonLetters) { - auto s = "!@(*$#)(@#=\\234_"; - ASSERT_EQ(toLower(s), s); - } - - // std::tolower() doesn't handle unicode characters. In the context of - // store paths this isn't relevant but doesn't hurt to record this behavior - // here. - TEST(toLower, umlauts) { - auto s = "ÄÖÜ"; - ASSERT_EQ(toLower(s), "ÄÖÜ"); - } - - /* ---------------------------------------------------------------------------- - * string2Float - * --------------------------------------------------------------------------*/ - - TEST(string2Float, emptyString) { - ASSERT_EQ(string2Float(""), std::nullopt); - } - - TEST(string2Float, trivialConversions) { - ASSERT_EQ(string2Float("1.0"), 1.0); - - ASSERT_EQ(string2Float("0.0"), 0.0); - - ASSERT_EQ(string2Float("-100.25"), -100.25); - } - - /* ---------------------------------------------------------------------------- - * string2Int - * --------------------------------------------------------------------------*/ - - TEST(string2Int, emptyString) { - ASSERT_EQ(string2Int(""), std::nullopt); - } - - TEST(string2Int, trivialConversions) { - ASSERT_EQ(string2Int("1"), 1); - - ASSERT_EQ(string2Int("0"), 0); - - ASSERT_EQ(string2Int("-100"), -100); - } - - /* ---------------------------------------------------------------------------- - * renderSize - * --------------------------------------------------------------------------*/ - - TEST(renderSize, misc) { - ASSERT_EQ(renderSize(0, true), " 0.0 KiB"); - ASSERT_EQ(renderSize(100, true), " 0.1 KiB"); - ASSERT_EQ(renderSize(100), "0.1 KiB"); - ASSERT_EQ(renderSize(972, true), " 0.9 KiB"); - ASSERT_EQ(renderSize(973, true), " 1.0 KiB"); // FIXME: should round down - ASSERT_EQ(renderSize(1024, true), " 1.0 KiB"); - ASSERT_EQ(renderSize(1024 * 1024, true), "1024.0 KiB"); - ASSERT_EQ(renderSize(1100 * 1024, true), " 1.1 MiB"); - ASSERT_EQ(renderSize(2ULL * 1024 * 1024 * 1024, true), " 2.0 GiB"); - ASSERT_EQ(renderSize(2100ULL * 1024 * 1024 * 1024, true), " 2.1 TiB"); - } - -#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes - /* ---------------------------------------------------------------------------- - * statusOk - * --------------------------------------------------------------------------*/ - - TEST(statusOk, zeroIsOk) { - ASSERT_EQ(statusOk(0), true); - ASSERT_EQ(statusOk(1), false); - } -#endif - - - /* ---------------------------------------------------------------------------- - * rewriteStrings - * --------------------------------------------------------------------------*/ - - TEST(rewriteStrings, emptyString) { - StringMap rewrites; - rewrites["this"] = "that"; - - ASSERT_EQ(rewriteStrings("", rewrites), ""); - } - - TEST(rewriteStrings, emptyRewrites) { - StringMap rewrites; - - ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); - } - - TEST(rewriteStrings, successfulRewrite) { - StringMap rewrites; - rewrites["this"] = "that"; - - ASSERT_EQ(rewriteStrings("this and that", rewrites), "that and that"); - } - - TEST(rewriteStrings, doesntOccur) { - StringMap rewrites; - rewrites["foo"] = "bar"; - - ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); - } - - /* ---------------------------------------------------------------------------- - * replaceStrings - * --------------------------------------------------------------------------*/ - - TEST(replaceStrings, emptyString) { - ASSERT_EQ(replaceStrings("", "this", "that"), ""); - ASSERT_EQ(replaceStrings("this and that", "", ""), "this and that"); - } - - TEST(replaceStrings, successfulReplace) { - ASSERT_EQ(replaceStrings("this and that", "this", "that"), "that and that"); - } - - TEST(replaceStrings, doesntOccur) { - ASSERT_EQ(replaceStrings("this and that", "foo", "bar"), "this and that"); - } - - /* ---------------------------------------------------------------------------- - * trim - * --------------------------------------------------------------------------*/ - - TEST(trim, emptyString) { - ASSERT_EQ(trim(""), ""); - } - - TEST(trim, removesWhitespace) { - ASSERT_EQ(trim("foo"), "foo"); - ASSERT_EQ(trim(" foo "), "foo"); - ASSERT_EQ(trim(" foo bar baz"), "foo bar baz"); - ASSERT_EQ(trim(" \t foo bar baz\n"), "foo bar baz"); - } - - /* ---------------------------------------------------------------------------- - * chomp - * --------------------------------------------------------------------------*/ - - TEST(chomp, emptyString) { - ASSERT_EQ(chomp(""), ""); - } - - TEST(chomp, removesWhitespace) { - ASSERT_EQ(chomp("foo"), "foo"); - ASSERT_EQ(chomp("foo "), "foo"); - ASSERT_EQ(chomp(" foo "), " foo"); - ASSERT_EQ(chomp(" foo bar baz "), " foo bar baz"); - ASSERT_EQ(chomp("\t foo bar baz\n"), "\t foo bar baz"); - } - - /* ---------------------------------------------------------------------------- - * quoteStrings - * --------------------------------------------------------------------------*/ - - TEST(quoteStrings, empty) { - Strings s = { }; - Strings expected = { }; - - ASSERT_EQ(quoteStrings(s), expected); - } - - TEST(quoteStrings, emptyStrings) { - Strings s = { "", "", "" }; - Strings expected = { "''", "''", "''" }; - ASSERT_EQ(quoteStrings(s), expected); - - } - - TEST(quoteStrings, trivialQuote) { - Strings s = { "foo", "bar", "baz" }; - Strings expected = { "'foo'", "'bar'", "'baz'" }; - - ASSERT_EQ(quoteStrings(s), expected); - } - - TEST(quoteStrings, quotedStrings) { - Strings s = { "'foo'", "'bar'", "'baz'" }; - Strings expected = { "''foo''", "''bar''", "''baz''" }; - - ASSERT_EQ(quoteStrings(s), expected); - } - - /* ---------------------------------------------------------------------------- - * tokenizeString - * --------------------------------------------------------------------------*/ - - TEST(tokenizeString, empty) { - Strings expected = { }; - - ASSERT_EQ(tokenizeString(""), expected); - } - - TEST(tokenizeString, tokenizeSpacesWithDefaults) { - auto s = "foo bar baz"; - Strings expected = { "foo", "bar", "baz" }; - - ASSERT_EQ(tokenizeString(s), expected); - } - - TEST(tokenizeString, tokenizeTabsWithDefaults) { - auto s = "foo\tbar\tbaz"; - Strings expected = { "foo", "bar", "baz" }; - - ASSERT_EQ(tokenizeString(s), expected); - } - - TEST(tokenizeString, tokenizeTabsSpacesWithDefaults) { - auto s = "foo\t bar\t baz"; - Strings expected = { "foo", "bar", "baz" }; - - ASSERT_EQ(tokenizeString(s), expected); - } - - TEST(tokenizeString, tokenizeTabsSpacesNewlineWithDefaults) { - auto s = "foo\t\n bar\t\n baz"; - Strings expected = { "foo", "bar", "baz" }; - - ASSERT_EQ(tokenizeString(s), expected); - } - - TEST(tokenizeString, tokenizeTabsSpacesNewlineRetWithDefaults) { - auto s = "foo\t\n\r bar\t\n\r baz"; - Strings expected = { "foo", "bar", "baz" }; - - ASSERT_EQ(tokenizeString(s), expected); - - auto s2 = "foo \t\n\r bar \t\n\r baz"; - Strings expected2 = { "foo", "bar", "baz" }; - - ASSERT_EQ(tokenizeString(s2), expected2); - } - - TEST(tokenizeString, tokenizeWithCustomSep) { - auto s = "foo\n,bar\n,baz\n"; - Strings expected = { "foo\n", "bar\n", "baz\n" }; - - ASSERT_EQ(tokenizeString(s, ","), expected); - } - - /* ---------------------------------------------------------------------------- - * get - * --------------------------------------------------------------------------*/ - - TEST(get, emptyContainer) { - StringMap s = { }; - auto expected = nullptr; - - ASSERT_EQ(get(s, "one"), expected); - } - - TEST(get, getFromContainer) { - StringMap s; - s["one"] = "yi"; - s["two"] = "er"; - auto expected = "yi"; - - ASSERT_EQ(*get(s, "one"), expected); - } - - TEST(getOr, emptyContainer) { - StringMap s = { }; - auto expected = "yi"; - - ASSERT_EQ(getOr(s, "one", "yi"), expected); - } - - TEST(getOr, getFromContainer) { - StringMap s; - s["one"] = "yi"; - s["two"] = "er"; - auto expected = "yi"; - - ASSERT_EQ(getOr(s, "one", "nope"), expected); - } - - /* ---------------------------------------------------------------------------- - * filterANSIEscapes - * --------------------------------------------------------------------------*/ - - TEST(filterANSIEscapes, emptyString) { - auto s = ""; - auto expected = ""; - - ASSERT_EQ(filterANSIEscapes(s), expected); - } - - TEST(filterANSIEscapes, doesntChangePrintableChars) { - auto s = "09 2q304ruyhr slk2-19024 kjsadh sar f"; - - ASSERT_EQ(filterANSIEscapes(s), s); - } - - TEST(filterANSIEscapes, filtersColorCodes) { - auto s = "\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m"; - - ASSERT_EQ(filterANSIEscapes(s, true, 2), " A" ); - ASSERT_EQ(filterANSIEscapes(s, true, 3), " A " ); - ASSERT_EQ(filterANSIEscapes(s, true, 4), " A " ); - ASSERT_EQ(filterANSIEscapes(s, true, 5), " A B" ); - ASSERT_EQ(filterANSIEscapes(s, true, 8), " A B C" ); - } - - TEST(filterANSIEscapes, expandsTabs) { - auto s = "foo\tbar\tbaz"; - - ASSERT_EQ(filterANSIEscapes(s, true), "foo bar baz" ); - } - - TEST(filterANSIEscapes, utf8) { - ASSERT_EQ(filterANSIEscapes("foobar", true, 5), "fooba"); - ASSERT_EQ(filterANSIEscapes("fóóbär", true, 6), "fóóbär"); - ASSERT_EQ(filterANSIEscapes("fóóbär", true, 5), "fóóbä"); - ASSERT_EQ(filterANSIEscapes("fóóbär", true, 3), "fóó"); - ASSERT_EQ(filterANSIEscapes("f€€bär", true, 4), "f€€b"); - ASSERT_EQ(filterANSIEscapes("f𐍈𐍈bär", true, 4), "f𐍈𐍈b"); - } - -} diff --git a/tests/unit/libutil/util.cc b/tests/unit/libutil/util.cc new file mode 100644 index 000000000..a3f7c720a --- /dev/null +++ b/tests/unit/libutil/util.cc @@ -0,0 +1,385 @@ +#include "util.hh" +#include "types.hh" +#include "file-system.hh" +#include "terminal.hh" +#include "strings.hh" + +#include +#include + +#include + +namespace nix { + +/* ----------- tests for util.hh --------------------------------------------*/ + +/* ---------------------------------------------------------------------------- + * hasPrefix + * --------------------------------------------------------------------------*/ + +TEST(hasPrefix, emptyStringHasNoPrefix) +{ + ASSERT_FALSE(hasPrefix("", "foo")); +} + +TEST(hasPrefix, emptyStringIsAlwaysPrefix) +{ + ASSERT_TRUE(hasPrefix("foo", "")); + ASSERT_TRUE(hasPrefix("jshjkfhsadf", "")); +} + +TEST(hasPrefix, trivialCase) +{ + ASSERT_TRUE(hasPrefix("foobar", "foo")); +} + +/* ---------------------------------------------------------------------------- + * hasSuffix + * --------------------------------------------------------------------------*/ + +TEST(hasSuffix, emptyStringHasNoSuffix) +{ + ASSERT_FALSE(hasSuffix("", "foo")); +} + +TEST(hasSuffix, trivialCase) +{ + ASSERT_TRUE(hasSuffix("foo", "foo")); + ASSERT_TRUE(hasSuffix("foobar", "bar")); +} + +/* ---------------------------------------------------------------------------- + * base64Encode + * --------------------------------------------------------------------------*/ + +TEST(base64Encode, emptyString) +{ + ASSERT_EQ(base64Encode(""), ""); +} + +TEST(base64Encode, encodesAString) +{ + ASSERT_EQ(base64Encode("quod erat demonstrandum"), "cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="); +} + +TEST(base64Encode, encodeAndDecode) +{ + auto s = "quod erat demonstrandum"; + auto encoded = base64Encode(s); + auto decoded = base64Decode(encoded); + + ASSERT_EQ(decoded, s); +} + +TEST(base64Encode, encodeAndDecodeNonPrintable) +{ + char s[256]; + std::iota(std::rbegin(s), std::rend(s), 0); + + auto encoded = base64Encode(s); + auto decoded = base64Decode(encoded); + + EXPECT_EQ(decoded.length(), 255); + ASSERT_EQ(decoded, s); +} + +/* ---------------------------------------------------------------------------- + * base64Decode + * --------------------------------------------------------------------------*/ + +TEST(base64Decode, emptyString) +{ + ASSERT_EQ(base64Decode(""), ""); +} + +TEST(base64Decode, decodeAString) +{ + ASSERT_EQ(base64Decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), "quod erat demonstrandum"); +} + +TEST(base64Decode, decodeThrowsOnInvalidChar) +{ + ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error); +} + +/* ---------------------------------------------------------------------------- + * getLine + * --------------------------------------------------------------------------*/ + +TEST(getLine, all) +{ + { + auto [line, rest] = getLine("foo\nbar\nxyzzy"); + ASSERT_EQ(line, "foo"); + ASSERT_EQ(rest, "bar\nxyzzy"); + } + + { + auto [line, rest] = getLine("foo\r\nbar\r\nxyzzy"); + ASSERT_EQ(line, "foo"); + ASSERT_EQ(rest, "bar\r\nxyzzy"); + } + + { + auto [line, rest] = getLine("foo\n"); + ASSERT_EQ(line, "foo"); + ASSERT_EQ(rest, ""); + } + + { + auto [line, rest] = getLine("foo"); + ASSERT_EQ(line, "foo"); + ASSERT_EQ(rest, ""); + } + + { + auto [line, rest] = getLine(""); + ASSERT_EQ(line, ""); + ASSERT_EQ(rest, ""); + } +} + +/* ---------------------------------------------------------------------------- + * toLower + * --------------------------------------------------------------------------*/ + +TEST(toLower, emptyString) +{ + ASSERT_EQ(toLower(""), ""); +} + +TEST(toLower, nonLetters) +{ + auto s = "!@(*$#)(@#=\\234_"; + ASSERT_EQ(toLower(s), s); +} + +// std::tolower() doesn't handle unicode characters. In the context of +// store paths this isn't relevant but doesn't hurt to record this behavior +// here. +TEST(toLower, umlauts) +{ + auto s = "ÄÖÜ"; + ASSERT_EQ(toLower(s), "ÄÖÜ"); +} + +/* ---------------------------------------------------------------------------- + * string2Float + * --------------------------------------------------------------------------*/ + +TEST(string2Float, emptyString) +{ + ASSERT_EQ(string2Float(""), std::nullopt); +} + +TEST(string2Float, trivialConversions) +{ + ASSERT_EQ(string2Float("1.0"), 1.0); + + ASSERT_EQ(string2Float("0.0"), 0.0); + + ASSERT_EQ(string2Float("-100.25"), -100.25); +} + +/* ---------------------------------------------------------------------------- + * string2Int + * --------------------------------------------------------------------------*/ + +TEST(string2Int, emptyString) +{ + ASSERT_EQ(string2Int(""), std::nullopt); +} + +TEST(string2Int, trivialConversions) +{ + ASSERT_EQ(string2Int("1"), 1); + + ASSERT_EQ(string2Int("0"), 0); + + ASSERT_EQ(string2Int("-100"), -100); +} + +/* ---------------------------------------------------------------------------- + * renderSize + * --------------------------------------------------------------------------*/ + +TEST(renderSize, misc) +{ + ASSERT_EQ(renderSize(0, true), " 0.0 KiB"); + ASSERT_EQ(renderSize(100, true), " 0.1 KiB"); + ASSERT_EQ(renderSize(100), "0.1 KiB"); + ASSERT_EQ(renderSize(972, true), " 0.9 KiB"); + ASSERT_EQ(renderSize(973, true), " 1.0 KiB"); // FIXME: should round down + ASSERT_EQ(renderSize(1024, true), " 1.0 KiB"); + ASSERT_EQ(renderSize(1024 * 1024, true), "1024.0 KiB"); + ASSERT_EQ(renderSize(1100 * 1024, true), " 1.1 MiB"); + ASSERT_EQ(renderSize(2ULL * 1024 * 1024 * 1024, true), " 2.0 GiB"); + ASSERT_EQ(renderSize(2100ULL * 1024 * 1024 * 1024, true), " 2.1 TiB"); +} + +/* ---------------------------------------------------------------------------- + * rewriteStrings + * --------------------------------------------------------------------------*/ + +TEST(rewriteStrings, emptyString) +{ + StringMap rewrites; + rewrites["this"] = "that"; + + ASSERT_EQ(rewriteStrings("", rewrites), ""); +} + +TEST(rewriteStrings, emptyRewrites) +{ + StringMap rewrites; + + ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); +} + +TEST(rewriteStrings, successfulRewrite) +{ + StringMap rewrites; + rewrites["this"] = "that"; + + ASSERT_EQ(rewriteStrings("this and that", rewrites), "that and that"); +} + +TEST(rewriteStrings, doesntOccur) +{ + StringMap rewrites; + rewrites["foo"] = "bar"; + + ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that"); +} + +/* ---------------------------------------------------------------------------- + * replaceStrings + * --------------------------------------------------------------------------*/ + +TEST(replaceStrings, emptyString) +{ + ASSERT_EQ(replaceStrings("", "this", "that"), ""); + ASSERT_EQ(replaceStrings("this and that", "", ""), "this and that"); +} + +TEST(replaceStrings, successfulReplace) +{ + ASSERT_EQ(replaceStrings("this and that", "this", "that"), "that and that"); +} + +TEST(replaceStrings, doesntOccur) +{ + ASSERT_EQ(replaceStrings("this and that", "foo", "bar"), "this and that"); +} + +/* ---------------------------------------------------------------------------- + * trim + * --------------------------------------------------------------------------*/ + +TEST(trim, emptyString) +{ + ASSERT_EQ(trim(""), ""); +} + +TEST(trim, removesWhitespace) +{ + ASSERT_EQ(trim("foo"), "foo"); + ASSERT_EQ(trim(" foo "), "foo"); + ASSERT_EQ(trim(" foo bar baz"), "foo bar baz"); + ASSERT_EQ(trim(" \t foo bar baz\n"), "foo bar baz"); +} + +/* ---------------------------------------------------------------------------- + * chomp + * --------------------------------------------------------------------------*/ + +TEST(chomp, emptyString) +{ + ASSERT_EQ(chomp(""), ""); +} + +TEST(chomp, removesWhitespace) +{ + ASSERT_EQ(chomp("foo"), "foo"); + ASSERT_EQ(chomp("foo "), "foo"); + ASSERT_EQ(chomp(" foo "), " foo"); + ASSERT_EQ(chomp(" foo bar baz "), " foo bar baz"); + ASSERT_EQ(chomp("\t foo bar baz\n"), "\t foo bar baz"); +} + +/* ---------------------------------------------------------------------------- + * quoteStrings + * --------------------------------------------------------------------------*/ + +TEST(quoteStrings, empty) +{ + Strings s = {}; + Strings expected = {}; + + ASSERT_EQ(quoteStrings(s), expected); +} + +TEST(quoteStrings, emptyStrings) +{ + Strings s = {"", "", ""}; + Strings expected = {"''", "''", "''"}; + ASSERT_EQ(quoteStrings(s), expected); +} + +TEST(quoteStrings, trivialQuote) +{ + Strings s = {"foo", "bar", "baz"}; + Strings expected = {"'foo'", "'bar'", "'baz'"}; + + ASSERT_EQ(quoteStrings(s), expected); +} + +TEST(quoteStrings, quotedStrings) +{ + Strings s = {"'foo'", "'bar'", "'baz'"}; + Strings expected = {"''foo''", "''bar''", "''baz''"}; + + ASSERT_EQ(quoteStrings(s), expected); +} + +/* ---------------------------------------------------------------------------- + * get + * --------------------------------------------------------------------------*/ + +TEST(get, emptyContainer) +{ + StringMap s = {}; + auto expected = nullptr; + + ASSERT_EQ(get(s, "one"), expected); +} + +TEST(get, getFromContainer) +{ + StringMap s; + s["one"] = "yi"; + s["two"] = "er"; + auto expected = "yi"; + + ASSERT_EQ(*get(s, "one"), expected); +} + +TEST(getOr, emptyContainer) +{ + StringMap s = {}; + auto expected = "yi"; + + ASSERT_EQ(getOr(s, "one", "yi"), expected); +} + +TEST(getOr, getFromContainer) +{ + StringMap s; + s["one"] = "yi"; + s["two"] = "er"; + auto expected = "yi"; + + ASSERT_EQ(getOr(s, "one", "nope"), expected); +} + +} // namespace nix From 0a00bd07b2421acfb21751a718292affa8c6e837 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 5 Aug 2024 18:56:02 +0200 Subject: [PATCH 773/910] PathSubstitutionGoal: Fix spurious "failed" count in the progress bar It is not an error if queryPathInfo() indicates that a path does not exist in the substituter. Fixes #11198. This was broken in 846869da0ed0580beb7f827b303fef9a8386de37. --- src/libstore/build/substitution-goal.cc | 8 +++++--- src/libstore/build/substitution-goal.hh | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index 7deeb4748..0152f1808 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -145,8 +145,10 @@ Goal::Co PathSubstitutionGoal::init() /* None left. Terminate this goal and let someone else deal with it. */ - worker.failedSubstitutions++; - worker.updateProgress(); + if (substituterFailed) { + worker.failedSubstitutions++; + worker.updateProgress(); + } /* Hack: don't indicate failure if there were no substituters. In that case the calling derivation should just do a @@ -158,7 +160,7 @@ Goal::Co PathSubstitutionGoal::init() } -Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool& substituterFailed) +Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool & substituterFailed) { trace("all references realised"); diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index c1de45379..f2cf797e5 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -66,7 +66,7 @@ public: */ Co init() override; Co gotInfo(); - Co tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool& substituterFailed); + Co tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool & substituterFailed); Co finished(); /** From 1c5f1de43f3497e47d638bb04fdf0a033de2036d Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Mon, 5 Aug 2024 14:15:14 -0400 Subject: [PATCH 774/910] copy string using filterANSIEscapes and enforce the max length --- src/nix/flake.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 48bec08c1..a5c6ff876 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -17,6 +17,7 @@ #include "eval-cache.hh" #include "markdown.hh" #include "users.hh" +#include "terminal.hh" #include #include @@ -1263,18 +1264,16 @@ struct CmdFlakeShow : FlakeCommand, MixJSON "package"; if (description && !description->empty()) { // Trim the string and only display the first line of the description. + const size_t maxLength = 77; auto trimmed = nix::trim(*description); auto newLinePos = trimmed.find('\n'); - auto length = newLinePos != std::string::npos ? newLinePos : trimmed.size(); + auto length = newLinePos != std::string::npos ? newLinePos : trimmed.length(); - // If the string is too long then resize add ellipses - std::string desc; - if (length > 77) { - trimmed.resize(77); - desc = trimmed.append("..."); - } - else { - desc = trimmed.substr(0, length); + // Resize/sanitize the string and if it's too long add ellipses + std::string desc = filterANSIEscapes(trimmed, false, length); + if (desc.length() > maxLength) { + desc.resize(maxLength); + desc = desc.append("..."); } logger->cout("%s: %s '%s' - '%s'", headerPrefix, type, name, desc); From 9bf6684b08368f850f6c451ec7313da0ae88738e Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Tue, 6 Aug 2024 09:39:42 -0400 Subject: [PATCH 775/910] Use window size --- src/nix/flake.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a5c6ff876..8ef19e3ea 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1263,8 +1263,11 @@ struct CmdFlakeShow : FlakeCommand, MixJSON attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" : "package"; if (description && !description->empty()) { + // Maximum length to print + size_t maxLength = getWindowSize().second; + if (maxLength == 0) + maxLength = 77; // Trim the string and only display the first line of the description. - const size_t maxLength = 77; auto trimmed = nix::trim(*description); auto newLinePos = trimmed.find('\n'); auto length = newLinePos != std::string::npos ? newLinePos : trimmed.length(); From 6c861b9c51b6dece17dbd94e9016d7984159c9a8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 5 Aug 2024 12:05:29 -0400 Subject: [PATCH 776/910] Factor out `lookupExecutable` and other PATH improvments This ended up motivating a good deal of other infra improvements in order to get Windows right: - `OsString` to complement `std::filesystem::path` - env var code for working with the underlying `OsString`s - Rename `PATHNG_LITERAL` to `OS_STR` - `NativePathTrait` renamed to `OsPathTrait`, given a character template parameter until #9205 is complete. Split `tests.cc` matching split of `util.{cc,hh}` last year. Co-authored-by: Robert Hensing --- .clang-format | 1 + maintainers/flake-module.nix | 3 - src/libutil/environment-variables.cc | 11 +- src/libutil/environment-variables.hh | 11 ++ src/libutil/executable-path.cc | 79 +++++++++++++ src/libutil/executable-path.hh | 64 +++++++++++ src/libutil/file-path-impl.hh | 9 +- src/libutil/file-path.hh | 25 +--- src/libutil/file-system.cc | 23 +++- src/libutil/file-system.hh | 6 + src/libutil/meson.build | 3 + src/libutil/os-string.hh | 43 +++++++ src/libutil/strings-inline.hh | 46 +++++++- src/libutil/strings.cc | 9 ++ src/libutil/strings.hh | 20 ++++ src/libutil/unix/environment-variables.cc | 10 ++ src/libutil/unix/file-path.cc | 10 -- src/libutil/unix/meson.build | 1 + src/libutil/unix/os-string.cc | 21 ++++ src/libutil/util.cc | 1 + src/libutil/windows/environment-variables.cc | 30 ++++- src/libutil/windows/file-path.cc | 12 -- src/libutil/windows/meson.build | 1 + src/libutil/windows/os-string.cc | 24 ++++ src/nix/config-check.cc | 36 ++++-- src/nix/develop.cc | 6 +- src/nix/env.cc | 9 +- src/nix/search.cc | 1 + src/nix/upgrade-nix.cc | 19 ++-- tests/unit/libutil/executable-path.cc | 64 +++++++++++ tests/unit/libutil/meson.build | 1 + tests/unit/libutil/strings.cc | 114 +++++++++++++++++++ 32 files changed, 616 insertions(+), 97 deletions(-) create mode 100644 src/libutil/executable-path.cc create mode 100644 src/libutil/executable-path.hh create mode 100644 src/libutil/os-string.hh create mode 100644 src/libutil/unix/os-string.cc create mode 100644 src/libutil/windows/os-string.cc create mode 100644 tests/unit/libutil/executable-path.cc diff --git a/.clang-format b/.clang-format index 3067583e1..4f191fc18 100644 --- a/.clang-format +++ b/.clang-format @@ -31,3 +31,4 @@ AlwaysBreakBeforeMultilineStrings: true IndentPPDirectives: AfterHash PPIndentWidth: 2 BinPackArguments: false +BreakBeforeTernaryOperators: true diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index be91df536..c3eaf671c 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -275,7 +275,6 @@ ''^src/libutil/current-process\.hh$'' ''^src/libutil/english\.cc$'' ''^src/libutil/english\.hh$'' - ''^src/libutil/environment-variables\.cc$'' ''^src/libutil/error\.cc$'' ''^src/libutil/error\.hh$'' ''^src/libutil/exit\.hh$'' @@ -357,7 +356,6 @@ ''^src/libutil/util\.cc$'' ''^src/libutil/util\.hh$'' ''^src/libutil/variant-wrapper\.hh$'' - ''^src/libutil/windows/environment-variables\.cc$'' ''^src/libutil/windows/file-descriptor\.cc$'' ''^src/libutil/windows/file-path\.cc$'' ''^src/libutil/windows/processes\.cc$'' @@ -485,7 +483,6 @@ ''^tests/unit/libutil/pool\.cc'' ''^tests/unit/libutil/references\.cc'' ''^tests/unit/libutil/suggestions\.cc'' - ''^tests/unit/libutil/tests\.cc'' ''^tests/unit/libutil/url\.cc'' ''^tests/unit/libutil/xml-writer\.cc'' ]; diff --git a/src/libutil/environment-variables.cc b/src/libutil/environment-variables.cc index d43197aa0..5947cf742 100644 --- a/src/libutil/environment-variables.cc +++ b/src/libutil/environment-variables.cc @@ -1,20 +1,23 @@ #include "util.hh" #include "environment-variables.hh" -extern char * * environ __attribute__((weak)); +extern char ** environ __attribute__((weak)); namespace nix { std::optional getEnv(const std::string & key) { char * value = getenv(key.c_str()); - if (!value) return {}; + if (!value) + return {}; return std::string(value); } -std::optional getEnvNonEmpty(const std::string & key) { +std::optional getEnvNonEmpty(const std::string & key) +{ auto value = getEnv(key); - if (value == "") return {}; + if (value == "") + return {}; return value; } diff --git a/src/libutil/environment-variables.hh b/src/libutil/environment-variables.hh index e0649adac..879e1f304 100644 --- a/src/libutil/environment-variables.hh +++ b/src/libutil/environment-variables.hh @@ -9,6 +9,7 @@ #include #include "types.hh" +#include "file-path.hh" namespace nix { @@ -17,6 +18,11 @@ namespace nix { */ std::optional getEnv(const std::string & key); +/** + * Like `getEnv`, but using `OsString` to avoid coercions. + */ +std::optional getEnvOs(const OsString & key); + /** * @return a non empty environment variable. Returns nullopt if the env * variable is set to "" @@ -43,6 +49,11 @@ int unsetenv(const char * name); */ int setEnv(const char * name, const char * value); +/** + * Like `setEnv`, but using `OsString` to avoid coercions. + */ +int setEnvOs(const OsString & name, const OsString & value); + /** * Clear the environment. */ diff --git a/src/libutil/executable-path.cc b/src/libutil/executable-path.cc new file mode 100644 index 000000000..1658e3667 --- /dev/null +++ b/src/libutil/executable-path.cc @@ -0,0 +1,79 @@ +#include "environment-variables.hh" +#include "executable-path.hh" +#include "strings-inline.hh" +#include "util.hh" +#include "file-path-impl.hh" + +namespace nix { + +namespace fs = std::filesystem; + +constexpr static const OsStringView path_var_separator{ + &ExecutablePath::separator, + 1, +}; + +ExecutablePath ExecutablePath::load() +{ + // "If PATH is unset or is set to null, the path search is + // implementation-defined." + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 + return ExecutablePath::parse(getEnvOs(OS_STR("PATH")).value_or(OS_STR(""))); +} + +ExecutablePath ExecutablePath::parse(const OsString & path) +{ + auto strings = path.empty() ? (std::list{}) + : basicSplitString, OsString::value_type>(path, path_var_separator); + + std::vector ret; + ret.reserve(strings.size()); + + std::transform( + std::make_move_iterator(strings.begin()), + std::make_move_iterator(strings.end()), + std::back_inserter(ret), + [](auto && str) { + return fs::path{ + str.empty() + // "A zero-length prefix is a legacy feature that + // indicates the current working directory. It + // appears as two adjacent characters + // ("::"), as an initial preceding the rest + // of the list, or as a trailing following + // the rest of the list." + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 + ? OS_STR(".") + : std::move(str), + }; + }); + + return {ret}; +} + +OsString ExecutablePath::render() const +{ + std::vector path2; + for (auto & p : directories) + path2.push_back(p.native()); + return basicConcatStringsSep(path_var_separator, path2); +} + +std::optional +ExecutablePath::find(const OsString & exe, std::function isExecutable) const +{ + // "If the pathname being sought contains a , the search + // through the path prefixes shall not be performed." + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 + assert(OsPathTrait::rfindPathSep(exe) == exe.npos); + + for (auto & dir : directories) { + auto candidate = dir / exe; + if (isExecutable(candidate)) + return std::filesystem::canonical(candidate); + } + + return std::nullopt; +} + +} // namespace nix diff --git a/src/libutil/executable-path.hh b/src/libutil/executable-path.hh new file mode 100644 index 000000000..bcb8d28e8 --- /dev/null +++ b/src/libutil/executable-path.hh @@ -0,0 +1,64 @@ +#pragma once +///@file + +#include "file-system.hh" + +namespace nix { + +struct ExecutablePath +{ + std::vector directories; + + constexpr static const OsString::value_type separator = +#ifdef WIN32 + L';' +#else + ':' +#endif + ; + + /** + * Parse `path` into a list of paths. + * + * On Unix we split on `:`, on Windows we split on `;`. + * + * For Unix, this is according to the POSIX spec for `PATH`. + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 + */ + static ExecutablePath parse(const OsString & path); + + /** + * Load the `PATH` environment variable and `parse` it. + */ + static ExecutablePath load(); + + /** + * Opposite of `parse` + */ + OsString render() const; + + /** + * Search for an executable. + * + * For Unix, this is according to the POSIX spec for `PATH`. + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 + * + * @param exe This must just be a name, and not contain any `/` (or + * `\` on Windows). in case it does, per the spec no lookup should + * be perfomed, and the path (it is not just a file name) as is. + * This is the caller's respsonsibility. + * + * This is a pure function, except for the default `isExecutable` + * argument, which uses the ambient file system to check if a file is + * executable (and exists). + * + * @return path to a resolved executable + */ + std::optional find( + const OsString & exe, + std::function isExecutableFile = isExecutableFileAmbient) const; + + bool operator==(const ExecutablePath &) const = default; +}; + +} // namespace nix diff --git a/src/libutil/file-path-impl.hh b/src/libutil/file-path-impl.hh index 4c90150fd..d7c823fd0 100644 --- a/src/libutil/file-path-impl.hh +++ b/src/libutil/file-path-impl.hh @@ -91,13 +91,10 @@ struct WindowsPathTrait }; -/** - * @todo Revisit choice of `char` or `wchar_t` for `WindowsPathTrait` - * argument. - */ -using NativePathTrait = +template +using OsPathTrait = #ifdef _WIN32 - WindowsPathTrait + WindowsPathTrait #else UnixPathTrait #endif diff --git a/src/libutil/file-path.hh b/src/libutil/file-path.hh index 6589c4060..8e4a88b9d 100644 --- a/src/libutil/file-path.hh +++ b/src/libutil/file-path.hh @@ -1,10 +1,10 @@ #pragma once ///@file -#include #include #include "types.hh" +#include "os-string.hh" namespace nix { @@ -22,39 +22,26 @@ typedef std::set PathSetNG; * * @todo drop `NG` suffix and replace the one in `types.hh`. */ -struct PathViewNG : std::basic_string_view +struct PathViewNG : OsStringView { - using string_view = std::basic_string_view; + using string_view = OsStringView; using string_view::string_view; PathViewNG(const std::filesystem::path & path) - : std::basic_string_view(path.native()) + : OsStringView{path.native()} { } - PathViewNG(const std::filesystem::path::string_type & path) - : std::basic_string_view(path) + PathViewNG(const OsString & path) + : OsStringView{path} { } const string_view & native() const { return *this; } string_view & native() { return *this; } }; -std::string os_string_to_string(PathViewNG::string_view path); - -std::filesystem::path::string_type string_to_os_string(std::string_view s); - std::optional maybePath(PathView path); std::filesystem::path pathNG(PathView path); -/** - * Create string literals with the native character width of paths - */ -#ifndef _WIN32 -# define PATHNG_LITERAL(s) s -#else -# define PATHNG_LITERAL(s) L ## s -#endif - } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 060a806fb..156dddf98 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -92,7 +92,7 @@ Path canonPath(PathView path, bool resolveSymlinks) arbitrary (but high) limit to prevent infinite loops. */ unsigned int followCount = 0, maxFollow = 1024; - auto ret = canonPathInner( + auto ret = canonPathInner>( path, [&followCount, &temp, maxFollow, resolveSymlinks] (std::string & result, std::string_view & remaining) { @@ -122,7 +122,7 @@ Path canonPath(PathView path, bool resolveSymlinks) Path dirOf(const PathView path) { - Path::size_type pos = NativePathTrait::rfindPathSep(path); + Path::size_type pos = OsPathTrait::rfindPathSep(path); if (pos == path.npos) return "."; return fs::path{path}.parent_path().string(); @@ -135,10 +135,10 @@ std::string_view baseNameOf(std::string_view path) return ""; auto last = path.size() - 1; - while (last > 0 && NativePathTrait::isPathSep(path[last])) + while (last > 0 && OsPathTrait::isPathSep(path[last])) last -= 1; - auto pos = NativePathTrait::rfindPathSep(path, last); + auto pos = OsPathTrait::rfindPathSep(path, last); if (pos == path.npos) pos = 0; else @@ -569,7 +569,7 @@ void replaceSymlink(const Path & target, const Path & link) } void setWriteTime( - const std::filesystem::path & path, + const fs::path & path, time_t accessedTime, time_t modificationTime, std::optional optIsSymlink) @@ -685,4 +685,17 @@ void moveFile(const Path & oldName, const Path & newName) ////////////////////////////////////////////////////////////////////// +bool isExecutableFileAmbient(const fs::path & exe) { + // Check file type, because directory being executable means + // something completely different. + return std::filesystem::is_regular_file(exe) + && access(exe.string().c_str(), +#ifdef WIN32 + 0 // TODO do better +#else + X_OK +#endif + ) == 0; +} + } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 4b215162d..5350ceb7e 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -263,6 +263,12 @@ std::pair createTempFile(const Path & prefix = "nix"); */ Path defaultTempDir(); +/** + * Interpret `exe` as a location in the ambient file system and return + * whether it exists AND is executable. + */ +bool isExecutableFileAmbient(const std::filesystem::path & exe); + /** * Used in various places. */ diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 8552c4c9d..8ff7ee51f 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -129,6 +129,7 @@ sources = files( 'english.cc', 'environment-variables.cc', 'error.cc', + 'executable-path.cc', 'exit.cc', 'experimental-features.cc', 'file-content-address.cc', @@ -183,6 +184,7 @@ headers = [config_h] + files( 'english.hh', 'environment-variables.hh', 'error.hh', + 'executable-path.hh', 'exit.hh', 'experimental-features.hh', 'file-content-address.hh', @@ -202,6 +204,7 @@ headers = [config_h] + files( 'lru-cache.hh', 'memory-source-accessor.hh', 'muxable-pipe.hh', + 'os-string.hh', 'pool.hh', 'position.hh', 'posix-source-accessor.hh', diff --git a/src/libutil/os-string.hh b/src/libutil/os-string.hh new file mode 100644 index 000000000..0d75173e5 --- /dev/null +++ b/src/libutil/os-string.hh @@ -0,0 +1,43 @@ +#pragma once +///@file + +#include +#include +#include + +namespace nix { + +/** + * Named because it is similar to the Rust type, except it is in the + * native encoding not WTF-8. + * + * Same as `std::filesystem::path::string_type`, but manually defined to + * avoid including a much more complex header. + */ +using OsString = std::basic_string< +#if defined(_WIN32) && !defined(__CYGWIN__) + wchar_t +#else + char +#endif + >; + +/** + * `std::string_view` counterpart for `OsString`. + */ +using OsStringView = std::basic_string_view; + +std::string os_string_to_string(OsStringView path); + +OsString string_to_os_string(std::string_view s); + +/** + * Create string literals with the native character width of paths + */ +#ifndef _WIN32 +# define OS_STR(s) s +#else +# define OS_STR(s) L##s +#endif + +} diff --git a/src/libutil/strings-inline.hh b/src/libutil/strings-inline.hh index d254d486d..25b8e0ff6 100644 --- a/src/libutil/strings-inline.hh +++ b/src/libutil/strings-inline.hh @@ -4,8 +4,8 @@ namespace nix { -template -C tokenizeString(std::string_view s, std::string_view separators) +template +C basicTokenizeString(std::basic_string_view s, std::basic_string_view separators) { C result; auto pos = s.find_first_not_of(separators, 0); @@ -13,14 +13,42 @@ C tokenizeString(std::string_view s, std::string_view separators) auto end = s.find_first_of(separators, pos + 1); if (end == s.npos) end = s.size(); - result.insert(result.end(), std::string(s, pos, end - pos)); + result.insert(result.end(), std::basic_string(s, pos, end - pos)); pos = s.find_first_not_of(separators, end); } return result; } template -std::string concatStringsSep(const std::string_view sep, const C & ss) +C tokenizeString(std::string_view s, std::string_view separators) +{ + return basicTokenizeString(s, separators); +} + +template +C basicSplitString(std::basic_string_view s, std::basic_string_view separators) +{ + C result; + size_t pos = 0; + while (pos <= s.size()) { + auto end = s.find_first_of(separators, pos); + if (end == s.npos) + end = s.size(); + result.insert(result.end(), std::basic_string(s, pos, end - pos)); + pos = end + 1; + } + + return result; +} + +template +C splitString(std::string_view s, std::string_view separators) +{ + return basicSplitString(s, separators); +} + +template +std::basic_string basicConcatStringsSep(const std::basic_string_view sep, const C & ss) { size_t size = 0; bool tail = false; @@ -28,10 +56,10 @@ std::string concatStringsSep(const std::string_view sep, const C & ss) for (const auto & s : ss) { if (tail) size += sep.size(); - size += std::string_view(s).size(); + size += std::basic_string_view{s}.size(); tail = true; } - std::string s; + std::basic_string s; s.reserve(size); tail = false; for (auto & i : ss) { @@ -43,6 +71,12 @@ std::string concatStringsSep(const std::string_view sep, const C & ss) return s; } +template +std::string concatStringsSep(const std::string_view sep, const C & ss) +{ + return basicConcatStringsSep(sep, ss); +} + template std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) { diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc index 2bb7f8c0a..60297228e 100644 --- a/src/libutil/strings.cc +++ b/src/libutil/strings.cc @@ -1,6 +1,8 @@ +#include #include #include "strings-inline.hh" +#include "os-string.hh" namespace nix { @@ -8,6 +10,13 @@ template std::list tokenizeString(std::string_view s, std::string_v template std::set tokenizeString(std::string_view s, std::string_view separators); template std::vector tokenizeString(std::string_view s, std::string_view separators); +template std::list splitString(std::string_view s, std::string_view separators); +template std::set splitString(std::string_view s, std::string_view separators); +template std::vector splitString(std::string_view s, std::string_view separators); + +template std::list basicSplitString( + std::basic_string_view s, std::basic_string_view separators); + template std::string concatStringsSep(std::string_view, const std::list &); template std::string concatStringsSep(std::string_view, const std::set &); template std::string concatStringsSep(std::string_view, const std::vector &); diff --git a/src/libutil/strings.hh b/src/libutil/strings.hh index 6e991e490..88b48d770 100644 --- a/src/libutil/strings.hh +++ b/src/libutil/strings.hh @@ -13,6 +13,12 @@ namespace nix { * * See also `basicSplitString()`, which preserves empty strings between separators, as well as at the start and end. */ +template +C basicTokenizeString(std::basic_string_view s, std::basic_string_view separators); + +/** + * Like `basicTokenizeString` but specialized to the default `char` + */ template C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); @@ -20,6 +26,20 @@ extern template std::list tokenizeString(std::string_view s, std::s extern template std::set tokenizeString(std::string_view s, std::string_view separators); extern template std::vector tokenizeString(std::string_view s, std::string_view separators); +/** + * Split a string, preserving empty strings between separators, as well as at the start and end. + * + * Returns a non-empty collection of strings. + */ +template +C basicSplitString(std::basic_string_view s, std::basic_string_view separators); +template +C splitString(std::string_view s, std::string_view separators); + +extern template std::list splitString(std::string_view s, std::string_view separators); +extern template std::set splitString(std::string_view s, std::string_view separators); +extern template std::vector splitString(std::string_view s, std::string_view separators); + /** * Concatenate the given strings with a separator between the elements. */ diff --git a/src/libutil/unix/environment-variables.cc b/src/libutil/unix/environment-variables.cc index 9c6fd3b18..cd7c8f5e5 100644 --- a/src/libutil/unix/environment-variables.cc +++ b/src/libutil/unix/environment-variables.cc @@ -9,4 +9,14 @@ int setEnv(const char * name, const char * value) return ::setenv(name, value, 1); } +std::optional getEnvOs(const std::string & key) +{ + return getEnv(key); +} + +int setEnvOs(const OsString & name, const OsString & value) +{ + return setEnv(name.c_str(), value.c_str()); +} + } diff --git a/src/libutil/unix/file-path.cc b/src/libutil/unix/file-path.cc index 294048a2f..cccee86a1 100644 --- a/src/libutil/unix/file-path.cc +++ b/src/libutil/unix/file-path.cc @@ -8,16 +8,6 @@ namespace nix { -std::string os_string_to_string(PathViewNG::string_view path) -{ - return std::string { path }; -} - -std::filesystem::path::string_type string_to_os_string(std::string_view s) -{ - return std::string { s }; -} - std::optional maybePath(PathView path) { return { path }; diff --git a/src/libutil/unix/meson.build b/src/libutil/unix/meson.build index 38e5cd3aa..1c5bf27fb 100644 --- a/src/libutil/unix/meson.build +++ b/src/libutil/unix/meson.build @@ -4,6 +4,7 @@ sources += files( 'file-path.cc', 'file-system.cc', 'muxable-pipe.cc', + 'os-string.cc', 'processes.cc', 'signals.cc', 'users.cc', diff --git a/src/libutil/unix/os-string.cc b/src/libutil/unix/os-string.cc new file mode 100644 index 000000000..8378afde2 --- /dev/null +++ b/src/libutil/unix/os-string.cc @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +#include "file-path.hh" +#include "util.hh" + +namespace nix { + +std::string os_string_to_string(PathViewNG::string_view path) +{ + return std::string{path}; +} + +std::filesystem::path::string_type string_to_os_string(std::string_view s) +{ + return std::string{s}; +} + +} diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 460802be3..db3ed1ddf 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1,5 +1,6 @@ #include "util.hh" #include "fmt.hh" +#include "file-path.hh" #include #include diff --git a/src/libutil/windows/environment-variables.cc b/src/libutil/windows/environment-variables.cc index 25ab9d63a..525d08c64 100644 --- a/src/libutil/windows/environment-variables.cc +++ b/src/libutil/windows/environment-variables.cc @@ -4,7 +4,30 @@ namespace nix { -int unsetenv(const char *name) +std::optional getEnvOs(const OsString & key) +{ + // Determine the required buffer size for the environment variable value + DWORD bufferSize = GetEnvironmentVariableW(key.c_str(), nullptr, 0); + if (bufferSize == 0) { + return std::nullopt; + } + + // Allocate a buffer to hold the environment variable value + std::wstring value{L'\0', bufferSize}; + + // Retrieve the environment variable value + DWORD resultSize = GetEnvironmentVariableW(key.c_str(), &value[0], bufferSize); + if (resultSize == 0) { + return std::nullopt; + } + + // Resize the string to remove the extra null characters + value.resize(resultSize); + + return value; +} + +int unsetenv(const char * name) { return -SetEnvironmentVariableA(name, nullptr); } @@ -14,4 +37,9 @@ int setEnv(const char * name, const char * value) return -SetEnvironmentVariableA(name, value); } +int setEnvOs(const OsString & name, const OsString & value) +{ + return -SetEnvironmentVariableW(name.c_str(), value.c_str()); +} + } diff --git a/src/libutil/windows/file-path.cc b/src/libutil/windows/file-path.cc index 3114ac4df..7405c426b 100644 --- a/src/libutil/windows/file-path.cc +++ b/src/libutil/windows/file-path.cc @@ -9,18 +9,6 @@ namespace nix { -std::string os_string_to_string(PathViewNG::string_view path) -{ - std::wstring_convert> converter; - return converter.to_bytes(std::filesystem::path::string_type { path }); -} - -std::filesystem::path::string_type string_to_os_string(std::string_view s) -{ - std::wstring_convert> converter; - return converter.from_bytes(std::string { s }); -} - std::optional maybePath(PathView path) { if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait::isPathSep(path[2])) { diff --git a/src/libutil/windows/meson.build b/src/libutil/windows/meson.build index 00320877f..1c645fe05 100644 --- a/src/libutil/windows/meson.build +++ b/src/libutil/windows/meson.build @@ -4,6 +4,7 @@ sources += files( 'file-path.cc', 'file-system.cc', 'muxable-pipe.cc', + 'os-string.cc', 'processes.cc', 'users.cc', 'windows-async-pipe.cc', diff --git a/src/libutil/windows/os-string.cc b/src/libutil/windows/os-string.cc new file mode 100644 index 000000000..7507f9030 --- /dev/null +++ b/src/libutil/windows/os-string.cc @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +#include "file-path.hh" +#include "file-path-impl.hh" +#include "util.hh" + +namespace nix { + +std::string os_string_to_string(PathViewNG::string_view path) +{ + std::wstring_convert> converter; + return converter.to_bytes(std::filesystem::path::string_type{path}); +} + +std::filesystem::path::string_type string_to_os_string(std::string_view s) +{ + std::wstring_convert> converter; + return converter.from_bytes(std::string{s}); +} + +} diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index 09d140733..1a6574de2 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -8,6 +8,7 @@ #include "store-api.hh" #include "local-fs-store.hh" #include "worker-protocol.hh" +#include "executable-path.hh" using namespace nix; @@ -39,6 +40,8 @@ void checkInfo(const std::string & msg) { } +namespace fs = std::filesystem; + struct CmdConfigCheck : StoreCommand { bool success = true; @@ -75,11 +78,13 @@ struct CmdConfigCheck : StoreCommand bool checkNixInPath() { - PathSet dirs; + std::set dirs; - for (auto & dir : tokenizeString(getEnv("PATH").value_or(""), ":")) - if (pathExists(dir + "/nix-env")) - dirs.insert(dirOf(canonPath(dir + "/nix-env", true))); + for (auto & dir : ExecutablePath::load().directories) { + auto candidate = dir / "nix-env"; + if (fs::exists(candidate)) + dirs.insert(fs::canonical(candidate).parent_path() ); + } if (dirs.size() != 1) { std::stringstream ss; @@ -94,18 +99,25 @@ struct CmdConfigCheck : StoreCommand bool checkProfileRoots(ref store) { - PathSet dirs; + std::set dirs; - for (auto & dir : tokenizeString(getEnv("PATH").value_or(""), ":")) { - Path profileDir = dirOf(dir); + for (auto & dir : ExecutablePath::load().directories) { + auto profileDir = dir.parent_path(); try { - Path userEnv = canonPath(profileDir, true); + auto userEnv = fs::weakly_canonical(profileDir); - if (store->isStorePath(userEnv) && hasSuffix(userEnv, "user-environment")) { - while (profileDir.find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir)) - profileDir = absPath(readLink(profileDir), dirOf(profileDir)); + auto noContainsProfiles = [&]{ + for (auto && part : profileDir) + if (part == "profiles") return false; + return true; + }; - if (profileDir.find("/profiles/") == std::string::npos) + if (store->isStorePath(userEnv.string()) && hasSuffix(userEnv.string(), "user-environment")) { + while (noContainsProfiles() && std::filesystem::is_symlink(profileDir)) + profileDir = fs::weakly_canonical( + profileDir.parent_path() / fs::read_symlink(profileDir)); + + if (noContainsProfiles()) dirs.insert(dir); } } catch (SystemError &) { diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 92ec3b78a..effc86a0a 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -415,7 +415,7 @@ struct Common : InstallableCommand, MixProfile if (buildEnvironment.providesStructuredAttrs()) { fixupStructuredAttrs( - PATHNG_LITERAL("sh"), + OS_STR("sh"), "NIX_ATTRS_SH_FILE", buildEnvironment.getAttrsSH(), rewrites, @@ -423,7 +423,7 @@ struct Common : InstallableCommand, MixProfile tmpDir ); fixupStructuredAttrs( - PATHNG_LITERAL("json"), + OS_STR("json"), "NIX_ATTRS_JSON_FILE", buildEnvironment.getAttrsJSON(), rewrites, @@ -447,7 +447,7 @@ struct Common : InstallableCommand, MixProfile const BuildEnvironment & buildEnvironment, const std::filesystem::path & tmpDir) { - auto targetFilePath = tmpDir / PATHNG_LITERAL(".attrs."); + auto targetFilePath = tmpDir / OS_STR(".attrs."); targetFilePath += ext; writeFile(targetFilePath.string(), content); diff --git a/src/nix/env.cc b/src/nix/env.cc index 9db03ca37..832320320 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -5,6 +5,7 @@ #include "eval.hh" #include "run.hh" #include "strings.hh" +#include "executable-path.hh" using namespace nix; @@ -95,10 +96,10 @@ struct CmdShell : InstallablesCommand, MixEnvironment } // TODO: split losslessly; empty means . - auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); - unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = concatStringsSep(":", unixPath); - setEnv("PATH", unixPathString.c_str()); + auto unixPath = ExecutablePath::load(); + unixPath.directories.insert(unixPath.directories.begin(), pathAdditions.begin(), pathAdditions.end()); + auto unixPathString = unixPath.render(); + setEnvOs(OS_STR("PATH"), unixPathString.c_str()); Strings args; for (auto & arg : command) diff --git a/src/nix/search.cc b/src/nix/search.cc index 7f8504d3f..c8d0b9e96 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -10,6 +10,7 @@ #include "eval-cache.hh" #include "attr-path.hh" #include "hilite.hh" +#include "strings-inline.hh" #include #include diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 7b3357700..9ca3f6087 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -8,6 +8,7 @@ #include "attr-path.hh" #include "names.hh" #include "progress-bar.hh" +#include "executable-path.hh" using namespace nix; @@ -102,23 +103,17 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand /* Return the profile in which Nix is installed. */ Path getProfileDir(ref store) { - Path where; - - for (auto & dir : tokenizeString(getEnv("PATH").value_or(""), ":")) - if (pathExists(dir + "/nix-env")) { - where = dir; - break; - } - - if (where == "") + auto whereOpt = ExecutablePath::load().find(OS_STR("nix-env")); + if (!whereOpt) throw Error("couldn't figure out how Nix is installed, so I can't upgrade it"); + auto & where = *whereOpt; printInfo("found Nix in '%s'", where); - if (hasPrefix(where, "/run/current-system")) + if (hasPrefix(where.string(), "/run/current-system")) throw Error("Nix on NixOS must be upgraded via 'nixos-rebuild'"); - Path profileDir = dirOf(where); + Path profileDir = where.parent_path().string(); // Resolve profile to /nix/var/nix/profiles/ link. while (canonPath(profileDir).find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir)) @@ -128,7 +123,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Path userEnv = canonPath(profileDir, true); - if (baseNameOf(where) != "bin" || + if (where.filename() != "bin" || !hasSuffix(userEnv, "user-environment")) throw Error("directory '%s' does not appear to be part of a Nix profile", where); diff --git a/tests/unit/libutil/executable-path.cc b/tests/unit/libutil/executable-path.cc new file mode 100644 index 000000000..8d182357d --- /dev/null +++ b/tests/unit/libutil/executable-path.cc @@ -0,0 +1,64 @@ +#include + +#include "executable-path.hh" + +namespace nix { + +#ifdef WIN32 +# define PATH_VAR_SEP L";" +#else +# define PATH_VAR_SEP ":" +#endif + +#define PATH_ENV_ROUND_TRIP(NAME, STRING_LIT, CXX_LIT) \ + TEST(ExecutablePath, NAME) \ + { \ + OsString s = STRING_LIT; \ + auto v = ExecutablePath::parse(s); \ + EXPECT_EQ(v, (ExecutablePath CXX_LIT)); \ + auto s2 = v.render(); \ + EXPECT_EQ(s2, s); \ + } + +PATH_ENV_ROUND_TRIP(emptyRoundTrip, OS_STR(""), ({})) + +PATH_ENV_ROUND_TRIP( + oneElemRoundTrip, + OS_STR("/foo"), + ({ + OS_STR("/foo"), + })) + +PATH_ENV_ROUND_TRIP( + twoElemsRoundTrip, + OS_STR("/foo" PATH_VAR_SEP "/bar"), + ({ + OS_STR("/foo"), + OS_STR("/bar"), + })) + +PATH_ENV_ROUND_TRIP( + threeElemsRoundTrip, + OS_STR("/foo" PATH_VAR_SEP "." PATH_VAR_SEP "/bar"), + ({ + OS_STR("/foo"), + OS_STR("."), + OS_STR("/bar"), + })) + +TEST(ExecutablePath, elementyElemNormalize) +{ + auto v = ExecutablePath::parse(PATH_VAR_SEP PATH_VAR_SEP PATH_VAR_SEP); + EXPECT_EQ( + v, + (ExecutablePath{{ + OS_STR("."), + OS_STR("."), + OS_STR("."), + OS_STR("."), + }})); + auto s2 = v.render(); + EXPECT_EQ(s2, OS_STR("." PATH_VAR_SEP "." PATH_VAR_SEP "." PATH_VAR_SEP ".")); +} + +} diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index f4f2ae7f9..83cec13ec 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -52,6 +52,7 @@ sources = files( 'closure.cc', 'compression.cc', 'config.cc', + 'executable-path.cc', 'file-content-address.cc', 'git.cc', 'hash.cc', diff --git a/tests/unit/libutil/strings.cc b/tests/unit/libutil/strings.cc index 0bd2fe0a5..8ceb16767 100644 --- a/tests/unit/libutil/strings.cc +++ b/tests/unit/libutil/strings.cc @@ -1,4 +1,5 @@ #include +#include #include "strings.hh" @@ -231,4 +232,117 @@ TEST(tokenizeString, tokenizeSepEmpty) ASSERT_EQ(tokenizeString(s, ","), expected); } +/* ---------------------------------------------------------------------------- + * splitString + * --------------------------------------------------------------------------*/ + +TEST(splitString, empty) +{ + Strings expected = {""}; + + ASSERT_EQ(splitString("", " \t\n\r"), expected); +} + +TEST(splitString, oneSep) +{ + Strings expected = {"", ""}; + + ASSERT_EQ(splitString(" ", " \t\n\r"), expected); +} + +TEST(splitString, twoSep) +{ + Strings expected = {"", "", ""}; + + ASSERT_EQ(splitString(" \n", " \t\n\r"), expected); +} + +TEST(splitString, tokenizeSpacesWithSpaces) +{ + auto s = "foo bar baz"; + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(splitString(s, " \t\n\r"), expected); +} + +TEST(splitString, tokenizeTabsWithDefaults) +{ + auto s = "foo\tbar\tbaz"; + // Using it like this is weird, but shows the difference with tokenizeString, which also has this test + Strings expected = {"foo", "bar", "baz"}; + + ASSERT_EQ(splitString(s, " \t\n\r"), expected); +} + +TEST(splitString, tokenizeTabsSpacesWithDefaults) +{ + auto s = "foo\t bar\t baz"; + // Using it like this is weird, but shows the difference with tokenizeString, which also has this test + Strings expected = {"foo", "", "bar", "", "baz"}; + + ASSERT_EQ(splitString(s, " \t\n\r"), expected); +} + +TEST(splitString, tokenizeTabsSpacesNewlineWithDefaults) +{ + auto s = "foo\t\n bar\t\n baz"; + // Using it like this is weird, but shows the difference with tokenizeString, which also has this test + Strings expected = {"foo", "", "", "bar", "", "", "baz"}; + + ASSERT_EQ(splitString(s, " \t\n\r"), expected); +} + +TEST(splitString, tokenizeTabsSpacesNewlineRetWithDefaults) +{ + auto s = "foo\t\n\r bar\t\n\r baz"; + // Using it like this is weird, but shows the difference with tokenizeString, which also has this test + Strings expected = {"foo", "", "", "", "bar", "", "", "", "baz"}; + + ASSERT_EQ(splitString(s, " \t\n\r"), expected); + + auto s2 = "foo \t\n\r bar \t\n\r baz"; + Strings expected2 = {"foo", "", "", "", "", "bar", "", "", "", "", "baz"}; + + ASSERT_EQ(splitString(s2, " \t\n\r"), expected2); +} + +TEST(splitString, tokenizeWithCustomSep) +{ + auto s = "foo\n,bar\n,baz\n"; + Strings expected = {"foo\n", "bar\n", "baz\n"}; + + ASSERT_EQ(splitString(s, ","), expected); +} + +TEST(splitString, tokenizeSepAtStart) +{ + auto s = ",foo,bar,baz"; + Strings expected = {"", "foo", "bar", "baz"}; + + ASSERT_EQ(splitString(s, ","), expected); +} + +TEST(splitString, tokenizeSepAtEnd) +{ + auto s = "foo,bar,baz,"; + Strings expected = {"foo", "bar", "baz", ""}; + + ASSERT_EQ(splitString(s, ","), expected); +} + +TEST(splitString, tokenizeSepEmpty) +{ + auto s = "foo,,baz"; + Strings expected = {"foo", "", "baz"}; + + ASSERT_EQ(splitString(s, ","), expected); +} + +// concatStringsSep sep . splitString sep = id if sep is 1 char +RC_GTEST_PROP(splitString, recoveredByConcatStringsSep, (const std::string & s)) +{ + RC_ASSERT(concatStringsSep("/", splitString(s, "/")) == s); + RC_ASSERT(concatStringsSep("a", splitString(s, "a")) == s); +} + } // namespace nix From 0646b6cd610d901438fb66eadf09708d302e1c66 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 7 Aug 2024 22:29:56 -0500 Subject: [PATCH 777/910] Update comments / documentation. Co-authored-by: Robert Hensing --- src/libutil/file-system.cc | 1 + src/libutil/file-system.hh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 156dddf98..536ae29ab 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -688,6 +688,7 @@ void moveFile(const Path & oldName, const Path & newName) bool isExecutableFileAmbient(const fs::path & exe) { // Check file type, because directory being executable means // something completely different. + // `is_regular_file` follows symlinks before checking. return std::filesystem::is_regular_file(exe) && access(exe.string().c_str(), #ifdef WIN32 diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 5350ceb7e..1ae5fa136 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -265,7 +265,7 @@ Path defaultTempDir(); /** * Interpret `exe` as a location in the ambient file system and return - * whether it exists AND is executable. + * whether it resolves to a file that is executable. */ bool isExecutableFileAmbient(const std::filesystem::path & exe); From 00f6db36fd72c9e82e923ce89d0ddb7d2e738528 Mon Sep 17 00:00:00 2001 From: Andrew Marshall Date: Thu, 8 Aug 2024 14:29:40 -0400 Subject: [PATCH 778/910] libstore: fix port binding in __darwinAllowLocalNetworking sandbox In d60c3f7f7c83134b5b4470ed84b6d5ed38e28753, this was changed to close a hole in the sandbox. Unfortunately, this was too restrictive such that it made local port binding fail, thus making derivations that needed `__darwinAllowLocalNetworking` gain nearly nothing, and thus largely fail (as the primary use for it is to enable port binding). This unfortunately does mean that a sandboxed build process can, in coordination with an actor outside the sandbox, escape the sandbox by binding a port and connecting to it externally to send data. I do not see a way around this with my experimentation and understanding of the (quite undocumented) macOS sandbox profile API. Notably it seems not possible to use the sandbox to do any of: - Restrict the remote IP of inbound network requests - Restrict the address being bound to As such, the `(local ip "*:*")` here appears to be functionally no different than `(local ip "localhost:*")` (however it *should* be different than removing the filter entirely, as that would make it also apply to non-IP networking). Doing `(allow network-inbound (require-all (local ip "localhost:*") (remote ip "localhost:*")))` causes listening to fail. Note that `network-inbound` implies `network-bind`. --- src/libstore/unix/build/sandbox-defaults.sb | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/unix/build/sandbox-defaults.sb b/src/libstore/unix/build/sandbox-defaults.sb index 6da01b735..15cd6daf5 100644 --- a/src/libstore/unix/build/sandbox-defaults.sb +++ b/src/libstore/unix/build/sandbox-defaults.sb @@ -49,6 +49,7 @@ R""( (if (param "_ALLOW_LOCAL_NETWORKING") (begin (allow network* (remote ip "localhost:*")) + (allow network-inbound (local ip "*:*")) ; required to bind and listen ; Allow access to /etc/resolv.conf (which is a symlink to ; /private/var/run/resolv.conf). From abbaba91223b47c87d270666d9c1a90ca0f55b34 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Thu, 8 Aug 2024 14:41:25 -0400 Subject: [PATCH 779/910] Use the window size for the entire length --- src/nix/flake.cc | 40 +++++++++++++++++++++++---------- tests/functional/flakes/show.sh | 4 ++-- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8ef19e3ea..839085b04 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1264,22 +1264,38 @@ struct CmdFlakeShow : FlakeCommand, MixJSON "package"; if (description && !description->empty()) { // Maximum length to print - size_t maxLength = getWindowSize().second; - if (maxLength == 0) - maxLength = 77; - // Trim the string and only display the first line of the description. - auto trimmed = nix::trim(*description); + size_t maxLength = getWindowSize().second > 0 ? getWindowSize().second : 80; + + // Trim the description and only use the first line + auto trimmed = trim(*description); auto newLinePos = trimmed.find('\n'); auto length = newLinePos != std::string::npos ? newLinePos : trimmed.length(); - // Resize/sanitize the string and if it's too long add ellipses - std::string desc = filterANSIEscapes(trimmed, false, length); - if (desc.length() > maxLength) { - desc.resize(maxLength); - desc = desc.append("..."); - } + // Sanitize the description and calculate the two parts of the line + // In order to get the length of the printable characters we need to + // filter out escape sequences. + auto beginningOfLine = fmt("%s: %s '%s'", headerPrefix, type, name); + auto beginningOfLineLength = filterANSIEscapes(beginningOfLine, true).length(); + auto restOfLine = fmt(" - '%s'", filterANSIEscapes(trimmed, false, length)); - logger->cout("%s: %s '%s' - '%s'", headerPrefix, type, name, desc); + // If we are already over the maximum length then do not trim + // and don't print the description (preserves existing behavior) + if (beginningOfLineLength >= maxLength) { + logger->cout("%s", beginningOfLine); + } + else { + auto line = beginningOfLine + restOfLine; + // FIXME: Specifying `true` here gives the correct length + // BUT removes colors/bold so something is not quite right here. + line = filterANSIEscapes(line, true, maxLength); + + // NOTE: This test might be incorrect since I get things like: + // 168 or 161 > maxLength. + if (line.length() > maxLength) { + line = line.replace(line.length() - 3, 3, "..."); + } + logger->cout("%s", line); + } } else { logger->cout("%s: %s '%s'", headerPrefix, type, name); diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh index 3d91613ee..0edc450c3 100755 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -110,5 +110,5 @@ nix flake show > ./show-output.txt test "$(awk -F '[:] ' '/aNoDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" test "$(awk -F '[:] ' '/bOneLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'one line'" test "$(awk -F '[:] ' '/cMultiLineDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - 'line one'" -test "$(awk -F '[:] ' '/dLongDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - '01234567890123456789012345678901234567890123456789012345678901234567890123456...'" -test "$(awk -F '[:] ' '/eEmptyDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" +test "$(awk -F '[:] ' '/dLongDescription/{print $NF}' ./show-output.txt)" = "package 'simple' - '012345678901234567890123456..." +test "$(awk -F '[:] ' '/eEmptyDescription/{print $NF}' ./show-output.txt)" = "package 'simple'" \ No newline at end of file From 3cc2e2a0ac7df557f5cd958a0736ce4eedf5ae07 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 11 Aug 2024 03:31:44 +0200 Subject: [PATCH 780/910] Edit docs --- doc/manual/rl-next/ban-integer-overflow.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual/rl-next/ban-integer-overflow.md b/doc/manual/rl-next/ban-integer-overflow.md index 49ecfe4c6..0e553af76 100644 --- a/doc/manual/rl-next/ban-integer-overflow.md +++ b/doc/manual/rl-next/ban-integer-overflow.md @@ -4,10 +4,10 @@ issues: [10968] prs: [11188] --- -Previously, integer overflow in the Nix language invoked C++ level signed overflow, which was undefined behaviour, but *probably* manifested as wrapping around on overflow. +Previously, integer overflow in the Nix language invoked C++ level signed overflow, which was undefined behaviour, but *usually* manifested as wrapping around on overflow. Since prior to the public release of Lix, Lix had C++ signed overflow defined to crash the process and nobody noticed this having accidentally removed overflow from the Nix language for three months until it was caught by fiddling around. -Given the significant body of actual Nix code that has been evaluated by Lix in that time, it does not appear that nixpkgs or much of importance depends on integer overflow, so it is safe to turn into an error. +Given the significant body of actual Nix code that has been evaluated by Lix in that time, it does not appear that nixpkgs or much of importance depends on integer overflow, so it appears safe to turn into an error. Some other overflows were fixed: - `builtins.fromJSON` of values greater than the maximum representable value in a signed 64-bit integer will generate an error. @@ -16,6 +16,6 @@ Some other overflows were fixed: Integer overflow now looks like the following: ``` -» nix eval --expr '9223372036854775807 + 1' +$ nix eval --expr '9223372036854775807 + 1' error: integer overflow in adding 9223372036854775807 + 1 ``` From f7c86d1a2f4460f58e8a91d004e438e69a3e133c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 11 Aug 2024 12:47:35 +0200 Subject: [PATCH 781/910] CONTRIBUTING.md: Add attribution and context rules We've recently had an incident where these rules were not followed, so let's add guidelines to increase the chances of contributors getting this right. Relevant discussion: https://discourse.nixos.org/t/code-attribution-policy/50445/2 --- CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12423366a..1c9ee9b35 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,6 +52,20 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). Link related issues to inform interested parties and future contributors about your change. If your pull request closes one or multiple issues, mention that in the description using `Closes: #`, as it will then happen automatically when your change is merged. + * Credit original authors when you're reusing or building on their work. + * Link to relevant changes in other projects, so that others can understand the full context of the change in the future when you or someone else will change or troubleshoot the code. + This is especially important when your change is based on work done in other repositories. + + Example: + ``` + This is based on the work of @user in . + This solution took inspiration from . + + Co-authored-by: User Name + ``` + + Use the `git cherry-pick -x` flag, and amend the commits to link to forks when applicable. + * Make sure to have [a clean history of commits on your branch by using rebase](https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request). * [Mark the pull request as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request) if you're not done with the changes. From 70dde8c70c937498f85f1f54372608bac6b1a5c9 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 11 Aug 2024 16:30:59 +0530 Subject: [PATCH 782/910] Use `std::filesystem::path` in build.cc --- src/nix/build.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nix/build.cc b/src/nix/build.cc index 479100186..da9132d02 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -43,22 +43,22 @@ static nlohmann::json builtPathsWithResultToJSON(const std::vector& buildables, LocalFSStore& store2) +static void createOutLinks(const std::filesystem::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; + auto symlink = outLink; if (i) symlink += fmt("-%d", i); - store2.addPermRoot(bo.path, absPath(symlink)); + store2.addPermRoot(bo.path, absPath(symlink.string())); }, [&](const BuiltPath::Built & bfd) { for (auto & output : bfd.outputs) { - std::string symlink = outLink; + auto symlink = outLink; if (i) symlink += fmt("-%d", i); if (output.first != "out") symlink += fmt("-%s", output.first); - store2.addPermRoot(output.second, absPath(symlink)); + store2.addPermRoot(output.second, absPath(symlink.string())); } }, }, buildable.path.raw()); From b0b19389821e0ab7aef35684bb91c65717136cce Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 11 Aug 2024 13:15:58 +0200 Subject: [PATCH 783/910] Urge contributors to read about contributing --- .github/PULL_REQUEST_TEMPLATE.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d12a4d36c..69da87db7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,22 @@ + + # Motivation From bd4e5a375b62ccc2f3eda22f8fa7f9d57910620f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 11 Aug 2024 13:16:36 +0200 Subject: [PATCH 784/910] Refer contributors to the matrix room We were basically sending contributors into the woods with that page. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 12423366a..9fc119708 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,4 +83,4 @@ For larger changes see the [Nix reference manual](https://nix.dev/manual/nix/dev ## Getting help Whenever you're stuck or do not know how to proceed, you can always ask for help. -The appropriate channels to do so can be found on the [NixOS Community](https://nixos.org/community/) page. +We invite you to use our [Matrix room](https://matrix.to/#/#nix-dev:nixos.org) to ask questions. From 2c12a6962eb916fac8ebe0ebf335bb65b1da48b1 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 11 Aug 2024 19:18:04 +0530 Subject: [PATCH 785/910] Remove unused variable --- src/nix/bundle.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 7d9aa7711..e152c26f2 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -117,8 +117,6 @@ struct CmdBundle : InstallableValueCommand }, }); - auto outPathS = store->printStorePath(outPath); - if (!outLink) { auto * attr = vRes->attrs()->get(evalState->sName); if (!attr) From 0abc664a789b2403420041d34695ce4f8e78d1ad Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 11 Aug 2024 19:19:46 +0530 Subject: [PATCH 786/910] Use `std::filesystem` in `eval` and `flake` ... ... executables --- src/nix/eval.cc | 8 ++++---- src/nix/flake.cc | 25 +++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 494735516..f8a9da736 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -78,14 +78,14 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption if (pathExists(*writeTo)) throw Error("path '%s' already exists", *writeTo); - std::function recurse; + std::function recurse; - recurse = [&](Value & v, const PosIdx pos, const Path & path) + recurse = [&](Value & v, const PosIdx pos, const std::filesystem::path & path) { state->forceValue(v, pos); if (v.type() == nString) // FIXME: disallow strings with contexts? - writeFile(path, v.string_view()); + writeFile(path.string(), v.string_view()); else if (v.type() == nAttrs) { if (mkdir(path.c_str() #ifndef _WIN32 // TODO abstract mkdir perms for Windows @@ -98,7 +98,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption try { if (name == "." || name == "..") throw Error("invalid file name '%s'", name); - recurse(*attr.value, attr.pos, concatStrings(path, "/", name)); + recurse(*attr.value, attr.pos, path / name); } catch (Error & e) { e.addTrace( state->positions[attr.pos], diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3f9f8f99b..7cfbf48a1 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -18,6 +18,7 @@ #include "markdown.hh" #include "users.hh" +#include #include #include @@ -870,27 +871,27 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand "If you've set '%s' to a string, try using a path instead.", templateDir, templateDirAttr->getAttrPathStr()).debugThrow(); - std::vector changedFiles; - std::vector conflictedFiles; + std::vector changedFiles; + std::vector conflictedFiles; - std::function copyDir; - copyDir = [&](const Path & from, const Path & to) + std::function copyDir; + copyDir = [&](const std::filesystem::path & from, const std::filesystem::path & to) { createDirs(to); for (auto & entry : std::filesystem::directory_iterator{from}) { checkInterrupt(); - auto from2 = entry.path().string(); - auto to2 = to + "/" + entry.path().filename().string(); - auto st = lstat(from2); - if (S_ISDIR(st.st_mode)) + auto from2 = entry.path(); + auto to2 = to / entry.path().filename(); + auto st = entry.symlink_status(); + if (std::filesystem::is_directory(st)) copyDir(from2, to2); - else if (S_ISREG(st.st_mode)) { + else if (std::filesystem::is_regular_file(st)) { auto contents = readFile(from2); if (pathExists(to2)) { auto contents2 = readFile(to2); if (contents != contents2) { - printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2, from2); + printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2.string(), from2.string()); conflictedFiles.push_back(to2); } else { notice("skipping identical file: %s", from2); @@ -899,11 +900,11 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand } else writeFile(to2, contents); } - else if (S_ISLNK(st.st_mode)) { + else if (std::filesystem::is_symlink(st)) { auto target = readLink(from2); if (pathExists(to2)) { if (readLink(to2) != target) { - printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2, from2); + printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2.string(), from2.string()); conflictedFiles.push_back(to2); } else { notice("skipping identical file: %s", from2); From 8e70f6f85021fb906b81114525cabbb2943d052a Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 11 Aug 2024 19:56:06 +0530 Subject: [PATCH 787/910] Use `std::filesystem::path` in `profile.cc` ... ...and `run.cc` --- src/nix/profile.cc | 12 ++++++------ src/nix/run.cc | 13 +++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 1096f4386..d751abdb1 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -122,9 +122,9 @@ struct ProfileManifest ProfileManifest() { } - ProfileManifest(EvalState & state, const Path & profile) + ProfileManifest(EvalState & state, const std::filesystem::path & profile) { - auto manifestPath = profile + "/manifest.json"; + auto manifestPath = profile / "manifest.json"; if (pathExists(manifestPath)) { auto json = nlohmann::json::parse(readFile(manifestPath)); @@ -176,12 +176,12 @@ struct ProfileManifest } } - else if (pathExists(profile + "/manifest.nix")) { + else if (pathExists(profile / "manifest.nix")) { // FIXME: needed because of pure mode; ugly. - state.allowPath(state.store->followLinksToStore(profile)); - state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix")); + state.allowPath(state.store->followLinksToStore(profile.string())); + state.allowPath(state.store->followLinksToStore((profile / "manifest.nix").string())); - auto packageInfos = queryInstalled(state, state.store->followLinksToStore(profile)); + auto packageInfos = queryInstalled(state, state.store->followLinksToStore(profile.string())); for (auto & packageInfo : packageInfos) { ProfileElement element; diff --git a/src/nix/run.cc b/src/nix/run.cc index ec6a4d1e8..dfe7f374f 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -11,6 +11,7 @@ #include "source-accessor.hh" #include "progress-bar.hh" #include "eval.hh" +#include #if __linux__ # include @@ -169,7 +170,7 @@ void chrootHelper(int argc, char * * argv) if (!pathExists(storeDir)) { // FIXME: Use overlayfs? - Path tmpDir = createTempDir(); + std::filesystem::path tmpDir = createTempDir(); createDirs(tmpDir + storeDir); @@ -178,16 +179,16 @@ void chrootHelper(int argc, char * * argv) for (auto entry : std::filesystem::directory_iterator{"/"}) { checkInterrupt(); - auto src = entry.path().string(); - Path dst = tmpDir + "/" + entry.path().filename().string(); + auto src = entry.path(); + Path dst = tmpDir / entry.path().filename(); if (pathExists(dst)) continue; - auto st = lstat(src); - if (S_ISDIR(st.st_mode)) { + auto st = entry.symlink_status(); + if (std::filesystem::is_directory(st)) { if (mkdir(dst.c_str(), 0700) == -1) throw SysError("creating directory '%s'", dst); if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, 0) == -1) throw SysError("mounting '%s' on '%s'", src, dst); - } else if (S_ISLNK(st.st_mode)) + } else if (std::filesystem::is_symlink(st)) createSymlink(readLink(src), dst); } From d98e06a58130cbfeecbe03a51a829d81e46bea93 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 11 Aug 2024 20:13:47 +0530 Subject: [PATCH 788/910] Use `std::filesystem::path` in more executables --- src/nix-channel/nix-channel.cc | 6 +++--- src/nix-collect-garbage/nix-collect-garbage.cc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc index 9f7f557b5..e2bedcbcb 100644 --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -17,7 +17,7 @@ using namespace nix; typedef std::map Channels; static Channels channels; -static Path channelsList; +static std::filesystem::path channelsList; // Reads the list of channels. static void readChannels() @@ -41,7 +41,7 @@ static void writeChannels() { auto channelsFD = AutoCloseFD{open(channelsList.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644)}; if (!channelsFD) - throw SysError("opening '%1%' for writing", channelsList); + throw SysError("opening '%1%' for writing", channelsList.string()); for (const auto & channel : channels) writeFull(channelsFD.get(), channel.second + " " + channel.first + "\n"); } @@ -165,7 +165,7 @@ static int main_nix_channel(int argc, char ** argv) { // Figure out the name of the `.nix-channels' file to use auto home = getHome(); - channelsList = settings.useXDGBaseDirectories ? createNixStateDir() + "/channels" : home + "/.nix-channels"; + channelsList = settings.useXDGBaseDirectories ? createNixStateDir() + OS_STR("/channels") : home + OS_STR("/.nix-channels"); nixDefExpr = getNixDefExpr(); // Figure out the name of the channels profile. diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 91209c978..66acd60bc 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -21,7 +21,7 @@ bool dryRun = false; * Of course, this makes rollbacks to before this point in time * impossible. */ -void removeOldGenerations(std::string dir) +void removeOldGenerations(std::filesystem::path dir) { if (access(dir.c_str(), R_OK) != 0) return; @@ -81,7 +81,7 @@ static int main_nix_collect_garbage(int argc, char * * argv) }); if (removeOld) { - std::set dirsToClean = { + std::set dirsToClean = { profilesDir(), settings.nixStateDir + "/profiles", dirOf(getDefaultProfile())}; for (auto & dir : dirsToClean) removeOldGenerations(dir); From b64d6aa7b0fcc801d8ee496c41658d9883af37aa Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 11 Aug 2024 18:07:19 +0200 Subject: [PATCH 789/910] CONTRIBUTING.md: Clarify use of cherry-pick on forks --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c9ee9b35..da56653b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,7 +64,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). Co-authored-by: User Name ``` - Use the `git cherry-pick -x` flag, and amend the commits to link to forks when applicable. + When cherry-picking from a different repository, use the `-x` flag, and then amend the commits to turn the hashes into URLs. * Make sure to have [a clean history of commits on your branch by using rebase](https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request). * [Mark the pull request as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request) if you're not done with the changes. From 9f6ee93f488c8935b560588ad7ba321d9618f588 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 12 Aug 2024 15:47:02 +0200 Subject: [PATCH 790/910] fetchers::downloadTarball(): Return a cacheable accessor downloadTarball() is used by `-I foo=` etc. fetchToStore() needs the accessor to have a fingerprint to enable caching. Fixes #11271. --- src/libcmd/common-eval-args.cc | 4 +++- src/libexpr/eval.cc | 4 +++- src/libexpr/primops/fetchTree.cc | 6 +++++- src/libfetchers/tarball.cc | 20 ++++++++++++++++++-- src/libfetchers/tarball.hh | 9 ++++++--- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index fcef92487..ae9994a05 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -171,7 +171,9 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas { if (EvalSettings::isPseudoUrl(s)) { auto accessor = fetchers::downloadTarball( - EvalSettings::resolvePseudoUrl(s)).accessor; + state.store, + state.fetchSettings, + EvalSettings::resolvePseudoUrl(s)); auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy); return state.rootPath(CanonPath(state.store->toRealPath(storePath))); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c9101678c..92320b554 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -3088,7 +3088,9 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa if (EvalSettings::isPseudoUrl(value)) { try { auto accessor = fetchers::downloadTarball( - EvalSettings::resolvePseudoUrl(value)).accessor; + store, + fetchSettings, + EvalSettings::resolvePseudoUrl(value)); auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); return finish(store->toRealPath(storePath)); } catch (Error & e) { diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 78328701d..5d074e623 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -507,7 +507,11 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v // https://github.com/NixOS/nix/issues/4313 auto storePath = unpack - ? fetchToStore(*state.store, fetchers::downloadTarball(*url).accessor, FetchMode::Copy, name) + ? fetchToStore( + *state.store, + fetchers::downloadTarball(state.store, state.fetchSettings, *url), + FetchMode::Copy, + name) : fetchers::downloadFile(state.store, *url, name).storePath; if (expectedHash) { diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 457210542..dd4f3b780 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -102,7 +102,7 @@ DownloadFileResult downloadFile( }; } -DownloadTarballResult downloadTarball( +static DownloadTarballResult downloadTarball_( const std::string & url, const Headers & headers) { @@ -202,6 +202,22 @@ DownloadTarballResult downloadTarball( return attrsToResult(infoAttrs); } +ref downloadTarball( + ref store, + const Settings & settings, + const std::string & url) +{ + /* Go through Input::getAccessor() to ensure that the resulting + accessor has a fingerprint. */ + fetchers::Attrs attrs; + attrs.insert_or_assign("type", "tarball"); + attrs.insert_or_assign("url", url); + + auto input = Input::fromAttrs(settings, std::move(attrs)); + + return input.getAccessor(store).first; +} + // An input scheme corresponding to a curl-downloadable resource. struct CurlInputScheme : InputScheme { @@ -353,7 +369,7 @@ struct TarballInputScheme : CurlInputScheme { auto input(_input); - auto result = downloadTarball(getStrAttr(input.attrs, "url"), {}); + auto result = downloadTarball_(getStrAttr(input.attrs, "url"), {}); result.accessor->setPathDisplay("«" + input.to_string() + "»"); diff --git a/src/libfetchers/tarball.hh b/src/libfetchers/tarball.hh index d9bdd123d..2042041d5 100644 --- a/src/libfetchers/tarball.hh +++ b/src/libfetchers/tarball.hh @@ -14,6 +14,8 @@ struct SourceAccessor; namespace nix::fetchers { +struct Settings; + struct DownloadFileResult { StorePath storePath; @@ -40,8 +42,9 @@ struct DownloadTarballResult * Download and import a tarball into the Git cache. The result is the * Git tree hash of the root directory. */ -DownloadTarballResult downloadTarball( - const std::string & url, - const Headers & headers = {}); +ref downloadTarball( + ref store, + const Settings & settings, + const std::string & url); } From 58b03ef1cd73385699e3ea5c46d0dc0d36c3bbcd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 24 Jul 2024 23:14:36 -0400 Subject: [PATCH 791/910] Move `NIX_BIN_DIR` and all logic using it to the Nix executable itself This is because with the split packages of the Meson build, we simply have no idea what directory the binaries will be installed in when we build the library. In the process of doing so, consolidate and make more sophisticated the logic to cope with a few corner cases (e.g. `NIX_BIN_DIR` exists, but no binaries are inside it). Co-authored-by: Robert Hensing --- doc/manual/rl-next/build-hook-default.md | 22 ++++++++++++++ src/libcmd/repl.cc | 38 +++++++++++------------- src/libcmd/repl.hh | 14 +++++++-- src/libstore/globals.cc | 29 ------------------ src/libstore/globals.hh | 7 +---- src/libstore/local.mk | 1 - src/libstore/meson.build | 2 -- src/libstore/unix/build/hook-instance.cc | 14 +++++++-- src/libutil/executable-path.cc | 18 ++++++++++- src/libutil/executable-path.hh | 15 +++++++++- src/nix-channel/nix-channel.cc | 11 +++---- src/nix/local.mk | 2 ++ src/nix/main.cc | 12 ++++++++ src/nix/meson.build | 19 +++++++++++- src/nix/repl.cc | 23 +++++++++++++- src/nix/self-exe.cc | 38 ++++++++++++++++++++++++ src/nix/self-exe.hh | 31 +++++++++++++++++++ src/nix/upgrade-nix.cc | 5 ++-- src/perl/lib/Nix/Config.pm.in | 1 - src/perl/lib/Nix/Store.pm | 2 +- src/perl/lib/Nix/Store.xs | 5 ---- 21 files changed, 227 insertions(+), 82 deletions(-) create mode 100644 doc/manual/rl-next/build-hook-default.md create mode 100644 src/nix/self-exe.cc create mode 100644 src/nix/self-exe.hh diff --git a/doc/manual/rl-next/build-hook-default.md b/doc/manual/rl-next/build-hook-default.md new file mode 100644 index 000000000..197290536 --- /dev/null +++ b/doc/manual/rl-next/build-hook-default.md @@ -0,0 +1,22 @@ +--- +synopsis: |- + The `build-hook` setting's default is less useful when using `libnixstore` as a library +prs: +- 11178 +--- + +*This is an obscure issue that only affects usage of the `libnixstore` library outside of the Nix executable.* + +As part the ongoing [rewrite of the build system](https://github.com/NixOS/nix/issues/2503) to use [Meson](https://mesonbuild.com/), we are also switching to packaging individual Nix components separately (and building them in separate derivations). +This means that when building `libnixstore` we do not know where the Nix binaries will be installed --- `libnixstore` doesn't know about downstream consumers like the Nix binaries at all. + +*This is also unrelated to the _`post`_-`build-hook`*, which is often used for pushing to a cache.* + +This has a small adverse affect on remote building --- the `build-remote` executable that is specified from the [`build-hook`](@docroot@/command-ref/conf-file.md#conf-build-hook) setting will not be gotten from the (presumed) installation location, but instead looked up on the `PATH`. +This means that other applications linking `libnixstore` that wish to use remote building must arrange for the `nix` command to be on the PATH (or manually overriding `build-hook`) in order for that to work. + +Long term we don't envision this being a downside, because we plan to [get rid of `build-remote` and the build hook setting entirely](https://github.com/NixOS/nix/issues/1221). +There is simply no need to add a second layer of remote-procedure-calling when we want to connect to a remote builder. +The build hook protocol did in principle support custom ways of remote building, but that can also be accomplished with a custom service for the ssh or daemon/ssh-ng protocols, or with a custom [store type](@docroot@/store/types/) i.e. `Store` subclass. + +The Perl bindings no longer expose `getBinDir` either, since they libraries those bindings wrap no longer know the location of installed binaries as described above. diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index efc04b029..e7c43367c 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -7,7 +7,6 @@ #include "ansicolor.hh" #include "shared.hh" -#include "config-global.hh" #include "eval.hh" #include "eval-settings.hh" #include "attr-path.hh" @@ -77,10 +76,14 @@ struct NixRepl int displ; StringSet varNames; + RunNix * runNixPtr; + + void runNix(Path program, const Strings & args, const std::optional & input = {}); + std::unique_ptr interacter; NixRepl(const LookupPath & lookupPath, nix::ref store,ref state, - std::function getValues); + std::function getValues, RunNix * runNix); virtual ~NixRepl() = default; ReplExitStatus mainLoop() override; @@ -125,32 +128,16 @@ std::string removeWhitespace(std::string s) NixRepl::NixRepl(const LookupPath & lookupPath, nix::ref store, ref state, - std::function getValues) + std::function getValues, RunNix * runNix = nullptr) : AbstractNixRepl(state) , debugTraceIndex(0) , getValues(getValues) , staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get())) + , runNixPtr{runNix} , interacter(make_unique(getDataDir() + "/nix/repl-history")) { } -void runNix(Path program, const Strings & args, - const std::optional & input = {}) -{ - auto subprocessEnv = getEnv(); - subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue(); - //isInteractive avoid grabling interactive commands - runProgram2(RunOptions { - .program = settings.nixBinDir+ "/" + program, - .args = args, - .environment = subprocessEnv, - .input = input, - .isInteractive = true, - }); - - return; -} - static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt) { if (dt.isError) @@ -833,9 +820,18 @@ void NixRepl::evalString(std::string s, Value & v) } +void NixRepl::runNix(Path program, const Strings & args, const std::optional & input) +{ + if (runNixPtr) + (*runNixPtr)(program, args, input); + else + throw Error("Cannot run '%s', no method of calling the Nix CLI provided", program); +} + + std::unique_ptr AbstractNixRepl::create( const LookupPath & lookupPath, nix::ref store, ref state, - std::function getValues) + std::function getValues, RunNix * runNix) { return std::make_unique( lookupPath, diff --git a/src/libcmd/repl.hh b/src/libcmd/repl.hh index 3fd4b2c39..11d1820f5 100644 --- a/src/libcmd/repl.hh +++ b/src/libcmd/repl.hh @@ -19,9 +19,19 @@ struct AbstractNixRepl typedef std::vector> AnnotatedValues; + using RunNix = void(Path program, const Strings & args, const std::optional & input); + + /** + * @param runNix Function to run the nix CLI to support various + * `:` commands. Optional; if not provided, + * everything else will still work fine, but those commands won't. + */ static std::unique_ptr create( - const LookupPath & lookupPath, nix::ref store, ref state, - std::function getValues); + const LookupPath & lookupPath, + nix::ref store, + ref state, + std::function getValues, + RunNix * runNix = nullptr); static ReplExitStatus runSimple( ref evalState, diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 439a6f97c..52ab35b4c 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -64,7 +64,6 @@ Settings::Settings() , 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(getEnvNonEmpty("NIX_BIN_DIR").value_or(NIX_BIN_DIR))) , nixManDir(canonPath(NIX_MAN_DIR)) , nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH))) { @@ -95,34 +94,6 @@ Settings::Settings() sandboxPaths = tokenizeString("/System/Library/Frameworks /System/Library/PrivateFrameworks /bin/sh /bin/bash /private/tmp /private/var/tmp /usr/lib"); allowedImpureHostPrefixes = tokenizeString("/System/Library /usr/lib /dev /bin/sh"); #endif - - /* Set the build hook location - - For builds we perform a self-invocation, so Nix has to be self-aware. - That is, it has to know where it is installed. We don't think it's sentient. - - Normally, nix is installed according to `nixBinDir`, which is set at compile time, - but can be overridden. This makes for a great default that works even if this - code is linked as a library into some other program whose main is not aware - that it might need to be a build remote hook. - - However, it may not have been installed at all. For example, if it's a static build, - there's a good chance that it has been moved out of its installation directory. - That makes `nixBinDir` useless. Instead, we'll query the OS for the path to the - current executable, using `getSelfExe()`. - - As a last resort, we resort to `PATH`. Hopefully we find a `nix` there that's compatible. - If you're porting Nix to a new platform, that might be good enough for a while, but - you'll want to improve `getSelfExe()` to work on your platform. - */ - std::string nixExePath = nixBinDir + "/nix"; - if (!pathExists(nixExePath)) { - nixExePath = getSelfExe().value_or("nix"); - } - buildHook = { - nixExePath, - "__build-remote", - }; } void loadConfFile(AbstractConfig & config) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 1ca9f02f5..65c7bf3bc 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -84,11 +84,6 @@ public: */ std::vector nixUserConfFiles; - /** - * The directory where the main programs are stored. - */ - Path nixBinDir; - /** * The directory where the man pages are stored. */ @@ -246,7 +241,7 @@ public: )", {"build-timeout"}}; - Setting buildHook{this, {}, "build-hook", + Setting buildHook{this, {"nix", "__build-remote"}, "build-hook", R"( The path to the helper program that executes remote builds. diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 5dc8f3370..88be6a366 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -71,7 +71,6 @@ libstore_CXXFLAGS += \ -DNIX_STATE_DIR=\"$(NIX_ROOT)$(localstatedir)/nix\" \ -DNIX_LOG_DIR=\"$(NIX_ROOT)$(localstatedir)/log/nix\" \ -DNIX_CONF_DIR=\"$(NIX_ROOT)$(sysconfdir)/nix\" \ - -DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" \ -DNIX_MAN_DIR=\"$(NIX_ROOT)$(mandir)\" \ -DLSOF=\"$(NIX_ROOT)$(lsof)\" diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 50b15e15d..d2cc235fd 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -328,7 +328,6 @@ prefix = get_option('prefix') path_opts = [ # Meson built-ins. 'datadir', - 'bindir', 'mandir', 'libdir', 'includedir', @@ -373,7 +372,6 @@ cpp_str_defines = { 'NIX_STATE_DIR': state_dir / 'nix', 'NIX_LOG_DIR': log_dir, 'NIX_CONF_DIR': sysconfdir / 'nix', - 'NIX_BIN_DIR': bindir, 'NIX_MAN_DIR': mandir, } diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index d73d86ff2..4f8492fe9 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -4,6 +4,7 @@ #include "file-system.hh" #include "child.hh" #include "strings.hh" +#include "executable-path.hh" namespace nix { @@ -16,11 +17,18 @@ HookInstance::HookInstance() if (buildHookArgs.empty()) throw Error("'build-hook' setting is empty"); - auto buildHook = canonPath(buildHookArgs.front()); + std::filesystem::path buildHook = buildHookArgs.front(); buildHookArgs.pop_front(); + try { + buildHook = ExecutablePath::load().findPath(buildHook); + } catch (ExecutableLookupError & e) { + e.addTrace(nullptr, "while resolving the 'build-hook' setting'"); + throw; + } + Strings args; - args.push_back(std::string(baseNameOf(buildHook))); + args.push_back(buildHook.filename().string()); for (auto & arg : buildHookArgs) args.push_back(arg); @@ -59,7 +67,7 @@ HookInstance::HookInstance() if (dup2(builderOut.readSide.get(), 5) == -1) throw SysError("dupping builder's stdout/stderr"); - execv(buildHook.c_str(), stringsToCharPtrs(args).data()); + execv(buildHook.native().c_str(), stringsToCharPtrs(args).data()); throw SysError("executing '%s'", buildHook); }); diff --git a/src/libutil/executable-path.cc b/src/libutil/executable-path.cc index 1658e3667..8005a19be 100644 --- a/src/libutil/executable-path.cc +++ b/src/libutil/executable-path.cc @@ -60,7 +60,7 @@ OsString ExecutablePath::render() const } std::optional -ExecutablePath::find(const OsString & exe, std::function isExecutable) const +ExecutablePath::findName(const OsString & exe, std::function isExecutable) const { // "If the pathname being sought contains a , the search // through the path prefixes shall not be performed." @@ -76,4 +76,20 @@ ExecutablePath::find(const OsString & exe, std::function return std::nullopt; } +fs::path ExecutablePath::findPath(const fs::path & exe, std::function isExecutable) const +{ + // "If the pathname being sought contains a , the search + // through the path prefixes shall not be performed." + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 + if (exe.filename() == exe) { + auto resOpt = findName(exe, isExecutable); + if (resOpt) + return *resOpt; + else + throw ExecutableLookupError("Could not find executable '%s'", exe.native()); + } else { + return exe; + } +} + } // namespace nix diff --git a/src/libutil/executable-path.hh b/src/libutil/executable-path.hh index bcb8d28e8..f46d5e212 100644 --- a/src/libutil/executable-path.hh +++ b/src/libutil/executable-path.hh @@ -5,6 +5,8 @@ namespace nix { +MakeError(ExecutableLookupError, Error); + struct ExecutablePath { std::vector directories; @@ -54,10 +56,21 @@ struct ExecutablePath * * @return path to a resolved executable */ - std::optional find( + std::optional findName( const OsString & exe, std::function isExecutableFile = isExecutableFileAmbient) const; + /** + * Like the `findName` but also allows a file path as input. + * + * This implements the full POSIX spec: if the path is just a name, + * it searches like the above. Otherwise, it returns the path as is. + * If (in the name case) the search fails, an exception is thrown. + */ + std::filesystem::path findPath( + const std::filesystem::path & exe, + std::function isExecutable = isExecutableFileAmbient) const; + bool operator==(const ExecutablePath &) const = default; }; diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc index 9f7f557b5..878f3c828 100644 --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -7,6 +7,7 @@ #include "eval-settings.hh" // for defexpr #include "users.hh" #include "tarball.hh" +#include "self-exe.hh" #include #include @@ -67,7 +68,7 @@ static void removeChannel(const std::string & name) channels.erase(name); writeChannels(); - runProgram(settings.nixBinDir + "/nix-env", true, { "--profile", profile, "--uninstall", name }); + runProgram(getNixBin("nix-env").string(), true, { "--profile", profile, "--uninstall", name }); } static Path nixDefExpr; @@ -118,7 +119,7 @@ static void update(const StringSet & channelNames) bool unpacked = false; if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) { - runProgram(settings.nixBinDir + "/nix-build", false, { "--no-out-link", "--expr", "import " + unpackChannelPath + + runProgram(getNixBin("nix-build").string(), false, { "--no-out-link", "--expr", "import " + unpackChannelPath + "{ name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; }" }); unpacked = true; } @@ -143,7 +144,7 @@ static void update(const StringSet & channelNames) for (auto & expr : exprs) envArgs.push_back(std::move(expr)); envArgs.push_back("--quiet"); - runProgram(settings.nixBinDir + "/nix-env", false, envArgs); + runProgram(getNixBin("nix-env").string(), false, envArgs); // Make the channels appear in nix-env. struct stat st; @@ -244,7 +245,7 @@ static int main_nix_channel(int argc, char ** argv) case cListGenerations: if (!args.empty()) throw UsageError("'--list-generations' expects no arguments"); - std::cout << runProgram(settings.nixBinDir + "/nix-env", false, {"--profile", profile, "--list-generations"}) << std::flush; + std::cout << runProgram(getNixBin("nix-env").string(), false, {"--profile", profile, "--list-generations"}) << std::flush; break; case cRollback: if (args.size() > 1) @@ -256,7 +257,7 @@ static int main_nix_channel(int argc, char ** argv) } else { envArgs.push_back("--rollback"); } - runProgram(settings.nixBinDir + "/nix-env", false, envArgs); + runProgram(getNixBin("nix-env").string(), false, envArgs); break; } diff --git a/src/nix/local.mk b/src/nix/local.mk index 28b30b586..b57f6b3e2 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -26,6 +26,8 @@ endif nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix) +nix_CXXFLAGS += -DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" + nix_LIBS = libexpr libmain libfetchers libflake libstore libutil libcmd nix_LDFLAGS = $(THREAD_LDFLAGS) $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) $(LOWDOWN_LIBS) diff --git a/src/nix/main.cc b/src/nix/main.cc index 9d7d617cc..34de79ac8 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -19,6 +19,7 @@ #include "network-proxy.hh" #include "eval-cache.hh" #include "flake/flake.hh" +#include "self-exe.hh" #include #include @@ -366,6 +367,17 @@ void mainWrapped(int argc, char * * argv) initGC(); flake::initLib(flakeSettings); + /* Set the build hook location + + For builds we perform a self-invocation, so Nix has to be + self-aware. That is, it has to know where it is installed. We + don't think it's sentient. + */ + settings.buildHook.setDefault(Strings { + getNixBin({}).string(), + "__build-remote", + }); + #if __linux__ if (isRootUser()) { try { diff --git a/src/nix/meson.build b/src/nix/meson.build index 7405548de..05bee6a87 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -33,6 +33,21 @@ subdir('build-utils-meson/threads') subdir('build-utils-meson/export-all-symbols') +configdata = configuration_data() + +fs = import('fs') + +bindir = get_option('bindir') +if not fs.is_absolute(bindir) + bindir = get_option('prefix') / bindir +endif +configdata.set_quoted('NIX_BIN_DIR', bindir) + +config_h = configure_file( + configuration : configdata, + output : 'config-nix-cli.hh', +) + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. @@ -42,15 +57,17 @@ add_project_arguments( #'-include', 'config-fetchers.hh', '-include', 'config-main.hh', '-include', 'config-cmd.hh', + '-include', 'config-nix-cli.hh', language : 'cpp', ) subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/generate-header') -nix_sources = files( +nix_sources = [config_h] + files( 'add-to-store.cc', 'app.cc', + 'self-exe.cc', 'build.cc', 'bundle.cc', 'cat.cc', diff --git a/src/nix/repl.cc b/src/nix/repl.cc index a2f3e033e..5a570749f 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -1,12 +1,32 @@ #include "eval.hh" #include "eval-settings.hh" +#include "config-global.hh" #include "globals.hh" #include "command.hh" #include "installable-value.hh" #include "repl.hh" +#include "processes.hh" +#include "self-exe.hh" namespace nix { +void runNix(Path program, const Strings & args, + const std::optional & input = {}) +{ + auto subprocessEnv = getEnv(); + subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue(); + //isInteractive avoid grabling interactive commands + runProgram2(RunOptions { + .program = getNixBin(program).string(), + .args = args, + .environment = subprocessEnv, + .input = input, + .isInteractive = true, + }); + + return; +} + struct CmdRepl : RawInstallablesCommand { CmdRepl() { @@ -81,7 +101,8 @@ struct CmdRepl : RawInstallablesCommand lookupPath, openStore(), state, - getValues + getValues, + runNix ); repl->autoArgs = getAutoArgs(*repl->state); repl->initEnv(); diff --git a/src/nix/self-exe.cc b/src/nix/self-exe.cc new file mode 100644 index 000000000..a260bafd5 --- /dev/null +++ b/src/nix/self-exe.cc @@ -0,0 +1,38 @@ +#include "current-process.hh" +#include "file-system.hh" +#include "globals.hh" +#include "self-exe.hh" + +namespace nix { + +namespace fs = std::filesystem; + +fs::path getNixBin(std::optional binaryNameOpt) +{ + auto getBinaryName = [&] { return binaryNameOpt ? *binaryNameOpt : "nix"; }; + + // If the environment variable is set, use it unconditionally + if (auto envOpt = getEnvNonEmpty("NIX_BIN_DIR")) + return fs::path{*envOpt} / std::string{getBinaryName()}; + + // Use some-times avaiable OS tricks to get to the path of this Nix, and try that + if (auto selfOpt = getSelfExe()) { + fs::path path{*selfOpt}; + if (binaryNameOpt) + path = path.parent_path() / std::string{*binaryNameOpt}; + if (fs::exists(path)) + return path; + } + + // If `nix` exists at the hardcoded fallback path, use it. + { + auto path = fs::path{NIX_BIN_DIR} / std::string{getBinaryName()}; + if (fs::exists(path)) + return path; + } + + // return just the name, hoping the exe is on the `PATH` + return getBinaryName(); +} + +} diff --git a/src/nix/self-exe.hh b/src/nix/self-exe.hh new file mode 100644 index 000000000..0772afa67 --- /dev/null +++ b/src/nix/self-exe.hh @@ -0,0 +1,31 @@ +#pragma once +///@file + +#include + +namespace nix { + +/** + * Get a path to the given Nix binary. + * + * Normally, nix is installed according to `NIX_BIN_DIR`, which is set + * at compile time, but can be overridden. + * + * However, it may not have been installed at all. For example, if it's + * a static build, there's a good chance that it has been moved out of + * its installation directory. That makes `NIX_BIN_DIR` useless. + * Instead, we'll query the OS for the path to the current executable, + * using `getSelfExe()`. + * + * As a last resort, we resort to `PATH`. Hopefully we find a `nix` + * there that's compatible. If you're porting Nix to a new platform, + * that might be good enough for a while, but you'll want to improve + * `getSelfExe()` to work on your platform. + * + * @param binary_name the exact binary name we're looking up. Might be + * `nix-*` instead of `nix` for the legacy CLI commands. Optional to use + * current binary name. + */ +std::filesystem::path getNixBin(std::optional binary_name = {}); + +} diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 9ca3f6087..f54cc59d0 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -9,6 +9,7 @@ #include "names.hh" #include "progress-bar.hh" #include "executable-path.hh" +#include "self-exe.hh" using namespace nix; @@ -93,7 +94,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand { Activity act(*logger, lvlInfo, actUnknown, fmt("installing '%s' into profile '%s'...", store->printStorePath(storePath), profileDir)); - runProgram(settings.nixBinDir + "/nix-env", false, + runProgram(getNixBin("nix-env").string(), false, {"--profile", profileDir, "-i", store->printStorePath(storePath), "--no-sandbox"}); } @@ -103,7 +104,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand /* Return the profile in which Nix is installed. */ Path getProfileDir(ref store) { - auto whereOpt = ExecutablePath::load().find(OS_STR("nix-env")); + auto whereOpt = ExecutablePath::load().findName(OS_STR("nix-env")); if (!whereOpt) throw Error("couldn't figure out how Nix is installed, so I can't upgrade it"); auto & where = *whereOpt; diff --git a/src/perl/lib/Nix/Config.pm.in b/src/perl/lib/Nix/Config.pm.in index 508a15e15..ad51cff3b 100644 --- a/src/perl/lib/Nix/Config.pm.in +++ b/src/perl/lib/Nix/Config.pm.in @@ -5,7 +5,6 @@ use Nix::Store; $version = "@PACKAGE_VERSION@"; -$binDir = Nix::Store::getBinDir; $storeDir = Nix::Store::getStoreDir; %config = (); diff --git a/src/perl/lib/Nix/Store.pm b/src/perl/lib/Nix/Store.pm index 16f2e17c8..f2ae7e88f 100644 --- a/src/perl/lib/Nix/Store.pm +++ b/src/perl/lib/Nix/Store.pm @@ -24,7 +24,7 @@ our @EXPORT = qw( hashPath hashFile hashString convertHash signString checkSignature - getBinDir getStoreDir + getStoreDir setVerbosity ); diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index f951437c8..172c3500d 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -424,11 +424,6 @@ StoreWrapper::addTempRoot(char * storePath) } -SV * getBinDir() - PPCODE: - XPUSHs(sv_2mortal(newSVpv(settings.nixBinDir.c_str(), 0))); - - SV * getStoreDir() PPCODE: XPUSHs(sv_2mortal(newSVpv(settings.nixStore.c_str(), 0))); From d49e14ba4ad4880a679f7265387ec83d01945b4d Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Mon, 12 Aug 2024 14:49:52 -0400 Subject: [PATCH 792/910] Take ANSI and tree characters into account --- src/nix/flake.cc | 66 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 839085b04..fdbadd390 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1263,6 +1263,46 @@ struct CmdFlakeShow : FlakeCommand, MixJSON attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" : "package"; if (description && !description->empty()) { + + // Takes a string and returns the # of characters displayed + auto columnLengthOfString = [](std::string_view s) -> unsigned int { + unsigned int columnCount = 0; + for (auto i = s.begin(); i < s.end();) { + // Test first character to determine if it is one of + // treeConn, treeLast, treeLine + if (*i == -30) { + i += 3; + ++columnCount; + } + // Escape sequences + // https://en.wikipedia.org/wiki/ANSI_escape_code + else if (*i == '\e') { + // Eat '[' + if (*(++i) == '[') { + ++i; + // Eat parameter bytes + while(*i >= 0x30 && *i <= 0x3f) ++i; + + // Eat intermediate bytes + while(*i >= 0x20 && *i <= 0x2f) ++i; + + // Eat final byte + if(*i >= 0x40 && *i <= 0x73) ++i; + } + else { + // Eat Fe Escape sequence + if (*i >= 0x40 && *i <= 0x5f) ++i; + } + } + else { + ++i; + ++columnCount; + } + } + + return columnCount; + }; + // Maximum length to print size_t maxLength = getWindowSize().second > 0 ? getWindowSize().second : 80; @@ -1271,29 +1311,25 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto newLinePos = trimmed.find('\n'); auto length = newLinePos != std::string::npos ? newLinePos : trimmed.length(); - // Sanitize the description and calculate the two parts of the line - // In order to get the length of the printable characters we need to - // filter out escape sequences. auto beginningOfLine = fmt("%s: %s '%s'", headerPrefix, type, name); - auto beginningOfLineLength = filterANSIEscapes(beginningOfLine, true).length(); - auto restOfLine = fmt(" - '%s'", filterANSIEscapes(trimmed, false, length)); + auto line = fmt("%s: %s '%s' - '%s'", headerPrefix, type, name, trimmed.substr(0, length)); // If we are already over the maximum length then do not trim // and don't print the description (preserves existing behavior) - if (beginningOfLineLength >= maxLength) { + if (columnLengthOfString(beginningOfLine) >= maxLength) { logger->cout("%s", beginningOfLine); } + // If the entire line fits then print that + else if (columnLengthOfString(line) < maxLength) { + logger->cout("%s", line); + } + // Otherwise we need to truncate else { - auto line = beginningOfLine + restOfLine; - // FIXME: Specifying `true` here gives the correct length - // BUT removes colors/bold so something is not quite right here. - line = filterANSIEscapes(line, true, maxLength); + auto lineLength = columnLengthOfString(line); + auto chopOff = lineLength - maxLength; + line.resize(line.length() - chopOff); + line = line.replace(line.length() - 3, 3, "..."); - // NOTE: This test might be incorrect since I get things like: - // 168 or 161 > maxLength. - if (line.length() > maxLength) { - line = line.replace(line.length() - 3, 3, "..."); - } logger->cout("%s", line); } } From f22bf867eb41a98441ab44b3667d3abb146ee603 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 12 Aug 2024 22:18:14 -0400 Subject: [PATCH 793/910] fix: use SymbolStr in constructor --- src/libexpr/value.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 257da1d2d..39144fe15 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -325,9 +325,9 @@ public: void mkStringMove(const char * s, const NixStringContext & context); - inline void mkString(const Symbol & s) + inline void mkString(const SymbolStr & s) { - mkString(((const std::string &) s).c_str()); + mkString(s.c_str()); } void mkPath(const SourcePath & path); From 95fe9f5ba18bb5fe025a06cbba0e18740ad101dd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 Jul 2024 14:46:26 -0400 Subject: [PATCH 794/910] Fix Meson installation of the Nix CLI Co-Authored-By: Qyriad --- src/nix/meson.build | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/nix/meson.build b/src/nix/meson.build index 05bee6a87..798c98e33 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -192,3 +192,55 @@ this_exe = executable( link_args: linker_export_flags, install : true, ) + +meson.override_find_program('nix', this_exe) + +nix_symlinks = [ + 'nix-build', + 'nix-channel', + 'nix-collect-garbage', + 'nix-copy-closure', + 'nix-daemon', + 'nix-env', + 'nix-hash', + 'nix-instantiate', + 'nix-prefetch-url', + 'nix-shell', + 'nix-store', +] + +foreach linkname : nix_symlinks + install_symlink( + linkname, + # TODO(Qyriad): should these continue to be relative symlinks? + pointing_to : 'nix', + install_dir : get_option('bindir'), + # The 'runtime' tag is what executables default to, which we want to emulate here. + install_tag : 'runtime' + ) + t = custom_target( + command: ['ln', '-sf', fs.name(this_exe), '@OUTPUT@'], + output: linkname, + # TODO(Ericson2314): Don't do this once we have the `meson.override_find_program` working) + build_by_default: true + ) + # TODO(Ericson3214): Dosen't yet work + #meson.override_find_program(linkname, t) +endforeach + +install_symlink( + 'build-remote', + pointing_to : '..' / '..'/ get_option('bindir') / 'nix', + install_dir : get_option('libexecdir') / 'nix', + # The 'runtime' tag is what executables default to, which we want to emulate here. + install_tag : 'runtime' +) + +custom_target( + command: ['ln', '-sf', fs.name(this_exe), '@OUTPUT@'], + output: 'build-remote', + # TODO(Ericson2314): Don't do this once we have the `meson.override_find_program` working) + build_by_default: true +) +# TODO(Ericson3214): Dosen't yet work +#meson.override_find_program(linkname, t) From 4956e7c44c5018c2c73a125913e264558cd638d6 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 13 Aug 2024 19:22:32 +0200 Subject: [PATCH 795/910] add cross-references to `nix-path` overriding (#11288) * add cross-references to `nix-path` overriding while this information is already present in the settings, it's more likely to be first accessed through the "lookup path" page, which currently requires following two links to get to the practically important bits. Co-authored-by: Robert Hensing --- doc/manual/src/language/constructs/lookup-path.md | 7 ++----- src/libexpr/primops.cc | 5 ++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/manual/src/language/constructs/lookup-path.md b/doc/manual/src/language/constructs/lookup-path.md index 11b9fe88c..a2e80280b 100644 --- a/doc/manual/src/language/constructs/lookup-path.md +++ b/doc/manual/src/language/constructs/lookup-path.md @@ -4,11 +4,8 @@ > > *lookup-path* = `<` *identifier* [ `/` *identifier* ]... `>` -A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/types.md#type-path) if the identifier matches a search path entry. - -The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath). - -See [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile) for details on lookup path resolution. +A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/types.md#type-path) if the identifier matches a search path entry in [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath). +The algorithm for lookup path resolution is described in the documentation on [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile). > **Example** > diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 2de6cbf4b..9de8ff599 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4857,7 +4857,10 @@ void EvalState::createBaseEnv() addConstant("__nixPath", v, { .type = nList, .doc = R"( - The value of the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path): a list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). + A list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). + Its value is primarily determined by the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path), which are + - Overridden by the [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) environment variable or the `--nix-path` option + - Extended by the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I) or `--extra-nix-path` > **Example** > From 77d84a8d8b82864fcd5dd7de6295ab3fc5f8e120 Mon Sep 17 00:00:00 2001 From: Noam Yorav-Raphael Date: Wed, 14 Aug 2024 14:35:42 +0300 Subject: [PATCH 796/910] /homeless-shelter -> /proc/homeless/shelter This makes it so even root can't create $HOME, for example by running `mkdir -p $HOME/.cache/foo`. --- doc/manual/src/language/derivations.md | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 8e3f0f791..c78c7b8c8 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -264,7 +264,7 @@ The [`builder`](#attr-builder) is executed as follows: - `PATH` is set to `/path-not-set` to prevent shells from initialising it to their built-in default value. - - `HOME` is set to `/homeless-shelter` to prevent programs from + - `HOME` is set to `/proc/homeless-shelter` to prevent programs from using `/etc/passwd` or the like to find the user's home directory, which could cause impurity. Usually, when `HOME` is set, it is used as the location of the home directory, even if diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index d3482df17..f6bbba8b4 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -102,7 +102,7 @@ void handleDiffHook( } } -const Path LocalDerivationGoal::homeDir = "/homeless-shelter"; +const Path LocalDerivationGoal::homeDir = "/proc/homeless-shelter"; LocalDerivationGoal::~LocalDerivationGoal() From 612fc76020495cefee6a5f5259fd83a531b0ef31 Mon Sep 17 00:00:00 2001 From: bryango Date: Wed, 14 Aug 2024 20:27:12 +0800 Subject: [PATCH 797/910] doc/manual: fix misaligned icons in custom.css (#11296) --- doc/manual/custom.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/custom.css b/doc/manual/custom.css index 9e8e3886f..7af150be3 100644 --- a/doc/manual/custom.css +++ b/doc/manual/custom.css @@ -12,8 +12,8 @@ h1.menu-title::before { } -h1.menu-title { - padding: 0.5em; +.menu-bar { + padding: 0.5em 0em; } .sidebar .sidebar-scrollbox { From cc9fe4dee713717c88b22c8701f9bdffc786baa1 Mon Sep 17 00:00:00 2001 From: Bryan Honof Date: Fri, 2 Aug 2024 00:10:08 +0200 Subject: [PATCH 798/910] Fix a few shellcheck tests Ref nixos/nix#10795 --- maintainers/flake-module.nix | 11 --- tests/functional/signing.sh | 94 +++++++++---------- tests/functional/simple.builder.sh | 7 +- tests/functional/simple.sh | 10 +- tests/functional/ssh-relay.sh | 8 +- tests/functional/store-info.sh | 4 +- tests/functional/structured-attrs.sh | 11 ++- .../functional/substitute-with-invalid-ca.sh | 8 +- tests/functional/suggestions.sh | 2 +- tests/functional/tarball.sh | 44 ++++----- tests/functional/test-infra.sh | 5 +- tests/functional/test-libstoreconsumer.sh | 2 +- tests/functional/timeout.sh | 2 +- 13 files changed, 102 insertions(+), 106 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index c3eaf671c..0b83e5696 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -641,19 +641,8 @@ ''^tests/functional/selfref-gc\.sh$'' ''^tests/functional/shell\.sh$'' ''^tests/functional/shell\.shebang\.sh$'' - ''^tests/functional/signing\.sh$'' ''^tests/functional/simple\.builder\.sh$'' - ''^tests/functional/simple\.sh$'' - ''^tests/functional/ssh-relay\.sh$'' - ''^tests/functional/store-info\.sh$'' - ''^tests/functional/structured-attrs\.sh$'' - ''^tests/functional/substitute-with-invalid-ca\.sh$'' - ''^tests/functional/suggestions\.sh$'' ''^tests/functional/supplementary-groups\.sh$'' - ''^tests/functional/tarball\.sh$'' - ''^tests/functional/test-infra\.sh$'' - ''^tests/functional/test-libstoreconsumer\.sh$'' - ''^tests/functional/timeout\.sh$'' ''^tests/functional/toString-path\.sh$'' ''^tests/functional/user-envs-migration\.sh$'' ''^tests/functional/user-envs-test-case\.sh$'' diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh index 890d1446f..8ec093a48 100755 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -5,108 +5,108 @@ source common.sh clearStoreIfPossible clearCache -nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 -pk1=$(cat $TEST_ROOT/pk1) -nix-store --generate-binary-cache-key cache2.example.org $TEST_ROOT/sk2 $TEST_ROOT/pk2 -pk2=$(cat $TEST_ROOT/pk2) +nix-store --generate-binary-cache-key cache1.example.org "$TEST_ROOT"/sk1 "$TEST_ROOT"/pk1 +pk1=$(cat "$TEST_ROOT"/pk1) +nix-store --generate-binary-cache-key cache2.example.org "$TEST_ROOT"/sk2 "$TEST_ROOT"/pk2 +pk2=$(cat "$TEST_ROOT"/pk2) # Build a path. outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROOT/sk1 $TEST_ROOT/sk2") # Verify that the path got signed. -info=$(nix path-info --json $outPath) -echo $info | jq -e '.[] | .ultimate == true' +info=$(nix path-info --json "$outPath") +echo "$info" | jq -e '.[] | .ultimate == true' TODO_NixOS # looks like an actual bug? Following line fails on NixOS: -echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' -echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' +echo "$info" | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo "$info" | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Test "nix store verify". -nix store verify -r $outPath +nix store verify -r "$outPath" -expect 2 nix store verify -r $outPath --sigs-needed 1 +expect 2 nix store verify -r "$outPath" --sigs-needed 1 -nix store verify -r $outPath --sigs-needed 1 --trusted-public-keys $pk1 +nix store verify -r "$outPath" --sigs-needed 1 --trusted-public-keys "$pk1" -expect 2 nix store verify -r $outPath --sigs-needed 2 --trusted-public-keys $pk1 +expect 2 nix store verify -r "$outPath" --sigs-needed 2 --trusted-public-keys "$pk1" -nix store verify -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" +nix store verify -r "$outPath" --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" nix store verify --all --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" # Build something unsigned. outPath2=$(nix-build simple.nix --no-out-link) -nix store verify -r $outPath +nix store verify -r "$outPath" # Verify that the path did not get signed but does have the ultimate bit. -info=$(nix path-info --json $outPath2) -echo $info | jq -e '.[] | .ultimate == true' -echo $info | jq -e '.[] | .signatures == []' +info=$(nix path-info --json "$outPath2") +echo "$info" | jq -e '.[] | .ultimate == true' +echo "$info" | jq -e '.[] | .signatures == []' # Test "nix store verify". -nix store verify -r $outPath2 +nix store verify -r "$outPath2" -expect 2 nix store verify -r $outPath2 --sigs-needed 1 +expect 2 nix store verify -r "$outPath2" --sigs-needed 1 -expect 2 nix store verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1 +expect 2 nix store verify -r "$outPath2" --sigs-needed 1 --trusted-public-keys "$pk1" # Test "nix store sign". -nix store sign --key-file $TEST_ROOT/sk1 $outPath2 +nix store sign --key-file "$TEST_ROOT"/sk1 "$outPath2" -nix store verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1 +nix store verify -r "$outPath2" --sigs-needed 1 --trusted-public-keys "$pk1" # Build something content-addressed. outPathCA=$(IMPURE_VAR1=foo IMPURE_VAR2=bar nix-build ./fixed.nix -A good.0 --no-out-link) -nix path-info --json $outPathCA | jq -e '.[] | .ca | startswith("fixed:md5:")' +nix path-info --json "$outPathCA" | jq -e '.[] | .ca | startswith("fixed:md5:")' # Content-addressed paths don't need signatures, so they verify # regardless of --sigs-needed. -nix store verify $outPathCA -nix store verify $outPathCA --sigs-needed 1000 +nix store verify "$outPathCA" +nix store verify "$outPathCA" --sigs-needed 1000 # Check that signing a content-addressed path doesn't overflow validSigs -nix store sign --key-file $TEST_ROOT/sk1 $outPathCA -nix store verify -r $outPathCA --sigs-needed 1000 --trusted-public-keys $pk1 +nix store sign --key-file "$TEST_ROOT"/sk1 "$outPathCA" +nix store verify -r "$outPathCA" --sigs-needed 1000 --trusted-public-keys "$pk1" # Copy to a binary cache. -nix copy --to file://$cacheDir $outPath2 +nix copy --to file://"$cacheDir" "$outPath2" # Verify that signatures got copied. -info=$(nix path-info --store file://$cacheDir --json $outPath2) -echo $info | jq -e '.[] | .ultimate == false' -echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' -echo $info | expect 4 jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' +info=$(nix path-info --store file://"$cacheDir" --json "$outPath2") +echo "$info" | jq -e '.[] | .ultimate == false' +echo "$info" | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo "$info" | expect 4 jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Verify that adding a signature to a path in a binary cache works. -nix store sign --store file://$cacheDir --key-file $TEST_ROOT/sk2 $outPath2 -info=$(nix path-info --store file://$cacheDir --json $outPath2) -echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' -echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' +nix store sign --store file://"$cacheDir" --key-file "$TEST_ROOT"/sk2 "$outPath2" +info=$(nix path-info --store file://"$cacheDir" --json "$outPath2") +echo "$info" | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo "$info" | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Copying to a diverted store should fail due to a lack of signatures by trusted keys. -chmod -R u+w $TEST_ROOT/store0 || true -rm -rf $TEST_ROOT/store0 +chmod -R u+w "$TEST_ROOT"/store0 || true +rm -rf "$TEST_ROOT"/store0 # Fails or very flaky only on GHA + macOS: # expectStderr 1 nix copy --to $TEST_ROOT/store0 $outPath | grepQuiet -E 'cannot add path .* because it lacks a signature by a trusted key' # but this works: -(! nix copy --to $TEST_ROOT/store0 $outPath) +(! nix copy --to "$TEST_ROOT"/store0 "$outPath") # But succeed if we supply the public keys. -nix copy --to $TEST_ROOT/store0 $outPath --trusted-public-keys $pk1 +nix copy --to "$TEST_ROOT"/store0 "$outPath" --trusted-public-keys "$pk1" -expect 2 nix store verify --store $TEST_ROOT/store0 -r $outPath +expect 2 nix store verify --store "$TEST_ROOT"/store0 -r "$outPath" -nix store verify --store $TEST_ROOT/store0 -r $outPath --trusted-public-keys $pk1 -nix store verify --store $TEST_ROOT/store0 -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" +nix store verify --store "$TEST_ROOT"/store0 -r "$outPath" --trusted-public-keys "$pk1" +nix store verify --store "$TEST_ROOT"/store0 -r "$outPath" --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" # It should also succeed if we disable signature checking. -(! nix copy --to $TEST_ROOT/store0 $outPath2) -nix copy --to $TEST_ROOT/store0?require-sigs=false $outPath2 +(! nix copy --to "$TEST_ROOT"/store0 "$outPath2") +nix copy --to "$TEST_ROOT"/store0?require-sigs=false "$outPath2" # But signatures should still get copied. -nix store verify --store $TEST_ROOT/store0 -r $outPath2 --trusted-public-keys $pk1 +nix store verify --store "$TEST_ROOT"/store0 -r "$outPath2" --trusted-public-keys "$pk1" # Content-addressed stuff can be copied without signatures. -nix copy --to $TEST_ROOT/store0 $outPathCA +nix copy --to "$TEST_ROOT"/store0 "$outPathCA" diff --git a/tests/functional/simple.builder.sh b/tests/functional/simple.builder.sh index 569e8ca88..97abf0676 100644 --- a/tests/functional/simple.builder.sh +++ b/tests/functional/simple.builder.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + echo "PATH=$PATH" # Verify that the PATH is empty. @@ -5,7 +7,6 @@ if mkdir foo 2> /dev/null; then exit 1; fi # Set a PATH (!!! impure). export PATH=$goodPath +mkdir "$out" -mkdir $out - -echo "Hello World!" > $out/hello \ No newline at end of file +echo "Hello World!" > "$out"/hello diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh index 86acca0c2..8afa369c2 100755 --- a/tests/functional/simple.sh +++ b/tests/functional/simple.sh @@ -12,7 +12,7 @@ outPath=$(nix-store -rvv "$drvPath") echo "output path is $outPath" -(! [ -w $outPath ]) +[[ ! -w $outPath ]] text=$(cat "$outPath/hello") if test "$text" != "Hello World!"; then exit 1; fi @@ -21,16 +21,16 @@ TODO_NixOS # Directed delete: $outPath is not reachable from a root, so it should # be deleteable. -nix-store --delete $outPath -(! [ -e $outPath/hello ]) +nix-store --delete "$outPath" +[[ ! -e $outPath/hello ]] -outPath="$(NIX_REMOTE=local?store=/foo\&real=$TEST_ROOT/real-store nix-instantiate --readonly-mode hash-check.nix)" +outPath="$(NIX_REMOTE='local?store=/foo&real='"$TEST_ROOT"'/real-store' nix-instantiate --readonly-mode hash-check.nix)" if test "$outPath" != "/foo/lfy1s6ca46rm5r6w4gg9hc0axiakjcnm-dependencies.drv"; then echo "hashDerivationModulo appears broken, got $outPath" exit 1 fi -outPath="$(NIX_REMOTE=local?store=/foo\&real=$TEST_ROOT/real-store nix-instantiate --readonly-mode big-derivation-attr.nix)" +outPath="$(NIX_REMOTE='local?store=/foo&real='"$TEST_ROOT"'/real-store' nix-instantiate --readonly-mode big-derivation-attr.nix)" if test "$outPath" != "/foo/xxiwa5zlaajv6xdjynf9yym9g319d6mn-big-derivation-attr.drv"; then echo "big-derivation-attr.nix hash appears broken, got $outPath. Memory corruption in large drv attr?" exit 1 diff --git a/tests/functional/ssh-relay.sh b/tests/functional/ssh-relay.sh index 059c66434..71b8ae9ab 100755 --- a/tests/functional/ssh-relay.sh +++ b/tests/functional/ssh-relay.sh @@ -2,10 +2,10 @@ source common.sh -echo foo > $TEST_ROOT/hello.sh +echo foo > "$TEST_ROOT"/hello.sh ssh_localhost=ssh://localhost -remote_store=?remote-store=$ssh_localhost +remote_store="?remote-store=$ssh_localhost" store=$ssh_localhost @@ -13,6 +13,6 @@ store+=$remote_store store+=$remote_store store+=$remote_store -out=$(nix store add-path --store "$store" $TEST_ROOT/hello.sh) +out=$(nix store add-path --store "$store" "$TEST_ROOT"/hello.sh) -[ foo = $(< $out) ] +[ foo = "$(< "$out")" ] diff --git a/tests/functional/store-info.sh b/tests/functional/store-info.sh index f37889fbb..beecc2dd9 100755 --- a/tests/functional/store-info.sh +++ b/tests/functional/store-info.sh @@ -8,12 +8,12 @@ STORE_INFO_JSON=$(nix store info --json) echo "$STORE_INFO" | grep "Store URL: ${NIX_REMOTE}" if [[ -v NIX_DAEMON_PACKAGE ]] && isDaemonNewer "2.7.0pre20220126"; then - DAEMON_VERSION=$($NIX_DAEMON_PACKAGE/bin/nix daemon --version | cut -d' ' -f3) + DAEMON_VERSION=$("$NIX_DAEMON_PACKAGE"/bin/nix daemon --version | cut -d' ' -f3) echo "$STORE_INFO" | grep "Version: $DAEMON_VERSION" [[ "$(echo "$STORE_INFO_JSON" | jq -r ".version")" == "$DAEMON_VERSION" ]] fi -expect 127 NIX_REMOTE=unix:$PWD/store nix store info || \ +expect 127 NIX_REMOTE=unix:"$PWD"/store nix store info || \ fail "nix store info on a non-existent store should fail" TODO_NixOS diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh index ec1282668..64d136e99 100755 --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -8,17 +8,19 @@ requireDaemonNewerThan "2.4pre20210712" clearStoreIfPossible -rm -f $TEST_ROOT/result +rm -f "$TEST_ROOT"/result -nix-build structured-attrs.nix -A all -o $TEST_ROOT/result +nix-build structured-attrs.nix -A all -o "$TEST_ROOT"/result -[[ $(cat $TEST_ROOT/result/foo) = bar ]] -[[ $(cat $TEST_ROOT/result-dev/foo) = foo ]] +[[ $(cat "$TEST_ROOT"/result/foo) = bar ]] +[[ $(cat "$TEST_ROOT"/result-dev/foo) = foo ]] export NIX_BUILD_SHELL=$SHELL +# shellcheck disable=SC2016 env NIX_PATH=nixpkgs=shell.nix nix-shell structured-attrs-shell.nix \ --run 'test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' +# shellcheck disable=SC2016 nix develop -f structured-attrs-shell.nix -c bash -c 'test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' TODO_NixOS # following line fails. @@ -26,6 +28,7 @@ TODO_NixOS # following line fails. # `nix develop` is a slightly special way of dealing with environment vars, it parses # these from a shell-file exported from a derivation. This is to test especially `outputs` # (which is an associative array in thsi case) being fine. +# shellcheck disable=SC2016 nix develop -f structured-attrs-shell.nix -c bash -c 'test -n "$out"' nix print-dev-env -f structured-attrs-shell.nix | grepQuiet 'NIX_ATTRS_JSON_FILE=' diff --git a/tests/functional/substitute-with-invalid-ca.sh b/tests/functional/substitute-with-invalid-ca.sh index d8af67237..33432e95d 100755 --- a/tests/functional/substitute-with-invalid-ca.sh +++ b/tests/functional/substitute-with-invalid-ca.sh @@ -11,16 +11,16 @@ getRemoteNarInfo () { echo "$cacheDir/$(getHash "$1").narinfo" } -cat < $TEST_HOME/good.txt +cat < "$TEST_HOME"/good.txt I’m a good path EOF -cat < $TEST_HOME/bad.txt +cat < "$TEST_HOME"/bad.txt I’m a bad path EOF -good=$(nix-store --add $TEST_HOME/good.txt) -bad=$(nix-store --add $TEST_HOME/bad.txt) +good=$(nix-store --add "$TEST_HOME"/good.txt) +bad=$(nix-store --add "$TEST_HOME"/bad.txt) nix copy --to "$BINARY_CACHE" "$good" nix copy --to "$BINARY_CACHE" "$bad" nix-collect-garbage >/dev/null 2>&1 diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh index 8db6f7b97..fbca93da8 100755 --- a/tests/functional/suggestions.sh +++ b/tests/functional/suggestions.sh @@ -37,7 +37,7 @@ NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION=$(! nix build .\#bar 2>&1 1>/dev/null) [[ ! "$NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION" =~ "Did you mean" ]] || \ fail "The nix build stderr shouldn’t suggest anything if there’s nothing relevant to suggest" -NIX_EVAL_STDERR_WITH_SUGGESTIONS=$(! nix build --impure --expr '(builtins.getFlake (builtins.toPath ./.)).packages.'$system'.fob' 2>&1 1>/dev/null) +NIX_EVAL_STDERR_WITH_SUGGESTIONS=$(! nix build --impure --expr '(builtins.getFlake (builtins.toPath ./.)).packages.'"$system"'.fob' 2>&1 1>/dev/null) [[ "$NIX_EVAL_STDERR_WITH_SUGGESTIONS" =~ "Did you mean one of fo1, fo2, foo or fooo?" ]] || \ fail "The evaluator should suggest the three closest possiblities" diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index 4d8945625..dee0a98f1 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -4,51 +4,51 @@ source common.sh clearStoreIfPossible -rm -rf $TEST_HOME +rm -rf "$TEST_HOME" tarroot=$TEST_ROOT/tarball -rm -rf $tarroot -mkdir -p $tarroot -cp dependencies.nix $tarroot/default.nix -cp config.nix dependencies.builder*.sh $tarroot/ -touch -d '@1000000000' $tarroot $tarroot/* +rm -rf "$tarroot" +mkdir -p "$tarroot" +cp dependencies.nix "$tarroot/default.nix" +cp config.nix dependencies.builder*.sh "$tarroot/" +touch -d '@1000000000' "$tarroot" "$tarroot"/* -hash=$(nix hash path $tarroot) +hash=$(nix hash path "$tarroot") test_tarball() { local ext="$1" local compressor="$2" tarball=$TEST_ROOT/tarball.tar$ext - (cd $TEST_ROOT && GNUTAR_REPRODUCIBLE= tar --mtime=$tarroot/default.nix --owner=0 --group=0 --numeric-owner --sort=name -c -f - tarball) | $compressor > $tarball + (cd "$TEST_ROOT" && GNUTAR_REPRODUCIBLE=1 tar --mtime="$tarroot"/default.nix --owner=0 --group=0 --numeric-owner --sort=name -c -f - tarball) | $compressor > "$tarball" - nix-env -f file://$tarball -qa --out-path | grepQuiet dependencies + nix-env -f file://"$tarball" -qa --out-path | grepQuiet dependencies - nix-build -o $TEST_ROOT/result file://$tarball + nix-build -o "$TEST_ROOT"/result file://"$tarball" - nix-build -o $TEST_ROOT/result '' -I foo=file://$tarball + nix-build -o "$TEST_ROOT"/result '' -I foo=file://"$tarball" - nix-build -o $TEST_ROOT/result -E "import (fetchTarball file://$tarball)" + nix-build -o "$TEST_ROOT"/result -E "import (fetchTarball file://$tarball)" # Do not re-fetch paths already present - nix-build -o $TEST_ROOT/result -E "import (fetchTarball { url = file:///does-not-exist/must-remain-unused/$tarball; sha256 = \"$hash\"; })" + nix-build -o "$TEST_ROOT"/result -E "import (fetchTarball { url = file:///does-not-exist/must-remain-unused/$tarball; sha256 = \"$hash\"; })" - nix-build -o $TEST_ROOT/result -E "import (fetchTree file://$tarball)" - nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; })" - nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })" + nix-build -o "$TEST_ROOT"/result -E "import (fetchTree file://$tarball)" + nix-build -o "$TEST_ROOT"/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; })" + nix-build -o "$TEST_ROOT"/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })" # Do not re-fetch paths already present - nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file:///does-not-exist/must-remain-unused/$tarball; narHash = \"$hash\"; })" - expectStderr 102 nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc=\"; })" | grep 'NAR hash mismatch in input' + nix-build -o "$TEST_ROOT"/result -E "import (fetchTree { type = \"tarball\"; url = file:///does-not-exist/must-remain-unused/$tarball; narHash = \"$hash\"; })" + expectStderr 102 nix-build -o "$TEST_ROOT"/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc=\"; })" | grep 'NAR hash mismatch in input' [[ $(nix eval --impure --expr "(fetchTree file://$tarball).lastModified") = 1000000000 ]] nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })) ? submodules)" >&2 nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })) ? submodules)" 2>&1 | grep 'true' - nix-instantiate --eval -E '1 + 2' -I fnord=file:///no-such-tarball.tar$ext - nix-instantiate --eval -E 'with ; 1 + 2' -I fnord=file:///no-such-tarball$ext - (! nix-instantiate --eval -E ' 1' -I fnord=file:///no-such-tarball$ext) + nix-instantiate --eval -E '1 + 2' -I fnord=file:///no-such-tarball.tar"$ext" + nix-instantiate --eval -E 'with ; 1 + 2' -I fnord=file:///no-such-tarball"$ext" + (! nix-instantiate --eval -E ' 1' -I fnord=file:///no-such-tarball"$ext") - nix-instantiate --eval -E '' -I fnord=file:///no-such-tarball$ext -I fnord=. + nix-instantiate --eval -E '' -I fnord=file:///no-such-tarball"$ext" -I fnord=. # Ensure that the `name` attribute isn’t accepted as that would mess # with the content-addressing diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index d02a11b46..2da26b08c 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -14,7 +14,7 @@ expect 1 false expect 1 expect 0 false function ret() { - return $1 + return "$1" } # `expect` can call functions, not just executables @@ -48,6 +48,7 @@ expectStderr 1 noisyFalse | grepQuiet NAY # `set -o pipefile` is enabled +# shellcheck disable=SC2317# shellcheck disable=SC2317 pipefailure () { # shellcheck disable=SC2216 true | false | true @@ -55,6 +56,7 @@ pipefailure () { expect 1 pipefailure unset pipefailure +# shellcheck disable=SC2317 pipefailure () { # shellcheck disable=SC2216 false | true | true @@ -82,6 +84,7 @@ expect 1 useUnbound # ! alone unfortunately negates `set -e`, but it works in functions: # shellcheck disable=SC2251 ! true +# shellcheck disable=SC2317 funBang () { ! true } diff --git a/tests/functional/test-libstoreconsumer.sh b/tests/functional/test-libstoreconsumer.sh index d1a1accb6..2adead1c0 100755 --- a/tests/functional/test-libstoreconsumer.sh +++ b/tests/functional/test-libstoreconsumer.sh @@ -5,4 +5,4 @@ source common.sh drv="$(nix-instantiate simple.nix)" cat "$drv" out="$(./test-libstoreconsumer/test-libstoreconsumer "$drv")" -cat "$out/hello" | grep -F "Hello World!" +grep -F "Hello World!" < "$out/hello" diff --git a/tests/functional/timeout.sh b/tests/functional/timeout.sh index f42354538..ae47fdc96 100755 --- a/tests/functional/timeout.sh +++ b/tests/functional/timeout.sh @@ -9,7 +9,7 @@ needLocalStore "see #4813" messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) && status=0 || status=$? -if [ $status -ne 101 ]; then +if [ "$status" -ne 101 ]; then echo "error: 'nix-store' exited with '$status'; should have exited 101" # FIXME: https://github.com/NixOS/nix/issues/4813 From 93f58150c9cd5f2fc9345c2730ce8a6da47dc474 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 13 Aug 2024 16:15:56 -0400 Subject: [PATCH 799/910] Coarse versions for constituent packages As discussed in our meeting, we should use a simplified version for the libraries without the date or commit hash. This will make rebuilding a lot faster in many cases. Progress on #10379 Co-Authored-By: Robert Hensing --- flake.nix | 19 +++++++++---------- package.nix | 5 ++--- packaging/components.nix | 31 +++++++++++++++++++++++++++---- packaging/dependencies.nix | 5 +---- packaging/hydra.nix | 9 ++++++++- 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/flake.nix b/flake.nix index 9e8592e3a..638f6b4bd 100644 --- a/flake.nix +++ b/flake.nix @@ -26,12 +26,6 @@ officialRelease = false; - version = lib.fileContents ./.version + versionSuffix; - versionSuffix = - if officialRelease - then "" - else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}"; - linux32BitSystems = [ "i686-linux" ]; linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ]; linuxSystems = linux32BitSystems ++ linux64BitSystems; @@ -130,12 +124,16 @@ # without "polluting" the top level "`pkgs`" attrset. # This also has the benefit of providing us with a distinct set of packages # we can iterate over. - nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix); + nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix { + inherit (final) lib; + inherit officialRelease; + src = self; + }); # The dependencies are in their own scope, so that they don't have to be # in Nixpkgs top level `pkgs` or `nixComponents`. nixDependencies = lib.makeScope final.newScope (import ./packaging/dependencies.nix { - inherit inputs stdenv versionSuffix; + inherit inputs stdenv; pkgs = final; }); @@ -170,6 +168,7 @@ linux64BitSystems nixpkgsFor self + officialRelease ; }; @@ -253,10 +252,10 @@ dockerImage = let pkgs = nixpkgsFor.${system}.native; - image = import ./docker.nix { inherit pkgs; tag = version; }; + image = import ./docker.nix { inherit pkgs; tag = pkgs.nix.version; }; in pkgs.runCommand - "docker-image-tarball-${version}" + "docker-image-tarball-${pkgs.nix.version}" { meta.description = "Docker image with Nix for ${system}"; } '' mkdir -p $out/nix-support diff --git a/package.nix b/package.nix index a7c8923e8..d41748b7c 100644 --- a/package.nix +++ b/package.nix @@ -47,7 +47,8 @@ , pname ? "nix" -, versionSuffix ? "" +, version +, versionSuffix # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true @@ -112,8 +113,6 @@ let inherit (lib) fileset; - version = lib.fileContents ./.version + versionSuffix; - # selected attributes with defaults, will be used to define some # things which should instead be gotten via `finalAttrs` in order to # work with overriding. diff --git a/packaging/components.nix b/packaging/components.nix index 870e9ae61..0e8334d10 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -1,11 +1,34 @@ +{ + lib, + src, + officialRelease, +}: + scope: + let inherit (scope) callPackage; + + baseVersion = lib.fileContents ../.version; + + versionSuffix = lib.optionalString (!officialRelease) "pre"; + + fineVersionSuffix = lib.optionalString + (!officialRelease) + "pre${builtins.substring 0 8 (src.lastModifiedDate or src.lastModified or "19700101")}_${src.shortRev or "dirty"}"; + + fineVersion = baseVersion + fineVersionSuffix; in # This becomes the pkgs.nixComponents attribute set { - nix = callPackage ../package.nix { }; + version = baseVersion + versionSuffix; + inherit versionSuffix; + + nix = callPackage ../package.nix { + version = fineVersion; + versionSuffix = fineVersionSuffix; + }; nix-util = callPackage ../src/libutil/package.nix { }; nix-util-c = callPackage ../src/libutil-c/package.nix { }; @@ -34,10 +57,10 @@ in nix-cmd = callPackage ../src/libcmd/package.nix { }; # Will replace `nix` once the old build system is gone. - nix-ng = callPackage ../src/nix/package.nix { }; + nix-ng = callPackage ../src/nix/package.nix { version = fineVersion; }; - nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; - nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { version = fineVersion; }; + nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { version = fineVersion; }; nix-perl-bindings = callPackage ../src/perl/package.nix { }; } diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index e0737593f..21c48e5cc 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -8,7 +8,6 @@ pkgs, stdenv, - versionSuffix, }: let @@ -73,11 +72,9 @@ let strictDeps = prevAttrs.strictDeps or true; enableParallelBuilding = true; }; - in scope: { - inherit stdenv versionSuffix; - version = lib.fileContents ../.version + versionSuffix; + inherit stdenv; aws-sdk-cpp = (pkgs.aws-sdk-cpp.override { apis = [ "s3" "transfer" ]; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index dbe992476..9752d90e3 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -6,6 +6,7 @@ , linux64BitSystems , nixpkgsFor , self +, officialRelease }: let inherit (inputs) nixpkgs nixpkgs-regression; @@ -16,7 +17,7 @@ let }; testNixVersions = pkgs: client: daemon: - pkgs.callPackage ../package.nix { + pkgs.nixComponents.callPackage ../package.nix { pname = "nix-tests" + lib.optionalString @@ -28,6 +29,12 @@ let test-daemon = daemon; doBuild = false; + + # This could be more accurate, but a shorter version will match the + # fine version with rev. This functionality is already covered in + # the normal test, so it's fine. + version = pkgs.nixComponents.version; + versionSuffix = pkgs.nixComponents.versionSuffix; }; # Technically we could just return `pkgs.nixComponents`, but for Hydra it's From 34fe2478a2b26569e5e0b244e927d3b50da0056d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 Jul 2024 16:07:06 -0400 Subject: [PATCH 800/910] Build Functional tests with Meson Co-Authored-By: Qyriad Co-authored-by: Robert Hensing --- flake.nix | 4 + meson.build | 1 + packaging/components.nix | 2 + packaging/hydra.nix | 7 +- src/libstore/meson.build | 2 +- src/nix-functional-tests | 1 + tests/functional/.version | 1 + tests/functional/ca/meson.build | 33 +++ tests/functional/common.sh | 3 +- .../{vars-and-functions.sh => functions.sh} | 107 ++----- tests/functional/common/init.sh | 4 +- tests/functional/common/meson.build | 5 + tests/functional/common/paths.sh | 19 +- tests/functional/common/subst-vars.sh.in | 20 +- tests/functional/common/vars.sh | 72 +++++ .../derivation-advanced-attributes.sh | 2 +- tests/functional/dyn-drv/meson.build | 19 ++ tests/functional/flakes/meson.build | 28 ++ tests/functional/git-hashing/meson.build | 8 + .../functional/local-overlay-store/common.sh | 3 +- .../local-overlay-store/meson.build | 18 ++ tests/functional/meson.build | 266 ++++++++++++++++++ tests/functional/nested-sandboxing/command.sh | 2 + tests/functional/nested-sandboxing/runner.nix | 7 +- tests/functional/package.nix | 117 ++++++++ tests/functional/plugins.sh | 9 +- tests/functional/plugins/meson.build | 16 ++ tests/functional/restricted.sh | 3 - .../test-libstoreconsumer/meson.build | 14 + 29 files changed, 678 insertions(+), 115 deletions(-) create mode 120000 src/nix-functional-tests create mode 120000 tests/functional/.version create mode 100644 tests/functional/ca/meson.build rename tests/functional/common/{vars-and-functions.sh => functions.sh} (76%) create mode 100644 tests/functional/common/meson.build create mode 100644 tests/functional/common/vars.sh create mode 100644 tests/functional/dyn-drv/meson.build create mode 100644 tests/functional/flakes/meson.build create mode 100644 tests/functional/git-hashing/meson.build create mode 100644 tests/functional/local-overlay-store/meson.build create mode 100644 tests/functional/meson.build create mode 100644 tests/functional/package.nix create mode 100644 tests/functional/plugins/meson.build create mode 100644 tests/functional/test-libstoreconsumer/meson.build diff --git a/flake.nix b/flake.nix index 638f6b4bd..22ca54118 100644 --- a/flake.nix +++ b/flake.nix @@ -210,6 +210,9 @@ "${nixpkgsPrefix}${pkgName}-${testName}" = test; }) ) + // lib.optionalAttrs (nixpkgs.stdenv.hostPlatform == nixpkgs.stdenv.buildPlatform) { + "${nixpkgsPrefix}nix-functional-tests" = nixpkgs.nixComponents.nix-functional-tests; + } ) // devFlake.checks.${system} or {} ); @@ -323,6 +326,7 @@ ++ lib.optionals havePerl pkgs.nixComponents.nix-perl-bindings.nativeBuildInputs ++ pkgs.nixComponents.nix-internal-api-docs.nativeBuildInputs ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs + ++ pkgs.nixComponents.nix-functional-tests.baseNativeBuildInputs ++ lib.optional (!stdenv.buildPlatform.canExecute stdenv.hostPlatform # Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479 diff --git a/meson.build b/meson.build index 1554244ab..715a3862d 100644 --- a/meson.build +++ b/meson.build @@ -42,3 +42,4 @@ subproject('nix-fetchers-tests') subproject('nix-expr-test-support') subproject('nix-expr-tests') subproject('nix-flake-tests') +subproject('nix-functional-tests') diff --git a/packaging/components.nix b/packaging/components.nix index 0e8334d10..f14613e8a 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -59,6 +59,8 @@ in # Will replace `nix` once the old build system is gone. nix-ng = callPackage ../src/nix/package.nix { version = fineVersion; }; + nix-functional-tests = callPackage ../src/nix-functional-tests/package.nix { version = fineVersion; }; + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { version = fineVersion; }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { version = fineVersion; }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 9752d90e3..46b4ff51d 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -62,6 +62,7 @@ let "nix-main-c" "nix-cmd" "nix-ng" + "nix-functional-tests" ]; in { @@ -75,8 +76,10 @@ in lib.genAttrs linux64BitSystems (system: nixpkgsFor.${system}.static.nixComponents.${pkgName})); buildCross = forAllPackages (pkgName: - forAllCrossSystems (crossSystem: - lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.nixComponents.${pkgName}))); + # Hack to avoid non-evaling package + (if pkgName == "nix-functional-tests" then lib.flip builtins.removeAttrs ["x86_64-w64-mingw32"] else lib.id) + (forAllCrossSystems (crossSystem: + lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.nixComponents.${pkgName})))); buildNoGc = forAllSystems (system: self.packages.${system}.nix.override { enableGC = false; } diff --git a/src/libstore/meson.build b/src/libstore/meson.build index d2cc235fd..8e30845e1 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -21,7 +21,7 @@ configdata = configuration_data() # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) -configdata.set_quoted('SYSTEM', host_machine.system()) +configdata.set_quoted('SYSTEM', host_machine.cpu_family() + '-' + host_machine.system()) deps_private_maybe_subproject = [ ] diff --git a/src/nix-functional-tests b/src/nix-functional-tests new file mode 120000 index 000000000..ed0cdf60b --- /dev/null +++ b/src/nix-functional-tests @@ -0,0 +1 @@ +../tests/functional \ No newline at end of file diff --git a/tests/functional/.version b/tests/functional/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/tests/functional/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/functional/ca/meson.build b/tests/functional/ca/meson.build new file mode 100644 index 000000000..f682ab28f --- /dev/null +++ b/tests/functional/ca/meson.build @@ -0,0 +1,33 @@ +configure_file( + input : 'config.nix.in', + output : 'config.nix', + configuration : test_confdata, +) + +suites += { + 'name': 'ca', + 'deps': [], + 'tests': [ + 'build-with-garbage-path.sh', + 'build.sh', + 'build-cache.sh', + 'concurrent-builds.sh', + 'derivation-json.sh', + 'duplicate-realisation-in-closure.sh', + 'eval-store.sh', + 'gc.sh', + 'import-derivation.sh', + 'new-build-cmd.sh', + 'nix-copy.sh', + 'nix-run.sh', + 'nix-shell.sh', + 'post-hook.sh', + 'recursive.sh', + 'repl.sh', + 'selfref-gc.sh', + 'signatures.sh', + 'substitute.sh', + 'why-depends.sh', + ], + 'workdir': meson.current_build_dir(), +} diff --git a/tests/functional/common.sh b/tests/functional/common.sh index d038aaf59..325fac44c 100644 --- a/tests/functional/common.sh +++ b/tests/functional/common.sh @@ -8,7 +8,8 @@ COMMON_SH_SOURCED=1 functionalTestsDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" -source "$functionalTestsDir/common/vars-and-functions.sh" +source "$functionalTestsDir/common/vars.sh" +source "$functionalTestsDir/common/functions.sh" source "$functionalTestsDir/common/init.sh" if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/functions.sh similarity index 76% rename from tests/functional/common/vars-and-functions.sh rename to tests/functional/common/functions.sh index 632c81a82..d05fac4e7 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/functions.sh @@ -1,10 +1,10 @@ -# NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk +# shellcheck shell=bash set -eu -o pipefail -if [[ -z "${COMMON_VARS_AND_FUNCTIONS_SH_SOURCED-}" ]]; then +if [[ -z "${COMMON_FUNCTIONS_SH_SOURCED-}" ]]; then -COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1 +COMMON_FUNCTIONS_SH_SOURCED=1 isTestOnNixOS() { [[ "${isTestOnNixOS:-}" == 1 ]] @@ -15,64 +15,14 @@ die() { exit 1 } -set +x - -commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" - -source "$commonDir/subst-vars.sh" -# Make sure shellcheck knows all these will be defined by the above generated snippet -: "${bindir?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" - -source "$commonDir/paths.sh" -source "$commonDir/test-root.sh" - -test_nix_conf_dir=$TEST_ROOT/etc -test_nix_conf=$test_nix_conf_dir/nix.conf - -export TEST_HOME=$TEST_ROOT/test-home - -if ! isTestOnNixOS; then - export NIX_STORE_DIR - if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then - # Maybe the build directory is symlinked. - export NIX_IGNORE_SYMLINK_STORE=1 - NIX_STORE_DIR=$TEST_ROOT/store - fi - export NIX_LOCALSTATE_DIR=$TEST_ROOT/var - export NIX_LOG_DIR=$TEST_ROOT/var/log/nix - export NIX_STATE_DIR=$TEST_ROOT/var/nix - export NIX_CONF_DIR=$test_nix_conf_dir - export NIX_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket - unset NIX_USER_CONF_FILES - export _NIX_TEST_SHARED=$TEST_ROOT/shared - if [[ -n $NIX_STORE ]]; then - export _NIX_TEST_NO_SANDBOX=1 - fi - export _NIX_IN_TEST=$TEST_ROOT/shared - export _NIX_TEST_NO_LSOF=1 - export NIX_REMOTE=${NIX_REMOTE_-} - -fi # ! isTestOnNixOS - -unset NIX_PATH -export HOME=$TEST_HOME -unset XDG_STATE_HOME -unset XDG_DATA_HOME -unset XDG_CONFIG_HOME -unset XDG_CONFIG_DIRS -unset XDG_CACHE_HOME - -export IMPURE_VAR1=foo -export IMPURE_VAR2=bar - -cacheDir=$TEST_ROOT/binary-cache - readLink() { + # TODO fix this + # shellcheck disable=SC2012 ls -l "$1" | sed 's/.*->\ //' } clearProfiles() { - profiles="$HOME"/.local/state/nix/profiles + profiles="$HOME/.local/state/nix/profiles" rm -rf "$profiles" } @@ -105,11 +55,11 @@ doClearStore() { } clearCache() { - rm -rf "$cacheDir" + rm -rf "${cacheDir?}" } clearCacheCache() { - rm -f $TEST_HOME/.cache/nix/binary-cache* + rm -f "$TEST_HOME/.cache/nix/binary-cache"* } startDaemon() { @@ -122,7 +72,7 @@ startDaemon() { return fi # Start the daemon, wait for the socket to appear. - rm -f $NIX_DAEMON_SOCKET_PATH + rm -f "$NIX_DAEMON_SOCKET_PATH" PATH=$DAEMON_PATH nix --extra-experimental-features 'nix-command' daemon & _NIX_TEST_DAEMON_PID=$! export _NIX_TEST_DAEMON_PID @@ -151,14 +101,14 @@ killDaemon() { if [[ "${_NIX_TEST_DAEMON_PID-}" == '' ]]; then return fi - kill $_NIX_TEST_DAEMON_PID + kill "$_NIX_TEST_DAEMON_PID" for i in {0..100}; do - kill -0 $_NIX_TEST_DAEMON_PID 2> /dev/null || break + kill -0 "$_NIX_TEST_DAEMON_PID" 2> /dev/null || break sleep 0.1 done - kill -9 $_NIX_TEST_DAEMON_PID 2> /dev/null || true - wait $_NIX_TEST_DAEMON_PID || true - rm -f $NIX_DAEMON_SOCKET_PATH + kill -9 "$_NIX_TEST_DAEMON_PID" 2> /dev/null || true + wait "$_NIX_TEST_DAEMON_PID" || true + rm -f "$NIX_DAEMON_SOCKET_PATH" # Indicate daemon is stopped unset _NIX_TEST_DAEMON_PID # Restore old nix remote @@ -177,14 +127,11 @@ restartDaemon() { startDaemon } -if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then - _canUseSandbox=1 -fi - isDaemonNewer () { [[ -n "${NIX_DAEMON_PACKAGE:-}" ]] || return 0 local requiredVersion="$1" - local daemonVersion=$($NIX_DAEMON_PACKAGE/bin/nix daemon --version | cut -d' ' -f3) + local daemonVersion + daemonVersion=$("$NIX_DAEMON_PACKAGE/bin/nix" daemon --version | cut -d' ' -f3) [[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''$requiredVersion''") -ge 0 ]] } @@ -237,7 +184,7 @@ expect() { shift "$@" && res=0 || res="$?" # also match "negative" codes, which wrap around to >127 - if [[ $res -ne $expected && $res -ne $[256 + expected] ]]; then + if [[ $res -ne $expected && $res -ne $((256 + expected)) ]]; then echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 return 1 fi @@ -252,7 +199,7 @@ expectStderr() { shift "$@" 2>&1 && res=0 || res="$?" # also match "negative" codes, which wrap around to >127 - if [[ $res -ne $expected && $res -ne $[256 + expected] ]]; then + if [[ $res -ne $expected && $res -ne $((256 + expected)) ]]; then echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 return 1 fi @@ -267,7 +214,7 @@ expectStderr() { # error: This error is expected # EOF assertStderr() { - diff -u /dev/stdin <($@ 2>/dev/null 2>&1) + diff -u /dev/stdin <("$@" 2>/dev/null 2>&1) } needLocalStore() { @@ -283,11 +230,9 @@ buggyNeedLocalStore() { enableFeatures() { local features="$1" - sed -i 's/experimental-features .*/& '"$features"'/' "$test_nix_conf_dir"/nix.conf + sed -i 's/experimental-features .*/& '"$features"'/' "${test_nix_conf?}" } -set -x - onError() { set +x echo "$0: test failed at:" >&2 @@ -311,15 +256,15 @@ callerPrefix() { local i file line fn savedFn # Use `caller` for i in $(seq 0 100); do - caller $i > /dev/null || { + caller "$i" > /dev/null || { if [[ -n "${file:-}" ]]; then echo "$file:$line: ${savedFn+in call to $savedFn: }" fi break } - line="$(caller $i | cut -d' ' -f1)" - fn="$(caller $i | cut -d' ' -f2)" - file="$(caller $i | cut -d' ' -f3)" + line="$(caller "$i" | cut -d' ' -f1)" + fn="$(caller "$i" | cut -d' ' -f2)" + file="$(caller "$i" | cut -d' ' -f3)" if [[ $file != "${BASH_SOURCE[0]}" ]]; then echo "$file:$line: ${savedFn+in call to $savedFn: }" return @@ -342,7 +287,7 @@ checkGrepArgs() { for arg in "$@"; do if [[ "$arg" != "${arg//$'\n'/_}" ]]; then echo "$(callerPrefix)newline not allowed in arguments; grep would try each line individually as if connected by an OR operator" >&2 - return -101 + return 155 # = -101 mod 256 fi done } @@ -400,4 +345,4 @@ count() { trap onError ERR -fi # COMMON_VARS_AND_FUNCTIONS_SH_SOURCED +fi # COMMON_FUNCTIONS_SH_SOURCED diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index d33ad5d57..d849c0734 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -7,10 +7,10 @@ if isTestOnNixOS; then mkdir -p "$test_nix_conf_dir" "$TEST_HOME" - export NIX_USER_CONF_FILES="$test_nix_conf_dir/nix.conf" + export NIX_USER_CONF_FILES="$test_nix_conf" mkdir -p "$test_nix_conf_dir" "$TEST_HOME" ! test -e "$test_nix_conf" - cat > "$test_nix_conf_dir/nix.conf" < "$test_nix_conf" < /dev/null); then + # Maybe the build directory is symlinked. + export NIX_IGNORE_SYMLINK_STORE=1 + NIX_STORE_DIR=$TEST_ROOT/store + fi + export NIX_LOCALSTATE_DIR=$TEST_ROOT/var + export NIX_LOG_DIR=$TEST_ROOT/var/log/nix + export NIX_STATE_DIR=$TEST_ROOT/var/nix + export NIX_CONF_DIR=$test_nix_conf_dir + export NIX_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket + unset NIX_USER_CONF_FILES + export _NIX_TEST_SHARED=$TEST_ROOT/shared + if [[ -n $NIX_STORE ]]; then + export _NIX_TEST_NO_SANDBOX=1 + fi + export _NIX_IN_TEST=$TEST_ROOT/shared + export _NIX_TEST_NO_LSOF=1 + export NIX_REMOTE=${NIX_REMOTE_-} + +fi # ! isTestOnNixOS + +unset NIX_PATH +export HOME=$TEST_HOME +unset XDG_STATE_HOME +unset XDG_DATA_HOME +unset XDG_CONFIG_HOME +unset XDG_CONFIG_DIRS +unset XDG_CACHE_HOME + +export IMPURE_VAR1=foo +export IMPURE_VAR2=bar + +# Used in other files +# shellcheck disable=SC2034 +cacheDir=$TEST_ROOT/binary-cache + +if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then + _canUseSandbox=1 +fi + +fi # COMMON_VARS_SH_SOURCED diff --git a/tests/functional/derivation-advanced-attributes.sh b/tests/functional/derivation-advanced-attributes.sh index 6c0c76b4c..271f17dc6 100755 --- a/tests/functional/derivation-advanced-attributes.sh +++ b/tests/functional/derivation-advanced-attributes.sh @@ -3,7 +3,7 @@ source common/test-root.sh source common/paths.sh -set -o pipefail +set -eu -o pipefail source characterisation/framework.sh diff --git a/tests/functional/dyn-drv/meson.build b/tests/functional/dyn-drv/meson.build new file mode 100644 index 000000000..3c671d013 --- /dev/null +++ b/tests/functional/dyn-drv/meson.build @@ -0,0 +1,19 @@ +configure_file( + input : 'config.nix.in', + output : 'config.nix', + configuration : test_confdata, +) + +suites += { + 'name': 'dyn-drv', + 'deps': [], + 'tests': [ + 'text-hashed-output.sh', + 'recursive-mod-json.sh', + 'build-built-drv.sh', + 'eval-outputOf.sh', + 'dep-built-drv.sh', + 'old-daemon-error-hack.sh', + ], + 'workdir': meson.current_build_dir(), +} diff --git a/tests/functional/flakes/meson.build b/tests/functional/flakes/meson.build new file mode 100644 index 000000000..8c1afd6ff --- /dev/null +++ b/tests/functional/flakes/meson.build @@ -0,0 +1,28 @@ +suites += { + 'name': 'flakes', + 'deps': [], + 'tests': [ + 'flakes.sh', + 'develop.sh', + 'edit.sh', + 'run.sh', + 'mercurial.sh', + 'circular.sh', + 'init.sh', + 'inputs.sh', + 'follow-paths.sh', + 'bundle.sh', + 'check.sh', + 'unlocked-override.sh', + 'absolute-paths.sh', + 'absolute-attr-paths.sh', + 'build-paths.sh', + 'flake-in-submodule.sh', + 'prefetch.sh', + 'eval-cache.sh', + 'search-root.sh', + 'config.sh', + 'show.sh', + ], + 'workdir': meson.current_build_dir(), +} diff --git a/tests/functional/git-hashing/meson.build b/tests/functional/git-hashing/meson.build new file mode 100644 index 000000000..7486bfb8f --- /dev/null +++ b/tests/functional/git-hashing/meson.build @@ -0,0 +1,8 @@ +suites += { + 'name': 'git-hashing', + 'deps': [], + 'tests': [ + 'simple.sh', + ], + 'workdir': meson.current_build_dir(), +} diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index b171f91f4..27338ea23 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -1,4 +1,5 @@ -source ../common/vars-and-functions.sh +source ../common/vars.sh +source ../common/functions.sh TODO_NixOS diff --git a/tests/functional/local-overlay-store/meson.build b/tests/functional/local-overlay-store/meson.build new file mode 100644 index 000000000..6ff5d3169 --- /dev/null +++ b/tests/functional/local-overlay-store/meson.build @@ -0,0 +1,18 @@ +suites += { + 'name': 'local-overlay-store', + 'deps': [], + 'tests': [ + 'check-post-init.sh', + 'redundant-add.sh', + 'build.sh', + 'bad-uris.sh', + 'add-lower.sh', + 'delete-refs.sh', + 'delete-duplicate.sh', + 'gc.sh', + 'verify.sh', + 'optimise.sh', + 'stale-file-handle.sh', + ], + 'workdir': meson.current_build_dir(), +} diff --git a/tests/functional/meson.build b/tests/functional/meson.build new file mode 100644 index 000000000..ebecdd9e8 --- /dev/null +++ b/tests/functional/meson.build @@ -0,0 +1,266 @@ +project('nix-functional-tests', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.3', + license : 'LGPL-2.1-or-later', +) + +fs = import('fs') + +# Need to combine source and build trees +run_command( + 'rsync', + '-a', + '--copy-unsafe-links', + meson.current_source_dir() / '', + meson.current_build_dir() / '', +) +# This current-source-escaping relative is no good because we don't know +# where the build directory will be, therefore we fix it up. Once the +# Make build system is gone, we should think about doing this better. +scripts_dir = fs.relative_to( + meson.current_source_dir() / '..' / '..' / 'scripts', + meson.current_build_dir(), +) +run_command( + 'sed', + '-i', meson.current_build_dir() / 'bash-profile.sh', + '-e', 's^../../scripts^@0@^'.format(scripts_dir), +) + +nix = find_program('nix') +bash = find_program('bash', native : true) +busybox = find_program('busybox', native : true, required : false) +coreutils = find_program('coreutils', native : true) +dot = find_program('dot', native : true, required : false) + +nix_bin_dir = fs.parent(nix.full_path()) + +test_confdata = { + 'bindir': nix_bin_dir, + 'coreutils': fs.parent(coreutils.full_path()), + 'dot': dot.found() ? dot.full_path() : '', + 'bash': bash.full_path(), + 'sandbox_shell': busybox.found() ? busybox.full_path() : '', + 'PACKAGE_VERSION': meson.project_version(), + 'system': host_machine.cpu_family() + '-' + host_machine.system(), +} + +# Just configures `common/vars-and-functions.sh.in`. +# Done as a subdir() so Meson places it under `common` in the build directory as well. +subdir('common') + +config_nix_in = configure_file( + input : 'config.nix.in', + output : 'config.nix', + configuration : test_confdata, +) + +suites = [ + { + 'name' : 'main', + 'deps': [], + 'tests': [ + 'test-infra.sh', + 'gc.sh', + 'nix-collect-garbage-d.sh', + 'remote-store.sh', + 'legacy-ssh-store.sh', + 'lang.sh', + 'lang-gc.sh', + 'characterisation-test-infra.sh', + 'experimental-features.sh', + 'fetchMercurial.sh', + 'gc-auto.sh', + 'user-envs.sh', + 'user-envs-migration.sh', + 'binary-cache.sh', + 'multiple-outputs.sh', + 'nix-build.sh', + 'gc-concurrent.sh', + 'repair.sh', + 'fixed.sh', + 'export-graph.sh', + 'timeout.sh', + 'fetchGitRefs.sh', + 'gc-runtime.sh', + 'tarball.sh', + 'fetchGit.sh', + 'fetchurl.sh', + 'fetchPath.sh', + 'fetchTree-file.sh', + 'simple.sh', + 'referrers.sh', + 'optimise-store.sh', + 'substitute-with-invalid-ca.sh', + 'signing.sh', + 'hash-convert.sh', + 'hash-path.sh', + 'gc-non-blocking.sh', + 'check.sh', + 'nix-shell.sh', + 'check-refs.sh', + 'build-remote-input-addressed.sh', + 'secure-drv-outputs.sh', + 'restricted.sh', + 'fetchGitSubmodules.sh', + 'fetchGitVerification.sh', + 'readfile-context.sh', + 'nix-channel.sh', + 'recursive.sh', + 'dependencies.sh', + 'check-reqs.sh', + 'build-remote-content-addressed-fixed.sh', + 'build-remote-content-addressed-floating.sh', + 'build-remote-trustless-should-pass-0.sh', + 'build-remote-trustless-should-pass-1.sh', + 'build-remote-trustless-should-pass-2.sh', + 'build-remote-trustless-should-pass-3.sh', + 'build-remote-trustless-should-fail-0.sh', + 'build-remote-with-mounted-ssh-ng.sh', + 'nar-access.sh', + 'impure-eval.sh', + 'pure-eval.sh', + 'eval.sh', + 'repl.sh', + 'binary-cache-build-remote.sh', + 'search.sh', + 'logging.sh', + 'export.sh', + 'config.sh', + 'add.sh', + 'chroot-store.sh', + 'filter-source.sh', + 'misc.sh', + 'dump-db.sh', + 'linux-sandbox.sh', + 'supplementary-groups.sh', + 'build-dry.sh', + 'structured-attrs.sh', + 'shell.sh', + 'brotli.sh', + 'zstd.sh', + 'compression-levels.sh', + 'nix-copy-ssh.sh', + 'nix-copy-ssh-ng.sh', + 'post-hook.sh', + 'function-trace.sh', + 'fmt.sh', + 'eval-store.sh', + 'why-depends.sh', + 'derivation-json.sh', + 'derivation-advanced-attributes.sh', + 'import-derivation.sh', + 'nix_path.sh', + 'case-hack.sh', + 'placeholders.sh', + 'ssh-relay.sh', + 'build.sh', + 'build-delete.sh', + 'output-normalization.sh', + 'selfref-gc.sh', + 'db-migration.sh', + 'bash-profile.sh', + 'pass-as-file.sh', + 'nix-profile.sh', + 'suggestions.sh', + 'store-info.sh', + 'fetchClosure.sh', + 'completions.sh', + 'impure-derivations.sh', + 'path-from-hash-part.sh', + 'path-info.sh', + 'toString-path.sh', + 'read-only-store.sh', + 'nested-sandboxing.sh', + 'impure-env.sh', + 'debugger.sh', + 'extra-sandbox-profile.sh', + 'help.sh', + ], + 'workdir': meson.current_build_dir(), + }, +] + +nix_store = dependency('nix-store', required : false) +if nix_store.found() + subdir('test-libstoreconsumer') + suites += { + 'name': 'libstoreconsumer', + 'deps': [ + libstoreconsumer_tester, + ], + 'tests': [ + 'test-libstoreconsumer.sh', + ], + 'workdir': meson.current_build_dir(), + } + +endif + +# Plugin tests require shared libraries support. +nix_expr = dependency('nix-expr', required : false) +if nix_expr.found() and get_option('default_library') != 'static' + subdir('plugins') + suites += { + 'name': 'plugins', + 'deps': [ + libplugintest, + ], + 'tests': [ + 'plugins.sh', + ], + 'workdir': meson.current_build_dir(), + } +endif + +subdir('ca') +subdir('dyn-drv') +subdir('flakes') +subdir('git-hashing') +subdir('local-overlay-store') + +foreach suite : suites + foreach script : suite['tests'] + workdir = suite['workdir'] + prefix = fs.relative_to(workdir, meson.project_build_root()) + + script = script + # Turns, e.g., `tests/functional/flakes/show.sh` into a Meson test target called + # `functional-flakes-show`. + name = fs.replace_suffix(prefix / script, '') + + test( + name, + bash, + args: [ + '-x', + '-e', + '-u', + '-o', 'pipefail', + script, + ], + suite : suite['name'], + env : { + 'TEST_NAME': name, + 'NIX_REMOTE': '', + 'PS4': '+(${BASH_SOURCE[0]-$0}:$LINENO) ', + }, + # some tests take 15+ seconds even on an otherwise idle machine, on a loaded machine + # this can easily drive them to failure. give them more time than default of 30sec + timeout : 300, + # Used for target dependency/ordering tracking, not adding compiler flags or anything. + depends : suite['deps'], + workdir : workdir, + # Won't pass until man pages are generated + should_fail : suite['name'] == 'main' and script == 'help.sh' + ) + endforeach +endforeach diff --git a/tests/functional/nested-sandboxing/command.sh b/tests/functional/nested-sandboxing/command.sh index 69366486c..e9c40a5d9 100644 --- a/tests/functional/nested-sandboxing/command.sh +++ b/tests/functional/nested-sandboxing/command.sh @@ -1,3 +1,5 @@ +set -eu -o pipefail + export NIX_BIN_DIR=$(dirname $(type -p nix)) # TODO Get Nix and its closure more flexibly export EXTRA_SANDBOX="/nix/store $(dirname $NIX_BIN_DIR)" diff --git a/tests/functional/nested-sandboxing/runner.nix b/tests/functional/nested-sandboxing/runner.nix index 9a5822c88..1e79d5065 100644 --- a/tests/functional/nested-sandboxing/runner.nix +++ b/tests/functional/nested-sandboxing/runner.nix @@ -6,7 +6,10 @@ mkDerivation { name = "nested-sandboxing"; busybox = builtins.getEnv "busybox"; EXTRA_SANDBOX = builtins.getEnv "EXTRA_SANDBOX"; - buildCommand = if altitude == 0 then '' + buildCommand = '' + set -x + set -eu -o pipefail + '' + (if altitude == 0 then '' echo Deep enough! > $out '' else '' cp -r ${../common} ./common @@ -20,5 +23,5 @@ mkDerivation { source ./nested-sandboxing/command.sh runNixBuild ${storeFun} ${toString altitude} >> $out - ''; + ''); } diff --git a/tests/functional/package.nix b/tests/functional/package.nix new file mode 100644 index 000000000..205b03614 --- /dev/null +++ b/tests/functional/package.nix @@ -0,0 +1,117 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config +, rsync + +, jq +, git +, mercurial +, util-linux + +, nix-store +, nix-expr +, nix-ng + +, rapidcheck +, gtest +, runCommand + +, busybox-sandbox-shell ? null + +# Configuration Options + +, version + +# For running the functional tests against a different pre-built Nix. +, test-daemon ? null +}: + +let + inherit (lib) fileset; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix-functional-tests"; + inherit version; + + workDir = ./.; + fileset = fileset.unions [ + ../../scripts/nix-profile.sh.in + ../../.version + ../../tests/functional + ./. + ]; + + # Hack for sake of the dev shell + passthru.baseNativeBuildInputs = [ + meson + ninja + pkg-config + rsync + + jq + git + mercurial + ] ++ lib.optionals stdenv.hostPlatform.isLinux [ + # For various sandboxing tests that needs a statically-linked shell, + # etc. + busybox-sandbox-shell + # For Overlay FS tests need `mount`, `umount`, and `unshare`. + # TODO use `unixtools` to be precise over which executables instead? + util-linux + ]; + + nativeBuildInputs = finalAttrs.passthru.baseNativeBuildInputs ++ [ + nix-ng + ]; + + buildInputs = [ + nix-store + nix-expr + ]; + + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../../.version + '' + # TEMP hack for Meson before make is gone, where + # `src/nix-functional-tests` is during the transition a symlink and + # not the actual directory directory. + + '' + cd $(readlink -e $PWD) + echo $PWD | grep tests/functional + ''; + + mesonCheckFlags = [ + "--print-errorlogs" + ]; + + preCheck = + # See https://github.com/NixOS/nix/issues/2523 + # Occurs often in tests since https://github.com/NixOS/nix/pull/9900 + lib.optionalString stdenv.hostPlatform.isDarwin '' + export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES + ''; + + doCheck = true; + + installPhase = '' + touch $out + ''; + + meta = { + platforms = lib.platforms.unix; + }; + +} // lib.optionalAttrs (test-daemon != null) { + NIX_DAEMON_PACKAGE = test-daemon; +}) diff --git a/tests/functional/plugins.sh b/tests/functional/plugins.sh index ab4876df9..fc2d1907c 100755 --- a/tests/functional/plugins.sh +++ b/tests/functional/plugins.sh @@ -2,10 +2,11 @@ source common.sh -if [[ $BUILD_SHARED_LIBS != 1 ]]; then - skipTest "Plugins are not supported" -fi +for ext in so dylib; do + plugin="$PWD/plugins/libplugintest.$ext" + [[ -f "$plugin" ]] && break +done -res=$(nix --option setting-set true --option plugin-files $PWD/plugins/libplugintest* eval --expr builtins.anotherNull) +res=$(nix --option setting-set true --option plugin-files "$plugin" eval --expr builtins.anotherNull) [ "$res"x = "nullx" ] diff --git a/tests/functional/plugins/meson.build b/tests/functional/plugins/meson.build new file mode 100644 index 000000000..3d6b2f0e1 --- /dev/null +++ b/tests/functional/plugins/meson.build @@ -0,0 +1,16 @@ +libplugintest = shared_module( + 'plugintest', + 'plugintest.cc', + cpp_args : [ + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + # '-include', 'config-fetchers.hh', + '-include', 'config-expr.hh', + ], + dependencies : [ + dependency('nix-expr'), + ], + build_by_default : false, +) diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh index 591367e9f..e5fe9c136 100755 --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -16,9 +16,6 @@ nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.n (! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix') nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../.. -(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel') -nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel' -I src=../../src - expectStderr 1 nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in builtins.readFile ' | grepQuiet "forbidden in restricted mode" nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in builtins.readFile ' -I src=. diff --git a/tests/functional/test-libstoreconsumer/meson.build b/tests/functional/test-libstoreconsumer/meson.build new file mode 100644 index 000000000..7076127f7 --- /dev/null +++ b/tests/functional/test-libstoreconsumer/meson.build @@ -0,0 +1,14 @@ +libstoreconsumer_tester = executable( + 'test-libstoreconsumer', + 'main.cc', + cpp_args : [ + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + ], + dependencies : [ + dependency('nix-store'), + ], + build_by_default : false, +) From 6f3045c2a225dca7b1ed8a9c9dc27ab50f575900 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 14 Aug 2024 15:40:32 -0400 Subject: [PATCH 801/910] Remove unit tests from old build system Now that we can run all tests with Meson, we want developers making code changes to use it. (Only the manual needs to be built with the build system, and that will change shortly.) This reverts commit b0bc2a97bfe007fbc32f584ed9de5e7cb75a521c. --- Makefile | 19 ------------ Makefile.config.in | 1 - configure.ac | 22 -------------- package.nix | 33 +------------------- tests/unit/libexpr-support/local.mk | 23 -------------- tests/unit/libexpr/local.mk | 45 ---------------------------- tests/unit/libfetchers/local.mk | 37 ----------------------- tests/unit/libflake/local.mk | 43 -------------------------- tests/unit/libstore-support/local.mk | 21 ------------- tests/unit/libstore/local.mk | 38 ----------------------- tests/unit/libutil-support/local.mk | 19 ------------ tests/unit/libutil/local.mk | 37 ----------------------- 12 files changed, 1 insertion(+), 337 deletions(-) delete mode 100644 tests/unit/libexpr-support/local.mk delete mode 100644 tests/unit/libexpr/local.mk delete mode 100644 tests/unit/libfetchers/local.mk delete mode 100644 tests/unit/libflake/local.mk delete mode 100644 tests/unit/libstore-support/local.mk delete mode 100644 tests/unit/libstore/local.mk delete mode 100644 tests/unit/libutil-support/local.mk delete mode 100644 tests/unit/libutil/local.mk diff --git a/Makefile b/Makefile index dbf510a3e..b51ae6cc7 100644 --- a/Makefile +++ b/Makefile @@ -38,18 +38,6 @@ makefiles += \ endif endif -ifeq ($(ENABLE_UNIT_TESTS), yes) -makefiles += \ - tests/unit/libutil/local.mk \ - tests/unit/libutil-support/local.mk \ - tests/unit/libstore/local.mk \ - tests/unit/libstore-support/local.mk \ - tests/unit/libfetchers/local.mk \ - tests/unit/libexpr/local.mk \ - tests/unit/libexpr-support/local.mk \ - tests/unit/libflake/local.mk -endif - ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ @@ -104,13 +92,6 @@ include mk/lib.mk # These must be defined after `mk/lib.mk`. Otherwise the first rule # incorrectly becomes the default target. -ifneq ($(ENABLE_UNIT_TESTS), yes) -.PHONY: check -check: - @echo "Unit tests are disabled. Configure without '--disable-unit-tests', or avoid calling 'make check'." - @exit 1 -endif - ifneq ($(ENABLE_FUNCTIONAL_TESTS), yes) .PHONY: installcheck installcheck: diff --git a/Makefile.config.in b/Makefile.config.in index 3100d2073..e131484f6 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -12,7 +12,6 @@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ ENABLE_S3 = @ENABLE_S3@ -ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ HAVE_LIBCPUID = @HAVE_LIBCPUID@ HAVE_SECCOMP = @HAVE_SECCOMP@ diff --git a/configure.ac b/configure.ac index 5c22ed176..18d718c07 100644 --- a/configure.ac +++ b/configure.ac @@ -141,18 +141,6 @@ AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]), ENABLE_BUILD=$enableval, ENABLE_BUILD=yes) AC_SUBST(ENABLE_BUILD) -# Building without unit tests is useful for bootstrapping with a smaller footprint -# or running the tests in a separate derivation. Otherwise, we do compile and -# run them. - -AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build the tests]), - ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) -AC_SUBST(ENABLE_UNIT_TESTS) - -AS_IF( - [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], - [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) - AC_ARG_ENABLE(functional-tests, AS_HELP_STRING([--disable-functional-tests],[Do not build the tests]), ENABLE_FUNCTIONAL_TESTS=$enableval, ENABLE_FUNCTIONAL_TESTS=yes) AC_SUBST(ENABLE_FUNCTIONAL_TESTS) @@ -358,16 +346,6 @@ if test "$gc" = yes; then CFLAGS="$old_CFLAGS" fi -AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ - -# Look for gtest. -PKG_CHECK_MODULES([GTEST], [gtest_main gmock_main]) - -# Look for rapidcheck. -PKG_CHECK_MODULES([RAPIDCHECK], [rapidcheck rapidcheck_gtest]) - -]) - # Look for nlohmann/json. PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9]) diff --git a/package.nix b/package.nix index d41748b7c..c0d04179d 100644 --- a/package.nix +++ b/package.nix @@ -53,10 +53,6 @@ # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true -# Run the unit tests as part of the build. See `installUnitTests` for an -# alternative to this. -, doCheck ? __forDefaults.canRunInstalled - # Run the functional tests as part of the build. , doInstallCheck ? test-client != null || __forDefaults.canRunInstalled @@ -89,11 +85,6 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to install unit tests. This is useful when cross compiling -# since we cannot run them natively during the build, but can do so -# later. -, installUnitTests ? doBuild && !__forDefaults.canExecuteHost - # For running the functional tests against a pre-built Nix. Probably # want to use in conjunction with `doBuild = false;`. , test-daemon ? null @@ -117,7 +108,7 @@ let # things which should instead be gotten via `finalAttrs` in order to # work with overriding. attrs = { - inherit doBuild doCheck doInstallCheck; + inherit doBuild doInstallCheck; }; mkDerivation = @@ -133,16 +124,11 @@ in mkDerivation (finalAttrs: let inherit (finalAttrs) - doCheck doInstallCheck ; doBuild = !finalAttrs.dontBuild; - # Either running the unit tests during the build, or installing them - # to be run later, requiresthe unit tests to be built. - buildUnitTests = doCheck || installUnitTests; - in { inherit pname version; @@ -176,8 +162,6 @@ in { ./scripts/local.mk ] ++ lib.optionals enableManual [ ./doc/manual - ] ++ lib.optionals buildUnitTests [ - ./tests/unit ] ++ lib.optionals doInstallCheck [ ./tests/functional ])); @@ -190,8 +174,6 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && enableManual) "doc" - ++ lib.optional installUnitTests "check" - ++ lib.optional doCheck "testresults" ; nativeBuildInputs = [ @@ -230,9 +212,6 @@ in { ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ lowdown - ] ++ lib.optionals buildUnitTests [ - gtest - rapidcheck ] ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies @@ -247,22 +226,16 @@ in { ); dontBuild = !attrs.doBuild; - doCheck = attrs.doCheck; configureFlags = [ (lib.enableFeature doBuild "build") - (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") - (lib.enableFeature installUnitTests "install-unit-tests") (lib.withFeatureAs true "readline-flavor" readlineFlavor) ] ++ lib.optionals (!forDevShell) [ "--sysconfdir=/etc" - ] ++ lib.optionals installUnitTests [ - "--with-check-bin-dir=${builtins.placeholder "check"}/bin" - "--with-check-lib-dir=${builtins.placeholder "check"}/lib" ] ++ lib.optionals (doBuild) [ "--with-boost=${boost}/lib" ] ++ lib.optionals (doBuild && stdenv.isLinux) [ @@ -343,10 +316,6 @@ in { platforms = lib.platforms.unix ++ lib.platforms.windows; mainProgram = "nix"; broken = !(lib.all (a: a) [ - # We cannot run or install unit tests if we don't build them or - # Nix proper (which they depend on). - (installUnitTests -> doBuild) - (doCheck -> doBuild) # The build process for the manual currently requires extracting # data from the Nix executable we are trying to document. (enableManual -> doBuild) diff --git a/tests/unit/libexpr-support/local.mk b/tests/unit/libexpr-support/local.mk deleted file mode 100644 index 0501de33c..000000000 --- a/tests/unit/libexpr-support/local.mk +++ /dev/null @@ -1,23 +0,0 @@ -libraries += libexpr-test-support - -libexpr-test-support_NAME = libnixexpr-test-support - -libexpr-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libexpr-test-support_INSTALL_DIR := $(checklibdir) -else - libexpr-test-support_INSTALL_DIR := -endif - -libexpr-test-support_SOURCES := \ - $(wildcard $(d)/tests/*.cc) \ - $(wildcard $(d)/tests/value/*.cc) - -libexpr-test-support_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) - -libexpr-test-support_LIBS = \ - libstore-test-support libutil-test-support \ - libexpr libstore libutil - -libexpr-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk deleted file mode 100644 index 1617e2823..000000000 --- a/tests/unit/libexpr/local.mk +++ /dev/null @@ -1,45 +0,0 @@ -check: libexpr-tests_RUN - -programs += libexpr-tests - -libexpr-tests_NAME := libnixexpr-tests - -libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml - -libexpr-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libexpr-tests_INSTALL_DIR := $(checkbindir) -else - libexpr-tests_INSTALL_DIR := -endif - -libexpr-tests_SOURCES := \ - $(wildcard $(d)/*.cc) \ - $(wildcard $(d)/value/*.cc) \ - $(wildcard $(d)/flake/*.cc) - -libexpr-tests_EXTRA_INCLUDES = \ - -I tests/unit/libexpr-support \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libexpr) \ - $(INCLUDE_libexprc) \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libstorec) \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) - -libexpr-tests_LIBS = \ - libexpr-test-support libstore-test-support libutil-test-support \ - libexpr libexprc libfetchers libstore libstorec libutil libutilc - -libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk deleted file mode 100644 index 30aa142a5..000000000 --- a/tests/unit/libfetchers/local.mk +++ /dev/null @@ -1,37 +0,0 @@ -check: libfetchers-tests_RUN - -programs += libfetchers-tests - -libfetchers-tests_NAME = libnixfetchers-tests - -libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml - -libfetchers-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libfetchers-tests_INSTALL_DIR := $(checkbindir) -else - libfetchers-tests_INSTALL_DIR := -endif - -libfetchers-tests_SOURCES := $(wildcard $(d)/*.cc) - -libfetchers-tests_EXTRA_INCLUDES = \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libutil) - -libfetchers-tests_CXXFLAGS += $(libfetchers-tests_EXTRA_INCLUDES) - -libfetchers-tests_LIBS = \ - libstore-test-support libutil-test-support \ - libfetchers libstore libutil - -libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) $(LIBGIT2_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk deleted file mode 100644 index 590bcf7c0..000000000 --- a/tests/unit/libflake/local.mk +++ /dev/null @@ -1,43 +0,0 @@ -check: libflake-tests_RUN - -programs += libflake-tests - -libflake-tests_NAME := libnixflake-tests - -libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml - -libflake-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libflake-tests_INSTALL_DIR := $(checkbindir) -else - libflake-tests_INSTALL_DIR := -endif - -libflake-tests_SOURCES := \ - $(wildcard $(d)/*.cc) \ - $(wildcard $(d)/value/*.cc) \ - $(wildcard $(d)/flake/*.cc) - -libflake-tests_EXTRA_INCLUDES = \ - -I tests/unit/libflake-support \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libflake) \ - $(INCLUDE_libexpr) \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libutil) \ - -libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) - -libflake-tests_LIBS = \ - libexpr-test-support libstore-test-support libutil-test-support \ - libflake libexpr libfetchers libstore libutil - -libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libstore-support/local.mk b/tests/unit/libstore-support/local.mk deleted file mode 100644 index 56dedd825..000000000 --- a/tests/unit/libstore-support/local.mk +++ /dev/null @@ -1,21 +0,0 @@ -libraries += libstore-test-support - -libstore-test-support_NAME = libnixstore-test-support - -libstore-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libstore-test-support_INSTALL_DIR := $(checklibdir) -else - libstore-test-support_INSTALL_DIR := -endif - -libstore-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) - -libstore-test-support_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) - -libstore-test-support_LIBS = \ - libutil-test-support \ - libstore libutil - -libstore-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk deleted file mode 100644 index 8d3d6b0af..000000000 --- a/tests/unit/libstore/local.mk +++ /dev/null @@ -1,38 +0,0 @@ -check: libstore-tests_RUN - -programs += libstore-tests - -libstore-tests_NAME = libnixstore-tests - -libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml - -libstore-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libstore-tests_INSTALL_DIR := $(checkbindir) -else - libstore-tests_INSTALL_DIR := -endif - -libstore-tests_SOURCES := $(wildcard $(d)/*.cc) - -libstore-tests_EXTRA_INCLUDES = \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libstore) \ - $(INCLUDE_libstorec) \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) - -libstore-tests_LIBS = \ - libstore-test-support libutil-test-support \ - libstore libstorec libutil libutilc - -libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libutil-support/local.mk b/tests/unit/libutil-support/local.mk deleted file mode 100644 index 5f7835c9f..000000000 --- a/tests/unit/libutil-support/local.mk +++ /dev/null @@ -1,19 +0,0 @@ -libraries += libutil-test-support - -libutil-test-support_NAME = libnixutil-test-support - -libutil-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libutil-test-support_INSTALL_DIR := $(checklibdir) -else - libutil-test-support_INSTALL_DIR := -endif - -libutil-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) - -libutil-test-support_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) - -libutil-test-support_LIBS = libutil - -libutil-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk deleted file mode 100644 index 404f35cf1..000000000 --- a/tests/unit/libutil/local.mk +++ /dev/null @@ -1,37 +0,0 @@ -check: libutil-tests_RUN - -programs += libutil-tests - -libutil-tests_NAME = libnixutil-tests - -libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml - -libutil-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libutil-tests_INSTALL_DIR := $(checkbindir) -else - libutil-tests_INSTALL_DIR := -endif - -libutil-tests_SOURCES := $(wildcard $(d)/*.cc) - -libutil-tests_EXTRA_INCLUDES = \ - -I tests/unit/libutil-support \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) - -libutil-tests_LIBS = libutil-test-support libutil libutilc - -libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif - -check: $(d)/data/git/check-data.sh.test - -$(eval $(call run-test,$(d)/data/git/check-data.sh)) From b41cc1a7555e59d5753bf4f8cbbfa9137107d2fe Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 14 Aug 2024 16:04:35 -0400 Subject: [PATCH 802/910] Make wrapper derivation This ensures just `nix build`-ing the flake doesn't forget to run all tests. One can still specifiy specific attributes to just build one thing. Co-authored-by: Robert Hensing --- .github/workflows/ci.yml | 14 ------ flake.nix | 51 ++++++++++++++------ packaging/components.nix | 6 ++- packaging/everything.nix | 93 ++++++++++++++++++++++++++++++++++++ packaging/hydra.nix | 5 +- tests/functional/package.nix | 6 +-- tests/nixos/quick-build.nix | 6 +-- 7 files changed, 142 insertions(+), 39 deletions(-) create mode 100644 packaging/everything.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4eb9cf10d..9831d0e0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -195,20 +195,6 @@ jobs: - uses: DeterminateSystems/magic-nix-cache-action@main - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user - meson_build: - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - uses: DeterminateSystems/nix-installer-action@main - - uses: DeterminateSystems/magic-nix-cache-action@main - # Only meson packages that don't have a tests.run derivation. - # Those that have it are already built and tested as part of nix flake check. - - run: nix build -L .#hydraJobs.build.{nix-cmd,nix-main}.$(nix-instantiate --eval --expr builtins.currentSystem | sed -e 's/"//g') - flake_regressions: needs: vm_tests runs-on: ubuntu-22.04 diff --git a/flake.nix b/flake.nix index 22ca54118..cded6c3a9 100644 --- a/flake.nix +++ b/flake.nix @@ -139,11 +139,6 @@ nix = final.nixComponents.nix; - nix_noTests = final.nix.override { - doInstallCheck = false; - doCheck = false; - }; - # See https://github.com/NixOS/nixpkgs/pull/214409 # Remove when fixed in this flake's nixpkgs pre-commit = @@ -222,7 +217,7 @@ # for which we don't apply the full build matrix such as cross or static. inherit (nixpkgsFor.${system}.native) changelog-d; - default = self.packages.${system}.nix; + default = self.packages.${system}.nix-ng; nix-internal-api-docs = nixpkgsFor.${system}.native.nixComponents.nix-internal-api-docs; nix-external-api-docs = nixpkgsFor.${system}.native.nixComponents.nix-external-api-docs; } @@ -230,22 +225,48 @@ // flatMapAttrs { # Components we'll iterate over in the upcoming lambda "nix" = { }; - # Temporarily disabled because GitHub Actions OOM issues. Once - # the old build system is gone and we are back to one build - # system, we should reenable these. - #"nix-util" = { }; - #"nix-store" = { }; - #"nix-fetchers" = { }; + "nix-util" = { }; + "nix-util-c" = { }; + "nix-util-test-support" = { }; + "nix-util-tests" = { }; + + "nix-store" = { }; + "nix-store-c" = { }; + "nix-store-test-support" = { }; + "nix-store-tests" = { }; + + "nix-fetchers" = { }; + "nix-fetchers-tests" = { }; + + "nix-expr" = { }; + "nix-expr-c" = { }; + "nix-expr-test-support" = { }; + "nix-expr-tests" = { }; + + "nix-flake" = { }; + "nix-flake-tests" = { }; + + "nix-main" = { }; + "nix-main-c" = { }; + + "nix-cmd" = { }; + + "nix-cli" = { }; + + "nix-functional-tests" = { supportsCross = false; }; + + "nix-perl-bindings" = { supportsCross = false; }; + "nix-ng" = { }; } - (pkgName: {}: { + (pkgName: { supportsCross ? true }: { # These attributes go right into `packages.`. "${pkgName}" = nixpkgsFor.${system}.native.nixComponents.${pkgName}; "${pkgName}-static" = nixpkgsFor.${system}.static.nixComponents.${pkgName}; } - // flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: { + // lib.optionalAttrs supportsCross (flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: { # These attributes go right into `packages.`. "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents.${pkgName}; - }) + })) // flatMapAttrs (lib.genAttrs stdenvs (_: { })) (stdenvName: {}: { # These attributes go right into `packages.`. "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nixComponents.${pkgName}; diff --git a/packaging/components.nix b/packaging/components.nix index f14613e8a..5fc3236cf 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -56,8 +56,7 @@ in nix-cmd = callPackage ../src/libcmd/package.nix { }; - # Will replace `nix` once the old build system is gone. - nix-ng = callPackage ../src/nix/package.nix { version = fineVersion; }; + nix-cli = callPackage ../src/nix/package.nix { version = fineVersion; }; nix-functional-tests = callPackage ../src/nix-functional-tests/package.nix { version = fineVersion; }; @@ -65,4 +64,7 @@ in nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { version = fineVersion; }; nix-perl-bindings = callPackage ../src/perl/package.nix { }; + + # Will replace `nix` once the old build system is gone. + nix-ng = callPackage ../packaging/everything.nix { }; } diff --git a/packaging/everything.nix b/packaging/everything.nix new file mode 100644 index 000000000..8c8ce6611 --- /dev/null +++ b/packaging/everything.nix @@ -0,0 +1,93 @@ +{ + lib, + stdenv, + buildEnv, + + nix-util, + nix-util-c, + nix-util-test-support, + nix-util-tests, + + nix-store, + nix-store-c, + nix-store-test-support, + nix-store-tests, + + nix-fetchers, + nix-fetchers-tests, + + nix-expr, + nix-expr-c, + nix-expr-test-support, + nix-expr-tests, + + nix-flake, + nix-flake-tests, + + nix-main, + nix-main-c, + + nix-cmd, + + nix-cli, + + nix-functional-tests, + + nix-internal-api-docs, + nix-external-api-docs, + + nix-perl-bindings, +}: + +(buildEnv rec { + name = "nix-${nix-cli.version}"; + paths = [ + nix-util + nix-util-c + nix-util-test-support + nix-util-tests + + nix-store + nix-store-c + nix-store-test-support + nix-store-tests + + nix-fetchers + nix-fetchers-tests + + nix-expr + nix-expr-c + nix-expr-test-support + nix-expr-tests + + nix-flake + nix-flake-tests + + nix-main + nix-main-c + + nix-cmd + + nix-cli + + nix-internal-api-docs + nix-external-api-docs + + ] ++ lib.optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [ + nix-perl-bindings + ]; +}).overrideAttrs (_: { + doCheck = true; + doInstallCheck = true; + + checkInputs = [ + # Actually run the unit tests too + nix-util-tests.tests.run + nix-store-tests.tests.run + nix-expr-tests.tests.run + nix-flake-tests.tests.run + ]; + installCheckInputs = [ + nix-functional-tests + ]; +}) diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 46b4ff51d..65978835c 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -61,8 +61,9 @@ let "nix-main" "nix-main-c" "nix-cmd" - "nix-ng" + "nix-cli" "nix-functional-tests" + "nix-ng" ]; in { @@ -85,7 +86,7 @@ in self.packages.${system}.nix.override { enableGC = false; } ); - buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nix_noTests); + buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents.nix-cli); # Toggles some settings for better coverage. Windows needs these # library combinations, and Debian build Nix with GNU readline too. diff --git a/tests/functional/package.nix b/tests/functional/package.nix index 205b03614..277711123 100644 --- a/tests/functional/package.nix +++ b/tests/functional/package.nix @@ -15,7 +15,7 @@ , nix-store , nix-expr -, nix-ng +, nix-cli , rapidcheck , gtest @@ -67,7 +67,7 @@ mkMesonDerivation (finalAttrs: { ]; nativeBuildInputs = finalAttrs.passthru.baseNativeBuildInputs ++ [ - nix-ng + nix-cli ]; buildInputs = [ @@ -105,7 +105,7 @@ mkMesonDerivation (finalAttrs: { doCheck = true; installPhase = '' - touch $out + mkdir $out ''; meta = { diff --git a/tests/nixos/quick-build.nix b/tests/nixos/quick-build.nix index 37169b4e2..57e3b9cdb 100644 --- a/tests/nixos/quick-build.nix +++ b/tests/nixos/quick-build.nix @@ -27,13 +27,13 @@ in }; config = { - passthru.quickBuild = + passthru.quickBuild = let withQuickBuild = extendModules { modules = [{ quickBuild = true; }]; }; in withQuickBuild.config.test; defaults = { pkgs, ... }: { config = lib.mkIf test.config.quickBuild { - nix.package = pkgs.nix_noTests; + nix.package = pkgs.nixComponents.nix-cli; system.forbiddenDependenciesRegexes = [ # This would indicate that the quickBuild feature is broken. @@ -44,4 +44,4 @@ in }; }; }; -} \ No newline at end of file +} From 0fabb348baf923bc10a61bc0dc3315023cba9588 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Thu, 18 Jul 2024 09:56:57 -0500 Subject: [PATCH 803/910] add script to migrate macOS 15 Sequoia nixbld UIDs While we don't have any easy way to forcibly notify everyone about the impending breakage (or forcibly migrate the users on their system), this script enables those who do hear about the problem to migrate their systems before they take the macOS update. It should also enable people who only discover it after the update when a build fails to ~fix their installs without a full reinstall. --- scripts/bigsur-nixbld-user-migration.sh | 4 +- scripts/sequoia-nixbld-user-migration.sh | 161 ++++++++++++++++++++--- 2 files changed, 146 insertions(+), 19 deletions(-) mode change 100644 => 100755 scripts/sequoia-nixbld-user-migration.sh diff --git a/scripts/bigsur-nixbld-user-migration.sh b/scripts/bigsur-nixbld-user-migration.sh index 0eb312e07..a2e2f30f0 100755 --- a/scripts/bigsur-nixbld-user-migration.sh +++ b/scripts/bigsur-nixbld-user-migration.sh @@ -2,7 +2,7 @@ ((NEW_NIX_FIRST_BUILD_UID=301)) -id_available(){ +id_unavailable(){ dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null } @@ -15,7 +15,7 @@ change_nixbld_names_and_ids(){ while read -r name uid; do echo " Checking $name (uid: $uid)" # iterate for a clean ID - while id_available "$next_id"; do + while id_unavailable "$next_id"; do ((next_id++)) if ((next_id >= 400)); then echo "We've hit UID 400 without placing all of your users :(" diff --git a/scripts/sequoia-nixbld-user-migration.sh b/scripts/sequoia-nixbld-user-migration.sh old mode 100644 new mode 100755 index 778a95688..9208a3605 --- a/scripts/sequoia-nixbld-user-migration.sh +++ b/scripts/sequoia-nixbld-user-migration.sh @@ -1,36 +1,163 @@ #!/usr/bin/env bash -((NEW_NIX_FIRST_BUILD_UID=331)) +set -x -id_available(){ +((NEW_NIX_FIRST_BUILD_UID=350)) +((TEMP_NIX_FIRST_BUILD_UID=31000)) + +nix_user_n() { + printf "_nixbld%d" "$1" +} + +id_unavailable(){ dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null } -change_nixbld_names_and_ids(){ - local name uid next_id - ((next_id=NEW_NIX_FIRST_BUILD_UID)) - echo "Attempting to migrate _nixbld users." - echo "Each _nixbld# user should have its UID moved to $next_id+" +any_nixbld(){ + dscl . list /Users UniqueID | grep -E '\b_nixbld' >/dev/null +} + +re_create_nixbld_user(){ + local name uid + + name="$1" + uid="$2" + + sudo /usr/bin/dscl . -create "/Users/$name" "UniqueID" "$uid" + sudo /usr/bin/dscl . -create "/Users/$name" "IsHidden" "1" + sudo /usr/bin/dscl . -create "/Users/$name" "NFSHomeDirectory" "/var/empty" + sudo /usr/bin/dscl . -create "/Users/$name" "RealName" "Nix build user $name" + sudo /usr/bin/dscl . -create "/Users/$name" "UserShell" "/sbin/nologin" + sudo /usr/bin/dscl . -create "/Users/$name" "PrimaryGroupID" "30001" +} + +hit_id_cap(){ + echo "We've hit UID 400 without placing all of your users :(" + echo "You should use the commands in this script as a starting" + echo "point to review your UID-space and manually move the" + echo "remaining users (or delete them, if you don't need them)." +} + +# evacuate the role-uid space to simplify final placement logic +temporarily_move_existing_nixbld_uids(){ + local name uid next_id user_n + + ((next_id=TEMP_NIX_FIRST_BUILD_UID)) + + echo "" + echo "Step 1: move existing _nixbld users out of the destination UID range." + while read -r name uid; do - echo " Checking $name (uid: $uid)" # iterate for a clean ID - while id_available "$next_id"; do + while id_unavailable "$next_id"; do ((next_id++)) - if ((next_id >= 400)); then - echo "We've hit UID 400 without placing all of your users :(" + # We really want to get these all placed, but I guess there's + # some risk we iterate forever--so we'll give up after 9k uids. + if ((next_id >= 40000)); then + echo "We've hit UID 40000 without temporarily placing all of your users :(" echo "You should use the commands in this script as a starting" echo "point to review your UID-space and manually move the" - echo "remaining users (or delete them, if you don't need them)." + echo "remaining users to any open UID over 1000." + exit 1 + fi + done + sudo dscl . -create "/Users/$name" UniqueID "$next_id" + echo " Temporarily moved $name from uid $uid -> $next_id" + + done < <(dscl . list /Users UniqueID | grep _nixbld | sort -n -k2) +} + +change_nixbld_uids(){ + local name next_id user_n + + ((next_id=NEW_NIX_FIRST_BUILD_UID)) + ((user_n=1)) + name="$(nix_user_n "$user_n")" + + # we know that we have *some* nixbld users, but macOS may have + # already clobbered the first few users if this system has been + # upgraded + + echo "" + echo "Step 2: re-create missing early _nixbld# users." + + until dscl . read "/Users/$name" &>/dev/null; do + # iterate for a clean ID + while id_unavailable "$next_id"; do + ((next_id++)) + if ((next_id >= 400)); then + hit_id_cap exit 1 fi done - # first 2 are cleanup, it's OK if they aren't here - sudo dscl . delete "/Users/$name" dsAttrTypeNative:_writers_passwd &>/dev/null || true - sudo dscl . change "/Users/$name" NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true - sudo dscl . change "/Users/$name" UniqueID "$uid" "$next_id" + re_create_nixbld_user "$name" "$next_id" + echo " $name was missing; created with uid: $next_id" + + ((user_n++)) + name="$(nix_user_n "$user_n")" + done + + echo "" + echo "Step 3: relocate remaining _nixbld# UIDs to $next_id+" + + # start at first _nixbld# not re-created above and increment + # until _nixbld doesn't exist + while dscl . read "/Users/$name" &>/dev/null; do + # iterate for a clean ID + while id_unavailable "$next_id"; do + ((next_id++)) + if ((next_id >= 400)); then + hit_id_cap + exit 1 + fi + done + + sudo dscl . -create "/Users/$name" UniqueID "$next_id" echo " $name migrated to uid: $next_id" + + ((user_n++)) + name="$(nix_user_n "$user_n")" + done + + if ((user_n == 1)); then + echo "Didn't find _nixbld1. Perhaps you have single-user Nix?" + exit 1 + else + echo "Migrated $((user_n - 1)) users. If you want to double-check, try:" + echo "dscl . list /Users UniqueID | grep _nixbld | sort -n -k2" + fi +} +needs_migration(){ + local name uid next_id user_n + + ((next_id=NEW_NIX_FIRST_BUILD_UID)) + ((user_n=1)) + + while read -r name uid; do + expected_name="$(nix_user_n "$user_n")" + if [[ "$expected_name" != "$name" ]]; then + return 0 + fi + if [[ "$next_id" != "$uid" ]]; then + return 0 + fi + ((next_id++)) + ((user_n++)) done < <(dscl . list /Users UniqueID | grep _nixbld | sort -n -k2) + return 1 } -change_nixbld_names_and_ids + +if any_nixbld; then + if needs_migration; then + echo "Attempting to migrate _nixbld users." + temporarily_move_existing_nixbld_uids + change_nixbld_uids + else + echo "_nixbld users already appear to be migrated." + fi +else + echo "Didn't find any _nixbld users. Perhaps you have single-user Nix?" + exit 1 +fi From 6068e32aa790fc53e7123d8442282714af45770b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 15 Aug 2024 11:36:44 +0200 Subject: [PATCH 804/910] refactor: Extract EvalState::addCallDepth --- src/libexpr/eval-inline.hh | 8 ++++++++ src/libexpr/eval.cc | 19 +------------------ src/libexpr/eval.hh | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 6fa34b062..d5ce238b2 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -4,6 +4,7 @@ #include "print.hh" #include "eval.hh" #include "eval-error.hh" +#include "eval-settings.hh" namespace nix { @@ -138,5 +139,12 @@ inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view e } } +[[gnu::always_inline]] +inline CallDepth EvalState::addCallDepth(const PosIdx pos) { + if (callDepth > settings.maxCallDepth) + error("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow(); + + return CallDepth(callDepth); +}; } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c9101678c..0e70ca262 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1471,26 +1471,9 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v) v.mkLambda(&env, this); } -namespace { -/** Increments a count on construction and decrements on destruction. - */ -class CallDepth { - size_t & count; -public: - CallDepth(size_t & count) : count(count) { - ++count; - } - ~CallDepth() { - --count; - } -}; -}; - void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos) { - if (callDepth > settings.maxCallDepth) - error("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow(); - CallDepth _level(callDepth); + auto _level = addCallDepth(pos); auto trace = settings.traceFunctionCalls ? std::make_unique(positions[pos]) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ddf5dcf94..9fd31e904 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -41,6 +41,21 @@ namespace eval_cache { class EvalCache; } +/** + * Increments a count on construction and decrements on destruction. + */ +class CallDepth { + size_t & count; + +public: + CallDepth(size_t & count) : count(count) { + ++count; + } + ~CallDepth() { + --count; + } +}; + /** * Function that implements a primop. */ @@ -649,6 +664,11 @@ private: public: + /** + * Check that the call depth is within limits, and increment it, until the returned object is destroyed. + */ + inline CallDepth addCallDepth(const PosIdx pos); + /** * Do a deep equality test between two values. That is, list * elements and attributes are compared recursively. From 72a4d1f52d46f1472b79a04d3af2dee194f13d52 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 15 Aug 2024 12:29:59 +0200 Subject: [PATCH 805/910] Add :doc support for __functor --- src/libexpr/eval.cc | 14 +++ src/libexpr/eval.hh | 6 ++ tests/functional/repl/doc-functor.expected | 101 +++++++++++++++++++++ tests/functional/repl/doc-functor.in | 10 ++ tests/functional/repl/doc-functor.nix | 101 +++++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 tests/functional/repl/doc-functor.expected create mode 100644 tests/functional/repl/doc-functor.in create mode 100644 tests/functional/repl/doc-functor.nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0e70ca262..b87d96be0 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -616,6 +616,20 @@ std::optional EvalState::getDoc(Value & v) strdup(ss.data()), }; } + if (isFunctor(v)) { + try { + Value & functor = *v.attrs()->find(sFunctor)->value; + Value * vp = &v; + Value partiallyApplied; + callFunction(functor, 1, &vp, partiallyApplied, noPos); + auto _level = addCallDepth(noPos); + return getDoc(partiallyApplied); + } + catch (Error & e) { + e.addTrace(nullptr, "while partially calling '%1%' to retrieve documentation", "__functor"); + throw; + } + } return {}; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 9fd31e904..da9dd2087 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -640,6 +640,12 @@ public: const char * doc; }; + /** + * Retrieve the documentation for a value. This will evaluate the value if + * it is a thunk, and it will partially apply __functor if applicable. + * + * @param v The value to get the documentation for. + */ std::optional getDoc(Value & v); private: diff --git a/tests/functional/repl/doc-functor.expected b/tests/functional/repl/doc-functor.expected new file mode 100644 index 000000000..8cb2706ef --- /dev/null +++ b/tests/functional/repl/doc-functor.expected @@ -0,0 +1,101 @@ +Nix +Type :? for help. + +nix-repl> :l doc-functor.nix +Added variables. + +nix-repl> :doc multiplier +Function `__functor`\ + … defined at /path/to/tests/functional/repl/doc-functor.nix:12:23 + + +Multiply the argument by the factor stored in the factor attribute. + +nix-repl> :doc doubler +Function `multiply`\ + … defined at /path/to/tests/functional/repl/doc-functor.nix:5:17 + + +Look, it's just like a function! + +nix-repl> :doc recursive +Function `__functor`\ + … defined at /path/to/tests/functional/repl/doc-functor.nix:77:23 + + +This looks bad, but the docs are ok because of the eta expansion. + +nix-repl> :doc recursive2 +error: + … while partially calling '__functor' to retrieve documentation + + … while calling '__functor' + at /path/to/tests/functional/repl/doc-functor.nix:85:17: + 84| */ + 85| __functor = self: self.__functor self; + | ^ + 86| }; + + … from call site + at /path/to/tests/functional/repl/doc-functor.nix:85:23: + 84| */ + 85| __functor = self: self.__functor self; + | ^ + 86| }; + + (19999 duplicate frames omitted) + + error: stack overflow; max-call-depth exceeded + at /path/to/tests/functional/repl/doc-functor.nix:85:23: + 84| */ + 85| __functor = self: self.__functor self; + | ^ + 86| }; + +nix-repl> :doc diverging +error: + … while partially calling '__functor' to retrieve documentation + + (10000 duplicate frames omitted) + + … while calling '__functor' + at /path/to/tests/functional/repl/doc-functor.nix:97:19: + 96| f = x: { + 97| __functor = self: (f (x + 1)); + | ^ + 98| }; + + error: stack overflow; max-call-depth exceeded + at /path/to/tests/functional/repl/doc-functor.nix:97:26: + 96| f = x: { + 97| __functor = self: (f (x + 1)); + | ^ + 98| }; + +nix-repl> :doc helper +Function `square`\ + … defined at /path/to/tests/functional/repl/doc-functor.nix:36:12 + + +Compute x^2 + +nix-repl> :doc helper2 +Function `__functor`\ + … defined at /path/to/tests/functional/repl/doc-functor.nix:45:23 + + +This is a function that can be overridden. + +nix-repl> :doc lib.helper3 +Function `__functor`\ + … defined at /path/to/tests/functional/repl/doc-functor.nix:45:23 + + +This is a function that can be overridden. + +nix-repl> :doc helper3 +Function `__functor`\ + … defined at /path/to/tests/functional/repl/doc-functor.nix:45:23 + + +This is a function that can be overridden. diff --git a/tests/functional/repl/doc-functor.in b/tests/functional/repl/doc-functor.in new file mode 100644 index 000000000..d2bb57a02 --- /dev/null +++ b/tests/functional/repl/doc-functor.in @@ -0,0 +1,10 @@ +:l doc-functor.nix +:doc multiplier +:doc doubler +:doc recursive +:doc recursive2 +:doc diverging +:doc helper +:doc helper2 +:doc lib.helper3 +:doc helper3 diff --git a/tests/functional/repl/doc-functor.nix b/tests/functional/repl/doc-functor.nix new file mode 100644 index 000000000..f526f453f --- /dev/null +++ b/tests/functional/repl/doc-functor.nix @@ -0,0 +1,101 @@ +rec { + /** + Look, it's just like a function! + */ + multiply = p: q: p * q; + + multiplier = { + factor = 2; + /** + Multiply the argument by the factor stored in the factor attribute. + */ + __functor = self: x: x * self.factor; + }; + + doubler = { + description = "bla"; + /** + Multiply by two. This doc probably won't be rendered because the + returned partial application won't have any reference to this location; + only pointing to the second lambda in the multiply function. + */ + __functor = self: multiply 2; + }; + + makeOverridable = f: { + /** + This is a function that can be overridden. + */ + __functor = self: f; + override = throw "not implemented"; + }; + + /** + Compute x^2 + */ + square = x: x * x; + + helper = makeOverridable square; + + # Somewhat analogous to the Nixpkgs makeOverridable function. + makeVeryOverridable = f: { + /** + This is a function that can be overridden. + */ + __functor = self: arg: f arg // { override = throw "not implemented"; overrideAttrs = throw "not implemented"; }; + override = throw "not implemented"; + }; + + helper2 = makeVeryOverridable square; + + # The RFC might be ambiguous here. The doc comment from makeVeryOverridable + # is "inner" in terms of values, but not inner in terms of expressions. + # Returning the following attribute comment might be allowed. + # TODO: I suppose we could look whether the attribute value expression + # contains a doc, and if not, return the attribute comment anyway? + + /** + Compute x^3 + */ + lib.helper3 = makeVeryOverridable (x: x * x * x); + + /** + Compute x^3... + */ + helper3 = makeVeryOverridable (x: x * x * x); + + + # ------ + + # getDoc traverses a potentially infinite structure in case of __functor, so + # we need to test with recursive inputs and diverging inputs. + + recursive = { + /** + This looks bad, but the docs are ok because of the eta expansion. + */ + __functor = self: x: self x; + }; + + recursive2 = { + /** + Docs probably won't work in this case, because the "partial" application + of self results in an infinite recursion. + */ + __functor = self: self.__functor self; + }; + + diverging = let + /** + Docs probably won't work in this case, because the "partial" application + of self results in an diverging computation that causes a stack overflow. + It's not an infinite recursion because each call is different. + This must be handled by the documentation retrieval logic, as it + reimplements the __functor invocation to be partial. + */ + f = x: { + __functor = self: (f (x + 1)); + }; + in f null; + +} From e225b630621712e407d610109d6e866c06e1e948 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 15 Aug 2024 13:51:15 +0200 Subject: [PATCH 806/910] doc: Document function application operator --- doc/manual/src/language/operators.md | 22 ++++++++++++++++++++-- doc/manual/src/language/syntax.md | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 2c2dcc276..ce5b034c6 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -3,7 +3,7 @@ | Name | Syntax | Associativity | Precedence | |----------------------------------------|--------------------------------------------|---------------|------------| | [Attribute selection] | *attrset* `.` *attrpath* \[ `or` *expr* \] | none | 1 | -| Function application | *func* *expr* | left | 2 | +| [Function application] | *func* *expr* | left | 2 | | [Arithmetic negation][arithmetic] | `-` *number* | none | 3 | | [Has attribute] | *attrset* `?` *attrpath* | none | 4 | | List concatenation | *list* `++` *list* | right | 5 | @@ -32,7 +32,7 @@ [string]: ./types.md#type-string [path]: ./types.md#type-path [number]: ./types.md#type-float -[list]: ./types.md#list +[list]: ./types.md#type-list [attribute set]: ./types.md#attribute-set @@ -48,6 +48,22 @@ If the attribute doesn’t exist, return the *expr* after `or` if provided, othe [Attribute selection]: #attribute-selection +## Function application + +> **Syntax** +> +> *func* *expr* + +Apply the callable value *func* to the argument *expr*. Note the absence of a visible operator symbol. +A callable value is either: +- a [user-defined function][function] +- a [built-in][builtins] function +- an attribute set with a [`__functor` attribute](./syntax.md#attr-__functor) + +> **Warning** +> +> This "operator" also separates [list] items, which means that calls in list items must be enclosed by parentheses. + ## Has attribute > **Syntax** @@ -215,3 +231,5 @@ Equivalent to `!`*b1* `||` *b2*. > ``` [Pipe operator]: #pipe-operators +[builtins]: ./builtins.md +[Function application]: #function-application diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md index a6a3ea052..506afbea1 100644 --- a/doc/manual/src/language/syntax.md +++ b/doc/manual/src/language/syntax.md @@ -218,7 +218,7 @@ a string), that attribute is simply not added to the set: This will evaluate to `{}` if `foo` evaluates to `false`. -A set that has a `__functor` attribute whose value is callable (i.e. is +A set that has a [`__functor`]{#attr-__functor} attribute whose value is callable (i.e. is itself a function or a set with a `__functor` attribute whose value is callable) can be applied as if it were a function, with the set itself passed in first , e.g., From ce62b766ef976f941b1be8a580afc2c1dc88e17f Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 15 Aug 2024 15:25:50 +0200 Subject: [PATCH 807/910] fix link from the readme (#11307) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 021e54a3b..ab647e53b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Full reference documentation can be found in the [Nix manual](https://nix.dev/re ## Building and developing -Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/building.html). +Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/development/building.html). ## Contributing From 06b18cff2021d7cca21ad013fea6da0c43109032 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 15 Aug 2024 18:53:42 +0200 Subject: [PATCH 808/910] doc: Edit language/operators Co-authored-by: John Ericson Co-authored-by: Valentin Gagarin --- doc/manual/src/language/operators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index ce5b034c6..27444258a 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -54,7 +54,7 @@ If the attribute doesn’t exist, return the *expr* after `or` if provided, othe > > *func* *expr* -Apply the callable value *func* to the argument *expr*. Note the absence of a visible operator symbol. +Apply the callable value *func* to the argument *expr*. Note the absence of any visible operator symbol. A callable value is either: - a [user-defined function][function] - a [built-in][builtins] function @@ -62,7 +62,7 @@ A callable value is either: > **Warning** > -> This "operator" also separates [list] items, which means that calls in list items must be enclosed by parentheses. +> [List][list] items are also separated by whitespace, which means that function calls in list items must be enclosed by parentheses. ## Has attribute From 30af4a9e27cdafd4be6997c9764c5c9b229ea550 Mon Sep 17 00:00:00 2001 From: Michael Gallagher Date: Thu, 15 Aug 2024 17:56:05 -0700 Subject: [PATCH 809/910] nix-daemon.sh profile script: operate under `set -u` in bash see https://github.com/NixOS/nix/commit/d459d3307c7b1b10f6489ed048fff192e7834928 --- scripts/nix-profile-daemon.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nix-profile-daemon.sh.in b/scripts/nix-profile-daemon.sh.in index eb124c0b5..59c00d491 100644 --- a/scripts/nix-profile-daemon.sh.in +++ b/scripts/nix-profile-daemon.sh.in @@ -52,7 +52,7 @@ elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS else # Fall back to what is in the nix profiles, favouring whatever is defined last. check_nix_profiles() { - if [ -n "$ZSH_VERSION" ]; then + if [ -n "${ZSH_VERSION:-}" ]; then # Zsh by default doesn't split words in unquoted parameter expansion. # Set local_options for these options to be reverted at the end of the function # and shwordsplit to force splitting words in $NIX_PROFILES below. From c4192a66174cb1c67840a97ba7bc0a706a2c1e2a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 16 Aug 2024 15:27:53 +0200 Subject: [PATCH 810/910] Add nix::execvpe --- src/libutil/unix/exec.hh | 13 +++++++++++++ src/libutil/unix/meson.build | 1 + src/libutil/unix/processes.cc | 7 +++++++ 3 files changed, 21 insertions(+) create mode 100644 src/libutil/unix/exec.hh diff --git a/src/libutil/unix/exec.hh b/src/libutil/unix/exec.hh new file mode 100644 index 000000000..e6b80889a --- /dev/null +++ b/src/libutil/unix/exec.hh @@ -0,0 +1,13 @@ +#pragma once + +namespace nix { + +/** + * `execvpe` is a GNU extension, so we need to implement it for other POSIX + * platforms. + * + * We use our own implementation unconditionally for consistency. + */ +int execvpe(const char * file0, char * const argv[], char * const envp[]); + +} diff --git a/src/libutil/unix/meson.build b/src/libutil/unix/meson.build index 1c5bf27fb..d36152db9 100644 --- a/src/libutil/unix/meson.build +++ b/src/libutil/unix/meson.build @@ -13,6 +13,7 @@ sources += files( include_dirs += include_directories('.') headers += files( + 'exec.hh', 'monitor-fd.hh', 'signals-impl.hh', ) diff --git a/src/libutil/unix/processes.cc b/src/libutil/unix/processes.cc index c5ce74acc..09acba35a 100644 --- a/src/libutil/unix/processes.cc +++ b/src/libutil/unix/processes.cc @@ -1,5 +1,6 @@ #include "current-process.hh" #include "environment-variables.hh" +#include "executable-path.hh" #include "signals.hh" #include "processes.hh" #include "finally.hh" @@ -419,4 +420,10 @@ bool statusOk(int status) return WIFEXITED(status) && WEXITSTATUS(status) == 0; } +int execvpe(const char * file0, char * const argv[], char * const envp[]) +{ + auto file = ExecutablePath::load().findPath(file0).string(); + return execve(file.c_str(), argv, envp); +} + } From a03bb4455cee010bbfcf7e322b10ec7e35123032 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 3 Jul 2024 16:51:25 +0200 Subject: [PATCH 811/910] Fix SSH invocation when local SHELL misbehaves Setting it to /bin/sh will make it more predictable when users have their favorite shell in SHELL, which might not behave as expected. For instance, a bad rc file could send something to stdout before our LocalCommand gets to write "started". This may help https://github.com/NixOS/nix/issues/11010 --- src/libstore/ssh.cc | 33 ++++++++++++++++++++++++++++++--- tests/nixos/remote-builds.nix | 17 ++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index e5d623adf..b8c5f4d97 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -3,6 +3,7 @@ #include "current-process.hh" #include "environment-variables.hh" #include "util.hh" +#include "exec.hh" namespace nix { @@ -44,6 +45,10 @@ void SSHMaster::addCommonSSHOpts(Strings & args) if (compress) args.push_back("-C"); + // We use this to make ssh signal back to us that the connection is established. + // It really does run locally; see createSSHEnv which sets up SHELL to make + // it launch more reliably. The local command runs synchronously, so presumably + // the remote session won't be garbled if the local command is slow. args.push_back("-oPermitLocalCommand=yes"); args.push_back("-oLocalCommand=echo started"); } @@ -56,6 +61,27 @@ bool SSHMaster::isMasterRunning() { return res.first == 0; } +Strings createSSHEnv() +{ + // Copy the environment and set SHELL=/bin/sh + std::map env = getEnv(); + + // SSH will invoke the "user" shell for -oLocalCommand, but that means + // $SHELL. To keep things simple and avoid potential issues with other + // shells, we set it to /bin/sh. + // Technically, we don't need that, and we could reinvoke ourselves to print + // "started". Self-reinvocation is tricky with library consumers, but mostly + // solved; refer to the development history of nixExePath in libstore/globals.cc. + env.insert_or_assign("SHELL", "/bin/sh"); + + Strings r; + for (auto & [k, v] : env) { + r.push_back(k + "=" + v); + } + + return r; +} + std::unique_ptr SSHMaster::startCommand( Strings && command, Strings && extraSshArgs) { @@ -104,8 +130,8 @@ std::unique_ptr SSHMaster::startCommand( } args.splice(args.end(), std::move(command)); - - execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); + auto env = createSSHEnv(); + nix::execvpe(args.begin()->c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(env).data()); // could not exec ssh/bash throw SysError("unable to execute '%s'", args.front()); @@ -172,7 +198,8 @@ Path SSHMaster::startMaster() if (verbosity >= lvlChatty) args.push_back("-v"); addCommonSSHOpts(args); - execvp(args.begin()->c_str(), stringsToCharPtrs(args).data()); + auto env = createSSHEnv(); + nix::execvpe(args.begin()->c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(env).data()); throw SysError("unable to execute '%s'", args.front()); }, options); diff --git a/tests/nixos/remote-builds.nix b/tests/nixos/remote-builds.nix index 8ddf6ad02..ab159eaad 100644 --- a/tests/nixos/remote-builds.nix +++ b/tests/nixos/remote-builds.nix @@ -81,6 +81,17 @@ in virtualisation.additionalPaths = [ config.system.build.extraUtils ]; nix.settings.substituters = lib.mkForce [ ]; programs.ssh.extraConfig = "ConnectTimeout 30"; + environment.systemPackages = [ + # `bad-shell` is used to make sure Nix works an environment with a misbehaving shell. + # + # More realistically, a bad shell would still run the command ("echo started") + # but considering that our solution is to avoid this shell (set via $SHELL), we + # don't need to bother with a more functional mock shell. + (pkgs.writeScriptBin "bad-shell" '' + #!${pkgs.runtimeShell} + echo "Hello, I am a broken shell" + '') + ]; }; }; @@ -114,9 +125,13 @@ in 'echo hello world on $(hostname)' >&2 """) + # Check that SSH uses SHELL for LocalCommand, as expected, and check that + # our test setup here is working. The next test will use this bad SHELL. + client.succeed(f"SHELL=$(which bad-shell) ssh -oLocalCommand='true' -oPermitLocalCommand=yes {builder1.name} 'echo hello world' | grep -F 'Hello, I am a broken shell'") + # Perform a build and check that it was performed on the builder. out = client.succeed( - "nix-build ${expr nodes.client 1} 2> build-output", + "SHELL=$(which bad-shell) nix-build ${expr nodes.client 1} 2> build-output", "grep -q Hello build-output" ) builder1.succeed(f"test -e {out}") From 8866d2cd838902d45782541efe08efc1e1f1a2ab Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Fri, 16 Aug 2024 07:09:27 -0700 Subject: [PATCH 812/910] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/63d37ccd2d178d54e7fb691d7ec76000740ea24a?narHash=sha256-7cCC8%2BTdq1%2B3OPyc3%2BgVo9dzUNkNIQfwSDJ2HSi2u3o%3D' (2024-07-21) → 'github:NixOS/nixpkgs/c3d4ac725177c030b1e289015989da2ad9d56af0?narHash=sha256-sqLwJcHYeWLOeP/XoLwAtYjr01TISlkOfz%2BNG82pbdg%3D' (2024-08-15) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 2ac413a69..b5d0b881c 100644 --- a/flake.lock +++ b/flake.lock @@ -80,11 +80,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1721548954, - "narHash": "sha256-7cCC8+Tdq1+3OPyc3+gVo9dzUNkNIQfwSDJ2HSi2u3o=", + "lastModified": 1723688146, + "narHash": "sha256-sqLwJcHYeWLOeP/XoLwAtYjr01TISlkOfz+NG82pbdg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "63d37ccd2d178d54e7fb691d7ec76000740ea24a", + "rev": "c3d4ac725177c030b1e289015989da2ad9d56af0", "type": "github" }, "original": { From aa3d35c1f4145c9532620a20d6727c2214eab054 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Fri, 16 Aug 2024 07:22:30 -0700 Subject: [PATCH 813/910] ci: check that all outputs for all systems can evaluate --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9831d0e0e..3463335b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: done ) & - run: nix --experimental-features 'nix-command flakes' flake check -L + - run: nix --experimental-features 'nix-command flakes' flake show --all-systems --json # Steps to test CI automation in your own fork. # Cachix: From d4aa7d5dc78f65b68b4c5f6c721b6fb3ae045874 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 14 Aug 2024 23:19:22 +0200 Subject: [PATCH 814/910] Use nixosTest.quickBuild behavior by default This wasn't the default behaviour because: > We don't enable this by default to avoid the mostly unnecessary work of > performing an additional build of the package in cases where we build > the package normally anyway, such as in our pre-merge CI. Since we have a componentized build, we've solved the duplication. In the new situation, building both with and without unit tests isn't any slow than just a build with unit tests, so there's no point in using the unit-tested build anymore. By using the otherwise untested build, we reduce the minimum build time towards the NixOS test, at no cost. If you want to run all tests, build all attributes. --- doc/manual/src/development/testing.md | 8 ++--- tests/nixos/default.nix | 13 ++++++-- tests/nixos/quick-build.nix | 47 --------------------------- 3 files changed, 12 insertions(+), 56 deletions(-) delete mode 100644 tests/nixos/quick-build.nix diff --git a/doc/manual/src/development/testing.md b/doc/manual/src/development/testing.md index 3949164d5..a1782d86c 100644 --- a/doc/manual/src/development/testing.md +++ b/doc/manual/src/development/testing.md @@ -276,14 +276,12 @@ To ensure that characterisation testing doesn't make it harder to intentionally We run the functional tests not just in the build, but also in VM tests. This helps us ensure that Nix works correctly on NixOS, and environments that have similar characteristics that are hard to reproduce in a build environment. -The recommended way to run these tests during development is: +These can be run with: ```shell -nix build .#hydraJobs.tests.functional_user.quickBuild +nix build .#hydraJobs.tests.functional_user ``` -The `quickBuild` attribute configures the test to use a `nix` package that's built without integration tests, so that you can iterate on the tests without performing recompilations due to the changed sources for `installCheck`. - Generally, this build is sufficient, but in nightly or CI we also test the attributes `functional_root` and `functional_trusted`, in which the test suite is run with different levels of authorization. ## Integration tests @@ -294,8 +292,6 @@ Because these tests are expensive and require more than what the standard github You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}`. -If you are testing a build of `nix` that you haven't compiled yet, you may iterate faster by appending the `quickBuild` attribute: `nix build .#hydraJobs.tests.{testName}.quickBuild`. - ## Installer tests After a one-time setup, the Nix repository's GitHub Actions continuous integration (CI) workflow can test the installer each time you push to a branch. diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 66174c928..3fa341ef1 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -4,20 +4,24 @@ let nixos-lib = import (nixpkgs + "/nixos/lib") { }; + noTests = pkg: pkg.overrideAttrs ( + finalAttrs: prevAttrs: { + doCheck = false; + doInstallCheck = false; + }); + # https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests runNixOSTestFor = system: test: (nixos-lib.runTest { imports = [ test - - # Add the quickBuild attribute to the check packages - ./quick-build.nix ]; hostPkgs = nixpkgsFor.${system}.native; defaults = { nixpkgs.pkgs = nixpkgsFor.${system}.native; nix.checkAllErrors = false; + nix.package = noTests nixpkgsFor.${system}.native.nix; }; _module.args.nixpkgs = nixpkgs; _module.args.system = system; @@ -29,6 +33,9 @@ let forNix = nixVersion: runNixOSTestFor system { imports = [test]; defaults.nixpkgs.overlays = [(curr: prev: { + # NOTE: noTests pkg might not have been built yet for some older versions of the package + # and in versions before 2.25, the untested build wasn't shared with the tested build yet + # Add noTests here when those versions become irrelevant. nix = (builtins.getFlake "nix/${nixVersion}").packages.${system}.nix; })]; }; diff --git a/tests/nixos/quick-build.nix b/tests/nixos/quick-build.nix deleted file mode 100644 index 57e3b9cdb..000000000 --- a/tests/nixos/quick-build.nix +++ /dev/null @@ -1,47 +0,0 @@ -test@{ lib, extendModules, ... }: -let - inherit (lib) mkOption types; -in -{ - options = { - quickBuild = mkOption { - description = '' - Whether to perform a "quick" build of the Nix package to test. - - When iterating on the functional tests, it's recommended to "set" this - to `true`, so that changes to the functional tests don't require any - recompilation of the package. - You can do so by building the `.quickBuild` attribute on the check package, - e.g: - ```console - nix build .#hydraJobs.functional_user.quickBuild - ``` - - We don't enable this by default to avoid the mostly unnecessary work of - performing an additional build of the package in cases where we build - the package normally anyway, such as in our pre-merge CI. - ''; - type = types.bool; - default = false; - }; - }; - - config = { - passthru.quickBuild = - let withQuickBuild = extendModules { modules = [{ quickBuild = true; }]; }; - in withQuickBuild.config.test; - - defaults = { pkgs, ... }: { - config = lib.mkIf test.config.quickBuild { - nix.package = pkgs.nixComponents.nix-cli; - - system.forbiddenDependenciesRegexes = [ - # This would indicate that the quickBuild feature is broken. - # It could happen if NixOS has a dependency on pkgs.nix instead of - # config.nix.package somewhere. - (builtins.unsafeDiscardStringContext pkgs.nix.outPath) - ]; - }; - }; - }; -} From 4ba57c9eb28952039164d83fcd21f454794fa3fe Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sat, 17 Aug 2024 02:46:58 -0400 Subject: [PATCH 815/910] ci: use attribute with version for docker --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3463335b9..84e5ab998 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,7 +146,7 @@ jobs: with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV + - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#nix.version | tr -d \")" >> $GITHUB_ENV - uses: cachix/cachix-action@v15 if: needs.check_secrets.outputs.cachix == 'true' with: From 80f20fa4cb75ad48d74047ca060869bb9138f776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sat, 17 Aug 2024 08:31:41 +0200 Subject: [PATCH 816/910] replace backport github action with mergify The current backport action cannot automerge because the github action bot does not trigger github CI actions. Mergify instead does not have this limitation and can also use a merge queue. On top we have now a declarative configuration to allow contributers to add new tests to required without having access to the github org. An example pull request and backport can be seen here: https://github.com/Mic92/nix-1/pull/4 and here: https://github.com/Mic92/nix-1/pull/5 To complete the setup the mergify app must be enabled for this repository. It's already installed in the nixos organization for nixos-hardware and other repositories. --- .github/workflows/backport.yml | 32 ------------ .mergify.yml | 92 ++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 32 deletions(-) delete mode 100644 .github/workflows/backport.yml create mode 100644 .mergify.yml diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml deleted file mode 100644 index dd110de6c..000000000 --- a/.github/workflows/backport.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Backport -on: - pull_request_target: - types: [closed, labeled] -permissions: - contents: read -jobs: - backport: - name: Backport Pull Request - permissions: - # for zeebe-io/backport-action - contents: write - pull-requests: write - if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name)) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - # required to find all branches - fetch-depth: 0 - - name: Create backport PRs - # should be kept in sync with `version` - uses: zeebe-io/backport-action@v3.0.2 - with: - # Config README: https://github.com/zeebe-io/backport-action#backport-action - github_token: ${{ secrets.GITHUB_TOKEN }} - github_workspace: ${{ github.workspace }} - pull_description: |- - Automatic backport to `${target_branch}`, triggered by a label in #${pull_number}. - # should be kept in sync with `uses` - version: v0.0.5 diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 000000000..663c45d92 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,92 @@ +queue_rules: + - name: default + # all required tests need to go here + merge_conditions: + - check-success=installer + - check-success=installer_test (macos-latest) + - check-success=installer_test (ubuntu-latest) + - check-success=tests (macos-latest) + - check-success=tests (ubuntu-latest) + - check-success=vm_tests + merge_method: rebase + batch_size: 5 + +pull_request_rules: + - name: merge using the merge queue + conditions: + - base=master + - label~=merge-queue|dependencies + actions: + queue: {} + +# The rules below will first create backport pull requests and put those in a merge queue. + + - name: backport patches to 2.18 + conditions: + - label=backport 2.18-maintenance + actions: + backport: + branches: + - 2.18-maintenance + labels: + - merge-queue + + - name: backport patches to 2.19 + conditions: + - label=backport 2.19-maintenance + actions: + backport: + branches: + - 2.19-maintenance + labels: + - merge-queue + + - name: backport patches to 2.20 + conditions: + - label=backport 2.20-maintenance + actions: + backport: + branches: + - 2.20-maintenance + labels: + - merge-queue + + - name: backport patches to 2.21 + conditions: + - label=backport 2.21-maintenance + actions: + backport: + branches: + - 2.21-maintenance + labels: + - merge-queue + + - name: backport patches to 2.22 + conditions: + - label=backport 2.22-maintenance + actions: + backport: + branches: + - 2.22-maintenance + labels: + - merge-queue + + - name: backport patches to 2.23 + conditions: + - label=backport 2.23-maintenance + actions: + backport: + branches: + - 2.23-maintenance + labels: + - merge-queue + + - name: backport patches to 2.24 + conditions: + - label=backport 2.24-maintenance + actions: + backport: + branches: + - "2.24-maintenance" + labels: + - merge-queue From a5f6ee8550b78badffe48993b0a236b7934c1be5 Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Wed, 14 Aug 2024 14:57:34 +0530 Subject: [PATCH 817/910] `nix flake show`: Support `meta` attribute for `apps` Metadata information for flake apps will be useful while exploring a flake using `nix flake show` --- src/nix/flake.cc | 12 +++++++++--- src/nix/run.md | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3f9f8f99b..09f41dca8 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1251,8 +1251,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON } j.emplace("type", "derivation"); j.emplace("name", name); - if (description) - j.emplace("description", *description); + j.emplace("description", description ? *description : ""); } else { logger->cout("%s: %s '%s'", headerPrefix, @@ -1340,12 +1339,19 @@ struct CmdFlakeShow : FlakeCommand, MixJSON (attrPath.size() == 3 && attrPathS[0] == "apps")) { auto aType = visitor.maybeGetAttr("type"); + std::optional description; + if (auto aMeta = visitor.maybeGetAttr(state->sMeta)) { + if (auto aDescription = aMeta->maybeGetAttr(state->sDescription)) + description = aDescription->getString(); + } if (!aType || aType->getString() != "app") state->error("not an app definition").debugThrow(); if (json) { j.emplace("type", "app"); + if (description) + j.emplace("description", *description); } else { - logger->cout("%s: app", headerPrefix); + logger->cout("%s: app: " ANSI_BOLD "%s" ANSI_NORMAL, headerPrefix, description ? *description : "no description"); } } diff --git a/src/nix/run.md b/src/nix/run.md index 250ea65aa..eb96e6b31 100644 --- a/src/nix/run.md +++ b/src/nix/run.md @@ -80,6 +80,7 @@ An app is specified by a flake output attribute named apps.x86_64-linux.blender_2_79 = { type = "app"; program = "${self.packages.x86_64-linux.blender_2_79}/bin/blender"; + meta.description = "Run Blender, a free and open-source 3D creation suite."; }; ``` @@ -90,4 +91,6 @@ The only supported attributes are: * `program` (required): The full path of the executable to run. It must reside in the Nix store. +* `meta.description` (optional): A description of the app. + )"" From adabca6e4f4a1863344992b51a44f943690ba42d Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 16 Aug 2024 03:12:02 +0530 Subject: [PATCH 818/910] `nix flake check`: Add apps check; Check if formatter is a derivation --- src/nix/flake.cc | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 09f41dca8..10cb6af9b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -437,14 +437,39 @@ struct CmdFlakeCheck : FlakeCommand auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { - #if 0 - // FIXME - auto app = App(*state, v); - for (auto & i : app.context) { - auto [drvPathS, outputName] = NixStringContextElem::parse(i); - store->parseStorePath(drvPathS); + Activity act(*logger, lvlInfo, actUnknown, fmt("checking app '%s'", attrPath)); + state->forceAttrs(v, pos, ""); + if (auto attr = v.attrs()->get(state->symbols.create("type"))) + state->forceStringNoCtx(*attr->value, attr->pos, ""); + else + throw Error("app '%s' lacks attribute 'type'", attrPath); + + if (auto attr = v.attrs()->get(state->symbols.create("program"))) { + if (attr->name == state->symbols.create("program")) { + NixStringContext context; + state->forceString(*attr->value, context, attr->pos, ""); + } + } else + throw Error("app '%s' lacks attribute 'program'", attrPath); + + if (auto attr = v.attrs()->get(state->symbols.create("meta"))) { + state->forceAttrs(*attr->value, attr->pos, ""); + if (auto dAttr = attr->value->attrs()->get(state->symbols.create("description"))) + state->forceStringNoCtx(*dAttr->value, dAttr->pos, ""); + else + logWarning({ + .msg = HintFmt("app '%s' lacks attribute 'meta.description'", attrPath), + }); + } else + logWarning({ + .msg = HintFmt("app '%s' lacks attribute 'meta'", attrPath), + }); + + for (auto & attr : *v.attrs()) { + std::string_view name(state->symbols[attr.name]); + if (name != "type" && name != "program" && name != "meta") + throw Error("app '%s' has unsupported attribute '%s'", attrPath, name); } - #endif } catch (Error & e) { e.addTrace(resolve(pos), HintFmt("while checking the app definition '%s'", attrPath)); reportError(e); @@ -629,7 +654,7 @@ struct CmdFlakeCheck : FlakeCommand const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { - checkApp( + checkDerivation( fmt("%s.%s", name, attr_name), *attr.value, attr.pos); }; From 2ab93fd5fda3f61f6b1560db7da21a34dbd13b7d Mon Sep 17 00:00:00 2001 From: shivaraj-bh Date: Fri, 16 Aug 2024 03:12:24 +0530 Subject: [PATCH 819/910] `nix flake check`: Add functional tests for apps and formatter --- tests/functional/flakes/check.sh | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/functional/flakes/check.sh b/tests/functional/flakes/check.sh index 3b83dcafe..27e73444a 100755 --- a/tests/functional/flakes/check.sh +++ b/tests/functional/flakes/check.sh @@ -91,3 +91,47 @@ nix flake check $flakeDir checkRes=$(nix flake check --all-systems --keep-going $flakeDir 2>&1 && fail "nix flake check --all-systems should have failed" || true) echo "$checkRes" | grepQuiet "packages.system-1.default" echo "$checkRes" | grepQuiet "packages.system-2.default" + +cat > $flakeDir/flake.nix < $flakeDir/flake.nix <&1 && fail "nix flake check --all-systems should have failed" || true) +echo "$checkRes" | grepQuiet "unknown-attr" + +cat > $flakeDir/flake.nix <&1 && fail "nix flake check --all-systems should have failed" || true) +echo "$checkRes" | grepQuiet "formatter.system-1" From 9ccad9708eac3593defab8a8f74d3acf1bb60870 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 18 Aug 2024 18:27:20 +0530 Subject: [PATCH 820/910] Don't use `OS_STR` in `nix-channel.cc` --- src/nix-channel/nix-channel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc index 52fa3c4f6..56d1d7abb 100644 --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -166,7 +166,7 @@ static int main_nix_channel(int argc, char ** argv) { // Figure out the name of the `.nix-channels' file to use auto home = getHome(); - channelsList = settings.useXDGBaseDirectories ? createNixStateDir() + OS_STR("/channels") : home + OS_STR("/.nix-channels"); + channelsList = settings.useXDGBaseDirectories ? createNixStateDir() + "/channels" : home + "/.nix-channels"; nixDefExpr = getNixDefExpr(); // Figure out the name of the channels profile. From 67de1932774b834377f704d9d358ef3d4951d0ef Mon Sep 17 00:00:00 2001 From: Sandro Date: Sun, 18 Aug 2024 18:44:59 +0200 Subject: [PATCH 821/910] Remove duplicated section (#11324) --- doc/manual/src/release-notes/rl-2.24.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md index 5bcc1d79c..08ec65be9 100644 --- a/doc/manual/src/release-notes/rl-2.24.md +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -261,12 +261,6 @@ Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) -- Improve handling of tarballs that don't consist of a single top-level directory [#11195](https://github.com/NixOS/nix/pull/11195) - - In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. - - Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) - - Setting to warn about large paths [#10778](https://github.com/NixOS/nix/pull/10778) Nix can now warn when evaluation of a Nix expression causes a large From 59db8fd62b5300afbbabb1e8a12d547b336a3bdf Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sun, 18 Aug 2024 22:35:54 -0400 Subject: [PATCH 822/910] fix: check to see if there are any lines before --- src/nix-build/nix-build.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 0ce987d8a..a5b9e1e54 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -163,7 +163,7 @@ static void main_nix_build(int argc, char * * argv) script = argv[1]; try { auto lines = tokenizeString(readFile(script), "\n"); - if (std::regex_search(lines.front(), std::regex("^#!"))) { + if (!lines.empty() && std::regex_search(lines.front(), std::regex("^#!"))) { lines.pop_front(); inShebang = true; for (int i = 2; i < argc; ++i) From 93853833478122a200045e6fc9cf45c532eed80c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 19 Aug 2024 15:17:13 +0200 Subject: [PATCH 823/910] Revert "Remove unit tests from old build system" `make check` was reverted too soon. The hacking guide wasn't brought up to date with the new workflow, and it's not clear how to use meson for everything. This reverts commit 6f3045c2a225dca7b1ed8a9c9dc27ab50f575900. --- Makefile | 19 ++++++++++++ Makefile.config.in | 1 + configure.ac | 22 ++++++++++++++ package.nix | 33 +++++++++++++++++++- tests/unit/libexpr-support/local.mk | 23 ++++++++++++++ tests/unit/libexpr/local.mk | 45 ++++++++++++++++++++++++++++ tests/unit/libfetchers/local.mk | 37 +++++++++++++++++++++++ tests/unit/libflake/local.mk | 43 ++++++++++++++++++++++++++ tests/unit/libstore-support/local.mk | 21 +++++++++++++ tests/unit/libstore/local.mk | 38 +++++++++++++++++++++++ tests/unit/libutil-support/local.mk | 19 ++++++++++++ tests/unit/libutil/local.mk | 37 +++++++++++++++++++++++ 12 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libexpr-support/local.mk create mode 100644 tests/unit/libexpr/local.mk create mode 100644 tests/unit/libfetchers/local.mk create mode 100644 tests/unit/libflake/local.mk create mode 100644 tests/unit/libstore-support/local.mk create mode 100644 tests/unit/libstore/local.mk create mode 100644 tests/unit/libutil-support/local.mk create mode 100644 tests/unit/libutil/local.mk diff --git a/Makefile b/Makefile index b51ae6cc7..dbf510a3e 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,18 @@ makefiles += \ endif endif +ifeq ($(ENABLE_UNIT_TESTS), yes) +makefiles += \ + tests/unit/libutil/local.mk \ + tests/unit/libutil-support/local.mk \ + tests/unit/libstore/local.mk \ + tests/unit/libstore-support/local.mk \ + tests/unit/libfetchers/local.mk \ + tests/unit/libexpr/local.mk \ + tests/unit/libexpr-support/local.mk \ + tests/unit/libflake/local.mk +endif + ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ @@ -92,6 +104,13 @@ include mk/lib.mk # These must be defined after `mk/lib.mk`. Otherwise the first rule # incorrectly becomes the default target. +ifneq ($(ENABLE_UNIT_TESTS), yes) +.PHONY: check +check: + @echo "Unit tests are disabled. Configure without '--disable-unit-tests', or avoid calling 'make check'." + @exit 1 +endif + ifneq ($(ENABLE_FUNCTIONAL_TESTS), yes) .PHONY: installcheck installcheck: diff --git a/Makefile.config.in b/Makefile.config.in index e131484f6..3100d2073 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -12,6 +12,7 @@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ ENABLE_S3 = @ENABLE_S3@ +ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ HAVE_LIBCPUID = @HAVE_LIBCPUID@ HAVE_SECCOMP = @HAVE_SECCOMP@ diff --git a/configure.ac b/configure.ac index 18d718c07..5c22ed176 100644 --- a/configure.ac +++ b/configure.ac @@ -141,6 +141,18 @@ AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]), ENABLE_BUILD=$enableval, ENABLE_BUILD=yes) AC_SUBST(ENABLE_BUILD) +# Building without unit tests is useful for bootstrapping with a smaller footprint +# or running the tests in a separate derivation. Otherwise, we do compile and +# run them. + +AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build the tests]), + ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) +AC_SUBST(ENABLE_UNIT_TESTS) + +AS_IF( + [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], + [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) + AC_ARG_ENABLE(functional-tests, AS_HELP_STRING([--disable-functional-tests],[Do not build the tests]), ENABLE_FUNCTIONAL_TESTS=$enableval, ENABLE_FUNCTIONAL_TESTS=yes) AC_SUBST(ENABLE_FUNCTIONAL_TESTS) @@ -346,6 +358,16 @@ if test "$gc" = yes; then CFLAGS="$old_CFLAGS" fi +AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ + +# Look for gtest. +PKG_CHECK_MODULES([GTEST], [gtest_main gmock_main]) + +# Look for rapidcheck. +PKG_CHECK_MODULES([RAPIDCHECK], [rapidcheck rapidcheck_gtest]) + +]) + # Look for nlohmann/json. PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9]) diff --git a/package.nix b/package.nix index c0d04179d..d41748b7c 100644 --- a/package.nix +++ b/package.nix @@ -53,6 +53,10 @@ # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true +# Run the unit tests as part of the build. See `installUnitTests` for an +# alternative to this. +, doCheck ? __forDefaults.canRunInstalled + # Run the functional tests as part of the build. , doInstallCheck ? test-client != null || __forDefaults.canRunInstalled @@ -85,6 +89,11 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" +# Whether to install unit tests. This is useful when cross compiling +# since we cannot run them natively during the build, but can do so +# later. +, installUnitTests ? doBuild && !__forDefaults.canExecuteHost + # For running the functional tests against a pre-built Nix. Probably # want to use in conjunction with `doBuild = false;`. , test-daemon ? null @@ -108,7 +117,7 @@ let # things which should instead be gotten via `finalAttrs` in order to # work with overriding. attrs = { - inherit doBuild doInstallCheck; + inherit doBuild doCheck doInstallCheck; }; mkDerivation = @@ -124,11 +133,16 @@ in mkDerivation (finalAttrs: let inherit (finalAttrs) + doCheck doInstallCheck ; doBuild = !finalAttrs.dontBuild; + # Either running the unit tests during the build, or installing them + # to be run later, requiresthe unit tests to be built. + buildUnitTests = doCheck || installUnitTests; + in { inherit pname version; @@ -162,6 +176,8 @@ in { ./scripts/local.mk ] ++ lib.optionals enableManual [ ./doc/manual + ] ++ lib.optionals buildUnitTests [ + ./tests/unit ] ++ lib.optionals doInstallCheck [ ./tests/functional ])); @@ -174,6 +190,8 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && enableManual) "doc" + ++ lib.optional installUnitTests "check" + ++ lib.optional doCheck "testresults" ; nativeBuildInputs = [ @@ -212,6 +230,9 @@ in { ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ lowdown + ] ++ lib.optionals buildUnitTests [ + gtest + rapidcheck ] ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies @@ -226,16 +247,22 @@ in { ); dontBuild = !attrs.doBuild; + doCheck = attrs.doCheck; configureFlags = [ (lib.enableFeature doBuild "build") + (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") + (lib.enableFeature installUnitTests "install-unit-tests") (lib.withFeatureAs true "readline-flavor" readlineFlavor) ] ++ lib.optionals (!forDevShell) [ "--sysconfdir=/etc" + ] ++ lib.optionals installUnitTests [ + "--with-check-bin-dir=${builtins.placeholder "check"}/bin" + "--with-check-lib-dir=${builtins.placeholder "check"}/lib" ] ++ lib.optionals (doBuild) [ "--with-boost=${boost}/lib" ] ++ lib.optionals (doBuild && stdenv.isLinux) [ @@ -316,6 +343,10 @@ in { platforms = lib.platforms.unix ++ lib.platforms.windows; mainProgram = "nix"; broken = !(lib.all (a: a) [ + # We cannot run or install unit tests if we don't build them or + # Nix proper (which they depend on). + (installUnitTests -> doBuild) + (doCheck -> doBuild) # The build process for the manual currently requires extracting # data from the Nix executable we are trying to document. (enableManual -> doBuild) diff --git a/tests/unit/libexpr-support/local.mk b/tests/unit/libexpr-support/local.mk new file mode 100644 index 000000000..0501de33c --- /dev/null +++ b/tests/unit/libexpr-support/local.mk @@ -0,0 +1,23 @@ +libraries += libexpr-test-support + +libexpr-test-support_NAME = libnixexpr-test-support + +libexpr-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libexpr-test-support_INSTALL_DIR := $(checklibdir) +else + libexpr-test-support_INSTALL_DIR := +endif + +libexpr-test-support_SOURCES := \ + $(wildcard $(d)/tests/*.cc) \ + $(wildcard $(d)/tests/value/*.cc) + +libexpr-test-support_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) + +libexpr-test-support_LIBS = \ + libstore-test-support libutil-test-support \ + libexpr libstore libutil + +libexpr-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk new file mode 100644 index 000000000..1617e2823 --- /dev/null +++ b/tests/unit/libexpr/local.mk @@ -0,0 +1,45 @@ +check: libexpr-tests_RUN + +programs += libexpr-tests + +libexpr-tests_NAME := libnixexpr-tests + +libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml + +libexpr-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libexpr-tests_INSTALL_DIR := $(checkbindir) +else + libexpr-tests_INSTALL_DIR := +endif + +libexpr-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libexpr-tests_EXTRA_INCLUDES = \ + -I tests/unit/libexpr-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libexprc) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) + +libexpr-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libexpr libexprc libfetchers libstore libstorec libutil libutilc + +libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk new file mode 100644 index 000000000..30aa142a5 --- /dev/null +++ b/tests/unit/libfetchers/local.mk @@ -0,0 +1,37 @@ +check: libfetchers-tests_RUN + +programs += libfetchers-tests + +libfetchers-tests_NAME = libnixfetchers-tests + +libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml + +libfetchers-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libfetchers-tests_INSTALL_DIR := $(checkbindir) +else + libfetchers-tests_INSTALL_DIR := +endif + +libfetchers-tests_SOURCES := $(wildcard $(d)/*.cc) + +libfetchers-tests_EXTRA_INCLUDES = \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) + +libfetchers-tests_CXXFLAGS += $(libfetchers-tests_EXTRA_INCLUDES) + +libfetchers-tests_LIBS = \ + libstore-test-support libutil-test-support \ + libfetchers libstore libutil + +libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) $(LIBGIT2_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk new file mode 100644 index 000000000..590bcf7c0 --- /dev/null +++ b/tests/unit/libflake/local.mk @@ -0,0 +1,43 @@ +check: libflake-tests_RUN + +programs += libflake-tests + +libflake-tests_NAME := libnixflake-tests + +libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml + +libflake-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libflake-tests_INSTALL_DIR := $(checkbindir) +else + libflake-tests_INSTALL_DIR := +endif + +libflake-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libflake-tests_EXTRA_INCLUDES = \ + -I tests/unit/libflake-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libflake) \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) \ + +libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) + +libflake-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libflake libexpr libfetchers libstore libutil + +libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libstore-support/local.mk b/tests/unit/libstore-support/local.mk new file mode 100644 index 000000000..56dedd825 --- /dev/null +++ b/tests/unit/libstore-support/local.mk @@ -0,0 +1,21 @@ +libraries += libstore-test-support + +libstore-test-support_NAME = libnixstore-test-support + +libstore-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libstore-test-support_INSTALL_DIR := $(checklibdir) +else + libstore-test-support_INSTALL_DIR := +endif + +libstore-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) + +libstore-test-support_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) + +libstore-test-support_LIBS = \ + libutil-test-support \ + libstore libutil + +libstore-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk new file mode 100644 index 000000000..8d3d6b0af --- /dev/null +++ b/tests/unit/libstore/local.mk @@ -0,0 +1,38 @@ +check: libstore-tests_RUN + +programs += libstore-tests + +libstore-tests_NAME = libnixstore-tests + +libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml + +libstore-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libstore-tests_INSTALL_DIR := $(checkbindir) +else + libstore-tests_INSTALL_DIR := +endif + +libstore-tests_SOURCES := $(wildcard $(d)/*.cc) + +libstore-tests_EXTRA_INCLUDES = \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libstore) \ + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) + +libstore-tests_LIBS = \ + libstore-test-support libutil-test-support \ + libstore libstorec libutil libutilc + +libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libutil-support/local.mk b/tests/unit/libutil-support/local.mk new file mode 100644 index 000000000..5f7835c9f --- /dev/null +++ b/tests/unit/libutil-support/local.mk @@ -0,0 +1,19 @@ +libraries += libutil-test-support + +libutil-test-support_NAME = libnixutil-test-support + +libutil-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libutil-test-support_INSTALL_DIR := $(checklibdir) +else + libutil-test-support_INSTALL_DIR := +endif + +libutil-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) + +libutil-test-support_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) + +libutil-test-support_LIBS = libutil + +libutil-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk new file mode 100644 index 000000000..404f35cf1 --- /dev/null +++ b/tests/unit/libutil/local.mk @@ -0,0 +1,37 @@ +check: libutil-tests_RUN + +programs += libutil-tests + +libutil-tests_NAME = libnixutil-tests + +libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml + +libutil-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libutil-tests_INSTALL_DIR := $(checkbindir) +else + libutil-tests_INSTALL_DIR := +endif + +libutil-tests_SOURCES := $(wildcard $(d)/*.cc) + +libutil-tests_EXTRA_INCLUDES = \ + -I tests/unit/libutil-support \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) + +libutil-tests_LIBS = libutil-test-support libutil libutilc + +libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif + +check: $(d)/data/git/check-data.sh.test + +$(eval $(call run-test,$(d)/data/git/check-data.sh)) From ceae25825fdb39fc973d23e7f0d2e355a6974bc9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 19 Aug 2024 10:24:38 -0400 Subject: [PATCH 824/910] Update documentation to refer to Meson not Make in most places This is necessary to make the Meson one the default and preferred one. Co-authored-by: Robert Hensing --- doc/manual/src/development/building.md | 88 +++++++++---------- doc/manual/src/development/testing.md | 58 ++++++------ .../src/installation/building-source.md | 21 ++--- maintainers/format.sh | 11 +++ maintainers/local.mk | 11 +-- 5 files changed, 89 insertions(+), 100 deletions(-) create mode 100755 maintainers/format.sh diff --git a/doc/manual/src/development/building.md b/doc/manual/src/development/building.md index 5a5fb3368..dbf080296 100644 --- a/doc/manual/src/development/building.md +++ b/doc/manual/src/development/building.md @@ -34,16 +34,21 @@ $ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages To build Nix itself in this shell: ```console -[nix-shell]$ autoreconfPhase -[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out -[nix-shell]$ make -j $NIX_BUILD_CORES +[nix-shell]$ mesonFlags+=" --prefix=$(pwd)/outputs/out" +[nix-shell]$ dontAddPrefix=1 mesonConfigurePhase +[nix-shell]$ ninjaBuildPhase ``` -To install it in `$(pwd)/outputs` and test it: +To test it: ```console -[nix-shell]$ make install -[nix-shell]$ make installcheck -j $NIX_BUILD_CORES +[nix-shell]$ mesonCheckPhase +``` + +To install it in `$(pwd)/outputs`: + +```console +[nix-shell]$ ninjaInstallPhase [nix-shell]$ ./outputs/out/bin/nix --version nix (Nix) 2.12 ``` @@ -85,16 +90,20 @@ $ nix develop .#native-clangStdenvPackages To build Nix itself in this shell: ```console -[nix-shell]$ autoreconfPhase -[nix-shell]$ configurePhase -[nix-shell]$ make -j $NIX_BUILD_CORES OPTIMIZE=0 +[nix-shell]$ mesonConfigurePhase +[nix-shell]$ ninjaBuildPhase ``` -To install it in `$(pwd)/outputs` and test it: +To test it: ```console -[nix-shell]$ make install OPTIMIZE=0 -[nix-shell]$ make installcheck check -j $NIX_BUILD_CORES +[nix-shell]$ mesonCheckPhase +``` + +To install it in `$(pwd)/outputs`: + +```console +[nix-shell]$ ninjaInstallPhase [nix-shell]$ nix --version nix (Nix) 2.12 ``` @@ -110,25 +119,6 @@ $ nix build You can also build Nix for one of the [supported platforms](#platforms). -## Makefile variables - -You may need `profiledir=$out/etc/profile.d` and `sysconfdir=$out/etc` to run `make install`. - -Run `make` with [`-e` / `--environment-overrides`](https://www.gnu.org/software/make/manual/make.html#index-_002de) to allow environment variables to override `Makefile` variables: - -- `ENABLE_BUILD=yes` to enable building the C++ code. -- `ENABLE_DOC_GEN=yes` to enable building the documentation (manual, man pages, etc.). - - The docs can take a while to build, so you may want to disable this for local development. -- `ENABLE_FUNCTIONAL_TESTS=yes` to enable building the functional tests. -- `OPTIMIZE=1` to enable optimizations. -- `libraries=libutil programs=` to only build a specific library. - - This will fail in the linking phase if the other libraries haven't been built, but is useful for checking types. -- `libraries= programs=nix` to only build a specific program. - - This will not work in general, because the programs need the libraries. - ## Platforms Nix can be built for various platforms, as specified in [`flake.nix`]: @@ -175,27 +165,38 @@ Add more [system types](#system-type) to `crossSystems` in `flake.nix` to bootst It is useful to perform multiple cross and native builds on the same source tree, for example to ensure that better support for one platform doesn't break the build for another. -In order to facilitate this, Nix has some support for being built out of tree – that is, placing build artefacts in a different directory than the source code: +Meson thankfully makes this very easy by confining all build products to the build directory --- one simple shares the source directory between multiple build directories, each of which contains the build for Nix to a different platform. -1. Create a directory for the build, e.g. +Nixpkgs's `mesonConfigurePhase` always chooses `build` in the current directory as the name and location of the build. +This makes having multiple build directories slightly more inconvenient. +The good news is that Meson/Ninja seem to cope well with relocating the build directory after it is created. + +Here's how to do that + +1. Configure as usual ```bash - mkdir build + mesonConfigurePhase ``` -2. Run the configure script from that directory, e.g. +2. Rename the build directory ```bash - cd build - ../configure + cd .. # since `mesonConfigurePhase` cd'd inside + mv build build-linux # or whatever name we want + cd build-linux ``` -3. Run make from the source directory, but with the build directory specified, e.g. +3. Build as usual ```bash - make builddir=build + ninjaBuildPhase ``` +> **N.B.** +> [`nixpkgs#335818`](https://github.com/NixOS/nixpkgs/issues/335818) tracks giving `mesonConfigurePhase` proper support for custom build directories. +> When it is fixed, we can simplify these instructions and then remove this notice. + ## System type Nix uses a string with the following format to identify the *system type* or *platform* it runs on: @@ -257,11 +258,8 @@ You can use any of the other supported environments in place of `nix-ccacheStden The `clangd` LSP server is installed by default on the `clang`-based `devShell`s. See [supported compilation environments](#compilation-environments) and instructions how to set up a shell [with flakes](#nix-with-flakes) or in [classic Nix](#classic-nix). -To use the LSP with your editor, you first need to [set up `clangd`](https://clangd.llvm.org/installation#project-setup) by running: - -```console -make compile_commands.json -``` +To use the LSP with your editor, you will want a `compile_commands.json` file telling `clangd` how we are compiling the code. +Meson's configure always produces this inside the build directory. Configure your editor to use the `clangd` from the `.#native-clangStdenvPackages` shell. You can do that either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration). @@ -276,7 +274,7 @@ Configure your editor to use the `clangd` from the `.#native-clangStdenvPackages You may run the formatters as a one-off using: ```console -make format +./maintainers/format.sh ``` If you'd like to run the formatters before every commit, install the hooks: diff --git a/doc/manual/src/development/testing.md b/doc/manual/src/development/testing.md index a1782d86c..8b8bc568d 100644 --- a/doc/manual/src/development/testing.md +++ b/doc/manual/src/development/testing.md @@ -135,60 +135,52 @@ Functional tests are run during `installCheck` in the `nix` package build, as we ### Running the whole test suite -The whole test suite can be run with: +The whole test suite (functional and unit tests) can be run with: ```shell-session -$ make install && make installcheck -ran test tests/functional/foo.sh... [PASS] -ran test tests/functional/bar.sh... [PASS] -... +$ mesonCheckPhase ``` ### Grouping tests Sometimes it is useful to group related tests so they can be easily run together without running the entire test suite. Each test group is in a subdirectory of `tests`. -For example, `tests/functional/ca/local.mk` defines a `ca` test group for content-addressed derivation outputs. +For example, `tests/functional/ca/meson.build` defines a `ca` test group for content-addressed derivation outputs. That test group can be run like this: ```shell-session -$ make ca.test-group -j50 -ran test tests/functional/ca/nix-run.sh... [PASS] -ran test tests/functional/ca/import-derivation.sh... [PASS] -... -``` - -The test group is defined in Make like this: -```makefile -$(test-group-name)-tests := \ - $(d)/test0.sh \ - $(d)/test1.sh \ - ... - -install-tests-groups += $(test-group-name) +$ meson test --suite ca +ninja: Entering directory `/home/jcericson/src/nix/master/build' +ninja: no work to do. +[1-20/20] 🌑 nix-functional-tests:ca / ca/why-depends 1/20 nix-functional-tests:ca / ca/nix-run OK 0.16s +[2-20/20] 🌒 nix-functional-tests:ca / ca/why-depends 2/20 nix-functional-tests:ca / ca/import-derivation OK 0.17s ``` ### Running individual tests -Individual tests can be run with `make`: +Individual tests can be run with `meson`: ```shell-session -$ make tests/functional/${testName}.sh.test -ran test tests/functional/${testName}.sh... [PASS] +$ meson test ${testName} +ninja: Entering directory `/home/jcericson/src/nix/master/build' +ninja: no work to do. +1/1 nix-functional-tests:main / ${testName} OK 0.41s + +Ok: 1 +Expected Fail: 0 +Fail: 0 +Unexpected Pass: 0 +Skipped: 0 +Timeout: 0 + +Full log written to /home/jcericson/src/nix/master/build/meson-logs/testlog.txt ``` -or without `make`: +or without `meson`, showing the output: ```shell-session -$ ./mk/run-test.sh tests/functional/${testName}.sh -ran test tests/functional/${testName}.sh... [PASS] -``` - -To see the complete output, one can also run: - -```shell-session -$ ./mk/debug-test.sh tests/functional/${testName}.sh +$ TEST_NAME=${testName} NIX_REMOTE='' PS4='+(${BASH_SOURCE[0]-$0}:$LINENO) tests/functional/${testName}.sh +(${testName}.sh:1) foo output from foo +(${testName}.sh:2) bar @@ -254,7 +246,7 @@ It is frequently useful to regenerate the expected output. To do that, rerun the failed test(s) with `_NIX_TEST_ACCEPT=1`. For example: ```bash -_NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test +_NIX_TEST_ACCEPT=1 meson test lang ``` This convention is shared with the [characterisation unit tests](#characterisation-testing-unit) too. diff --git a/doc/manual/src/installation/building-source.md b/doc/manual/src/installation/building-source.md index 7dad9805a..d35cc18c2 100644 --- a/doc/manual/src/installation/building-source.md +++ b/doc/manual/src/installation/building-source.md @@ -1,31 +1,26 @@ # Building Nix from Source -After cloning Nix's Git repository, issue the following commands: +Nix is built with [Meson](https://mesonbuild.com/). +It is broken up into multiple Meson packages, which are optionally combined in a single project using Meson's [subprojects](https://mesonbuild.com/Subprojects.html) feature. -```console -$ autoreconf -vfi -$ ./configure options... -$ make -$ make install -``` +There are no mandatory extra steps to the building process: +generic Meson installation instructions like [this](https://mesonbuild.com/Quick-guide.html#using-meson-as-a-distro-packager) should work. -Nix requires GNU Make so you may need to invoke `gmake` instead. - -The installation path can be specified by passing the `--prefix=prefix` +The installation path can be specified by passing the `-Dprefix=prefix` to `configure`. The default installation directory is `/usr/local`. You can change this to any location you like. You must have write permission to the *prefix* path. Nix keeps its *store* (the place where packages are stored) in `/nix/store` by default. This can be changed using -`--with-store-dir=path`. +`-Dstore-dir=path`. > **Warning** -> +> > It is best *not* to change the Nix store from its default, since doing > so makes it impossible to use pre-built binaries from the standard > Nixpkgs channels — that is, all packages will need to be built from > source. Nix keeps state (such as its database and log files) in `/nix/var` by -default. This can be changed using `--localstatedir=path`. +default. This can be changed using `-Dlocalstatedir=path`. diff --git a/maintainers/format.sh b/maintainers/format.sh new file mode 100755 index 000000000..a2a6d8b41 --- /dev/null +++ b/maintainers/format.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +if ! type -p pre-commit &>/dev/null; then + echo "format.sh: pre-commit not found. Please use \`nix develop\`."; + exit 1; +fi; +if test -z "$_NIX_PRE_COMMIT_HOOKS_CONFIG"; then + echo "format.sh: _NIX_PRE_COMMIT_HOOKS_CONFIG not set. Please use \`nix develop\`."; + exit 1; +fi; +pre-commit run --config "$_NIX_PRE_COMMIT_HOOKS_CONFIG" --all-files diff --git a/maintainers/local.mk b/maintainers/local.mk index 88d594d67..e81517eda 100644 --- a/maintainers/local.mk +++ b/maintainers/local.mk @@ -3,13 +3,6 @@ print-top-help += echo ' format: Format source code' # This uses the cached .pre-commit-hooks.yaml file +fmt_script := $(d)/format.sh format: - @if ! type -p pre-commit &>/dev/null; then \ - echo "make format: pre-commit not found. Please use \`nix develop\`."; \ - exit 1; \ - fi; \ - if test -z "$$_NIX_PRE_COMMIT_HOOKS_CONFIG"; then \ - echo "make format: _NIX_PRE_COMMIT_HOOKS_CONFIG not set. Please use \`nix develop\`."; \ - exit 1; \ - fi; \ - pre-commit run --config $$_NIX_PRE_COMMIT_HOOKS_CONFIG --all-files + @$(fmt_script) From 2f0db04da08e67f29577c27fae3c0eb758fc0879 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 19 Aug 2024 16:41:40 +0200 Subject: [PATCH 825/910] tests.installer: Load profile with -o unset Tested with an ubuntu case. Might need revision depending on what hydra thinks of the rest. --- tests/installer/default.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/installer/default.nix b/tests/installer/default.nix index 4aed6eae4..7c82045ad 100644 --- a/tests/installer/default.nix +++ b/tests/installer/default.nix @@ -217,10 +217,16 @@ let $ssh < Date: Mon, 19 Aug 2024 16:48:33 +0200 Subject: [PATCH 826/910] Fix doc build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes ``` GEN /home/eelco/Dev/nix-master/outputs/out/share/doc/nix/manual/index.html error: File not found: ../store/types/ ┌─ release-notes/rl-next.md:60:197 │ 60 │ The build hook protocol did in principle support custom ways of remote building, but that can also be accomplished with a custom service for the ssh or daemon/ssh-ng protocols, or with a custom [store type](../store/types/) i.e. `Store` subclass. │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File not found: ../store/types/ Error: One or more incorrect links 2024-08-19 16:47:57 [ERROR] (mdbook::renderer): Renderer exited with non-zero return code. 2024-08-19 16:47:57 [ERROR] (mdbook::utils): Error: Rendering failed 2024-08-19 16:47:57 [ERROR] (mdbook::utils): Caused By: The "linkcheck" renderer failed ``` --- doc/manual/rl-next/build-hook-default.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/build-hook-default.md b/doc/manual/rl-next/build-hook-default.md index 197290536..0d5a130c0 100644 --- a/doc/manual/rl-next/build-hook-default.md +++ b/doc/manual/rl-next/build-hook-default.md @@ -17,6 +17,6 @@ This means that other applications linking `libnixstore` that wish to use remote Long term we don't envision this being a downside, because we plan to [get rid of `build-remote` and the build hook setting entirely](https://github.com/NixOS/nix/issues/1221). There is simply no need to add a second layer of remote-procedure-calling when we want to connect to a remote builder. -The build hook protocol did in principle support custom ways of remote building, but that can also be accomplished with a custom service for the ssh or daemon/ssh-ng protocols, or with a custom [store type](@docroot@/store/types/) i.e. `Store` subclass. +The build hook protocol did in principle support custom ways of remote building, but that can also be accomplished with a custom service for the ssh or daemon/ssh-ng protocols, or with a custom [store type](@docroot@/store/types/index.md) i.e. `Store` subclass. The Perl bindings no longer expose `getBinDir` either, since they libraries those bindings wrap no longer know the location of installed binaries as described above. From b52e58903e346a883c62d6af3b406e51e602f471 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 17 Aug 2024 13:34:45 +0200 Subject: [PATCH 827/910] doc: Manage expectations for eval-cache Incorrectly high expectations lead to frustration for users who stick around to experience how useless it is for e.g. a devShell https://functional.cafe/@arianvp/112976284363120036: > Flakes doesn't have eval caching. It has command line argument > caching. It literally just stores the cli argument you passed > in a sqlite database and yes that's as useless as it sounds > When I discovered flakes had no expression level caching whatsoever > I kind of felt lied to and betrayed. --- doc/manual/src/release-notes/rl-2.4.md | 2 ++ src/libexpr/eval-settings.hh | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.4.md b/doc/manual/src/release-notes/rl-2.4.md index 1201e53b6..dbec5a29d 100644 --- a/doc/manual/src/release-notes/rl-2.4.md +++ b/doc/manual/src/release-notes/rl-2.4.md @@ -141,6 +141,8 @@ more than 2800 commits from 195 contributors since release 2.3. the evaluation cache. This is made possible by the hermetic evaluation model of flakes. + Intermediate results are not cached. + * The new `--offline` flag disables substituters and causes all locally cached tarballs and repositories to be considered up-to-date. diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 0cfc14c1b..3d412bbbf 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -185,7 +185,11 @@ struct EvalSettings : Config )"}; Setting useEvalCache{this, true, "eval-cache", - "Whether to use the flake evaluation cache."}; + R"( + Whether to use the flake evaluation cache. + Certain commands won't have to evaluate when invoked for the second time with a particular version of a flake. + Intermediate results are not cached. + )"}; Setting ignoreExceptionsDuringTry{this, false, "ignore-try", R"( From 3e5bf903413f420c1f997e4b55140761172b8434 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Fri, 2 Aug 2024 20:02:57 -0400 Subject: [PATCH 828/910] feat: better warning for common SSL errors --- src/libstore/filetransfer.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 5ea8b6f96..58c52acc0 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -54,6 +54,8 @@ struct curlFileTransfer : public FileTransfer bool done = false; // whether either the success or failure function has been called Callback callback; CURL * req = 0; + // buffer to accompany the `req` above + char errbuf[CURL_ERROR_SIZE]; bool active = false; // whether the handle has been added to the multi object std::string statusMsg; @@ -370,6 +372,9 @@ struct curlFileTransfer : public FileTransfer if (writtenToSink) curl_easy_setopt(req, CURLOPT_RESUME_FROM_LARGE, writtenToSink); + curl_easy_setopt(req, CURLOPT_ERRORBUFFER, errbuf); + errbuf[0] = 0; + result.data.clear(); result.bodySize = 0; } @@ -484,8 +489,8 @@ struct curlFileTransfer : public FileTransfer code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code))) : FileTransferError(err, std::move(response), - "unable to %s '%s': %s (%d)", - request.verb(), request.uri, curl_easy_strerror(code), code); + "unable to %s '%s': %s (%d) %s", + request.verb(), request.uri, curl_easy_strerror(code), code, errbuf); /* If this is a transient error, then maybe retry the download after a while. If we're writing to a From 84ea12ad7fbedddf3f9bfc1d7c2159df43b9219b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 19 Aug 2024 11:02:46 -0400 Subject: [PATCH 829/910] Fix build errors on Windows --- src/libutil/executable-path.cc | 2 +- .../nix-collect-garbage.cc | 4 +- src/nix/eval.cc | 8 +--- src/nix/flake.cc | 43 ++++++++++--------- src/nix/profile.cc | 6 +-- 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/libutil/executable-path.cc b/src/libutil/executable-path.cc index 8005a19be..da71088e7 100644 --- a/src/libutil/executable-path.cc +++ b/src/libutil/executable-path.cc @@ -86,7 +86,7 @@ fs::path ExecutablePath::findPath(const fs::path & exe, std::functionsymbols[attr.name]; try { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6d4408718..b7bbb767b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -25,6 +25,8 @@ #include "strings-inline.hh" +namespace fs = std::filesystem; + using namespace nix; using namespace nix::flake; using json = nlohmann::json; @@ -897,25 +899,26 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand "If you've set '%s' to a string, try using a path instead.", templateDir, templateDirAttr->getAttrPathStr()).debugThrow(); - std::vector changedFiles; - std::vector conflictedFiles; + std::vector changedFiles; + std::vector conflictedFiles; - std::function copyDir; - copyDir = [&](const std::filesystem::path & from, const std::filesystem::path & to) + std::function copyDir; + copyDir = [&](const fs::path & from, const fs::path & to) { - createDirs(to); + fs::create_directories(to); - for (auto & entry : std::filesystem::directory_iterator{from}) { + for (auto & entry : fs::directory_iterator{from}) { checkInterrupt(); auto from2 = entry.path(); auto to2 = to / entry.path().filename(); auto st = entry.symlink_status(); - if (std::filesystem::is_directory(st)) + auto to_st = fs::symlink_status(to2); + if (fs::is_directory(st)) copyDir(from2, to2); - else if (std::filesystem::is_regular_file(st)) { - auto contents = readFile(from2); - if (pathExists(to2)) { - auto contents2 = readFile(to2); + else if (fs::is_regular_file(st)) { + auto contents = readFile(from2.string()); + if (fs::exists(to_st)) { + auto contents2 = readFile(to2.string()); if (contents != contents2) { printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2.string(), from2.string()); conflictedFiles.push_back(to2); @@ -924,12 +927,12 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand } continue; } else - writeFile(to2, contents); + writeFile(to2.string(), contents); } - else if (std::filesystem::is_symlink(st)) { - auto target = readLink(from2); - if (pathExists(to2)) { - if (readLink(to2) != target) { + else if (fs::is_symlink(st)) { + auto target = fs::read_symlink(from2); + if (fs::exists(to_st)) { + if (fs::read_symlink(to2) != target) { printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2.string(), from2.string()); conflictedFiles.push_back(to2); } else { @@ -937,7 +940,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand } continue; } else - createSymlink(target, to2); + fs::create_symlink(target, to2); } else throw Error("file '%s' has unsupported type", from2); @@ -948,9 +951,9 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand copyDir(templateDir, flakeDir); - if (!changedFiles.empty() && pathExists(flakeDir + "/.git")) { + if (!changedFiles.empty() && fs::exists(std::filesystem::path{flakeDir} / ".git")) { Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" }; - for (auto & s : changedFiles) args.push_back(s); + for (auto & s : changedFiles) args.emplace_back(s.string()); runProgram("git", true, args); } auto welcomeText = cursor->maybeGetAttr("welcomeText"); @@ -1275,7 +1278,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (auto aDescription = aMeta->maybeGetAttr(state->sDescription)) description = aDescription->getString(); } - + if (json) { j.emplace("type", "derivation"); j.emplace("name", name); diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d751abdb1..324fd6330 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -126,8 +126,8 @@ struct ProfileManifest { auto manifestPath = profile / "manifest.json"; - if (pathExists(manifestPath)) { - auto json = nlohmann::json::parse(readFile(manifestPath)); + if (std::filesystem::exists(manifestPath)) { + auto json = nlohmann::json::parse(readFile(manifestPath.string())); auto version = json.value("version", 0); std::string sUrl; @@ -176,7 +176,7 @@ struct ProfileManifest } } - else if (pathExists(profile / "manifest.nix")) { + else if (std::filesystem::exists(profile / "manifest.nix")) { // FIXME: needed because of pure mode; ugly. state.allowPath(state.store->followLinksToStore(profile.string())); state.allowPath(state.store->followLinksToStore((profile / "manifest.nix").string())); From 9243457cb24dd9734bbc8b9c4b532954ca6caba8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 19 Aug 2024 17:07:22 +0200 Subject: [PATCH 830/910] Fix umount failure Fixes ``` umount: /tmp/nix-shell.i3xRwX/nix-test/local-overlay-store/delete-refs/stores/merged-store/nix/store: filesystem was unmounted, but failed to update userspace mount table. make: *** [mk/lib.mk:93: tests/functional/local-overlay-store/delete-refs.sh.test] Error 16 ``` in a dev shell. Note: this previously worked before we didn't have umount in the dev shell, so we got /run/wrappers/bin/umount. --- tests/functional/local-overlay-store/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 27338ea23..cbdb3a1f7 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -69,7 +69,7 @@ mountOverlayfs () { || skipTest "overlayfs is not supported" cleanupOverlay () { - umount "$storeBRoot/nix/store" + umount -n "$storeBRoot/nix/store" rm -r $storeVolume/workdir } trap cleanupOverlay EXIT From 39daa4a0d3e7451680e070d0bff7998ec0aea787 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 19 Aug 2024 17:49:26 +0200 Subject: [PATCH 831/910] withFramedSink(): Don't use a thread to monitor the other side Since withFramedSink() is now used a lot more than in the past (for every addToStore() variant), we were creating a lot of threads, e.g. nix flake show --no-eval-cache --all-systems github:NixOS/nix/afdd12be5e19c0001ff3297dea544301108d298 would create 46418 threads. While threads on Linux are cheap, this is still substantial overhead. So instead, just poll from FramedSink before every write whether there are pending messages from the daemon. This could slightly increase the latency on log messages from the daemon, but not on exceptions (which were only synchronously checked from FramedSink anyway). This speeds up the command above from 19.2s to 17.5s on my machine (a 9% speedup). --- src/libstore/remote-store-connection.hh | 2 +- src/libstore/remote-store.cc | 42 +++++----------------- src/libstore/worker-protocol-connection.cc | 15 +++++--- src/libstore/worker-protocol-connection.hh | 5 +-- src/libutil/serialise.cc | 20 +++++++++++ src/libutil/serialise.hh | 29 +++++++++------ 6 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/libstore/remote-store-connection.hh b/src/libstore/remote-store-connection.hh index 405120ee9..513bd6838 100644 --- a/src/libstore/remote-store-connection.hh +++ b/src/libstore/remote-store-connection.hh @@ -49,7 +49,7 @@ struct RemoteStore::ConnectionHandle RemoteStore::Connection & operator * () { return *handle; } RemoteStore::Connection * operator -> () { return &*handle; } - void processStderr(Sink * sink = 0, Source * source = 0, bool flush = true); + void processStderr(Sink * sink = 0, Source * source = 0, bool flush = true, bool block = true); void withFramedSink(std::function fun); }; diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 555936c18..69bbc64fc 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -153,9 +153,9 @@ RemoteStore::ConnectionHandle::~ConnectionHandle() } } -void RemoteStore::ConnectionHandle::processStderr(Sink * sink, Source * source, bool flush) +void RemoteStore::ConnectionHandle::processStderr(Sink * sink, Source * source, bool flush, bool block) { - handle->processStderr(&daemonException, sink, source, flush); + handle->processStderr(&daemonException, sink, source, flush, block); } @@ -926,43 +926,17 @@ void RemoteStore::ConnectionHandle::withFramedSink(std::functionto.flush(); - std::exception_ptr ex; - - /* Handle log messages / exceptions from the remote on a separate - thread. */ - std::thread stderrThread([&]() { - try { - ReceiveInterrupts receiveInterrupts; - processStderr(nullptr, nullptr, false); - } catch (...) { - ex = std::current_exception(); - } - }); - - Finally joinStderrThread([&]() - { - if (stderrThread.joinable()) { - stderrThread.join(); - if (ex) { - try { - std::rethrow_exception(ex); - } catch (...) { - ignoreException(); - } - } - } - }); - - { - FramedSink sink((*this)->to, ex); + FramedSink sink((*this)->to, [&]() { + /* Periodically process stderr messages and exceptions + from the daemon. */ + processStderr(nullptr, nullptr, false, false); + }); fun(sink); sink.flush(); } - stderrThread.join(); - if (ex) - std::rethrow_exception(ex); + processStderr(nullptr, nullptr, false); } } diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc index a47dbb689..ae434c7f0 100644 --- a/src/libstore/worker-protocol-connection.cc +++ b/src/libstore/worker-protocol-connection.cc @@ -32,7 +32,8 @@ static Logger::Fields readFields(Source & from) return fields; } -std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink * sink, Source * source, bool flush) +std::exception_ptr +WorkerProto::BasicClientConnection::processStderrReturn(Sink * sink, Source * source, bool flush, bool block) { if (flush) to.flush(); @@ -41,6 +42,9 @@ std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink while (true) { + if (!block && !from.hasData()) + break; + auto msg = readNum(from); if (msg == STDERR_WRITE) { @@ -95,8 +99,10 @@ std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink logger->result(act, type, fields); } - else if (msg == STDERR_LAST) + else if (msg == STDERR_LAST) { + assert(block); break; + } else throw Error("got unknown message type %x from Nix daemon", msg); @@ -130,9 +136,10 @@ std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink } } -void WorkerProto::BasicClientConnection::processStderr(bool * daemonException, Sink * sink, Source * source, bool flush) +void WorkerProto::BasicClientConnection::processStderr( + bool * daemonException, Sink * sink, Source * source, bool flush, bool block) { - auto ex = processStderrReturn(sink, source, flush); + auto ex = processStderrReturn(sink, source, flush, block); if (ex) { *daemonException = true; std::rethrow_exception(ex); diff --git a/src/libstore/worker-protocol-connection.hh b/src/libstore/worker-protocol-connection.hh index 9c96195b5..9665067dd 100644 --- a/src/libstore/worker-protocol-connection.hh +++ b/src/libstore/worker-protocol-connection.hh @@ -70,9 +70,10 @@ struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection virtual void closeWrite() = 0; - std::exception_ptr processStderrReturn(Sink * sink = 0, Source * source = 0, bool flush = true); + std::exception_ptr processStderrReturn(Sink * sink = 0, Source * source = 0, bool flush = true, bool block = true); - void processStderr(bool * daemonException, Sink * sink = 0, Source * source = 0, bool flush = true); + void + processStderr(bool * daemonException, Sink * sink = 0, Source * source = 0, bool flush = true, bool block = true); /** * Establishes connection, negotiating version. diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 4899134d7..1e30d27b7 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -10,6 +10,8 @@ #ifdef _WIN32 # include # include "windows-error.hh" +#else +# include #endif @@ -158,6 +160,24 @@ bool FdSource::good() } +bool FdSource::hasData() +{ + if (BufferedSource::hasData()) return true; + + while (true) { + struct pollfd fds[1]; + fds[0].fd = fd; + fds[0].events = POLLIN; + auto n = poll(fds, 1, 0); + if (n < 0) { + if (errno == EINTR) continue; + throw SysError("polling file descriptor"); + } + return n == 1 && (fds[0].events & POLLIN); + } +} + + size_t StringSource::read(char * data, size_t len) { if (pos == s.size()) throw EndOfFile("end of string reached"); diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index c7290dcef..964b9a30d 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -104,6 +104,9 @@ struct BufferedSource : Source size_t read(char * data, size_t len) override; + /** + * Return true if the buffer is not empty. + */ bool hasData(); protected: @@ -162,6 +165,13 @@ struct FdSource : BufferedSource FdSource & operator=(FdSource && s) = default; bool good() override; + + /** + * Return true if the buffer is not empty after a non-blocking + * read. + */ + bool hasData(); + protected: size_t readUnbuffered(char * data, size_t len) override; private: @@ -522,15 +532,16 @@ struct FramedSource : Source /** * Write as chunks in the format expected by FramedSource. * - * The exception_ptr reference can be used to terminate the stream when you - * detect that an error has occurred on the remote end. + * The `checkError` function can be used to terminate the stream when you + * detect that an error has occurred. */ struct FramedSink : nix::BufferedSink { BufferedSink & to; - std::exception_ptr & ex; + std::function checkError; - FramedSink(BufferedSink & to, std::exception_ptr & ex) : to(to), ex(ex) + FramedSink(BufferedSink & to, std::function && checkError) + : to(to), checkError(checkError) { } ~FramedSink() @@ -545,13 +556,9 @@ struct FramedSink : nix::BufferedSink void writeUnbuffered(std::string_view data) override { - /* Don't send more data if the remote has - encountered an error. */ - if (ex) { - auto ex2 = ex; - ex = nullptr; - std::rethrow_exception(ex2); - } + /* Don't send more data if an error has occured. */ + checkError(); + to << data.size(); to(data); }; From 62b9a26f60753af73d08528cf39b80ba7b210cef Mon Sep 17 00:00:00 2001 From: Noam Yorav-Raphael Date: Mon, 19 Aug 2024 20:18:08 +0300 Subject: [PATCH 832/910] Set $HOME=/proc/homeless-shelter on Linux, and /homeless-shelter on OSX. --- doc/manual/src/language/derivations.md | 3 ++- src/libstore/unix/build/local-derivation-goal.cc | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index c78c7b8c8..b57f6bb6a 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -264,7 +264,8 @@ The [`builder`](#attr-builder) is executed as follows: - `PATH` is set to `/path-not-set` to prevent shells from initialising it to their built-in default value. - - `HOME` is set to `/proc/homeless-shelter` to prevent programs from + - `HOME` is set to `/proc/homeless-shelter` on Linux and `/homeless-shelter` + on OSX, to prevent programs from using `/etc/passwd` or the like to find the user's home directory, which could cause impurity. Usually, when `HOME` is set, it is used as the location of the home directory, even if diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index f6bbba8b4..e1035fbdd 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -102,7 +102,14 @@ void handleDiffHook( } } +// We want $HOME to be un-creatable in the sandbox. On Linux, +// you can't create anything inside /proc since it's a virtual filesystem. +// On Darwin it seems that `/homeless-shelter` is good enough. +#if __linux__ const Path LocalDerivationGoal::homeDir = "/proc/homeless-shelter"; +#else +const Path LocalDerivationGoal::homeDir = "/homeless-shelter"; +#endif LocalDerivationGoal::~LocalDerivationGoal() From 2926a859a36befed99bcf8f09031869aa71fba54 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Mon, 19 Aug 2024 14:46:46 -0400 Subject: [PATCH 833/910] Release note for nix flake show change --- .../rl-next/nix-flake-show-description.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 doc/manual/rl-next/nix-flake-show-description.md diff --git a/doc/manual/rl-next/nix-flake-show-description.md b/doc/manual/rl-next/nix-flake-show-description.md new file mode 100644 index 000000000..7feb08483 --- /dev/null +++ b/doc/manual/rl-next/nix-flake-show-description.md @@ -0,0 +1,25 @@ +--- +synopsis: Show package descriptions with `nix flake show` +issues: [10977] +prs: [10980] +--- + +`nix flake show` will now display a package's `meta.description` if it exists. If the description does not fit in the terminal it will be truncated to fit the terminal width. If the size of the terminal width is unknown the description will be capped at 80 characters. + +``` +$ nix flake show +└───packages + └───x86_64-linux + ├───builderImage: package 'docker-image-ara-builder-image.tar.gz' - 'Docker image hosting the nix build environment' + └───runnerImage: package 'docker-image-gitlab-runner.tar.gz' - 'Docker image hosting the gitlab-runner executable' +``` + +In a narrower terminal: + +``` +$ nix flake show +└───packages + └───x86_64-linux + ├───builderImage: package 'docker-image-ara-builder-image.tar.gz' - 'Docker image hosting the nix b... + └───runnerImage: package 'docker-image-gitlab-runner.tar.gz' - 'Docker image hosting the gitlab-run... +``` From df36ff0d1e60f59eb3e0442fa335252421ec8057 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Tue, 2 Jul 2024 21:02:45 -0500 Subject: [PATCH 834/910] install-darwin: fix _nixbld uids for macOS sequoia Starting in macOS 15 Sequoia, macOS daemon UIDs are encroaching on our default UIDs of 301-332. This commit relocates our range up to avoid clashing with the current UIDs of 301-304 and buy us a little time while still leaving headroom for people installing more than 32 users. --- scripts/bigsur-nixbld-user-migration.sh | 2 +- scripts/install-darwin-multi-user.sh | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/bigsur-nixbld-user-migration.sh b/scripts/bigsur-nixbld-user-migration.sh index 0eb312e07..bc42e02e6 100755 --- a/scripts/bigsur-nixbld-user-migration.sh +++ b/scripts/bigsur-nixbld-user-migration.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -((NEW_NIX_FIRST_BUILD_UID=301)) +((NEW_NIX_FIRST_BUILD_UID=350)) id_available(){ dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null diff --git a/scripts/install-darwin-multi-user.sh b/scripts/install-darwin-multi-user.sh index 24c9052f9..bd1a54ad8 100644 --- a/scripts/install-darwin-multi-user.sh +++ b/scripts/install-darwin-multi-user.sh @@ -4,7 +4,17 @@ set -eu set -o pipefail # System specific settings -export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-301}" +# Notes: +# - up to macOS Big Sur we used the same GID/UIDs as Linux (30000:30001-32) +# - we changed UID to 301 because Big Sur updates failed into recovery mode +# we're targeting the 200-400 UID range for role users mentioned in the +# usage note for sysadminctl +# - we changed UID to 350 because Sequoia now uses UIDs 300-304 for its own +# daemon users +# - we changed GID to 350 alongside above just because it hides the nixbld +# group from the Users & Groups settings panel :) +export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-350}" +export NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-350}" export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d" readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist From 75567423fb6163559575c38867cda09b754364d7 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Tue, 2 Jul 2024 21:22:35 -0500 Subject: [PATCH 835/910] install-darwin: move nixbld gid to match first UID --- scripts/install-multi-user.sh | 6 ++---- scripts/install-systemd-multi-user.sh | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index 6aee073e3..a487d459f 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -23,10 +23,10 @@ readonly RED='\033[31m' # installer allows overriding build user count to speed up installation # as creating each user takes non-trivial amount of time on macos readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32} -readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}" readonly NIX_BUILD_GROUP_NAME="nixbld" # each system specific installer must set these: # NIX_FIRST_BUILD_UID +# NIX_BUILD_GROUP_ID # NIX_BUILD_USER_NAME_TEMPLATE # Please don't change this. We don't support it, because the # default shell profile that comes with Nix doesn't support it. @@ -530,9 +530,7 @@ It seems the build group $NIX_BUILD_GROUP_NAME already exists, but with the UID $primary_group_id. This script can't really handle that right now, so I'm going to give up. -You can fix this by editing this script and changing the -NIX_BUILD_GROUP_ID variable near the top to from $NIX_BUILD_GROUP_ID -to $primary_group_id and re-run. +You can export NIX_BUILD_GROUP_ID=$primary_group_id and re-run. EOF else row " Exists" "Yes" diff --git a/scripts/install-systemd-multi-user.sh b/scripts/install-systemd-multi-user.sh index a62ed7e3a..a79a69990 100755 --- a/scripts/install-systemd-multi-user.sh +++ b/scripts/install-systemd-multi-user.sh @@ -5,6 +5,7 @@ set -o pipefail # System specific settings export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}" +export NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}" export NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d" readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service From 03b258bf97f1740b90cdcdcadfa65266180a01a0 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Fri, 9 Aug 2024 21:17:52 +0200 Subject: [PATCH 836/910] libutil: rename and optimize closeMostFDs this is only used to close non-stdio files in derivation sandboxes. we may as well encode that in its name, drop the unnecessary integer set, and use close_range to deal with the actual closing of files. not only is this clearer, it also makes sandbox setup on linux fast by 1ms each (cherry-picked and adapted from https://git.lix.systems/lix-project/lix/commit/c7d97802e4f59b8621e67cf62275d6a7fde8fe62) Co-authored-by: Eelco Dolstra Co-authored-by: Cole Helbling Co-authored-by: John Ericson --- .../unix/build/local-derivation-goal.cc | 2 +- src/libutil/file-descriptor.hh | 4 +-- src/libutil/unix/file-descriptor.cc | 30 +++++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index e1035fbdd..43a9a9191 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1993,7 +1993,7 @@ void LocalDerivationGoal::runChild() throw SysError("changing into '%1%'", tmpDir); /* Close all other file descriptors. */ - unix::closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}); + unix::closeExtraFDs(); #if __linux__ linux::setPersonality(drv->platform); diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index be61375f6..bf8354087 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -143,10 +143,10 @@ public: namespace unix { /** - * Close all file descriptors except those listed in the given set. + * Close all file descriptors except stdio fds (ie 0, 1, 2). * Good practice in child processes. */ -void closeMostFDs(const std::set & exceptions); +void closeExtraFDs(); /** * Set the close-on-exec flag for the given file descriptor. diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index a3af1623f..f867199c0 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -120,14 +120,35 @@ void Pipe::create() ////////////////////////////////////////////////////////////////////// -void unix::closeMostFDs(const std::set & exceptions) +#if __linux__ || __FreeBSD__ +// In future we can use a syscall wrapper, but at the moment musl and older glibc version don't support it. +static int unix_close_range(unsigned int first, unsigned int last, int flags) { + return syscall(SYS_close_range, first, last, (unsigned int)flags); +} +#endif + +void unix::closeExtraFDs() +{ + constexpr int MAX_KEPT_FD = 2; + static_assert(std::max({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}) == MAX_KEPT_FD); + +#if __linux__ || __FreeBSD__ + // first try to close_range everything we don't care about. if this + // returns an error with these parameters we're running on a kernel + // that does not implement close_range (i.e. pre 5.9) and fall back + // to the old method. we should remove that though, in some future. + if (unix_close_range(MAX_KEPT_FD + 1, ~0U, 0) == 0) { + return; + } +#endif + #if __linux__ try { for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) { checkInterrupt(); auto fd = std::stoi(s.path().filename()); - if (!exceptions.count(fd)) { + if (fd > MAX_KEPT_FD) { debug("closing leaked FD %d", fd); close(fd); } @@ -142,9 +163,8 @@ void unix::closeMostFDs(const std::set & exceptions) #if HAVE_SYSCONF maxFD = sysconf(_SC_OPEN_MAX); #endif - for (int fd = 0; fd < maxFD; ++fd) - if (!exceptions.count(fd)) - close(fd); /* ignore result */ + for (int fd = MAX_KEPT_FD + 1; fd < maxFD; ++fd) + close(fd); /* ignore result */ } From 3fe1b60c57074ee7aad17e0147f1c53bc83c9a7c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Aug 2024 16:45:46 +0200 Subject: [PATCH 837/910] Remove dead makeMutable() function This code wasn't being compiled because the #if conditional didn't trigger. Anyway, Nix 0.15 is 15 years old so we don't need to keep this around. --- src/libstore/local-store.cc | 59 ------------------------------------- src/libstore/local-store.hh | 2 -- 2 files changed, 61 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 819cee345..eeecde6e7 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -42,7 +42,6 @@ # include # include # include -# include #endif #ifdef __CYGWIN__ @@ -366,8 +365,6 @@ LocalStore::LocalStore( have performed the upgrade already. */ curSchema = getSchema(); - if (curSchema < 7) { upgradeStore7(); } - openDB(*state, false); if (curSchema < 8) { @@ -1596,62 +1593,6 @@ std::optional LocalStore::isTrustedClient() } -#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL) - -static void makeMutable(const Path & path) -{ - checkInterrupt(); - - auto st = lstat(path); - - if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) return; - - if (S_ISDIR(st.st_mode)) { - for (auto & i : readDirectory(path)) - makeMutable(path + "/" + i.name); - } - - /* The O_NOFOLLOW is important to prevent us from changing the - mutable bit on the target of a symlink (which would be a - security hole). */ - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW -#ifndef _WIN32 - | O_CLOEXEC -#endif - ); - if (fd == INVALID_DESCRIPTOR) { - if (errno == ELOOP) return; // it's a symlink - throw SysError("opening file '%1%'", path); - } - - unsigned int flags = 0, old; - - /* Silently ignore errors getting/setting the immutable flag so - that we work correctly on filesystems that don't support it. */ - if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return; - old = flags; - flags &= ~FS_IMMUTABLE_FL; - if (old == flags) return; - if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return; -} - -/* Upgrade from schema 6 (Nix 0.15) to schema 7 (Nix >= 1.3). */ -void LocalStore::upgradeStore7() -{ - if (!isRootUser()) return; - printInfo("removing immutable bits from the Nix store (this may take a while)..."); - makeMutable(realStoreDir); -} - -#else - -void LocalStore::upgradeStore7() -{ -} - -#endif - - void LocalStore::vacuumDB() { auto state(_state.lock()); diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index a03cfc03b..21848cc4d 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -373,8 +373,6 @@ private: void updatePathInfo(State & state, const ValidPathInfo & info); - void upgradeStore6(); - void upgradeStore7(); PathSet queryValidPathsOld(); ValidPathInfo queryPathInfoOld(const Path & path); From efbf4996355f4ce846c38dbeb9b5b7a4b418f322 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Aug 2024 16:50:18 +0200 Subject: [PATCH 838/910] Remove redundant " --- src/libstore/globals.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 7a307f894..ec0c69851 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -401,9 +401,9 @@ public: Setting fsyncStorePaths{this, false, "fsync-store-paths", R"( - "Whether to call `fsync()` on store paths before registering them, to - flush them to disk. This improves robustness in case of system crashes, - but reduces performance. The default is `false`. + Whether to call `fsync()` on store paths before registering them, to + flush them to disk. This improves robustness in case of system crashes, + but reduces performance. The default is `false`. )"}; Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", From e8752ca57a92ec31e250d740307423134b96814f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Aug 2024 17:05:36 +0200 Subject: [PATCH 839/910] Add FIXME --- src/libstore/globals.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index ec0c69851..e5e7024cb 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -410,6 +410,7 @@ public: "Whether SQLite should use WAL mode."}; #ifndef _WIN32 + // FIXME: remove this option, `fsync-store-paths` is faster. Setting syncBeforeRegistering{this, false, "sync-before-registering", "Whether to call `sync()` before registering a path as valid."}; #endif From 02446918f410b57bd7c1182bc1799639ba798556 Mon Sep 17 00:00:00 2001 From: Noam Yorav-Raphael Date: Wed, 21 Aug 2024 18:29:45 +0300 Subject: [PATCH 840/910] Add changelog for homeless-shelter-to-proc --- doc/manual/rl-next/homeless-shelter-to-proc.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 doc/manual/rl-next/homeless-shelter-to-proc.md diff --git a/doc/manual/rl-next/homeless-shelter-to-proc.md b/doc/manual/rl-next/homeless-shelter-to-proc.md new file mode 100644 index 000000000..4f9519eb4 --- /dev/null +++ b/doc/manual/rl-next/homeless-shelter-to-proc.md @@ -0,0 +1,9 @@ +--- +synopsis: On linux, set $HOME=/proc/homeless-shelter instead of /homeless-shelter +issues: [8313, 11295] +prs: [11300] +--- + +When building, $HOME is set to a non-existing dir. Previously it was always set to `/homeless-shelter`. After a build, the builder verifies that it doesn't exist. In some scenarios (specifically when using the linux sandbox with a single-user installation), it is possible to create the `/homeless-shelter` directory, and some tools will create it, resulting in a build error. + +Now, on Linux, $HOME is set to `/proc/homeless-shelter`. This directory can never be created, since `/proc` is a virtual filesystem. This resolves the issue. From a643c9b1f9270183fd5b0fe49711cf507272101d Mon Sep 17 00:00:00 2001 From: Noam Yorav-Raphael Date: Wed, 21 Aug 2024 19:58:16 +0300 Subject: [PATCH 841/910] Update doc/manual/rl-next/homeless-shelter-to-proc.md Co-authored-by: Eelco Dolstra --- doc/manual/rl-next/homeless-shelter-to-proc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/homeless-shelter-to-proc.md b/doc/manual/rl-next/homeless-shelter-to-proc.md index 4f9519eb4..73213c0df 100644 --- a/doc/manual/rl-next/homeless-shelter-to-proc.md +++ b/doc/manual/rl-next/homeless-shelter-to-proc.md @@ -4,6 +4,6 @@ issues: [8313, 11295] prs: [11300] --- -When building, $HOME is set to a non-existing dir. Previously it was always set to `/homeless-shelter`. After a build, the builder verifies that it doesn't exist. In some scenarios (specifically when using the linux sandbox with a single-user installation), it is possible to create the `/homeless-shelter` directory, and some tools will create it, resulting in a build error. +When building, $HOME is set to a non-existing directory. Previously it was always set to `/homeless-shelter`. Before a build, Nix verifies that it doesn't exist. In some scenarios (specifically when using the Linux sandbox with a single-user installation), it is possible to create the `/homeless-shelter` directory, and some tools will create it, resulting in a build error. Now, on Linux, $HOME is set to `/proc/homeless-shelter`. This directory can never be created, since `/proc` is a virtual filesystem. This resolves the issue. From ce7cf4a2d32d3221eed50f087fe53f17f5c5ca12 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Aug 2024 19:50:24 +0200 Subject: [PATCH 842/910] Update src/libutil/serialise.hh Co-authored-by: John Ericson --- src/libutil/serialise.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 964b9a30d..36e3e68e3 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -533,7 +533,7 @@ struct FramedSource : Source * Write as chunks in the format expected by FramedSource. * * The `checkError` function can be used to terminate the stream when you - * detect that an error has occurred. + * detect that an error has occurred. It does so by throwing an exception. */ struct FramedSink : nix::BufferedSink { From 270c8469d712a232915e736eeecacca696f77a01 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Aug 2024 20:54:02 +0200 Subject: [PATCH 843/910] select() -> poll() for Windows compat --- src/libutil/serialise.cc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 1e30d27b7..056c33531 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -165,15 +165,20 @@ bool FdSource::hasData() if (BufferedSource::hasData()) return true; while (true) { - struct pollfd fds[1]; - fds[0].fd = fd; - fds[0].events = POLLIN; - auto n = poll(fds, 1, 0); + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + auto n = select(fd + 1, &fds, nullptr, nullptr, &timeout); if (n < 0) { if (errno == EINTR) continue; throw SysError("polling file descriptor"); } - return n == 1 && (fds[0].events & POLLIN); + return FD_ISSET(fd, &fds); } } From fac756fed4c0c5c4c78924ed8ecc900809f8c596 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 21 Aug 2024 21:08:26 +0200 Subject: [PATCH 844/910] Add FIXME --- src/libstore/daemon.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index ef3326cd6..6079eae7b 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -402,6 +402,9 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); auto pathInfo = [&]() { // NB: FramedSource must be out of scope before logger->stopWork(); + // FIXME: this means that if there is an error + // half-way through, the client will keep sending + // data, since we haven't sent it the error yet. auto [contentAddressMethod, hashAlgo] = ContentAddressMethod::parseWithAlgo(camStr); FramedSource source(conn.from); FileSerialisationMethod dumpMethod; From 528e4904181072f9abc1c5657ba40fa181a27e48 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Aug 2024 13:47:33 +0200 Subject: [PATCH 845/910] Disable the static build in CI GHA builds are now frequently timing out, so let's not do this. If wanted, it could be done in a separate job. --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index cded6c3a9..d7a6eead9 100644 --- a/flake.nix +++ b/flake.nix @@ -195,7 +195,8 @@ } // lib.optionalAttrs (! nixpkgsFor.${system}.native.stdenv.hostPlatform.isDarwin) { # TODO: enable static builds for darwin, blocked on: # https://github.com/NixOS/nixpkgs/issues/320448 - "static-" = nixpkgsFor.${system}.static; + # TODO: disabled to speed up GHA CI. + #"static-" = nixpkgsFor.${system}.static; }) (nixpkgsPrefix: nixpkgs: flatMapAttrs nixpkgs.nixComponents From 3af73bae5c3d21d74718d5da08c93eb1f7fe8636 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Aug 2024 13:48:46 +0200 Subject: [PATCH 846/910] Remove checks for nixComponents We are currently building Nix twice in the main GHA CI job, which is frequently timing out. Obviously, we want this to be fast, so only do the main build for now. --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index d7a6eead9..f5893d8f6 100644 --- a/flake.nix +++ b/flake.nix @@ -189,6 +189,7 @@ # system, we should reenable this. #perlBindings = self.hydraJobs.perlBindings.${system}; } + /* # Add "passthru" tests // flatMapAttrs ({ "" = nixpkgsFor.${system}.native; @@ -210,6 +211,7 @@ "${nixpkgsPrefix}nix-functional-tests" = nixpkgs.nixComponents.nix-functional-tests; } ) + */ // devFlake.checks.${system} or {} ); From 21a164aa0399b16f8484f9f2d70036cf7e6a28f9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Aug 2024 15:26:34 +0200 Subject: [PATCH 847/910] Fix hang Signed-off-by: Eelco Dolstra --- src/libutil/file-system.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index ded7335f9..aa5f3670c 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -339,7 +339,7 @@ void recursiveSync(const Path & path) while (!dirsToEnumerate.empty()) { auto currentDir = dirsToEnumerate.back(); dirsToEnumerate.pop_back(); - for (auto & entry : std::filesystem::directory_iterator(path)) { + for (auto & entry : std::filesystem::directory_iterator(currentDir)) { auto st = entry.symlink_status(); if (fs::is_directory(st)) { dirsToEnumerate.emplace_back(entry.path()); From 9ff0b55d4ef8c810455035e5735488349912999f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Aug 2024 15:34:19 +0200 Subject: [PATCH 848/910] Add a VM test for fsync-store-paths Based on https://github.com/squalus/nix-durability-tests/blob/master/flake.nix. --- tests/nixos/default.nix | 2 ++ tests/nixos/fsync.nix | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/nixos/fsync.nix diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 3fa341ef1..40d29b371 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -155,4 +155,6 @@ in user-sandboxing = runNixOSTestFor "x86_64-linux" ./user-sandboxing; s3-binary-cache-store = runNixOSTestFor "x86_64-linux" ./s3-binary-cache-store.nix; + + fsync = runNixOSTestFor "x86_64-linux" ./fsync.nix; } diff --git a/tests/nixos/fsync.nix b/tests/nixos/fsync.nix new file mode 100644 index 000000000..99ac2b25d --- /dev/null +++ b/tests/nixos/fsync.nix @@ -0,0 +1,39 @@ +{ lib, config, nixpkgs, pkgs, ... }: + +let + pkg1 = pkgs.go; +in + +{ + name = "fsync"; + + nodes.machine = + { config, lib, pkgs, ... }: + { virtualisation.emptyDiskImages = [ 1024 ]; + environment.systemPackages = [ pkg1 ]; + nix.settings.experimental-features = [ "nix-command" ]; + nix.settings.fsync-store-paths = true; + nix.settings.require-sigs = false; + boot.supportedFilesystems = [ "ext4" "btrfs" "xfs" ]; + }; + + testScript = { nodes }: '' + # fmt: off + for fs in ("ext4", "btrfs", "xfs"): + machine.succeed("mkfs.{} {} /dev/vdb".format(fs, "-F" if fs == "ext4" else "-f")) + machine.succeed("mkdir -p /mnt") + machine.succeed("mount /dev/vdb /mnt") + machine.succeed("sync") + machine.succeed("nix copy --offline ${pkg1} --to /mnt") + machine.crash() + + machine.start() + machine.wait_for_unit("multi-user.target") + machine.succeed("mkdir -p /mnt") + machine.succeed("mount /dev/vdb /mnt") + machine.succeed("nix path-info --offline --store /mnt ${pkg1}") + machine.succeed("nix store verify --all --store /mnt --no-trust") + + machine.succeed("umount /dev/vdb") + ''; +} From 71e7188e07b807497c5d93fa20d34c6bf731f9c1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 22 Aug 2024 15:48:36 +0200 Subject: [PATCH 849/910] Add release note --- doc/manual/rl-next/fsync-store-paths.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 doc/manual/rl-next/fsync-store-paths.md diff --git a/doc/manual/rl-next/fsync-store-paths.md b/doc/manual/rl-next/fsync-store-paths.md new file mode 100644 index 000000000..0e9e7f7f2 --- /dev/null +++ b/doc/manual/rl-next/fsync-store-paths.md @@ -0,0 +1,9 @@ +--- +synopsis: Add setting `fsync-store-paths` +issues: [1218] +prs: [7126] +--- + +Nix now has a setting `fsync-store-paths` that ensures that new store paths are durably written to disk before they are registered as "valid" in Nix's database. This can prevent Nix store corruption if the system crashes or there is a power loss. This setting defaults to `false`. + +Author: [**@squalus**](https://github.com/squalus) From 168bf9c3cdcf458060b17b4b8df06a28e28d5c69 Mon Sep 17 00:00:00 2001 From: aleksana Date: Thu, 22 Aug 2024 23:06:43 +0800 Subject: [PATCH 850/910] README: update CI badge The `Test` workflow was renamed to `CI` in https://github.com/NixOS/nix/commit/9aa486c4be2801a25847b9c75012054d04e792d0. It still seems to be showing the status it was last running on the master branch. This information is misleading and should be corrected. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab647e53b..54a6fcc39 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Nix [![Open Collective supporters](https://opencollective.com/nixos/tiers/supporter/badge.svg?label=Supporters&color=brightgreen)](https://opencollective.com/nixos) -[![Test](https://github.com/NixOS/nix/workflows/Test/badge.svg)](https://github.com/NixOS/nix/actions) +[![CI](https://github.com/NixOS/nix/workflows/CI/badge.svg)](https://github.com/NixOS/nix/actions/workflows/ci.yml) Nix is a powerful package manager for Linux and other Unix systems that makes package management reliable and reproducible. Please refer to the [Nix manual](https://nix.dev/reference/nix-manual) From 5d28a0032282970788f723910f40a67e402d2e09 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Mon, 19 Aug 2024 16:33:23 +0300 Subject: [PATCH 851/910] Add nix_store_copy_closure to libstore-c --- src/libstore-c/nix_api_store.cc | 12 ++++++++++++ src/libstore-c/nix_api_store.h | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index 79841ca49..fb7391276 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -144,3 +144,15 @@ StorePath * nix_store_path_clone(const StorePath * p) { return new StorePath{p->path}; } + +nix_err nix_store_copy_closure(nix_c_context * context, Store * srcStore, Store * dstStore, StorePath * path) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::RealisedPath::Set paths; + paths.insert(path->path); + nix::copyClosure(*srcStore->ptr, *dstStore->ptr, paths); + } + NIXC_CATCH_ERRS +} diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index 4b2134457..93208cb7c 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -161,6 +161,16 @@ nix_err nix_store_realise( nix_err nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data); +/** + * @brief Copy the closure of `path` from `srcStore` to `dstStore`. + * + * @param[out] context Optional, stores error information + * @param[in] srcStore nix source store reference + * @param[in] srcStore nix destination store reference + * @param[in] path Path to copy + */ +nix_err nix_store_copy_closure(nix_c_context * context, Store * srcStore, Store * dstStore, StorePath * path); + // cffi end #ifdef __cplusplus } From 2a14a20bb3c8db049e48b6c7887909d378e92ef4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 23 Aug 2024 20:47:22 +0200 Subject: [PATCH 852/910] packaging: Expose libs This exposes the libraries in a way that is easily replicated in Nixpkgs, without having to create a package set within a package set. --- packaging/everything.nix | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/packaging/everything.nix b/packaging/everything.nix index 8c8ce6611..6dae7f1c2 100644 --- a/packaging/everything.nix +++ b/packaging/everything.nix @@ -76,7 +76,7 @@ ] ++ lib.optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [ nix-perl-bindings ]; -}).overrideAttrs (_: { +}).overrideAttrs (finalAttrs: prevAttrs: { doCheck = true; doInstallCheck = true; @@ -90,4 +90,37 @@ installCheckInputs = [ nix-functional-tests ]; + passthru = prevAttrs.passthru // { + /** + These are the libraries that are part of the Nix project. They are used + by the Nix CLI and other tools. + + If you need to use these libraries in your project, we recommend to use + the `-c` C API libraries exclusively, if possible. + + We also recommend that you build the complete package to ensure that the unit tests pass. + You could do this in CI, or by passing it in an unused environment variable. e.g in a `mkDerivation` call: + + ```nix + buildInputs = [ nix.libs.nix-util-c nix.libs.nix-store-c ]; + # Make sure the nix libs we use are ok + unusedInputsForTests = [ nix ]; + disallowedReferences = nix.all; + ``` + */ + libs = { + inherit + nix-util + nix-util-c + nix-store + nix-store-c + nix-fetchers + nix-expr + nix-expr-c + nix-flake + nix-main + nix-main-c + ; + }; + }; }) From c9f45677b585dabb3a83570e21426257d92746bd Mon Sep 17 00:00:00 2001 From: Bryan Honof Date: Wed, 21 Aug 2024 16:57:06 +0200 Subject: [PATCH 853/910] fix: Error on malformed URI query parameter Signed-off-by: Bryan Honof --- src/libutil/url.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libutil/url.cc b/src/libutil/url.cc index bcbe9ea4e..78c832440 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -79,10 +79,15 @@ std::map decodeQuery(const std::string & query) for (auto s : tokenizeString(query, "&")) { auto e = s.find('='); - if (e != std::string::npos) - result.emplace( - s.substr(0, e), - percentDecode(std::string_view(s).substr(e + 1))); + + if (e == std::string::npos) { + warn("invalid URI query '%s', did you forget an equals sign `=`?", s); + continue; + } + + result.emplace( + s.substr(0, e), + percentDecode(std::string_view(s).substr(e + 1))); } return result; From 096bec8eb27a534b54c4cd810a5a9d8250703a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 25 Aug 2024 09:04:47 +0200 Subject: [PATCH 854/910] Revert "Merge pull request #11300 from noamraph/homeless-shelter-to-proc" This reverts commit 43e82c944671ad7ce5da1b75991a4c1f48b545c4, reversing changes made to d79b9bdec0557315b0897707394339082cec004c. Since /proc/homeless-shelter returns a different errno than /homeless-shelter (ENOENT vs EACCES), we need to revert this change. Software depends on this error code i.e. cargo and therefore breaks. --- doc/manual/src/language/derivations.md | 3 +-- src/libstore/unix/build/local-derivation-goal.cc | 7 ------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index b57f6bb6a..8e3f0f791 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -264,8 +264,7 @@ The [`builder`](#attr-builder) is executed as follows: - `PATH` is set to `/path-not-set` to prevent shells from initialising it to their built-in default value. - - `HOME` is set to `/proc/homeless-shelter` on Linux and `/homeless-shelter` - on OSX, to prevent programs from + - `HOME` is set to `/homeless-shelter` to prevent programs from using `/etc/passwd` or the like to find the user's home directory, which could cause impurity. Usually, when `HOME` is set, it is used as the location of the home directory, even if diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index e1035fbdd..d3482df17 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -102,14 +102,7 @@ void handleDiffHook( } } -// We want $HOME to be un-creatable in the sandbox. On Linux, -// you can't create anything inside /proc since it's a virtual filesystem. -// On Darwin it seems that `/homeless-shelter` is good enough. -#if __linux__ -const Path LocalDerivationGoal::homeDir = "/proc/homeless-shelter"; -#else const Path LocalDerivationGoal::homeDir = "/homeless-shelter"; -#endif LocalDerivationGoal::~LocalDerivationGoal() From 90560eeccc990315fa44bf18e78ed69aa7d552a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sun, 25 Aug 2024 09:03:59 +0200 Subject: [PATCH 855/910] Revert "Merge pull request #11350 from noamraph/homeless-shelter-to-proc-changelog" This reverts commit fa49d2e356d44d416ac86d2286189e8f5f6878ba, reversing changes made to af26fe39344faff70e009d980820b8667c319cb2. --- doc/manual/rl-next/homeless-shelter-to-proc.md | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 doc/manual/rl-next/homeless-shelter-to-proc.md diff --git a/doc/manual/rl-next/homeless-shelter-to-proc.md b/doc/manual/rl-next/homeless-shelter-to-proc.md deleted file mode 100644 index 73213c0df..000000000 --- a/doc/manual/rl-next/homeless-shelter-to-proc.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: On linux, set $HOME=/proc/homeless-shelter instead of /homeless-shelter -issues: [8313, 11295] -prs: [11300] ---- - -When building, $HOME is set to a non-existing directory. Previously it was always set to `/homeless-shelter`. Before a build, Nix verifies that it doesn't exist. In some scenarios (specifically when using the Linux sandbox with a single-user installation), it is possible to create the `/homeless-shelter` directory, and some tools will create it, resulting in a build error. - -Now, on Linux, $HOME is set to `/proc/homeless-shelter`. This directory can never be created, since `/proc` is a virtual filesystem. This resolves the issue. From 93a8b06070306df388a168a2285a3476070c7399 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 26 Aug 2024 05:26:49 +0100 Subject: [PATCH 856/910] sequoia-nixbld-user-migration: increment base UID by 1 --- scripts/sequoia-nixbld-user-migration.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/sequoia-nixbld-user-migration.sh b/scripts/sequoia-nixbld-user-migration.sh index 9208a3605..644249192 100755 --- a/scripts/sequoia-nixbld-user-migration.sh +++ b/scripts/sequoia-nixbld-user-migration.sh @@ -2,7 +2,7 @@ set -x -((NEW_NIX_FIRST_BUILD_UID=350)) +((NEW_NIX_FIRST_BUILD_UID=351)) ((TEMP_NIX_FIRST_BUILD_UID=31000)) nix_user_n() { From 77ddcbe12e169051f9bad1ab5e7581b148a94883 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 26 Aug 2024 16:15:13 +0200 Subject: [PATCH 857/910] getDoc: Explain why we partially apply __functor --- src/libexpr/eval.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b87d96be0..ca41a9944 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -621,6 +621,11 @@ std::optional EvalState::getDoc(Value & v) Value & functor = *v.attrs()->find(sFunctor)->value; Value * vp = &v; Value partiallyApplied; + // The first paramater is not user-provided, and may be + // handled by code that is opaque to the user, like lib.const = x: y: y; + // So preferably we show docs that are relevant to the + // "partially applied" function returned by e.g. `const`. + // We apply the first argument: callFunction(functor, 1, &vp, partiallyApplied, noPos); auto _level = addCallDepth(noPos); return getDoc(partiallyApplied); From 11cf29b15c8ea144035eb6a9d9f31bb05eee2048 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 26 Aug 2024 17:59:58 +0100 Subject: [PATCH 858/910] install-darwin: increment base UID by 1 (#15) --- scripts/bigsur-nixbld-user-migration.sh | 2 +- scripts/install-darwin-multi-user.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/bigsur-nixbld-user-migration.sh b/scripts/bigsur-nixbld-user-migration.sh index bc42e02e6..57f65da72 100755 --- a/scripts/bigsur-nixbld-user-migration.sh +++ b/scripts/bigsur-nixbld-user-migration.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -((NEW_NIX_FIRST_BUILD_UID=350)) +((NEW_NIX_FIRST_BUILD_UID=351)) id_available(){ dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null diff --git a/scripts/install-darwin-multi-user.sh b/scripts/install-darwin-multi-user.sh index bd1a54ad8..89c66b8f4 100644 --- a/scripts/install-darwin-multi-user.sh +++ b/scripts/install-darwin-multi-user.sh @@ -9,11 +9,11 @@ set -o pipefail # - we changed UID to 301 because Big Sur updates failed into recovery mode # we're targeting the 200-400 UID range for role users mentioned in the # usage note for sysadminctl -# - we changed UID to 350 because Sequoia now uses UIDs 300-304 for its own +# - we changed UID to 351 because Sequoia now uses UIDs 300-304 for its own # daemon users # - we changed GID to 350 alongside above just because it hides the nixbld # group from the Users & Groups settings panel :) -export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-350}" +export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-351}" export NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-350}" export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d" From dbabfc92d4e1864f793f167a438e532673afdc14 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 26 Aug 2024 15:42:09 -0400 Subject: [PATCH 859/910] Make sure we have an `execvpe` on Windows too Necessary to fix a build (that was already broken in other ways) after PR #11021. --- src/libutil/{unix => }/exec.hh | 7 ++++++- src/libutil/meson.build | 1 + src/libutil/unix/meson.build | 1 - src/libutil/unix/processes.cc | 8 +++++--- src/libutil/windows/processes.cc | 8 ++++++++ 5 files changed, 20 insertions(+), 5 deletions(-) rename src/libutil/{unix => }/exec.hh (53%) diff --git a/src/libutil/unix/exec.hh b/src/libutil/exec.hh similarity index 53% rename from src/libutil/unix/exec.hh rename to src/libutil/exec.hh index e6b80889a..405e19268 100644 --- a/src/libutil/unix/exec.hh +++ b/src/libutil/exec.hh @@ -1,5 +1,7 @@ #pragma once +#include "os-string.hh" + namespace nix { /** @@ -8,6 +10,9 @@ namespace nix { * * We use our own implementation unconditionally for consistency. */ -int execvpe(const char * file0, char * const argv[], char * const envp[]); +int execvpe( + const OsString::value_type * file0, + const OsString::value_type * const argv[], + const OsString::value_type * const envp[]); } diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 200eeb4e9..72ff461ca 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -129,6 +129,7 @@ sources = files( 'english.cc', 'environment-variables.cc', 'error.cc', + 'exec.hh', 'executable-path.cc', 'exit.cc', 'experimental-features.cc', diff --git a/src/libutil/unix/meson.build b/src/libutil/unix/meson.build index d36152db9..1c5bf27fb 100644 --- a/src/libutil/unix/meson.build +++ b/src/libutil/unix/meson.build @@ -13,7 +13,6 @@ sources += files( include_dirs += include_directories('.') headers += files( - 'exec.hh', 'monitor-fd.hh', 'signals-impl.hh', ) diff --git a/src/libutil/unix/processes.cc b/src/libutil/unix/processes.cc index 09acba35a..43d9179d9 100644 --- a/src/libutil/unix/processes.cc +++ b/src/libutil/unix/processes.cc @@ -420,10 +420,12 @@ bool statusOk(int status) return WIFEXITED(status) && WEXITSTATUS(status) == 0; } -int execvpe(const char * file0, char * const argv[], char * const envp[]) +int execvpe(const char * file0, const char * const argv[], const char * const envp[]) { - auto file = ExecutablePath::load().findPath(file0).string(); - return execve(file.c_str(), argv, envp); + auto file = ExecutablePath::load().findPath(file0); + // `const_cast` is safe. See the note in + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html + return execve(file.c_str(), const_cast(argv), const_cast(envp)); } } diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 9cd714f84..7f34c5632 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -1,6 +1,7 @@ #include "current-process.hh" #include "environment-variables.hh" #include "error.hh" +#include "executable-path.hh" #include "file-descriptor.hh" #include "file-path.hh" #include "signals.hh" @@ -377,4 +378,11 @@ bool statusOk(int status) { return status == 0; } + +int execvpe(const wchar_t * file0, const wchar_t * const argv[], const wchar_t * const envp[]) +{ + auto file = ExecutablePath::load().findPath(file0); + return _wexecve(file.c_str(), argv, envp); +} + } From a97a08411c4e34323b48e95ed593bb53d67bde23 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 26 Aug 2024 12:24:37 -0400 Subject: [PATCH 860/910] More support for `std::filepath` in libnixutil We're not replacing `Path` in exposed definitions in many cases, but just adding alternatives. This will allow us to "top down" change `Path` to `std::fileysystem::path`, and then we can remove the `Path`-using utilities which will become unused. Also add some test files which we forgot to include in the libutil unit tests `meson.build`. Co-Authored-By: siddhantCodes --- src/libcmd/common-eval-args.cc | 6 +- src/libcmd/installables.cc | 4 +- src/libcmd/repl.cc | 4 +- src/libexpr/parser.y | 2 +- src/libstore/gc.cc | 2 +- src/libstore/local-overlay-store.cc | 2 +- src/libutil/args.hh | 22 ++++++ src/libutil/exec.hh | 5 +- src/libutil/executable-path.cc | 6 +- src/libutil/executable-path.hh | 6 +- src/libutil/file-system.cc | 32 ++++++--- src/libutil/file-system.hh | 69 ++++++++++++++++++- src/libutil/linux/namespaces.cc | 2 +- src/libutil/os-string.hh | 17 +++-- src/libutil/posix-source-accessor.cc | 2 +- src/libutil/serialise.cc | 6 +- src/libutil/strings.cc | 4 +- src/libutil/unix/users.cc | 2 + src/nix/bundle.cc | 4 +- src/nix/config-check.cc | 4 +- src/nix/develop.cc | 6 +- src/nix/flake.cc | 10 +-- src/nix/run.cc | 18 ++--- src/nix/self-exe.cc | 4 +- tests/unit/libfetchers/public-key.cc | 6 +- .../libstore-support/tests/nix_api_store.hh | 2 +- tests/unit/libstore-support/tests/protocol.hh | 6 +- .../libstore/derivation-advanced-attrs.cc | 6 +- tests/unit/libstore/derivation.cc | 6 +- tests/unit/libstore/machines.cc | 20 +++--- tests/unit/libstore/nar-info.cc | 6 +- tests/unit/libstore/path-info.cc | 6 +- tests/unit/libstore/store-reference.cc | 6 +- .../libutil-support/tests/characterization.hh | 10 +-- tests/unit/libutil/file-system.cc | 56 ++++++++------- tests/unit/libutil/git.cc | 6 +- tests/unit/libutil/meson.build | 3 + 37 files changed, 258 insertions(+), 120 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index ae9994a05..ccbf957d9 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -18,6 +18,8 @@ namespace nix { +namespace fs { using namespace std::filesystem; } + fetchers::Settings fetchSettings; static GlobalConfig::Register rFetchSettings(&fetchSettings); @@ -119,8 +121,8 @@ MixEvalArgs::MixEvalArgs() .category = category, .labels = {"original-ref", "resolved-ref"}, .handler = {[&](std::string _from, std::string _to) { - auto from = parseFlakeRef(fetchSettings, _from, absPath(".")); - auto to = parseFlakeRef(fetchSettings, _to, absPath(".")); + auto from = parseFlakeRef(fetchSettings, _from, fs::current_path().string()); + auto to = parseFlakeRef(fetchSettings, _to, fs::current_path().string()); fetchers::Attrs extraAttrs; if (to.subdir != "") extraAttrs["dir"] = to.subdir; fetchers::overrideRegistry(from.input, to.input, extraAttrs); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 0fe956ec0..22e7eb546 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -31,6 +31,8 @@ namespace nix { +namespace fs { using namespace std::filesystem; } + void completeFlakeInputPath( AddCompletions & completions, ref evalState, @@ -341,7 +343,7 @@ void completeFlakeRefWithFragment( auto flakeRefS = std::string(prefix.substr(0, hash)); // TODO: ideally this would use the command base directory instead of assuming ".". - auto flakeRef = parseFlakeRef(fetchSettings, expandTilde(flakeRefS), absPath(".")); + auto flakeRef = parseFlakeRef(fetchSettings, expandTilde(flakeRefS), fs::current_path().string()); auto evalCache = openEvalCache(*evalState, std::make_shared(lockFlake( diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index e7c43367c..63f6c1bdd 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -622,7 +622,7 @@ ProcessLineResult NixRepl::processLine(std::string line) // When missing, trigger the normal exception // e.g. :doc builtins.foo // behaves like - // nix-repl> builtins.foo + // nix-repl> builtins.foo // error: attribute 'foo' missing evalString(arg, v); assert(false); @@ -720,7 +720,7 @@ void NixRepl::loadFlake(const std::string & flakeRefS) if (flakeRefS.empty()) throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)"); - auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, absPath("."), true); + auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, std::filesystem::current_path().string(), true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index f2ccca7fc..a79abbf16 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -350,7 +350,7 @@ string_parts_interpolated path_start : PATH { - Path path(absPath({$1.p, $1.l}, state->basePath.path.abs())); + Path path(absPath(std::string_view{$1.p, $1.l}, state->basePath.path.abs())); /* add back in the trailing '/' to the first segment */ if ($1.p[$1.l-1] == '/' && $1.l > 1) path += "/"; diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 1494712da..91cf76366 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -333,7 +333,7 @@ static std::string quoteRegexChars(const std::string & raw) } #if __linux__ -static void readFileRoots(const char * path, UncheckedRoots & roots) +static void readFileRoots(const std::filesystem::path & path, UncheckedRoots & roots) { try { roots[readFile(path)].emplace(path); diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index ec2c5f4e9..b86beba2c 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -31,7 +31,7 @@ LocalOverlayStore::LocalOverlayStore(std::string_view scheme, PathView path, con if (checkMount.get()) { std::smatch match; std::string mountInfo; - auto mounts = readFile("/proc/self/mounts"); + auto mounts = readFile(std::filesystem::path{"/proc/self/mounts"}); auto regex = std::regex(R"((^|\n)overlay )" + realStoreDir.get() + R"( .*(\n|$))"); // Mount points can be stacked, so there might be multiple matching entries. diff --git a/src/libutil/args.hh b/src/libutil/args.hh index c0236ee3d..513b8d811 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -113,6 +113,16 @@ protected: , arity(1) { } + Handler(std::filesystem::path * dest) + : fun([dest](std::vector ss) { *dest = ss[0]; }) + , arity(1) + { } + + Handler(std::optional * dest) + : fun([dest](std::vector ss) { *dest = ss[0]; }) + , arity(1) + { } + template Handler(T * dest, const T & val) : fun([dest, val](std::vector ss) { *dest = val; }) @@ -283,6 +293,18 @@ public: }); } + /** + * Expect a path argument. + */ + void expectArg(const std::string & label, std::filesystem::path * dest, bool optional = false) + { + expectArgs({ + .label = label, + .optional = optional, + .handler = {dest} + }); + } + /** * Expect 0 or more arguments. */ diff --git a/src/libutil/exec.hh b/src/libutil/exec.hh index 405e19268..cbbe80c4e 100644 --- a/src/libutil/exec.hh +++ b/src/libutil/exec.hh @@ -10,9 +10,6 @@ namespace nix { * * We use our own implementation unconditionally for consistency. */ -int execvpe( - const OsString::value_type * file0, - const OsString::value_type * const argv[], - const OsString::value_type * const envp[]); +int execvpe(const OsChar * file0, const OsChar * const argv[], const OsChar * const envp[]); } diff --git a/src/libutil/executable-path.cc b/src/libutil/executable-path.cc index da71088e7..9fb5214b2 100644 --- a/src/libutil/executable-path.cc +++ b/src/libutil/executable-path.cc @@ -6,7 +6,9 @@ namespace nix { -namespace fs = std::filesystem; +namespace fs { +using namespace std::filesystem; +} constexpr static const OsStringView path_var_separator{ &ExecutablePath::separator, @@ -24,7 +26,7 @@ ExecutablePath ExecutablePath::load() ExecutablePath ExecutablePath::parse(const OsString & path) { auto strings = path.empty() ? (std::list{}) - : basicSplitString, OsString::value_type>(path, path_var_separator); + : basicSplitString, OsChar>(path, path_var_separator); std::vector ret; ret.reserve(strings.size()); diff --git a/src/libutil/executable-path.hh b/src/libutil/executable-path.hh index f46d5e212..c5cfa1c39 100644 --- a/src/libutil/executable-path.hh +++ b/src/libutil/executable-path.hh @@ -7,11 +7,15 @@ namespace nix { MakeError(ExecutableLookupError, Error); +/** + * @todo rename, it is not just good for execuatable paths, but also + * other lists of paths. + */ struct ExecutablePath { std::vector directories; - constexpr static const OsString::value_type separator = + constexpr static const OsChar separator = #ifdef WIN32 L';' #else diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index aa5f3670c..edcacb50a 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -26,10 +26,10 @@ #include "strings-inline.hh" -namespace fs = std::filesystem; - namespace nix { +namespace fs { using namespace std::filesystem; } + /** * Treat the string as possibly an absolute path, by inspecting the * start of it. Return whether it was probably intended to be @@ -73,6 +73,10 @@ Path absPath(PathView path, std::optional dir, bool resolveSymlinks) return canonPath(path, resolveSymlinks); } +std::filesystem::path absPath(const std::filesystem::path & path, bool resolveSymlinks) +{ + return absPath(path.string(), std::nullopt, resolveSymlinks); +} Path canonPath(PathView path, bool resolveSymlinks) { @@ -206,10 +210,10 @@ bool pathExists(const Path & path) return maybeLstat(path).has_value(); } -bool pathAccessible(const Path & path) +bool pathAccessible(const std::filesystem::path & path) { try { - return pathExists(path); + return pathExists(path.string()); } catch (SysError & e) { // swallow EPERM if (e.errNo == EPERM) return false; @@ -238,6 +242,11 @@ std::string readFile(const Path & path) return readFile(fd.get()); } +std::string readFile(const std::filesystem::path & path) +{ + return readFile(os_string_to_string(PathViewNG { path })); +} + void readFile(const Path & path, Sink & sink) { @@ -324,7 +333,7 @@ void recursiveSync(const Path & path) /* If it's a file, just fsync and return. */ auto st = lstat(path); if (S_ISREG(st.st_mode)) { - AutoCloseFD fd = open(path.c_str(), O_RDONLY, 0); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY, 0)); if (!fd) throw SysError("opening file '%1%'", path); fd.fsync(); @@ -344,7 +353,7 @@ void recursiveSync(const Path & path) if (fs::is_directory(st)) { dirsToEnumerate.emplace_back(entry.path()); } else if (fs::is_regular_file(st)) { - AutoCloseFD fd = open(entry.path().c_str(), O_RDONLY, 0); + AutoCloseFD fd = toDescriptor(open(entry.path().string().c_str(), O_RDONLY, 0)); if (!fd) throw SysError("opening file '%1%'", entry.path()); fd.fsync(); @@ -355,7 +364,7 @@ void recursiveSync(const Path & path) /* Fsync all the directories. */ for (auto dir = dirsToFsync.rbegin(); dir != dirsToFsync.rend(); ++dir) { - AutoCloseFD fd = open(dir->c_str(), O_RDONLY, 0); + AutoCloseFD fd = toDescriptor(open(dir->string().c_str(), O_RDONLY, 0)); if (!fd) throw SysError("opening directory '%1%'", *dir); fd.fsync(); @@ -595,19 +604,20 @@ void createSymlink(const Path & target, const Path & link) fs::create_symlink(target, link); } -void replaceSymlink(const Path & target, const Path & link) +void replaceSymlink(const fs::path & target, const fs::path & link) { for (unsigned int n = 0; true; n++) { - Path tmp = canonPath(fmt("%s/.%d_%s", dirOf(link), n, baseNameOf(link))); + auto tmp = link.parent_path() / fs::path{fmt(".%d_%s", n, link.filename().string())}; + tmp = tmp.lexically_normal(); try { - createSymlink(target, tmp); + fs::create_symlink(target, tmp); } catch (fs::filesystem_error & e) { if (e.code() == std::errc::file_exists) continue; throw; } - std::filesystem::rename(tmp, link); + fs::rename(tmp, link); break; } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 0f406a2de..eb3e4ec66 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -46,16 +46,33 @@ struct Source; * @return An absolutized path, resolving paths relative to the * specified directory, or the current directory otherwise. The path * is also canonicalised. + * + * In the process of being deprecated for `std::filesystem::absolute`. */ Path absPath(PathView path, std::optional dir = {}, bool resolveSymlinks = false); +inline Path absPath(const Path & path, + std::optional dir = {}, + bool resolveSymlinks = false) +{ + return absPath(PathView{path}, dir, resolveSymlinks); +} + +std::filesystem::path absPath(const std::filesystem::path & path, + bool resolveSymlinks = false); + /** * Canonicalise a path by removing all `.` or `..` components and * double or trailing slashes. Optionally resolves all symlink * components such that each component of the resulting path is *not* * a symbolic link. + * + * In the process of being deprecated for + * `std::filesystem::path::lexically_normal` (for the `resolveSymlinks = + * false` case), and `std::filesystem::weakly_canonical` (for the + * `resolveSymlinks = true` case). */ Path canonPath(PathView path, bool resolveSymlinks = false); @@ -64,12 +81,18 @@ Path canonPath(PathView path, bool resolveSymlinks = false); * everything before the final `/`. If the path is the root or an * immediate child thereof (e.g., `/foo`), this means `/` * is returned. + * + * In the process of being deprecated for + * `std::filesystem::path::parent_path`. */ Path dirOf(const PathView path); /** * @return the base name of the given canonical path, i.e., everything * following the final `/` (trailing slashes are removed). + * + * In the process of being deprecated for + * `std::filesystem::path::filename`. */ std::string_view baseNameOf(std::string_view path); @@ -98,20 +121,42 @@ std::optional maybeLstat(const Path & path); /** * @return true iff the given path exists. + * + * In the process of being deprecated for `fs::symlink_exists`. */ bool pathExists(const Path & path); +namespace fs { + +/** + * ``` + * symlink_exists(p) = std::filesystem::exists(std::filesystem::symlink_status(p)) + * ``` + * Missing convenience analogous to + * ``` + * std::filesystem::exists(p) = std::filesystem::exists(std::filesystem::status(p)) + * ``` + */ +inline bool symlink_exists(const std::filesystem::path & path) { + return std::filesystem::exists(std::filesystem::symlink_status(path)); +} + +} // namespace fs + /** * A version of pathExists that returns false on a permission error. * Useful for inferring default paths across directories that might not * be readable. * @return true iff the given path can be accessed and exists */ -bool pathAccessible(const Path & path); +bool pathAccessible(const std::filesystem::path & path); /** * Read the contents (target) of a symbolic link. The result is not * in any way canonicalised. + * + * In the process of being deprecated for + * `std::filesystem::read_symlink`. */ Path readLink(const Path & path); @@ -124,14 +169,23 @@ Descriptor openDirectory(const std::filesystem::path & path); * Read the contents of a file into a string. */ std::string readFile(const Path & path); +std::string readFile(const std::filesystem::path & path); void readFile(const Path & path, Sink & sink); /** * Write a string to a file. */ void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false); +static inline void writeFile(const std::filesystem::path & path, std::string_view s, mode_t mode = 0666, bool sync = false) +{ + return writeFile(path.string(), s, mode, sync); +} void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false); +static inline void writeFile(const std::filesystem::path & path, Source & source, mode_t mode = 0666, bool sync = false) +{ + return writeFile(path.string(), source, mode, sync); +} /** * Flush a path's parent directory to disk. @@ -154,6 +208,9 @@ void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed); /** * Create a directory and all its parents, if necessary. + * + * In the process of being deprecated for + * `std::filesystem::create_directories`. */ void createDirs(const Path & path); inline void createDirs(PathView path) @@ -192,13 +249,21 @@ void setWriteTime(const std::filesystem::path & path, const struct stat & st); /** * Create a symlink. + * + * In the process of being deprecated for + * `std::filesystem::create_symlink`. */ void createSymlink(const Path & target, const Path & link); /** * Atomically create or replace a symlink. */ -void replaceSymlink(const Path & target, const Path & link); +void replaceSymlink(const std::filesystem::path & target, const std::filesystem::path & link); + +inline void replaceSymlink(const Path & target, const Path & link) +{ + return replaceSymlink(std::filesystem::path{target}, std::filesystem::path{link}); +} /** * Similar to 'renameFile', but fallback to a copy+remove if `src` and `dst` diff --git a/src/libutil/linux/namespaces.cc b/src/libutil/linux/namespaces.cc index d4766cbba..c5e21dffc 100644 --- a/src/libutil/linux/namespaces.cc +++ b/src/libutil/linux/namespaces.cc @@ -118,7 +118,7 @@ void saveMountNamespace() void restoreMountNamespace() { try { - auto savedCwd = absPath("."); + auto savedCwd = std::filesystem::current_path(); if (fdSavedMountNamespace && setns(fdSavedMountNamespace.get(), CLONE_NEWNS) == -1) throw SysError("restoring parent mount namespace"); diff --git a/src/libutil/os-string.hh b/src/libutil/os-string.hh index 0d75173e5..3e24763fb 100644 --- a/src/libutil/os-string.hh +++ b/src/libutil/os-string.hh @@ -11,21 +11,30 @@ namespace nix { * Named because it is similar to the Rust type, except it is in the * native encoding not WTF-8. * - * Same as `std::filesystem::path::string_type`, but manually defined to + * Same as `std::filesystem::path::value_type`, but manually defined to * avoid including a much more complex header. */ -using OsString = std::basic_string< +using OsChar = #if defined(_WIN32) && !defined(__CYGWIN__) wchar_t #else char #endif - >; + ; + +/** + * Named because it is similar to the Rust type, except it is in the + * native encoding not WTF-8. + * + * Same as `std::filesystem::path::string_type`, but manually defined + * for the same reason as `OsChar`. + */ +using OsString = std::basic_string; /** * `std::string_view` counterpart for `OsString`. */ -using OsStringView = std::basic_string_view; +using OsStringView = std::basic_string_view; std::string os_string_to_string(OsStringView path); diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 2b1a485d5..8cec3388d 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -20,7 +20,7 @@ PosixSourceAccessor::PosixSourceAccessor() SourcePath PosixSourceAccessor::createAtRoot(const std::filesystem::path & path) { - std::filesystem::path path2 = absPath(path.string()); + std::filesystem::path path2 = absPath(path); return { make_ref(path2.root_path()), CanonPath { path2.relative_path().string() }, diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 8a57858f5..4aa5ae385 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -9,6 +9,7 @@ #ifdef _WIN32 # include +# include # include "windows-error.hh" #else # include @@ -167,13 +168,14 @@ bool FdSource::hasData() while (true) { fd_set fds; FD_ZERO(&fds); - FD_SET(fd, &fds); + int fd_ = fromDescriptorReadOnly(fd); + FD_SET(fd_, &fds); struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; - auto n = select(fd + 1, &fds, nullptr, nullptr, &timeout); + auto n = select(fd_ + 1, &fds, nullptr, nullptr, &timeout); if (n < 0) { if (errno == EINTR) continue; throw SysError("polling file descriptor"); diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc index 60297228e..5cad95758 100644 --- a/src/libutil/strings.cc +++ b/src/libutil/strings.cc @@ -14,8 +14,8 @@ template std::list splitString(std::string_view s, std::string_view template std::set splitString(std::string_view s, std::string_view separators); template std::vector splitString(std::string_view s, std::string_view separators); -template std::list basicSplitString( - std::basic_string_view s, std::basic_string_view separators); +template std::list +basicSplitString(std::basic_string_view s, std::basic_string_view separators); template std::string concatStringsSep(std::string_view, const std::list &); template std::string concatStringsSep(std::string_view, const std::set &); diff --git a/src/libutil/unix/users.cc b/src/libutil/unix/users.cc index 58063a953..107a6e04f 100644 --- a/src/libutil/unix/users.cc +++ b/src/libutil/unix/users.cc @@ -9,6 +9,8 @@ namespace nix { +namespace fs { using namespace std::filesystem; } + std::string getUserName() { auto pw = getpwuid(geteuid()); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index e152c26f2..5b7862c4e 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -6,6 +6,8 @@ #include "local-fs-store.hh" #include "eval-inline.hh" +namespace nix::fs { using namespace std::filesystem; } + using namespace nix; struct CmdBundle : InstallableValueCommand @@ -78,7 +80,7 @@ struct CmdBundle : InstallableValueCommand auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] = parseFlakeRefWithFragmentAndExtendedOutputsSpec( - fetchSettings, bundler, absPath(".")); + fetchSettings, bundler, fs::current_path().string()); const flake::LockFlags lockFlags{ .writeLockFile = false }; InstallableFlake bundler{this, evalState, std::move(bundlerFlakeRef), bundlerName, std::move(extendedOutputsSpec), diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index 1a6574de2..6cf73785e 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -10,6 +10,8 @@ #include "worker-protocol.hh" #include "executable-path.hh" +namespace nix::fs { using namespace std::filesystem; } + using namespace nix; namespace { @@ -40,8 +42,6 @@ void checkInfo(const std::string & msg) { } -namespace fs = std::filesystem; - struct CmdConfigCheck : StoreCommand { bool success = true; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index effc86a0a..04672e2ad 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -21,6 +21,8 @@ #include "strings.hh" +namespace nix::fs { using namespace std::filesystem; } + using namespace nix; struct DevelopSettings : Config @@ -341,7 +343,7 @@ struct Common : InstallableCommand, MixProfile ref store, const BuildEnvironment & buildEnvironment, const std::filesystem::path & tmpDir, - const std::filesystem::path & outputsDir = std::filesystem::path { absPath(".") } / "outputs") + const std::filesystem::path & outputsDir = fs::path { fs::current_path() } / "outputs") { // A list of colon-separated environment variables that should be // prepended to, rather than overwritten, in order to keep the shell usable. @@ -450,7 +452,7 @@ struct Common : InstallableCommand, MixProfile auto targetFilePath = tmpDir / OS_STR(".attrs."); targetFilePath += ext; - writeFile(targetFilePath.string(), content); + writeFile(targetFilePath, content); auto fileInBuilderEnv = buildEnvironment.vars.find(envVar); assert(fileInBuilderEnv != buildEnvironment.vars.end()); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b7bbb767b..2db1e039e 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -25,7 +25,7 @@ #include "strings-inline.hh" -namespace fs = std::filesystem; +namespace nix::fs { using namespace std::filesystem; } using namespace nix; using namespace nix::flake; @@ -53,7 +53,7 @@ public: FlakeRef getFlakeRef() { - return parseFlakeRef(fetchSettings, flakeUrl, absPath(".")); //FIXME + return parseFlakeRef(fetchSettings, flakeUrl, fs::current_path().string()); //FIXME } LockedFlake lockFlake() @@ -65,7 +65,7 @@ public: { return { // Like getFlakeRef but with expandTilde calld first - parseFlakeRef(fetchSettings, expandTilde(flakeUrl), absPath(".")) + parseFlakeRef(fetchSettings, expandTilde(flakeUrl), fs::current_path().string()) }; } }; @@ -880,7 +880,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand auto evalState = getEvalState(); auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment( - fetchSettings, templateUrl, absPath(".")); + fetchSettings, templateUrl, fs::current_path().string()); auto installable = InstallableFlake(nullptr, evalState, std::move(templateFlakeRef), templateName, ExtendedOutputsSpec::Default(), @@ -927,7 +927,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand } continue; } else - writeFile(to2.string(), contents); + writeFile(to2, contents); } else if (fs::is_symlink(st)) { auto target = fs::read_symlink(from2); diff --git a/src/nix/run.cc b/src/nix/run.cc index dfe7f374f..63ae8a195 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -20,6 +20,8 @@ #include +namespace nix::fs { using namespace std::filesystem; } + using namespace nix; std::string chrootHelperName = "__run_in_chroot"; @@ -170,25 +172,25 @@ void chrootHelper(int argc, char * * argv) if (!pathExists(storeDir)) { // FIXME: Use overlayfs? - std::filesystem::path tmpDir = createTempDir(); + fs::path tmpDir = createTempDir(); createDirs(tmpDir + storeDir); if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND, 0) == -1) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); - for (auto entry : std::filesystem::directory_iterator{"/"}) { + for (auto entry : fs::directory_iterator{"/"}) { checkInterrupt(); auto src = entry.path(); - Path dst = tmpDir / entry.path().filename(); + fs::path dst = tmpDir / entry.path().filename(); if (pathExists(dst)) continue; auto st = entry.symlink_status(); - if (std::filesystem::is_directory(st)) { + if (fs::is_directory(st)) { if (mkdir(dst.c_str(), 0700) == -1) throw SysError("creating directory '%s'", dst); if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, 0) == -1) throw SysError("mounting '%s' on '%s'", src, dst); - } else if (std::filesystem::is_symlink(st)) + } else if (fs::is_symlink(st)) createSymlink(readLink(src), dst); } @@ -205,9 +207,9 @@ void chrootHelper(int argc, char * * argv) if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND, 0) == -1) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); - writeFile("/proc/self/setgroups", "deny"); - writeFile("/proc/self/uid_map", fmt("%d %d %d", uid, uid, 1)); - writeFile("/proc/self/gid_map", fmt("%d %d %d", gid, gid, 1)); + writeFile(fs::path{"/proc/self/setgroups"}, "deny"); + writeFile(fs::path{"/proc/self/uid_map"}, fmt("%d %d %d", uid, uid, 1)); + writeFile(fs::path{"/proc/self/gid_map"}, fmt("%d %d %d", gid, gid, 1)); #if __linux__ if (system != "") diff --git a/src/nix/self-exe.cc b/src/nix/self-exe.cc index a260bafd5..81a117e60 100644 --- a/src/nix/self-exe.cc +++ b/src/nix/self-exe.cc @@ -5,7 +5,9 @@ namespace nix { -namespace fs = std::filesystem; +namespace fs { +using namespace std::filesystem; +} fs::path getNixBin(std::optional binaryNameOpt) { diff --git a/tests/unit/libfetchers/public-key.cc b/tests/unit/libfetchers/public-key.cc index 8a639da9f..80796bd0f 100644 --- a/tests/unit/libfetchers/public-key.cc +++ b/tests/unit/libfetchers/public-key.cc @@ -10,11 +10,11 @@ using nlohmann::json; class PublicKeyTest : public CharacterizationTest { - Path unitTestData = getUnitTestData() + "/public-key"; + std::filesystem::path unitTestData = getUnitTestData() / "public-key"; public: - Path goldenMaster(std::string_view testStem) const override { - return unitTestData + "/" + testStem; + std::filesystem::path goldenMaster(std::string_view testStem) const override { + return unitTestData / testStem; } }; diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh index a2d35d083..193b44970 100644 --- a/tests/unit/libstore-support/tests/nix_api_store.hh +++ b/tests/unit/libstore-support/tests/nix_api_store.hh @@ -10,7 +10,7 @@ #include #include -namespace fs = std::filesystem; +namespace fs { using namespace std::filesystem; } namespace nixC { class nix_api_store_test : public nix_api_util_context diff --git a/tests/unit/libstore-support/tests/protocol.hh b/tests/unit/libstore-support/tests/protocol.hh index 3c9e52c11..3f6799d1c 100644 --- a/tests/unit/libstore-support/tests/protocol.hh +++ b/tests/unit/libstore-support/tests/protocol.hh @@ -12,10 +12,10 @@ namespace nix { template class ProtoTest : public CharacterizationTest, public LibStoreTest { - Path unitTestData = getUnitTestData() + "/" + protocolDir; + std::filesystem::path unitTestData = getUnitTestData() / protocolDir; - Path goldenMaster(std::string_view testStem) const override { - return unitTestData + "/" + testStem + ".bin"; + std::filesystem::path goldenMaster(std::string_view testStem) const override { + return unitTestData / (std::string { testStem + ".bin" }); } }; diff --git a/tests/unit/libstore/derivation-advanced-attrs.cc b/tests/unit/libstore/derivation-advanced-attrs.cc index 26cf947a8..4d839ddab 100644 --- a/tests/unit/libstore/derivation-advanced-attrs.cc +++ b/tests/unit/libstore/derivation-advanced-attrs.cc @@ -16,12 +16,12 @@ using nlohmann::json; class DerivationAdvancedAttrsTest : public CharacterizationTest, public LibStoreTest { - Path unitTestData = getUnitTestData() + "/derivation"; + std::filesystem::path unitTestData = getUnitTestData() / "derivation"; public: - Path goldenMaster(std::string_view testStem) const override + std::filesystem::path goldenMaster(std::string_view testStem) const override { - return unitTestData + "/" + testStem; + return unitTestData / testStem; } }; diff --git a/tests/unit/libstore/derivation.cc b/tests/unit/libstore/derivation.cc index 71979f885..14652921a 100644 --- a/tests/unit/libstore/derivation.cc +++ b/tests/unit/libstore/derivation.cc @@ -13,11 +13,11 @@ using nlohmann::json; class DerivationTest : public CharacterizationTest, public LibStoreTest { - Path unitTestData = getUnitTestData() + "/derivation"; + std::filesystem::path unitTestData = getUnitTestData() / "derivation"; public: - Path goldenMaster(std::string_view testStem) const override { - return unitTestData + "/" + testStem; + std::filesystem::path goldenMaster(std::string_view testStem) const override { + return unitTestData / testStem; } /** diff --git a/tests/unit/libstore/machines.cc b/tests/unit/libstore/machines.cc index 2307f4d62..2d66e9534 100644 --- a/tests/unit/libstore/machines.cc +++ b/tests/unit/libstore/machines.cc @@ -13,6 +13,8 @@ using testing::Eq; using testing::Field; using testing::SizeIs; +namespace nix::fs { using namespace std::filesystem; } + using namespace nix; TEST(machines, getMachinesWithEmptyBuilders) { @@ -135,10 +137,10 @@ TEST(machines, getMachinesWithIncorrectFormat) { } TEST(machines, getMachinesWithCorrectFileReference) { - auto path = absPath(getUnitTestData() + "/machines/valid"); - ASSERT_TRUE(pathExists(path)); + auto path = fs::weakly_canonical(getUnitTestData() / "machines/valid"); + ASSERT_TRUE(fs::exists(path)); - auto actual = Machine::parseConfig({}, "@" + path); + auto actual = Machine::parseConfig({}, "@" + path.string()); ASSERT_THAT(actual, SizeIs(3)); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); @@ -146,20 +148,22 @@ TEST(machines, getMachinesWithCorrectFileReference) { } TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) { - auto path = "/dev/null"; - ASSERT_TRUE(pathExists(path)); + fs::path path = "/dev/null"; + ASSERT_TRUE(fs::exists(path)); - auto actual = Machine::parseConfig({}, std::string{"@"} + path); + auto actual = Machine::parseConfig({}, "@" + path.string()); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesWithIncorrectFileReference) { - auto actual = Machine::parseConfig({}, "@" + absPath("/not/a/file")); + auto path = fs::weakly_canonical("/not/a/file"); + ASSERT_TRUE(!fs::exists(path)); + auto actual = Machine::parseConfig({}, "@" + path.string()); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesWithCorrectFileReferenceToIncorrectFile) { EXPECT_THROW( - Machine::parseConfig({}, "@" + absPath(getUnitTestData() + "/machines/bad_format")), + Machine::parseConfig({}, "@" + fs::weakly_canonical(getUnitTestData() / "machines" / "bad_format").string()), FormatError); } diff --git a/tests/unit/libstore/nar-info.cc b/tests/unit/libstore/nar-info.cc index a6cb62de4..0d155743d 100644 --- a/tests/unit/libstore/nar-info.cc +++ b/tests/unit/libstore/nar-info.cc @@ -13,10 +13,10 @@ using nlohmann::json; class NarInfoTest : public CharacterizationTest, public LibStoreTest { - Path unitTestData = getUnitTestData() + "/nar-info"; + std::filesystem::path unitTestData = getUnitTestData() / "nar-info"; - Path goldenMaster(PathView testStem) const override { - return unitTestData + "/" + testStem + ".json"; + std::filesystem::path goldenMaster(PathView testStem) const override { + return unitTestData / (testStem + ".json"); } }; diff --git a/tests/unit/libstore/path-info.cc b/tests/unit/libstore/path-info.cc index 9e9c6303d..d6c4c2a7f 100644 --- a/tests/unit/libstore/path-info.cc +++ b/tests/unit/libstore/path-info.cc @@ -12,10 +12,10 @@ using nlohmann::json; class PathInfoTest : public CharacterizationTest, public LibStoreTest { - Path unitTestData = getUnitTestData() + "/path-info"; + std::filesystem::path unitTestData = getUnitTestData() / "path-info"; - Path goldenMaster(PathView testStem) const override { - return unitTestData + "/" + testStem + ".json"; + std::filesystem::path goldenMaster(PathView testStem) const override { + return unitTestData / (testStem + ".json"); } }; diff --git a/tests/unit/libstore/store-reference.cc b/tests/unit/libstore/store-reference.cc index 052cd7bed..d4c42f0fd 100644 --- a/tests/unit/libstore/store-reference.cc +++ b/tests/unit/libstore/store-reference.cc @@ -13,11 +13,11 @@ using nlohmann::json; class StoreReferenceTest : public CharacterizationTest, public LibStoreTest { - Path unitTestData = getUnitTestData() + "/store-reference"; + std::filesystem::path unitTestData = getUnitTestData() / "store-reference"; - Path goldenMaster(PathView testStem) const override + std::filesystem::path goldenMaster(PathView testStem) const override { - return unitTestData + "/" + testStem + ".txt"; + return unitTestData / (testStem + ".txt"); } }; diff --git a/tests/unit/libutil-support/tests/characterization.hh b/tests/unit/libutil-support/tests/characterization.hh index 19ba824ac..5e790e75b 100644 --- a/tests/unit/libutil-support/tests/characterization.hh +++ b/tests/unit/libutil-support/tests/characterization.hh @@ -13,7 +13,7 @@ namespace nix { * The path to the unit test data directory. See the contributing guide * in the manual for further details. */ -static inline Path getUnitTestData() { +static inline std::filesystem::path getUnitTestData() { return getEnv("_NIX_TEST_UNIT_DATA").value(); } @@ -36,7 +36,7 @@ protected: * While the "golden master" for this characterization test is * located. It should not be shared with any other test. */ - virtual Path goldenMaster(PathView testStem) const = 0; + virtual std::filesystem::path goldenMaster(PathView testStem) const = 0; public: /** @@ -77,7 +77,7 @@ public: if (testAccept()) { - createDirs(dirOf(file)); + std::filesystem::create_directories(file.parent_path()); writeFile2(file, got); GTEST_SKIP() << "Updating golden master " @@ -97,10 +97,10 @@ public: { writeTest( testStem, test, - [](const Path & f) -> std::string { + [](const std::filesystem::path & f) -> std::string { return readFile(f); }, - [](const Path & f, const std::string & c) { + [](const std::filesystem::path & f, const std::string & c) { return writeFile(f, c); }); } diff --git a/tests/unit/libutil/file-system.cc b/tests/unit/libutil/file-system.cc index cfddaae1c..7ef804f34 100644 --- a/tests/unit/libutil/file-system.cc +++ b/tests/unit/libutil/file-system.cc @@ -12,8 +12,8 @@ #include #ifdef _WIN32 -# define FS_SEP "\\" -# define FS_ROOT "C:" FS_SEP // Need a mounted one, C drive is likely +# define FS_SEP L"\\" +# define FS_ROOT L"C:" FS_SEP // Need a mounted one, C drive is likely #else # define FS_SEP "/" # define FS_ROOT FS_SEP @@ -23,6 +23,12 @@ # define PATH_MAX 4096 #endif +#ifdef _WIN32 +# define GET_CWD _wgetcwd +#else +# define GET_CWD getcwd +#endif + namespace nix { /* ----------- tests for file-system.hh -------------------------------------*/ @@ -33,34 +39,34 @@ namespace nix { TEST(absPath, doesntChangeRoot) { - auto p = absPath(FS_ROOT); + auto p = absPath(std::filesystem::path{FS_ROOT}); ASSERT_EQ(p, FS_ROOT); } TEST(absPath, turnsEmptyPathIntoCWD) { - char cwd[PATH_MAX + 1]; - auto p = absPath(""); + OsChar cwd[PATH_MAX + 1]; + auto p = absPath(std::filesystem::path{""}); - ASSERT_EQ(p, getcwd((char *) &cwd, PATH_MAX)); + ASSERT_EQ(p, GET_CWD((OsChar *) &cwd, PATH_MAX)); } TEST(absPath, usesOptionalBasePathWhenGiven) { - char _cwd[PATH_MAX + 1]; - char * cwd = getcwd((char *) &_cwd, PATH_MAX); + OsChar _cwd[PATH_MAX + 1]; + OsChar * cwd = GET_CWD((OsChar *) &_cwd, PATH_MAX); - auto p = absPath("", cwd); + auto p = absPath(std::filesystem::path{""}.string(), std::filesystem::path{cwd}.string()); - ASSERT_EQ(p, cwd); + ASSERT_EQ(p, std::filesystem::path{cwd}.string()); } TEST(absPath, isIdempotent) { - char _cwd[PATH_MAX + 1]; - char * cwd = getcwd((char *) &_cwd, PATH_MAX); - auto p1 = absPath(cwd); + OsChar _cwd[PATH_MAX + 1]; + OsChar * cwd = GET_CWD((OsChar *) &_cwd, PATH_MAX); + auto p1 = absPath(std::filesystem::path{cwd}); auto p2 = absPath(p1); ASSERT_EQ(p1, p2); @@ -68,8 +74,8 @@ TEST(absPath, isIdempotent) TEST(absPath, pathIsCanonicalised) { - auto path = FS_ROOT "some/path/with/trailing/dot/."; - auto p1 = absPath(path); + auto path = FS_ROOT OS_STR("some/path/with/trailing/dot/."); + auto p1 = absPath(std::filesystem::path{path}); auto p2 = absPath(p1); ASSERT_EQ(p1, FS_ROOT "some" FS_SEP "path" FS_SEP "with" FS_SEP "trailing" FS_SEP "dot"); @@ -82,26 +88,26 @@ TEST(absPath, pathIsCanonicalised) TEST(canonPath, removesTrailingSlashes) { - auto path = FS_ROOT "this/is/a/path//"; - auto p = canonPath(path); + std::filesystem::path path = FS_ROOT "this/is/a/path//"; + auto p = canonPath(path.string()); - ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); + ASSERT_EQ(p, std::filesystem::path{FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"}.string()); } TEST(canonPath, removesDots) { - auto path = FS_ROOT "this/./is/a/path/./"; - auto p = canonPath(path); + std::filesystem::path path = FS_ROOT "this/./is/a/path/./"; + auto p = canonPath(path.string()); - ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); + ASSERT_EQ(p, std::filesystem::path{FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"}.string()); } TEST(canonPath, removesDots2) { - auto path = FS_ROOT "this/a/../is/a////path/foo/.."; - auto p = canonPath(path); + std::filesystem::path path = FS_ROOT "this/a/../is/a////path/foo/.."; + auto p = canonPath(path.string()); - ASSERT_EQ(p, FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"); + ASSERT_EQ(p, std::filesystem::path{FS_ROOT "this" FS_SEP "is" FS_SEP "a" FS_SEP "path"}.string()); } TEST(canonPath, requiresAbsolutePath) @@ -243,7 +249,7 @@ TEST(isDirOrInDir, DISABLED_shouldWork) TEST(pathExists, rootExists) { - ASSERT_TRUE(pathExists(FS_ROOT)); + ASSERT_TRUE(pathExists(std::filesystem::path{FS_ROOT}.string())); } TEST(pathExists, cwdExists) diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index 3d01d9806..9232de5b9 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -11,12 +11,12 @@ using namespace git; class GitTest : public CharacterizationTest { - Path unitTestData = getUnitTestData() + "/git"; + std::filesystem::path unitTestData = getUnitTestData() / "git"; public: - Path goldenMaster(std::string_view testStem) const override { - return unitTestData + "/" + testStem; + std::filesystem::path goldenMaster(std::string_view testStem) const override { + return unitTestData / std::string(testStem); } /** diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index 83cec13ec..c39db8cda 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -48,12 +48,14 @@ subdir('build-utils-meson/diagnostics') sources = files( 'args.cc', 'canon-path.cc', + 'checked-arithmetic.cc', 'chunked-vector.cc', 'closure.cc', 'compression.cc', 'config.cc', 'executable-path.cc', 'file-content-address.cc', + 'file-system.cc', 'git.cc', 'hash.cc', 'hilite.cc', @@ -62,6 +64,7 @@ sources = files( 'lru-cache.cc', 'nix_api_util.cc', 'pool.cc', + 'position.cc', 'processes.cc', 'references.cc', 'spawn.cc', From 8bce63f30a773611f782ea3a53b9ab46180392c7 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 26 Aug 2024 16:44:19 -0400 Subject: [PATCH 861/910] More `std::filesystem` for `nix-collect-garbage` Co-Authored-By: siddhantCodes --- .../nix-collect-garbage.cc | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 457e5f3c9..20d5161df 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -11,6 +11,8 @@ #include #include +namespace nix::fs { using namespace std::filesystem; } + using namespace nix; std::string deleteOlderThan; @@ -21,23 +23,23 @@ bool dryRun = false; * Of course, this makes rollbacks to before this point in time * impossible. */ -void removeOldGenerations(std::filesystem::path dir) +void removeOldGenerations(fs::path dir) { if (access(dir.string().c_str(), R_OK) != 0) return; bool canWrite = access(dir.string().c_str(), W_OK) == 0; - for (auto & i : std::filesystem::directory_iterator{dir}) { + for (auto & i : fs::directory_iterator{dir}) { checkInterrupt(); auto path = i.path().string(); auto type = i.symlink_status().type(); - if (type == std::filesystem::file_type::symlink && canWrite) { + if (type == fs::file_type::symlink && canWrite) { std::string link; try { link = readLink(path); - } catch (std::filesystem::filesystem_error & e) { + } catch (fs::filesystem_error & e) { if (e.code() == std::errc::no_such_file_or_directory) continue; throw; } @@ -49,7 +51,7 @@ void removeOldGenerations(std::filesystem::path dir) } else deleteOldGenerations(path, dryRun); } - } else if (type == std::filesystem::file_type::directory) { + } else if (type == fs::file_type::directory) { removeOldGenerations(path); } } @@ -81,8 +83,11 @@ static int main_nix_collect_garbage(int argc, char * * argv) }); if (removeOld) { - std::set dirsToClean = { - profilesDir(), settings.nixStateDir + "/profiles", dirOf(getDefaultProfile())}; + std::set dirsToClean = { + profilesDir(), + fs::path{settings.nixStateDir} / "profiles", + fs::path{getDefaultProfile()}.parent_path(), + }; for (auto & dir : dirsToClean) removeOldGenerations(dir); } From c7ec33605e8c2dff0ebe40e4a1beba7a98530432 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 14 Aug 2024 16:19:35 -0400 Subject: [PATCH 862/910] Meson misc things Meson-ify a few things, scripts, completions, etc. Should make our Meson build complete except for docs. Co-Authored-By: Qyriad Co-Authored-By: eldritch horrors --- build-utils-meson/export/meson.build | 3 +++ flake.nix | 3 ++- meson.build | 4 +++- misc/bash/meson.build | 8 +++++++ misc/fish/meson.build | 8 +++++++ misc/meson.build | 5 +++++ misc/systemd/meson.build | 25 ++++++++++++++++++++++ misc/zsh/meson.build | 10 +++++++++ scripts/meson.build | 29 ++++++++++++++++++++++++++ src/libstore/meson.build | 22 ++++++++++++++----- src/libstore/meson.options | 4 ---- src/libutil/meson.build | 2 +- src/nix/meson.build | 14 ++++++++++++- src/nix/meson.options | 6 ++++++ src/nix/misc | 1 + src/nix/package.nix | 8 ++++++- src/nix/scripts | 1 + subprojects | 1 + tests/unit/libutil-support/meson.build | 1 + 19 files changed, 141 insertions(+), 14 deletions(-) create mode 100644 misc/bash/meson.build create mode 100644 misc/fish/meson.build create mode 100644 misc/meson.build create mode 100644 misc/systemd/meson.build create mode 100644 misc/zsh/meson.build create mode 100644 scripts/meson.build create mode 100644 src/nix/meson.options create mode 120000 src/nix/misc create mode 120000 src/nix/scripts create mode 120000 subprojects diff --git a/build-utils-meson/export/meson.build b/build-utils-meson/export/meson.build index 40f6dcd59..9f5950572 100644 --- a/build-utils-meson/export/meson.build +++ b/build-utils-meson/export/meson.build @@ -10,6 +10,7 @@ foreach dep : deps_public_subproject endforeach requires_public += deps_public +extra_pkg_config_variables = get_variable('extra_pkg_config_variables', {}) import('pkgconfig').generate( this_library, filebase : meson.project_name(), @@ -20,6 +21,7 @@ import('pkgconfig').generate( requires : requires_public, requires_private : requires_private, libraries_private : libraries_private, + variables : extra_pkg_config_variables, ) meson.override_dependency(meson.project_name(), declare_dependency( @@ -27,4 +29,5 @@ meson.override_dependency(meson.project_name(), declare_dependency( link_with : this_library, compile_args : ['-std=c++2a'], dependencies : deps_public_subproject + deps_public, + variables : extra_pkg_config_variables, )) diff --git a/flake.nix b/flake.nix index f5893d8f6..5ca9c1a45 100644 --- a/flake.nix +++ b/flake.nix @@ -295,6 +295,7 @@ devShells = let makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: let + buildCanExecuteHost = stdenv.buildPlatform.canExecute stdenv.hostPlatform; modular = devFlake.getSystem stdenv.buildPlatform.system; transformFlag = prefix: flag: assert builtins.isString flag; @@ -352,7 +353,7 @@ ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs ++ pkgs.nixComponents.nix-functional-tests.baseNativeBuildInputs ++ lib.optional - (!stdenv.buildPlatform.canExecute stdenv.hostPlatform + (!buildCanExecuteHost # Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479 && !(stdenv.hostPlatform.isWindows && stdenv.buildPlatform.isDarwin) && stdenv.hostPlatform.emulatorAvailable pkgs.buildPackages diff --git a/meson.build b/meson.build index 715a3862d..8dd44cc10 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,9 @@ project('nix-dev-shell', 'cpp', version : files('.version'), - subproject_dir : 'src', + default_options : [ + 'localstatedir=/nix/var', + ] ) # Internal Libraries diff --git a/misc/bash/meson.build b/misc/bash/meson.build new file mode 100644 index 000000000..8a97a02cb --- /dev/null +++ b/misc/bash/meson.build @@ -0,0 +1,8 @@ +configure_file( + input : 'completion.sh', + output : 'nix', + install : true, + install_dir : get_option('datadir') / 'bash-completion' / 'completions', + install_mode : 'rw-r--r--', + copy : true, +) diff --git a/misc/fish/meson.build b/misc/fish/meson.build new file mode 100644 index 000000000..e7e89b438 --- /dev/null +++ b/misc/fish/meson.build @@ -0,0 +1,8 @@ +configure_file( + input : 'completion.fish', + output : 'nix.fish', + install : true, + install_dir : get_option('datadir') / 'fish' / 'vendor_completions.d', + install_mode : 'rw-r--r--', + copy : true, +) diff --git a/misc/meson.build b/misc/meson.build new file mode 100644 index 000000000..a6d1f944b --- /dev/null +++ b/misc/meson.build @@ -0,0 +1,5 @@ +subdir('bash') +subdir('fish') +subdir('zsh') + +subdir('systemd') diff --git a/misc/systemd/meson.build b/misc/systemd/meson.build new file mode 100644 index 000000000..58b30f30b --- /dev/null +++ b/misc/systemd/meson.build @@ -0,0 +1,25 @@ +foreach config : [ 'nix-daemon.socket', 'nix-daemon.service' ] + configure_file( + input : config + '.in', + output : config, + install : true, + install_dir : get_option('prefix') / 'lib/systemd/system', + install_mode : 'rw-r--r--', + configuration : { + 'storedir' : store_dir, + 'localstatedir' : localstatedir, + 'bindir' : get_option('datadir'), + }, + ) +endforeach + +configure_file( + input : 'nix-daemon.conf.in', + output : 'nix-daemon.conf', + install : true, + install_dir : get_option('prefix') / 'lib/tmpfiles.d', + install_mode : 'rw-r--r--', + configuration : { + 'localstatedir' : localstatedir, + }, +) diff --git a/misc/zsh/meson.build b/misc/zsh/meson.build new file mode 100644 index 000000000..f3d0426e7 --- /dev/null +++ b/misc/zsh/meson.build @@ -0,0 +1,10 @@ +foreach script : [ [ 'completion.zsh', '_nix' ], [ 'run-help-nix' ] ] + configure_file( + input : script[0], + output : script.get(1, script[0]), + install : true, + install_dir : get_option('datadir') / 'zsh/site-functions', + install_mode : 'rw-r--r--', + copy : true, + ) +endforeach diff --git a/scripts/meson.build b/scripts/meson.build new file mode 100644 index 000000000..2671e6a13 --- /dev/null +++ b/scripts/meson.build @@ -0,0 +1,29 @@ +# configures `scripts/nix-profile.sh.in` (and copies the original to the build directory). +# this is only needed for tests, but running it unconditionally does not hurt enough to care. +configure_file( + input : 'nix-profile.sh.in', + output : 'nix-profile.sh', + configuration : { + 'localstatedir': localstatedir, + } +) + +# https://github.com/mesonbuild/meson/issues/860 +configure_file( + input : 'nix-profile.sh.in', + output : 'nix-profile.sh.in', + copy : true, +) + +foreach rc : [ '.sh', '.fish', '-daemon.sh', '-daemon.fish' ] + configure_file( + input : 'nix-profile' + rc + '.in', + output : 'nix' + rc, + install : true, + install_dir : get_option('profile-dir'), + install_mode : 'rw-r--r--', + configuration : { + 'localstatedir': localstatedir, + }, + ) +endforeach diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 8e30845e1..2adc9b3e4 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -7,6 +7,7 @@ project('nix-store', 'cpp', 'debug=true', 'optimization=2', 'errorlogs=true', # Please print logs for tests that fail + 'localstatedir=/nix/var', ], meson_version : '>= 1.1', license : 'LGPL-2.1-or-later', @@ -324,7 +325,7 @@ fs = import('fs') prefix = get_option('prefix') # For each of these paths, assume that it is relative to the prefix unless -# it is already an absolute path (which is the default for store-dir, state-dir, and log-dir). +# it is already an absolute path (which is the default for store-dir, localstatedir, and log-dir). path_opts = [ # Meson built-ins. 'datadir', @@ -334,13 +335,13 @@ path_opts = [ 'libexecdir', # Homecooked Nix directories. 'store-dir', - 'state-dir', + 'localstatedir', 'log-dir', ] # For your grepping pleasure, this loop sets the following variables that aren't mentioned # literally above: # store_dir -# state_dir +# localstatedir # log_dir # profile_dir foreach optname : path_opts @@ -364,12 +365,12 @@ lsof = find_program('lsof', required : false) # Aside from prefix itself, each of these was made into an absolute path # by joining it with prefix, unless it was already an absolute path -# (which is the default for store-dir, state-dir, and log-dir). +# (which is the default for store-dir, localstatedir, and log-dir). cpp_str_defines = { 'NIX_PREFIX': prefix, 'NIX_STORE_DIR': store_dir, 'NIX_DATA_DIR': datadir, - 'NIX_STATE_DIR': state_dir / 'nix', + 'NIX_STATE_DIR': localstatedir / 'nix', 'NIX_LOG_DIR': log_dir, 'NIX_CONF_DIR': sysconfdir / 'nix', 'NIX_MAN_DIR': mandir, @@ -421,4 +422,15 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] +extra_pkg_config_variables = { + 'storedir' : get_option('store-dir'), +} + +# Working around https://github.com/mesonbuild/meson/issues/13584 +if host_machine.system() != 'macos' + extra_pkg_config_variables += { + 'localstatedir' : get_option('localstatedir'), + } +endif + subdir('build-utils-meson/export') diff --git a/src/libstore/meson.options b/src/libstore/meson.options index 723a8e020..ebad24dc4 100644 --- a/src/libstore/meson.options +++ b/src/libstore/meson.options @@ -16,10 +16,6 @@ option('store-dir', type : 'string', value : '/nix/store', description : 'path of the Nix store', ) -option('state-dir', type : 'string', value : '/nix/var', - description : 'path to store state in for Nix', -) - option('log-dir', type : 'string', value : '/nix/var/log/nix', description : 'path to store logs in for Nix', ) diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 72ff461ca..797dcae6d 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -129,7 +129,6 @@ sources = files( 'english.cc', 'environment-variables.cc', 'error.cc', - 'exec.hh', 'executable-path.cc', 'exit.cc', 'experimental-features.cc', @@ -186,6 +185,7 @@ headers = [config_h] + files( 'english.hh', 'environment-variables.hh', 'error.hh', + 'exec.hh', 'executable-path.hh', 'exit.hh', 'experimental-features.hh', diff --git a/src/nix/meson.build b/src/nix/meson.build index 798c98e33..6edb768e3 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -7,6 +7,7 @@ project('nix', 'cpp', 'debug=true', 'optimization=2', 'errorlogs=true', # Please print logs for tests that fail + 'localstatedir=/nix/var', ], meson_version : '>= 1.1', license : 'LGPL-2.1-or-later', @@ -16,9 +17,11 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') +nix_store = dependency('nix-store') + deps_private_maybe_subproject = [ dependency('nix-util'), - dependency('nix-store'), + nix_store, dependency('nix-expr'), dependency('nix-flake'), dependency('nix-fetchers'), @@ -244,3 +247,12 @@ custom_target( ) # TODO(Ericson3214): Dosen't yet work #meson.override_find_program(linkname, t) + +localstatedir = nix_store.get_variable( + 'localstatedir', + default_value : get_option('localstatedir'), +) +assert(localstatedir == get_option('localstatedir')) +store_dir = nix_store.get_variable('storedir') +subdir('scripts') +subdir('misc') diff --git a/src/nix/meson.options b/src/nix/meson.options new file mode 100644 index 000000000..8430dd669 --- /dev/null +++ b/src/nix/meson.options @@ -0,0 +1,6 @@ +# vim: filetype=meson + +# A relative path means it gets appended to prefix. +option('profile-dir', type : 'string', value : 'etc/profile.d', + description : 'the path to install shell profile files', +) diff --git a/src/nix/misc b/src/nix/misc new file mode 120000 index 000000000..2825552c9 --- /dev/null +++ b/src/nix/misc @@ -0,0 +1 @@ +../../misc \ No newline at end of file diff --git a/src/nix/package.nix b/src/nix/package.nix index ef7265458..3e19c6dca 100644 --- a/src/nix/package.nix +++ b/src/nix/package.nix @@ -36,9 +36,10 @@ mkMesonDerivation (finalAttrs: { ../../.version ./.version ./meson.build - # ./meson.options + ./meson.options # Symbolic links to other dirs + ## exes ./build-remote ./doc ./nix-build @@ -48,6 +49,11 @@ mkMesonDerivation (finalAttrs: { ./nix-env ./nix-instantiate ./nix-store + ## dirs + ./scripts + ../../scripts + ./misc + ../../misc # Doc nix files for --help ../../doc/manual/generate-manpage.nix diff --git a/src/nix/scripts b/src/nix/scripts new file mode 120000 index 000000000..c5efc95eb --- /dev/null +++ b/src/nix/scripts @@ -0,0 +1 @@ +../../scripts \ No newline at end of file diff --git a/subprojects b/subprojects new file mode 120000 index 000000000..e8310385c --- /dev/null +++ b/subprojects @@ -0,0 +1 @@ +src \ No newline at end of file diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build index 6be4972c6..42b49a6a0 100644 --- a/tests/unit/libutil-support/meson.build +++ b/tests/unit/libutil-support/meson.build @@ -46,6 +46,7 @@ include_dirs = [include_directories('.')] headers = files( 'tests/characterization.hh', + 'tests/gtest-with-params.hh', 'tests/hash.hh', 'tests/nix_api_util.hh', 'tests/string_callback.hh', From 8e63dc4455b80c096c74a97f07cfc15a1264502d Mon Sep 17 00:00:00 2001 From: Seggy Umboh Date: Tue, 27 Aug 2024 17:23:27 -0700 Subject: [PATCH 863/910] Fix diskutil deleteVolume command in Uninstall guide (#11371) Co-authored-by: Valentin Gagarin --- doc/manual/src/installation/uninstall.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/installation/uninstall.md b/doc/manual/src/installation/uninstall.md index 590327fea..91fb90bc0 100644 --- a/doc/manual/src/installation/uninstall.md +++ b/doc/manual/src/installation/uninstall.md @@ -133,7 +133,9 @@ which you may remove. diskutil list ``` - If you _do_ find a "Nix Store" volume, delete it by running `diskutil deleteVolume` with the store volume's `diskXsY` identifier. + If you _do_ find a "Nix Store" volume, delete it by running `diskutil apfs deleteVolume` with the store volume's `diskXsY` identifier. + + If you get an error that the volume is in use by the kernel, reboot and immediately delete the volume before starting any other process. > **Note** > From cb4b9be4587efcee0c642eeb0aafab006b314d95 Mon Sep 17 00:00:00 2001 From: Jan Hrcek <2716069+jhrcek@users.noreply.github.com> Date: Wed, 28 Aug 2024 09:01:56 +0200 Subject: [PATCH 864/910] Fix few duplicate word occurrences (#11381) --- doc/manual/src/development/json-guideline.md | 2 +- doc/manual/src/protocols/store-path.md | 2 +- doc/manual/src/release-notes/rl-2.23.md | 2 +- package.nix | 2 +- src/libstore/local-overlay-store.md | 4 ++-- src/nix/flake.md | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/manual/src/development/json-guideline.md b/doc/manual/src/development/json-guideline.md index b4bc92af9..309b4b3a0 100644 --- a/doc/manual/src/development/json-guideline.md +++ b/doc/manual/src/development/json-guideline.md @@ -90,7 +90,7 @@ This representation is extensible and preserves the ordering: ## Self-describing values -As described in the previous section, it's crucial that schemas can be extended with with new fields without breaking compatibility. +As described in the previous section, it's crucial that schemas can be extended with new fields without breaking compatibility. However, that should *not* mean we use the presence/absence of fields to indicate optional information *within* a version of the schema. Instead, always include the field, and use `null` to indicate the "nothing" case. diff --git a/doc/manual/src/protocols/store-path.md b/doc/manual/src/protocols/store-path.md index 52352d358..8ec6f8201 100644 --- a/doc/manual/src/protocols/store-path.md +++ b/doc/manual/src/protocols/store-path.md @@ -82,7 +82,7 @@ where - if `type` = `"source:" ...`: - the the hash of the [Nix Archive (NAR)] serialization of the [file system object](@docroot@/store/file-system-object.md) of the store object. + the hash of the [Nix Archive (NAR)] serialization of the [file system object](@docroot@/store/file-system-object.md) of the store object. - if `type` = `"output:" id`: diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index ac842fdc0..76e9534a8 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -85,7 +85,7 @@ - Store object info JSON format now uses `null` rather than omitting fields [#9995](https://github.com/NixOS/nix/pull/9995) The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. - For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. + For example, `"ca": null` is used to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. This makes records of this sort more self-describing, and easier to consume programmatically. We will follow this design principle going forward; diff --git a/package.nix b/package.nix index d41748b7c..5c8d9f9b6 100644 --- a/package.nix +++ b/package.nix @@ -60,7 +60,7 @@ # Run the functional tests as part of the build. , doInstallCheck ? test-client != null || __forDefaults.canRunInstalled -# Check test coverage of Nix. Probably want to use with with at least +# Check test coverage of Nix. Probably want to use with at least # one of `doCHeck` or `doInstallCheck` enabled. , withCoverageChecks ? false diff --git a/src/libstore/local-overlay-store.md b/src/libstore/local-overlay-store.md index 1e1a3d26c..baa395895 100644 --- a/src/libstore/local-overlay-store.md +++ b/src/libstore/local-overlay-store.md @@ -77,13 +77,13 @@ The parts of a local overlay store are as follows: The lower store directory and upper layer directory are combined via OverlayFS to create this directory. Nix doesn't do this itself, because it typically wouldn't have the permissions to do so, so it is the responsibility of the user to set this up first. - Nix can, however, optionally check that that the OverlayFS mount settings appear as expected, matching Nix's own settings. + Nix can, however, optionally check that the OverlayFS mount settings appear as expected, matching Nix's own settings. - **Upper SQLite database**: > Not directly specified. > The location of the database instead depends on the [`state`](#store-experimental-local-overlay-store-state) setting. - > It is is always `${state}/db`. + > It is always `${state}/db`. This contains the metadata of all of the upper layer [store objects][store object] (everything beyond their file system objects), and also duplicate copies of some lower layer store object's metadta. The duplication is so the metadata for the [closure](@docroot@/glossary.md#gloss-closure) of upper layer [store objects][store object] can be found entirely within the upper layer. diff --git a/src/nix/flake.md b/src/nix/flake.md index 46d5a3867..d8ee4ac71 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -120,7 +120,7 @@ Contrary to URL-like references, path-like flake references can contain arbitrar ### Examples -* `.`: The flake to which the current directory belongs to. +* `.`: The flake to which the current directory belongs. * `/home/alice/src/patchelf`: A flake in some other directory. * `./../sub directory/with Ûñî©ôδ€`: A flake in another relative directory that has Unicode characters in its name. From 9b1cefe27e542d890aa346996a03dfecd9793dfe Mon Sep 17 00:00:00 2001 From: Bryan Honof Date: Wed, 28 Aug 2024 18:48:18 +0200 Subject: [PATCH 865/910] Update src/libutil/url.cc Co-authored-by: Robert Hensing --- src/libutil/url.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/url.cc b/src/libutil/url.cc index 78c832440..8ef1857bb 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -81,7 +81,7 @@ std::map decodeQuery(const std::string & query) auto e = s.find('='); if (e == std::string::npos) { - warn("invalid URI query '%s', did you forget an equals sign `=`?", s); + warn("dubious URI query '%s' is missing equal sign '%s'", s, "="); continue; } From 9cc550d65252d3ad822cc12496ef71482c47ff7e Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Sat, 31 Aug 2024 15:59:18 +0200 Subject: [PATCH 866/910] Don't refer to public keys as secret keys in error This constructor is used for public keys as well. --- src/libutil/signature/local-keys.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/signature/local-keys.cc b/src/libutil/signature/local-keys.cc index 858b036f5..00c4543f2 100644 --- a/src/libutil/signature/local-keys.cc +++ b/src/libutil/signature/local-keys.cc @@ -22,7 +22,7 @@ Key::Key(std::string_view s) key = ss.payload; if (name == "" || key == "") - throw Error("secret key is corrupt"); + throw Error("key is corrupt"); key = base64Decode(key); } From a33cb8af5693af56dd69073dc5dddb4c6900ad7a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Sep 2024 17:28:11 +0200 Subject: [PATCH 867/910] Respect max-substitution-jobs again This broke in #11005. Any number of PathSubstitutionGoals would be woken up by a single build slot becoming available. If there are a lot of substitution goals active, this could lead to us running out of file descriptors (especially on macOS where the default limit is 256). --- src/libstore/build/substitution-goal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index 0152f1808..a26eea820 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -183,7 +183,7 @@ Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref sub, /* Make sure that we are allowed to start a substitution. Note that even if maxSubstitutionJobs == 0, we still allow a substituter to run. This prevents infinite waiting. */ - if (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) { + while (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) { worker.waitForBuildSlot(shared_from_this()); co_await Suspend{}; } From b7acd1c4145c7316085f2a12bfa26ef742ac6146 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Sep 2024 17:28:55 +0200 Subject: [PATCH 868/910] "unsigned" -> size_t Slight cleanup. --- src/libstore/build/worker.cc | 4 ++-- src/libstore/build/worker.hh | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index ab0ba67b5..dbe86f43f 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -184,13 +184,13 @@ void Worker::wakeUp(GoalPtr goal) } -unsigned Worker::getNrLocalBuilds() +size_t Worker::getNrLocalBuilds() { return nrLocalBuilds; } -unsigned Worker::getNrSubstitutions() +size_t Worker::getNrSubstitutions() { return nrSubstitutions; } diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index 33a7bf015..e083dbea6 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -92,12 +92,12 @@ private: * Number of build slots occupied. This includes local builds but does not * include substitutions or remote builds via the build hook. */ - unsigned int nrLocalBuilds; + size_t nrLocalBuilds; /** * Number of substitution slots occupied. */ - unsigned int nrSubstitutions; + size_t nrSubstitutions; /** * Maps used to prevent multiple instantiations of a goal for the @@ -235,12 +235,12 @@ public: * Return the number of local build processes currently running (but not * remote builds via the build hook). */ - unsigned int getNrLocalBuilds(); + size_t getNrLocalBuilds(); /** * Return the number of substitution processes currently running. */ - unsigned int getNrSubstitutions(); + size_t getNrSubstitutions(); /** * Registers a running child process. `inBuildSlot` means that From 13100eaa4f5b8e1934e312ac9300bd0e84d450c2 Mon Sep 17 00:00:00 2001 From: Sandro Date: Mon, 2 Sep 2024 23:25:44 +0200 Subject: [PATCH 869/910] Fix link anchor --- src/libstore/globals.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index e5e7024cb..be922c9f7 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1204,7 +1204,7 @@ public: If the user is trusted (see `trusted-users` option), when building a fixed-output derivation, environment variables set in this option - will be passed to the builder if they are listed in [`impureEnvVars`](@docroot@/language/advanced-attributes.md##adv-attr-impureEnvVars). + will be passed to the builder if they are listed in [`impureEnvVars`](@docroot@/language/advanced-attributes.md#adv-attr-impureEnvVars). This option is useful for, e.g., setting `https_proxy` for fixed-output derivations and in a multi-user Nix installation, or From 4decd6f8b3bf11636f6710a3d8db396ba30fefe2 Mon Sep 17 00:00:00 2001 From: Jeremy Kerfs Date: Tue, 3 Sep 2024 03:36:00 -0500 Subject: [PATCH 870/910] add removal of ~root/.cache/nix to uninstall instructions (#11407) --- doc/manual/src/installation/uninstall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/installation/uninstall.md b/doc/manual/src/installation/uninstall.md index 91fb90bc0..bb21af24e 100644 --- a/doc/manual/src/installation/uninstall.md +++ b/doc/manual/src/installation/uninstall.md @@ -19,7 +19,7 @@ If you are on Linux with systemd: Remove files created by Nix: ```console -sudo rm -rf /etc/nix /etc/profile.d/nix.sh /etc/tmpfiles.d/nix-daemon.conf /nix ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile +sudo rm -rf /etc/nix /etc/profile.d/nix.sh /etc/tmpfiles.d/nix-daemon.conf /nix ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix ``` Remove build users and their group: From 02bb633a582b05991e77b65e866a06ef21b2482a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 3 Sep 2024 16:49:57 +0200 Subject: [PATCH 871/910] maintainers/upload-release.pl: Delete temporary directories when we're done --- maintainers/upload-release.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 731988568..8a470c7cc 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -112,7 +112,7 @@ sub copyManual { system("xz -d < '$manualNar' | nix-store --restore $tmpDir/manual.tmp") == 0 or die "unable to unpack $manualNar\n"; rename("$tmpDir/manual.tmp/share/doc/nix/manual", "$tmpDir/manual") or die; - system("rm -rf '$tmpDir/manual.tmp'") == 0 or die; + File::Path::remove_tree("$tmpDir/manual.tmp", {safe => 1}); } system("aws s3 sync '$tmpDir/manual' s3://$releasesBucketName/$releaseDir/manual") == 0 @@ -281,3 +281,6 @@ system("git remote update origin") == 0 or die; system("git tag --force --sign $version $nixRev -m 'Tagging release $version'") == 0 or die; system("git push --tags") == 0 or die; system("git push --force-with-lease origin $nixRev:refs/heads/latest-release") == 0 or die if $isLatest; + +File::Path::remove_tree($narCache, {safe => 1}); +File::Path::remove_tree($tmpDir, {safe => 1}); From 46b31880458f0cbea2a45a42734d1ac76c9ea88f Mon Sep 17 00:00:00 2001 From: Parker Hoyes Date: Mon, 26 Aug 2024 13:50:22 +0000 Subject: [PATCH 872/910] Move daemon process into sub-cgroup The daemon process is now moved into a new sub-cgroup called nix-daemon when the daemon starts. This is necessary to abide by the no-processes-in-inner-nodes rule, because the service cgroup becomes an inner node when the child cgroups for the build are created (see LocalDerivationGoal::startBuilder()). See #9675 --- .../unix/build/local-derivation-goal.cc | 21 ++++++------ src/libutil/linux/cgroup.cc | 32 +++++++++++++++++++ src/libutil/linux/cgroup.hh | 9 ++++++ src/nix/unix/daemon.cc | 25 +++++++++++++++ 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 01a133766..d55278a52 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -444,25 +444,22 @@ void LocalDerivationGoal::startBuilder() #if __linux__ experimentalFeatureSettings.require(Xp::Cgroups); + /* If we're running from the daemon, then this will return the + root cgroup of the service. Otherwise, it will return the + current cgroup. */ + auto rootCgroup = getRootCgroup(); auto cgroupFS = getCgroupFS(); if (!cgroupFS) throw Error("cannot determine the cgroups file system"); - - auto ourCgroups = getCgroups("/proc/self/cgroup"); - auto ourCgroup = ourCgroups[""]; - if (ourCgroup == "") - throw Error("cannot determine cgroup name from /proc/self/cgroup"); - - auto ourCgroupPath = canonPath(*cgroupFS + "/" + ourCgroup); - - if (!pathExists(ourCgroupPath)) - throw Error("expected cgroup directory '%s'", ourCgroupPath); + auto rootCgroupPath = canonPath(*cgroupFS + "/" + rootCgroup); + if (!pathExists(rootCgroupPath)) + throw Error("expected cgroup directory '%s'", rootCgroupPath); static std::atomic counter{0}; cgroup = buildUser - ? fmt("%s/nix-build-uid-%d", ourCgroupPath, buildUser->getUID()) - : fmt("%s/nix-build-pid-%d-%d", ourCgroupPath, getpid(), counter++); + ? fmt("%s/nix-build-uid-%d", rootCgroupPath, buildUser->getUID()) + : fmt("%s/nix-build-pid-%d-%d", rootCgroupPath, getpid(), counter++); debug("using cgroup '%s'", *cgroup); diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 140ff4566..706f0f159 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -144,4 +145,35 @@ CgroupStats destroyCgroup(const Path & cgroup) return destroyCgroup(cgroup, true); } +std::string getCurrentCgroup() +{ + auto cgroupFS = getCgroupFS(); + if (!cgroupFS) + throw Error("cannot determine the cgroups file system"); + + auto ourCgroups = getCgroups("/proc/self/cgroup"); + auto ourCgroup = ourCgroups[""]; + if (ourCgroup == "") + throw Error("cannot determine cgroup name from /proc/self/cgroup"); + return ourCgroup; +} + +static std::optional rootCgroup; +static std::mutex rootCgroupMutex; + +std::string getRootCgroup() +{ + { + std::lock_guard guard(rootCgroupMutex); + if (rootCgroup) + return *rootCgroup; + } + auto current = getCurrentCgroup(); + std::lock_guard guard(rootCgroupMutex); + if (rootCgroup) + return *rootCgroup; + rootCgroup = current; + return current; +} + } diff --git a/src/libutil/linux/cgroup.hh b/src/libutil/linux/cgroup.hh index 783a0ab87..87d135ba6 100644 --- a/src/libutil/linux/cgroup.hh +++ b/src/libutil/linux/cgroup.hh @@ -25,4 +25,13 @@ struct CgroupStats */ CgroupStats destroyCgroup(const Path & cgroup); +std::string getCurrentCgroup(); + +/** + * Get the cgroup that should be used as the parent when creating new + * sub-cgroups. The first time this is called, the current cgroup will be + * returned, and then all subsequent calls will return the original cgroup. + */ +std::string getRootCgroup(); + } diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index 66d8dbcf0..746963a01 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -33,6 +33,10 @@ #include #include +#if __linux__ +#include "cgroup.hh" +#endif + #if __APPLE__ || __FreeBSD__ #include #endif @@ -312,6 +316,27 @@ static void daemonLoop(std::optional forceTrustClientOpt) // Get rid of children automatically; don't let them become zombies. setSigChldAction(true); + #if __linux__ + if (settings.useCgroups) { + experimentalFeatureSettings.require(Xp::Cgroups); + + // This also sets the root cgroup to the current one. + auto rootCgroup = getRootCgroup(); + auto cgroupFS = getCgroupFS(); + if (!cgroupFS) + throw Error("cannot determine the cgroups file system"); + auto rootCgroupPath = canonPath(*cgroupFS + "/" + rootCgroup); + if (!pathExists(rootCgroupPath)) + throw Error("expected cgroup directory '%s'", rootCgroupPath); + auto daemonCgroupPath = rootCgroupPath + "/nix-daemon"; + // Create new sub-cgroup for the daemon. + if (mkdir(daemonCgroupPath.c_str(), 0755) != 0 && errno != EEXIST) + throw SysError("creating cgroup '%s'", daemonCgroupPath); + // Move daemon into the new cgroup. + writeFile(daemonCgroupPath + "/cgroup.procs", fmt("%d", getpid())); + } + #endif + // Loop accepting connections. while (1) { From 4c88deef38678154f3043ca2e0f4bd84a3c6cbc5 Mon Sep 17 00:00:00 2001 From: Parker Hoyes Date: Tue, 3 Sep 2024 17:27:56 +0000 Subject: [PATCH 873/910] Add tests for daemon with cgroups --- tests/nixos/cgroups/default.nix | 40 +++++++++++++++++++++++++++++++++ tests/nixos/cgroups/hang.nix | 10 +++++++++ tests/nixos/default.nix | 2 ++ 3 files changed, 52 insertions(+) create mode 100644 tests/nixos/cgroups/default.nix create mode 100644 tests/nixos/cgroups/hang.nix diff --git a/tests/nixos/cgroups/default.nix b/tests/nixos/cgroups/default.nix new file mode 100644 index 000000000..b8febbf4b --- /dev/null +++ b/tests/nixos/cgroups/default.nix @@ -0,0 +1,40 @@ +{ nixpkgs, ... }: + +{ + name = "cgroups"; + + nodes = + { + host = + { config, pkgs, ... }: + { virtualisation.additionalPaths = [ pkgs.stdenvNoCC ]; + nix.extraOptions = + '' + extra-experimental-features = nix-command auto-allocate-uids cgroups + extra-system-features = uid-range + ''; + nix.settings.use-cgroups = true; + nix.nixPath = [ "nixpkgs=${nixpkgs}" ]; + }; + }; + + testScript = { nodes }: '' + start_all() + + host.wait_for_unit("multi-user.target") + + # Start build in background + host.execute("NIX_REMOTE=daemon nix build --auto-allocate-uids --file ${./hang.nix} >&2 &") + service = "/sys/fs/cgroup/system.slice/nix-daemon.service" + + # Wait for cgroups to be created + host.succeed(f"until [ -e {service}/nix-daemon ]; do sleep 1; done", timeout=30) + host.succeed(f"until [ -e {service}/nix-build-uid-* ]; do sleep 1; done", timeout=30) + + # Check that there aren't processes where there shouldn't be, and that there are where there should be + host.succeed(f'[ -z "$(cat {service}/cgroup.procs)" ]') + host.succeed(f'[ -n "$(cat {service}/nix-daemon/cgroup.procs)" ]') + host.succeed(f'[ -n "$(cat {service}/nix-build-uid-*/cgroup.procs)" ]') + ''; + +} diff --git a/tests/nixos/cgroups/hang.nix b/tests/nixos/cgroups/hang.nix new file mode 100644 index 000000000..cefe2d031 --- /dev/null +++ b/tests/nixos/cgroups/hang.nix @@ -0,0 +1,10 @@ +{ }: + +with import {}; + +runCommand "hang" + { requiredSystemFeatures = "uid-range"; + } + '' + sleep infinity + '' diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 40d29b371..62fc6b10f 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -157,4 +157,6 @@ in s3-binary-cache-store = runNixOSTestFor "x86_64-linux" ./s3-binary-cache-store.nix; fsync = runNixOSTestFor "x86_64-linux" ./fsync.nix; + + cgroups = runNixOSTestFor "x86_64-linux" ./cgroups; } From 62a99049c40be4971220f4115f00943f5e299b07 Mon Sep 17 00:00:00 2001 From: Parker Hoyes Date: Tue, 3 Sep 2024 19:07:18 +0000 Subject: [PATCH 874/910] Enable cgroups delegation for systemd --- misc/systemd/nix-daemon.service.in | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/systemd/nix-daemon.service.in b/misc/systemd/nix-daemon.service.in index 45fbea02c..b3055cfe2 100644 --- a/misc/systemd/nix-daemon.service.in +++ b/misc/systemd/nix-daemon.service.in @@ -11,6 +11,7 @@ ExecStart=@@bindir@/nix-daemon nix-daemon --daemon KillMode=process LimitNOFILE=1048576 TasksMax=1048576 +Delegate=yes [Install] WantedBy=multi-user.target From 9e79061bac9d60cc2704964dc6fcc736c91173b2 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Tue, 3 Sep 2024 13:27:11 -0700 Subject: [PATCH 875/910] fixup: use the real bindir for systemd unit's bindir Prior to this commit, the unit contained this line: ExecStart=@share/nix-daemon nix-daemon --daemon which caused systemd to complain: Failed to restart nix-daemon.service: Unit nix-daemon.service has a bad unit file setting. See system logs and 'systemctl status nix-daemon.service' for details. and had this in the unit output: Sep 03 13:34:59 scadrial systemd[1]: /etc/systemd/system/nix-daemon.service:10: Neither a valid executable name nor an absolute path: share/nix-daemon Sep 03 13:34:59 scadrial systemd[1]: nix-daemon.service: Unit configuration has fatal error, unit will not be started. (Notice how it's trying to execute `share/nix-daemon`, which is unlikely to exist.) Now with this commit, the path to the daemon binary is properly set: ExecStart=@/nix/store/lcbx6d8gzznf3z3c8lsv9jy3j6c67x6r-nix-2.25.0pre20240903_dirty/bin/nix-daemon nix-daemon --daemon --- misc/systemd/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/systemd/meson.build b/misc/systemd/meson.build index 58b30f30b..6ccb6a873 100644 --- a/misc/systemd/meson.build +++ b/misc/systemd/meson.build @@ -8,7 +8,7 @@ foreach config : [ 'nix-daemon.socket', 'nix-daemon.service' ] configuration : { 'storedir' : store_dir, 'localstatedir' : localstatedir, - 'bindir' : get_option('datadir'), + 'bindir' : bindir, }, ) endforeach From 46f65058655550bff87cf547954f7ca5622d1b2d Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Wed, 4 Sep 2024 10:14:51 -0400 Subject: [PATCH 876/910] Pull fut.get() out of the lock This is https://gerrit.lix.systems/c/lix/+/1462 by @jade_ see: https://git.lix.systems/lix-project/lix/issues/366 see: https://gerrit.lix.systems/c/lix/+/1462 --- src/libstore/store-api.cc | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 8eef340cc..fc03133f8 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -822,14 +822,25 @@ StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag m auto doQuery = [&](const StorePath & path) { checkInterrupt(); queryPathInfo(path, {[path, &state_, &wakeup](std::future> fut) { - auto state(state_.lock()); + bool exists = false; + std::exception_ptr newExc{}; + try { auto info = fut.get(); - state->valid.insert(path); + exists = true; } catch (InvalidPath &) { } catch (...) { - state->exc = std::current_exception(); + newExc = std::current_exception(); } + + auto state(state_.lock()); + + if (exists) + state->valid.insert(path); + + if (newExc != nullptr) + state->exc = newExc; + assert(state->left); if (!--state->left) wakeup.notify_one(); From 8152c5c828e845bf39334318c65297cf3dfbf518 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Wed, 4 Sep 2024 12:55:32 -0400 Subject: [PATCH 877/910] Remote nullptr Co-authored-by: Eelco Dolstra --- src/libstore/store-api.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index fc03133f8..fc8d51c39 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -838,7 +838,7 @@ StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag m if (exists) state->valid.insert(path); - if (newExc != nullptr) + if (newExc) state->exc = newExc; assert(state->left); From 03484641a1c712b6d10f636e7dbb5280c1668c4b Mon Sep 17 00:00:00 2001 From: Parker Hoyes Date: Wed, 4 Sep 2024 18:11:16 +0000 Subject: [PATCH 878/910] Simplify getRootCgroup() Static local initializers are atomic in C++. --- src/libutil/linux/cgroup.cc | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 706f0f159..ad3e8a017 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -158,22 +157,10 @@ std::string getCurrentCgroup() return ourCgroup; } -static std::optional rootCgroup; -static std::mutex rootCgroupMutex; - std::string getRootCgroup() { - { - std::lock_guard guard(rootCgroupMutex); - if (rootCgroup) - return *rootCgroup; - } - auto current = getCurrentCgroup(); - std::lock_guard guard(rootCgroupMutex); - if (rootCgroup) - return *rootCgroup; - rootCgroup = current; - return current; + static std::string rootCgroup = getCurrentCgroup(); + return rootCgroup; } } From bd6ae2f3b929e8bf280692f3cae36bce7f688b4f Mon Sep 17 00:00:00 2001 From: Parker Hoyes Date: Wed, 4 Sep 2024 19:10:31 +0000 Subject: [PATCH 879/910] Use getCurrentCgroup() in getMaxCPU() --- src/libutil/current-process.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index 0bc46d746..ed1c1ca6c 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -32,11 +32,7 @@ unsigned int getMaxCPU() auto cgroupFS = getCgroupFS(); if (!cgroupFS) return 0; - auto cgroups = getCgroups("/proc/self/cgroup"); - auto cgroup = cgroups[""]; - if (cgroup == "") return 0; - - auto cpuFile = *cgroupFS + "/" + cgroup + "/cpu.max"; + auto cpuFile = *cgroupFS + "/" + getCurrentCgroup() + "/cpu.max"; auto cpuMax = readFile(cpuFile); auto cpuMaxParts = tokenizeString>(cpuMax, " \n"); From 495d32e1b8e5d5143f048d1be755a96bea822b19 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 4 Sep 2024 21:43:59 +0200 Subject: [PATCH 880/910] NAR parser: Fix check for duplicate / incorrectly sorted entries "prevName" was always empty because it was declared in the wrong scope. --- src/libutil/archive.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 9ed65be6a..e4a6a3181 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -214,11 +214,13 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath else if (t == "directory") { sink.createDirectory(path); + std::string prevName; + while (1) { s = getString(); if (s == "entry") { - std::string name, prevName; + std::string name; s = getString(); if (s != "(") throw badArchive("expected open tag"); From ef3d3c568248ce5f2b2b7ff1bebb837d22b7df65 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Wed, 4 Sep 2024 20:19:39 -0500 Subject: [PATCH 881/910] use existing GID in sequoia migration script I hardcoded the wrong GID (30001 instead of 30000), but it's better to just pick up the GID from the existing group. --- scripts/sequoia-nixbld-user-migration.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/sequoia-nixbld-user-migration.sh b/scripts/sequoia-nixbld-user-migration.sh index 644249192..ecf19fc87 100755 --- a/scripts/sequoia-nixbld-user-migration.sh +++ b/scripts/sequoia-nixbld-user-migration.sh @@ -17,18 +17,23 @@ any_nixbld(){ dscl . list /Users UniqueID | grep -E '\b_nixbld' >/dev/null } +dsclattr() { + dscl . -read "$1" | awk "/$2/ { print \$2 }" +} + re_create_nixbld_user(){ local name uid name="$1" uid="$2" + gid="$3" sudo /usr/bin/dscl . -create "/Users/$name" "UniqueID" "$uid" sudo /usr/bin/dscl . -create "/Users/$name" "IsHidden" "1" sudo /usr/bin/dscl . -create "/Users/$name" "NFSHomeDirectory" "/var/empty" sudo /usr/bin/dscl . -create "/Users/$name" "RealName" "Nix build user $name" sudo /usr/bin/dscl . -create "/Users/$name" "UserShell" "/sbin/nologin" - sudo /usr/bin/dscl . -create "/Users/$name" "PrimaryGroupID" "30001" + sudo /usr/bin/dscl . -create "/Users/$name" "PrimaryGroupID" "$gid" } hit_id_cap(){ @@ -68,11 +73,12 @@ temporarily_move_existing_nixbld_uids(){ } change_nixbld_uids(){ - local name next_id user_n + local existing_gid name next_id user_n ((next_id=NEW_NIX_FIRST_BUILD_UID)) ((user_n=1)) name="$(nix_user_n "$user_n")" + existing_gid="$(dsclattr "/Groups/nixbld" "PrimaryGroupID")" # we know that we have *some* nixbld users, but macOS may have # already clobbered the first few users if this system has been @@ -91,7 +97,7 @@ change_nixbld_uids(){ fi done - re_create_nixbld_user "$name" "$next_id" + re_create_nixbld_user "$name" "$next_id" "$existing_gid" echo " $name was missing; created with uid: $next_id" ((user_n++)) From 38d9d536a884db28edb4f31a415921029c93faae Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 5 Sep 2024 03:28:06 +0200 Subject: [PATCH 882/910] docs: small fixups on the default expression - highlighted example - linked definitions to the glossary (this is a shorter read) - fixed some artefact --- .../files/default-nix-expression.md | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/doc/manual/src/command-ref/files/default-nix-expression.md b/doc/manual/src/command-ref/files/default-nix-expression.md index 620f7035c..2bd45ff5d 100644 --- a/doc/manual/src/command-ref/files/default-nix-expression.md +++ b/doc/manual/src/command-ref/files/default-nix-expression.md @@ -1,6 +1,6 @@ ## Default Nix expression -The source for the default [Nix expressions](@docroot@/language/index.md) used by [`nix-env`]: +The source for the [Nix expressions](@docroot@/glossary.md#gloss-nix-expression) used by [`nix-env`] by default: - `~/.nix-defexpr` - `$XDG_STATE_HOME/nix/defexpr` if [`use-xdg-base-directories`] is set to `true`. @@ -18,24 +18,25 @@ Then, the resulting expression is interpreted like this: - If the expression is an attribute set, it is used as the default Nix expression. - If the expression is a function, an empty set is passed as argument and the return value is used as the default Nix expression. - -For example, if the default expression contains two files, `foo.nix` and `bar.nix`, then the default Nix expression will be equivalent to - -```nix -{ - foo = import ~/.nix-defexpr/foo.nix; - bar = import ~/.nix-defexpr/bar.nix; -} -``` +> **Example** +> +> If the default expression contains two files, `foo.nix` and `bar.nix`, then the default Nix expression will be equivalent to +> +> ```nix +> { +> foo = import ~/.nix-defexpr/foo.nix; +> bar = import ~/.nix-defexpr/bar.nix; +> } +> ``` The file [`manifest.nix`](@docroot@/command-ref/files/manifest.nix.md) is always ignored. -The command [`nix-channel`] places a symlink to the user's current [channels profile](@docroot@/command-ref/files/channels.md) in this directory. +The command [`nix-channel`] places a symlink to the current user's [channels] in this directory, the [user channel link](#user-channel-link). This makes all subscribed channels available as attributes in the default expression. ## User channel link -A symlink that ensures that [`nix-env`] can find your channels: +A symlink that ensures that [`nix-env`] can find the current user's [channels]: - `~/.nix-defexpr/channels` - `$XDG_STATE_HOME/defexpr/channels` if [`use-xdg-base-directories`] is set to `true`. @@ -45,8 +46,9 @@ This symlink points to: - `$XDG_STATE_HOME/profiles/channels` for regular users - `$NIX_STATE_DIR/profiles/per-user/root/channels` for `root` -In a multi-user installation, you may also have `~/.nix-defexpr/channels_root`, which links to the channels of the root user.[`nix-env`]: ../nix-env.md +In a multi-user installation, you may also have `~/.nix-defexpr/channels_root`, which links to the channels of the root user. -[`nix-env`]: @docroot@/command-ref/nix-env.md [`nix-channel`]: @docroot@/command-ref/nix-channel.md +[`nix-env`]: @docroot@/command-ref/nix-env.md [`use-xdg-base-directories`]: @docroot@/command-ref/conf-file.md#conf-use-xdg-base-directories +[channels]: @docroot@/command-ref/files/channels.md From 48249e001a7c1bc9fac7a94a997566164944b17c Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 5 Sep 2024 04:13:43 +0200 Subject: [PATCH 883/910] docs: reword glossary entry on Nix expression this makes it less cumbersome to read and puts the statements in meaningful order. --- doc/manual/src/glossary.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 877c4668b..808ef477c 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -182,13 +182,18 @@ - [Nix expression]{#gloss-nix-expression} - 1. Commonly, a high-level description of software packages and compositions - thereof. Deploying software using Nix entails writing Nix - expressions for your packages. Nix expressions specify [derivations][derivation], - which are [instantiated][instantiate] into the Nix store as [store derivations][store derivation]. - These derivations can then be [realised][realise] to produce [outputs][output]. + A syntactically valid use of the [Nix language]. - 2. A syntactically valid use of the [Nix language]. For example, the contents of a `.nix` file form an expression. + > **Example** + > + > The contents of a `.nix` file form a Nix expression. + + Nix expressions specify [derivations][derivation], which are [instantiated][instantiate] into the Nix store as [store derivations][store derivation]. + These derivations can then be [realised][realise] to produce [outputs][output]. + + > **Example** + > + > Building and deploying software using Nix entails writing Nix expressions as a high-level description of packages and compositions thereof. - [reference]{#gloss-reference} From 17655ecfefc1f804caf935902469ebb1f621deac Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 5 Sep 2024 04:18:02 +0200 Subject: [PATCH 884/910] docs: explain provenance of default `nix-path` values this should make it more obvious how things are related to each other, and also hopefully expose the historical context without having to say on every corner that these details are accounting for legacy decisions. --- src/libexpr/eval-settings.hh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 3d412bbbf..115e3ee50 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -87,10 +87,19 @@ struct EvalSettings : Config If the respective paths are accessible, the default values are: - `$HOME/.nix-defexpr/channels` + + The [user channel link](@docroot@/command-ref/files/default-nix-expression.md#user-channel-link), pointing to the current state of [channels](@docroot@/command-ref/files/channels.md) for the current user. + - `nixpkgs=$NIX_STATE_DIR/profiles/per-user/root/channels/nixpkgs` + + The current state of the `nixpkgs` channel for the `root` user. + - `$NIX_STATE_DIR/profiles/per-user/root/channels` - See [`NIX_STATE_DIR`](@docroot@/command-ref/env-common.md#env-NIX_STATE_DIR) for details. + The current state of all channels for the `root` user. + + These files are set up by the [Nix installer](@docroot@/installation/installing-binary.md). + See [`NIX_STATE_DIR`](@docroot@/command-ref/env-common.md#env-NIX_STATE_DIR) for details on the environment variable. > **Note** > From a1cc362d9d249b95e4c9ad403f1e6e26ca302413 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 5 Sep 2024 10:34:07 +0200 Subject: [PATCH 885/910] fix broken link (#11435) --- doc/manual/src/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 877c4668b..aacee2680 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -119,7 +119,7 @@ A store object consists of a [file system object], [references][reference] to other store objects, and other metadata. It can be referred to by a [store path]. - See [Store Object](@docroot@/store/index.md#store-object) for details. + See [Store Object](@docroot@/store/store-object.md) for details. [store object]: #gloss-store-object From a81083d080152eb06200c250a4879ece451165d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 5 Sep 2024 15:16:51 +0200 Subject: [PATCH 886/910] Revert "Update src/libutil/url.cc" This reverts commit 9b1cefe27e542d890aa346996a03dfecd9793dfe. --- src/libutil/url.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/url.cc b/src/libutil/url.cc index 8ef1857bb..78c832440 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -81,7 +81,7 @@ std::map decodeQuery(const std::string & query) auto e = s.find('='); if (e == std::string::npos) { - warn("dubious URI query '%s' is missing equal sign '%s'", s, "="); + warn("invalid URI query '%s', did you forget an equals sign `=`?", s); continue; } From 5a5a010120928262341a11b88b58373a55f013b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 5 Sep 2024 15:16:53 +0200 Subject: [PATCH 887/910] Revert "fix: Error on malformed URI query parameter" This reverts commit c9f45677b585dabb3a83570e21426257d92746bd. This now triggers on simple cases like `nix build .#nix`. Reverting for now. --- src/libutil/url.cc | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/libutil/url.cc b/src/libutil/url.cc index 78c832440..bcbe9ea4e 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -79,15 +79,10 @@ std::map decodeQuery(const std::string & query) for (auto s : tokenizeString(query, "&")) { auto e = s.find('='); - - if (e == std::string::npos) { - warn("invalid URI query '%s', did you forget an equals sign `=`?", s); - continue; - } - - result.emplace( - s.substr(0, e), - percentDecode(std::string_view(s).substr(e + 1))); + if (e != std::string::npos) + result.emplace( + s.substr(0, e), + percentDecode(std::string_view(s).substr(e + 1))); } return result; From 83d5b32803e5b828967a27b1ea93c5728d3a4d0a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 5 Sep 2024 16:41:15 +0200 Subject: [PATCH 888/910] Add test case for NARs with duplicate directory entries This test was made by @puckipedia. --- tests/functional/duplicate.nar | Bin 0 -> 1400 bytes tests/functional/local.mk | 2 +- tests/functional/{case-hack.sh => nars.sh} | 9 +++++---- 3 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 tests/functional/duplicate.nar rename tests/functional/{case-hack.sh => nars.sh} (79%) diff --git a/tests/functional/duplicate.nar b/tests/functional/duplicate.nar new file mode 100644 index 0000000000000000000000000000000000000000..1d0993ed4cab41a6d45907ac0c17026afd5471a2 GIT binary patch literal 1400 zcmdT@+it=z49zZ#4T*h25D#ojRW~kz9 z$BsP}-LYn0DAbktf#N+v9qTBW&+onV;7jX2S0C@V9t<{lr}pt&I-XgF4v29E z3g3EyMu?&G+_E0O>ztu< "$TEST_ROOT/case.nar" cmp case.nar "$TEST_ROOT/case.nar" From da1ad28912334bb57f923afb4745273fd68f695c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 5 Sep 2024 16:48:43 +0200 Subject: [PATCH 889/910] Test that nix-store --restore fails if the output already exists This restores the behaviour from before the std::filesystem refactorings. --- src/libutil/fs-sink.cc | 3 ++- tests/functional/nars.sh | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 154346cee..b1e342c77 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -71,7 +71,8 @@ static GlobalConfig::Register r1(&restoreSinkSettings); void RestoreSink::createDirectory(const CanonPath & path) { - std::filesystem::create_directory(dstPath / path.rel()); + if (!std::filesystem::create_directory(dstPath / path.rel())) + throw Error("path '%s' already exists", (dstPath / path.rel()).string()); }; struct RestoreRegularFile : CreateRegularFileSink { diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index c58d12cd5..106bd10fc 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -10,6 +10,9 @@ clearStore rm -rf "$TEST_ROOT/out" expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "NAR directory is not sorted" +# Check that nix-store --restore fails if the output already exists. +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out/' already exists" + # Check whether restoring and dumping a NAR that contains case # collisions is round-tripping, even on a case-insensitive system. rm -rf "$TEST_ROOT/case" From 77c090cdbd56220895a2447efae79f68ed7861c5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 5 Sep 2024 16:54:12 +0200 Subject: [PATCH 890/910] More tests --- tests/functional/nars.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index 106bd10fc..b2b6b2b1a 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -13,6 +13,17 @@ expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet # Check that nix-store --restore fails if the output already exists. expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out/' already exists" +rm -rf "$TEST_ROOT/out" +echo foo > "$TEST_ROOT/out" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "cannot create directory.*File exists" + +rm -rf "$TEST_ROOT/out" +ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "cannot create directory.*File exists" + +mkdir -p "$TEST_ROOT/out2" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out/' already exists" + # Check whether restoring and dumping a NAR that contains case # collisions is round-tripping, even on a case-insensitive system. rm -rf "$TEST_ROOT/case" From 35575873813f60fff26f27a65e09038986f17cb5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 5 Sep 2024 19:26:10 +0200 Subject: [PATCH 891/910] Detect NAR directory entries that collide with another path after case-hacking The test was made by @puckipedia. --- src/libutil/archive.cc | 3 +++ tests/functional/case-collision.nar | Bin 0 -> 1928 bytes tests/functional/nars.sh | 6 ++++++ 3 files changed, 9 insertions(+) create mode 100644 tests/functional/case-collision.nar diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index e4a6a3181..0d72f910d 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -243,6 +243,9 @@ static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath debug("case collision between '%1%' and '%2%'", i->first, name); name += caseHackSuffix; name += std::to_string(++i->second); + auto j = names.find(name); + if (j != names.end()) + throw Error("NAR contains file name '%s' that collides with case-hacked file name '%s'", prevName, j->first); } else names[name] = 0; } diff --git a/tests/functional/case-collision.nar b/tests/functional/case-collision.nar new file mode 100644 index 0000000000000000000000000000000000000000..2eff86901c617be2a830d23074923cb5b3b69aa3 GIT binary patch literal 1928 zcmd^9%}&EG3@&2)Y!WvfAc(_YXsQr5o`XF=mU?TnHklH4TQ7Zf(qMC#G>KJ{av&Gy za}?+EXU7lO&ocTjmrj*>2lMyfx+4Dz*%4W6x6p6LgbVFJp>-|c8?s<9`cB0$vW{^$ z?iYCMuQE2ai07y7GmkrZ&%wH>q|5FJD{C-t@C1MJc_jzOWqdC0M~c()?t*xok{-HJ zs!i9+H#iU9)|ED!?3UuAbZZF8FyEZ~jG6y2J~toM9S7FoQvGmE`2|Vij(PpHA1=*f z7ka8+sd=Qc8V} DaOkrB literal 0 HcmV?d00001 diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index b2b6b2b1a..f2339af88 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -37,3 +37,9 @@ cmp case.nar "$TEST_ROOT/case.nar" # removal of the suffix). touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3" (! nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > /dev/null) + +# Detect NARs that have a directory entry that after case-hacking +# collides with another entry (e.g. a directory containing 'Test', +# 'Test~nix~case~hack~1' and 'test'). +rm -rf "$TEST_ROOT/case" +expectStderr 1 nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case-collision.nar | grepQuiet "NAR contains file name 'test' that collides with case-hacked file name 'Test~nix~case~hack~1'" From 7a765a6aafa27267659eb7339cf7039990f30caa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 5 Sep 2024 20:37:26 +0200 Subject: [PATCH 892/910] Test that deserializing NARs with names with equal Unicode normal forms fails on macOS The test is based on the one by @puckipedia but with the file names swapped to make them sorted. --- tests/functional/nars.sh | 11 +++++++++++ tests/functional/unnormalized.nar | Bin 0 -> 1728 bytes 2 files changed, 11 insertions(+) create mode 100644 tests/functional/unnormalized.nar diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index f2339af88..b16650e7e 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -43,3 +43,14 @@ touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3" # 'Test~nix~case~hack~1' and 'test'). rm -rf "$TEST_ROOT/case" expectStderr 1 nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case-collision.nar | grepQuiet "NAR contains file name 'test' that collides with case-hacked file name 'Test~nix~case~hack~1'" + +# Deserializing a NAR that contains file names that Unicode-normalize +# to the same name should fail on macOS but succeed on Linux. +rm -rf "$TEST_ROOT/out" +if [[ $(uname) = Darwin ]]; then + expectStderr 1 nix-store --restore "$TEST_ROOT/out" < unnormalized.nar | grepQuiet "cannot create directory.*File exists" +else + nix-store --restore "$TEST_ROOT/out" < unnormalized.nar + [[ -e $TEST_ROOT/out/â ]] + [[ -e $TEST_ROOT/out/â ]] +fi diff --git a/tests/functional/unnormalized.nar b/tests/functional/unnormalized.nar new file mode 100644 index 0000000000000000000000000000000000000000..4b7edb17e0b4a9b75cf2958e9f12cceca22d267c GIT binary patch literal 1728 zcmd^9&2GXl4DNo}ka&koJMc51YTAwW-~mEvXhfQz#07fgQFxVI_fQML(N5J=2`NbQ zV*7LLe6bx5vh%0qe#)&Vu$+Qc|z8XR?vo72w}Ja>8T2af{uR|2^gTKAx4X{4ZTc z-^V~CHIFT~SHUB7Jzi)&Hr6bq0+*^U@tqW~ Date: Thu, 5 Sep 2024 20:55:24 +0200 Subject: [PATCH 893/910] Fix test on macOS --- tests/functional/nars.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index b16650e7e..bd2c49fce 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -48,7 +48,7 @@ expectStderr 1 nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case-collisi # to the same name should fail on macOS but succeed on Linux. rm -rf "$TEST_ROOT/out" if [[ $(uname) = Darwin ]]; then - expectStderr 1 nix-store --restore "$TEST_ROOT/out" < unnormalized.nar | grepQuiet "cannot create directory.*File exists" + expectStderr 1 nix-store --restore "$TEST_ROOT/out" < unnormalized.nar | grepQuiet "path '.*/out/â' already exists" else nix-store --restore "$TEST_ROOT/out" < unnormalized.nar [[ -e $TEST_ROOT/out/â ]] From 9fcb588dd8a7b3f0d7d103cea449abcf9f736ad6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 5 Sep 2024 22:21:53 +0200 Subject: [PATCH 894/910] RestoreSink::createDirectory(): Use append() On macOS, `mkdir("x/')` behaves differently than `mkdir("x")` if `x` is a dangling symlink (the formed succeed while the latter fails). So make sure we always strip the trailing slash. --- src/libutil/fs-sink.cc | 20 ++++++++++---------- tests/functional/nars.sh | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index b1e342c77..72e5c731f 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -68,11 +68,19 @@ static RestoreSinkSettings restoreSinkSettings; static GlobalConfig::Register r1(&restoreSinkSettings); +static std::filesystem::path append(const std::filesystem::path & src, const CanonPath & path) +{ + auto dst = src; + if (!path.rel().empty()) + dst /= path.rel(); + return dst; +} void RestoreSink::createDirectory(const CanonPath & path) { - if (!std::filesystem::create_directory(dstPath / path.rel())) - throw Error("path '%s' already exists", (dstPath / path.rel()).string()); + auto p = append(dstPath, path); + if (!std::filesystem::create_directory(p)) + throw Error("path '%s' already exists", p.string()); }; struct RestoreRegularFile : CreateRegularFileSink { @@ -94,14 +102,6 @@ struct RestoreRegularFile : CreateRegularFileSink { void preallocateContents(uint64_t size) override; }; -static std::filesystem::path append(const std::filesystem::path & src, const CanonPath & path) -{ - auto dst = src; - if (!path.rel().empty()) - dst /= path.rel(); - return dst; -} - void RestoreSink::createRegularFile(const CanonPath & path, std::function func) { auto p = append(dstPath, path); diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index bd2c49fce..4f2470ea7 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -11,18 +11,18 @@ rm -rf "$TEST_ROOT/out" expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "NAR directory is not sorted" # Check that nix-store --restore fails if the output already exists. -expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out/' already exists" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out' already exists" rm -rf "$TEST_ROOT/out" echo foo > "$TEST_ROOT/out" -expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "cannot create directory.*File exists" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "File exists" rm -rf "$TEST_ROOT/out" ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out" -expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "cannot create directory.*File exists" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "File exists" mkdir -p "$TEST_ROOT/out2" -expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out/' already exists" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out' already exists" # Check whether restoring and dumping a NAR that contains case # collisions is round-tripping, even on a case-insensitive system. From 92be107c8eb324460a040e104be1f790cf0b57fd Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 6 Sep 2024 10:33:12 +0200 Subject: [PATCH 895/910] update instructions to build the manual --- doc/manual/src/development/documentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/development/documentation.md b/doc/manual/src/development/documentation.md index 63f574ab7..d5a95e0c1 100644 --- a/doc/manual/src/development/documentation.md +++ b/doc/manual/src/development/documentation.md @@ -13,13 +13,13 @@ Incremental refactorings of the documentation build setup to make it faster or e Build the manual from scratch: ```console -nix-build $(nix-instantiate)'!doc' +nix-build -E '(import ./.).packages.${builtins.currentSystem}.nix.doc' ``` or ```console -nix build .#^doc +nix build .#nix^doc ``` and open `./result-doc/share/doc/nix/manual/index.html`. From 52ba3cc5eac0418218a90c0cddb06688d4c7b5d3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Sep 2024 16:28:09 +0200 Subject: [PATCH 896/910] Test that deserializing regular files / symlinks is exclusive --- tests/functional/nars.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index 4f2470ea7..ed19637a1 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -24,6 +24,44 @@ expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet mkdir -p "$TEST_ROOT/out2" expectStderr 1 nix-store --restore "$TEST_ROOT/out" < duplicate.nar | grepQuiet "path '.*/out' already exists" +# The same, but for a regular file. +nix-store --dump ./nars.sh > "$TEST_ROOT/tmp.nar" + +rm -rf "$TEST_ROOT/out" +nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + +rm -rf "$TEST_ROOT/out" +mkdir -p "$TEST_ROOT/out" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + +rm -rf "$TEST_ROOT/out" +ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + +mkdir -p "$TEST_ROOT/out2" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + +# The same, but for a symlink +ln -sfn foo "$TEST_ROOT/symlink" +nix-store --dump "$TEST_ROOT/symlink" > "$TEST_ROOT/tmp.nar" + +rm -rf "$TEST_ROOT/out" +nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" +[[ -L "$TEST_ROOT/out" ]] +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + +rm -rf "$TEST_ROOT/out" +mkdir -p "$TEST_ROOT/out" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + +rm -rf "$TEST_ROOT/out" +ln -s "$TEST_ROOT/out2" "$TEST_ROOT/out" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + +mkdir -p "$TEST_ROOT/out2" +expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" + # Check whether restoring and dumping a NAR that contains case # collisions is round-tripping, even on a case-insensitive system. rm -rf "$TEST_ROOT/case" From 9df5236c468839ead6627131886f3d1153035bf3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 27 Aug 2024 13:43:50 +0200 Subject: [PATCH 897/910] progress-bar: Only write when truly updated --- src/libmain/progress-bar.cc | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index bb4c52ef7..d864d9473 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -75,6 +75,9 @@ private: bool active = true; bool paused = false; bool haveUpdate = true; + + /** Helps avoid unnecessary redraws, see `draw()` */ + std::string lastOutput; }; Sync state_; @@ -360,6 +363,31 @@ public: } std::chrono::milliseconds draw(State & state) + { + // Call draw() and render if the output has changed. + + // Excessive redrawing is noticable on slow terminals, and it interferes + // with text selection in some terminals, including libvte-based terminal + // emulators. + + std::optional newOutput; + auto nextWakeup = draw(state, newOutput); + { + auto state(state_.lock()); + if (newOutput && *newOutput != state->lastOutput) { + writeToStderr(*newOutput); + state->lastOutput = std::move(*newOutput); + } + } + return nextWakeup; + } + + /** + * @param output[out] `nullopt` if nothing is to be drawn. Otherwise, a + * string of ANSI terminal output that can be used to + * render the progress bar. + */ + std::chrono::milliseconds draw(State & state, std::optional & output) { auto nextWakeup = std::chrono::milliseconds::max(); @@ -412,7 +440,7 @@ public: auto width = getWindowSize().second; if (width <= 0) width = std::numeric_limits::max(); - writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); + output = "\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"; return nextWakeup; } From 047d9643b54aa857dfff81ba8aed7881a97938a4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 8 Sep 2024 01:09:51 +0200 Subject: [PATCH 898/910] refact: Extract ProgressBar::redraw(newOutput) --- src/libmain/progress-bar.cc | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index d864d9473..2ea743034 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -362,23 +362,28 @@ public: updateCV.notify_one(); } + /** + * Redraw, if the output has changed. + * + * Excessive redrawing is noticable on slow terminals, and it interferes + * with text selection in some terminals, including libvte-based terminal + * emulators. + */ + void redraw(std::string newOutput) + { + auto state(state_.lock()); + if (newOutput != state->lastOutput) { + writeToStderr(newOutput); + state->lastOutput = std::move(newOutput); + } + } + std::chrono::milliseconds draw(State & state) { - // Call draw() and render if the output has changed. - - // Excessive redrawing is noticable on slow terminals, and it interferes - // with text selection in some terminals, including libvte-based terminal - // emulators. - std::optional newOutput; auto nextWakeup = draw(state, newOutput); - { - auto state(state_.lock()); - if (newOutput && *newOutput != state->lastOutput) { - writeToStderr(*newOutput); - state->lastOutput = std::move(*newOutput); - } - } + if (newOutput) + redraw(*newOutput); return nextWakeup; } From e10ea78f9313d8df381301ac2fc3023de3993689 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 8 Sep 2024 01:21:40 +0200 Subject: [PATCH 899/910] refact: Inline ProgressBar::draw(state, newOutput), inline local output --- src/libmain/progress-bar.cc | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index 2ea743034..ce513d204 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -379,20 +379,6 @@ public: } std::chrono::milliseconds draw(State & state) - { - std::optional newOutput; - auto nextWakeup = draw(state, newOutput); - if (newOutput) - redraw(*newOutput); - return nextWakeup; - } - - /** - * @param output[out] `nullopt` if nothing is to be drawn. Otherwise, a - * string of ANSI terminal output that can be used to - * render the progress bar. - */ - std::chrono::milliseconds draw(State & state, std::optional & output) { auto nextWakeup = std::chrono::milliseconds::max(); @@ -445,7 +431,7 @@ public: auto width = getWindowSize().second; if (width <= 0) width = std::numeric_limits::max(); - output = "\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"; + redraw("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); return nextWakeup; } From c955563b6440ffe7abb22f15744ceea8b4ce2d9c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 8 Sep 2024 11:44:24 +0200 Subject: [PATCH 900/910] fix: Avoid deadlock in ProgressBar::redraw() --- src/libmain/progress-bar.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index ce513d204..e63d4f13f 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -75,11 +75,11 @@ private: bool active = true; bool paused = false; bool haveUpdate = true; - - /** Helps avoid unnecessary redraws, see `draw()` */ - std::string lastOutput; }; + /** Helps avoid unnecessary redraws, see `redraw()` */ + Sync lastOutput_; + Sync state_; std::thread updateThread; @@ -371,10 +371,10 @@ public: */ void redraw(std::string newOutput) { - auto state(state_.lock()); - if (newOutput != state->lastOutput) { + auto lastOutput(lastOutput_.lock()); + if (newOutput != *lastOutput) { writeToStderr(newOutput); - state->lastOutput = std::move(newOutput); + *lastOutput = std::move(newOutput); } } From 4cfa59fdb32aa4fcc58b735d8843ce308692a652 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 9 Sep 2024 14:11:35 +0200 Subject: [PATCH 901/910] Typo --- tests/functional/nars.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/nars.sh b/tests/functional/nars.sh index ed19637a1..9f5f43dc6 100755 --- a/tests/functional/nars.sh +++ b/tests/functional/nars.sh @@ -42,7 +42,7 @@ expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | gre mkdir -p "$TEST_ROOT/out2" expectStderr 1 nix-store --restore "$TEST_ROOT/out" < "$TEST_ROOT/tmp.nar" | grepQuiet "File exists" -# The same, but for a symlink +# The same, but for a symlink. ln -sfn foo "$TEST_ROOT/symlink" nix-store --dump "$TEST_ROOT/symlink" > "$TEST_ROOT/tmp.nar" From 5ca2f58798e6f514b5194c16c0fea0d8ec128171 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 9 Sep 2024 14:29:05 +0200 Subject: [PATCH 902/910] Improve use-case-hack description slightly --- src/libutil/archive.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 0d72f910d..e26b7eb93 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -23,7 +23,7 @@ struct ArchiveSettings : Config false, #endif "use-case-hack", - "Whether to enable a Darwin-specific hack for dealing with file name collisions."}; + "Whether to enable a macOS-specific hack for dealing with file name case collisions."}; }; static ArchiveSettings archiveSettings; From c5a4dfa6602796ebb3c62493a2fd33f2b58ec91c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 9 Sep 2024 15:41:38 +0200 Subject: [PATCH 903/910] nix flake {metadata,archive}: Fix chroot stores Fixes $ nix flake metadata --store /tmp/nix nixpkgs error: path '/tmp/nix/nix/store/65xpqkz92d9j7k5ric4z8lzhiigxsfbg-source/flake.nix' is not in the Nix store This has been broken since 598deb2b23bc59df61c92ea25745d675686f3991. --- src/libflake/flake/flake.cc | 27 ++++++++++++++++----------- src/libflake/flake/flake.hh | 10 ++++++++++ src/nix/flake.cc | 4 ++-- tests/functional/flakes/flakes.sh | 3 +++ 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index fd1183514..35db13f7e 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -751,6 +751,21 @@ LockedFlake lockFlake( } } +std::pair sourcePathToStorePath( + ref store, + const SourcePath & _path) +{ + auto path = _path.path.abs(); + + if (auto store2 = store.dynamic_pointer_cast()) { + auto realStoreDir = store2->getRealStoreDir(); + if (isInDir(path, realStoreDir)) + path = store2->storeDir + path.substr(realStoreDir.size()); + } + + return store->toStorePath(path); +} + void callFlake(EvalState & state, const LockedFlake & lockedFlake, Value & vRes) @@ -768,17 +783,7 @@ void callFlake(EvalState & state, auto lockedNode = node.dynamic_pointer_cast(); - // FIXME: This is a hack to support chroot stores. Remove this - // once we can pass a sourcePath rather than a storePath to - // call-flake.nix. - auto path = sourcePath.path.abs(); - if (auto store = state.store.dynamic_pointer_cast()) { - auto realStoreDir = store->getRealStoreDir(); - if (isInDir(path, realStoreDir)) - path = store->storeDir + path.substr(realStoreDir.size()); - } - - auto [storePath, subdir] = state.store->toStorePath(path); + auto [storePath, subdir] = sourcePathToStorePath(state.store, sourcePath); emitTreeAttrs( state, diff --git a/src/libflake/flake/flake.hh b/src/libflake/flake/flake.hh index cce17009c..496e18673 100644 --- a/src/libflake/flake/flake.hh +++ b/src/libflake/flake/flake.hh @@ -214,6 +214,16 @@ void callFlake( const LockedFlake & lockedFlake, Value & v); +/** + * Map a `SourcePath` to the corresponding store path. This is a + * temporary hack to support chroot stores while we don't have full + * lazy trees. FIXME: Remove this once we can pass a sourcePath rather + * than a storePath to call-flake.nix. + */ +std::pair sourcePathToStorePath( + ref store, + const SourcePath & path); + } void emitTreeAttrs( diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 2db1e039e..8e109b327 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -214,7 +214,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON auto & flake = lockedFlake.flake; // Currently, all flakes are in the Nix store via the rootFS accessor. - auto storePath = store->printStorePath(store->toStorePath(flake.path.path.abs()).first); + auto storePath = store->printStorePath(sourcePathToStorePath(store, flake.path).first); if (json) { nlohmann::json j; @@ -1079,7 +1079,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun StorePathSet sources; - auto storePath = store->toStorePath(flake.flake.path.path.abs()).first; + auto storePath = sourcePathToStorePath(store, flake.flake.path).first; sources.insert(storePath); diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 26b91eda7..aa4cb1e18 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -184,6 +184,9 @@ nix registry list | grepInverse '^user' # nothing in user registry nix flake metadata flake1 nix flake metadata flake1 | grepQuiet 'Locked URL:.*flake1.*' +# Test 'nix flake metadata' on a chroot store. +nix flake metadata --store $TEST_ROOT/chroot-store flake1 + # Test 'nix flake metadata' on a local flake. (cd "$flake1Dir" && nix flake metadata) | grepQuiet 'URL:.*flake1.*' (cd "$flake1Dir" && nix flake metadata .) | grepQuiet 'URL:.*flake1.*' From c55b285cf91139c4936ea439e7a43811f554dcdf Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 9 Sep 2024 22:15:45 -0400 Subject: [PATCH 904/910] tests: test was re-named --- tests/functional/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index ebecdd9e8..5167fa814 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -159,7 +159,7 @@ suites = [ 'derivation-advanced-attributes.sh', 'import-derivation.sh', 'nix_path.sh', - 'case-hack.sh', + 'nars.sh', 'placeholders.sh', 'ssh-relay.sh', 'build.sh', From be480971c20fdd859a13053bd6ebefa0b3a04042 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 10 Sep 2024 15:26:35 +0200 Subject: [PATCH 905/910] doc: add HACKING.md symlink As a hacker, I should be able to checkout the repo, and find relevant information on how to develop in the project somewhere in the top-level. Either in the README.md, or CONTRIBUTING.md or HACKING.md files. This PR symlinks the HACKING.md into the right place in the manual. --- HACKING.md | 1 + 1 file changed, 1 insertion(+) create mode 120000 HACKING.md diff --git a/HACKING.md b/HACKING.md new file mode 120000 index 000000000..d3576d60d --- /dev/null +++ b/HACKING.md @@ -0,0 +1 @@ +doc/manual/src/development/building.md \ No newline at end of file From 1ca1439b1f7bdaad9115e60b9d221c006cff1395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 10 Sep 2024 16:40:08 +0200 Subject: [PATCH 906/910] add missing filesystem include (x86_64-darwin fix) --- src/libutil/args.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 513b8d811..127a0809e 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -2,6 +2,7 @@ ///@file #include +#include #include #include #include From c4766d7b8b659ce3f6fb1c339aeeec9ca3c7eeba Mon Sep 17 00:00:00 2001 From: zimbatm Date: Thu, 5 Sep 2024 10:29:48 +0200 Subject: [PATCH 907/910] fix(nix fmt): remove the default "." argument When `nix fmt` is called without an argument, Nix appends the "." argument before calling the formatter. The comment in the code is: > Format the current flake out of the box This also happens when formatting sub-folders. This means that the formatter is now unable to distinguish, as an interface, whether the "." argument is coming from the flake or the user's intent to format the current folder. This decision should be up to the formatter. Treefmt, for example, will automatically look up the project's root and format all the files. This is the desired behaviour. But because the "." argument is passed, it cannot function as expected. --- doc/manual/rl-next/nix-fmt-default-argument.md | 17 +++++++++++++++++ src/nix/fmt.cc | 10 ++-------- tests/functional/fmt.sh | 11 +++++++---- tests/functional/fmt.simple.sh | 3 ++- 4 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 doc/manual/rl-next/nix-fmt-default-argument.md diff --git a/doc/manual/rl-next/nix-fmt-default-argument.md b/doc/manual/rl-next/nix-fmt-default-argument.md new file mode 100644 index 000000000..54161ab30 --- /dev/null +++ b/doc/manual/rl-next/nix-fmt-default-argument.md @@ -0,0 +1,17 @@ +--- +synopsis: Removing the default argument passed to the `nix fmt` formatter +issues: [] +prs: [11438] +--- + +The underlying formatter no longer receives the ". " default argument when `nix fmt` is called with no arguments. + +This change was necessary as the formatter wasn't able to distinguish between +a user wanting to format the current folder with `nix fmt .` or the generic +`nix fmt`. + +The default behaviour is now the responsibility of the formatter itself, and +allows tools such as treefmt to format the whole tree instead of only the +current directory and below. + +Author: [**@zimbatm**](https://github.com/zimbatm) diff --git a/src/nix/fmt.cc b/src/nix/fmt.cc index d65834495..f444d6add 100644 --- a/src/nix/fmt.cc +++ b/src/nix/fmt.cc @@ -40,14 +40,8 @@ struct CmdFmt : SourceExprCommand { Strings programArgs{app.program}; // Propagate arguments from the CLI - if (args.empty()) { - // Format the current flake out of the box - programArgs.push_back("."); - } else { - // User wants more power, let them decide which paths to include/exclude - for (auto &i : args) { - programArgs.push_back(i); - } + for (auto &i : args) { + programArgs.push_back(i); } // Release our references to eval caches to ensure they are persisted to disk, because diff --git a/tests/functional/fmt.sh b/tests/functional/fmt.sh index b29fe64d6..9a79260e6 100755 --- a/tests/functional/fmt.sh +++ b/tests/functional/fmt.sh @@ -5,11 +5,11 @@ source common.sh TODO_NixOS # Provide a `shell` variable. Try not to `export` it, perhaps. clearStoreIfPossible -rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local +rm -rf "$TEST_HOME"/.cache "$TEST_HOME"/.config "$TEST_HOME"/.local -cp ./simple.nix ./simple.builder.sh ./fmt.simple.sh ./config.nix $TEST_HOME +cp ./simple.nix ./simple.builder.sh ./fmt.simple.sh ./config.nix "$TEST_HOME" -cd $TEST_HOME +cd "$TEST_HOME" nix fmt --help | grep "Format" @@ -30,6 +30,9 @@ cat << EOF > flake.nix }; } EOF -nix fmt ./file ./folder | grep 'Formatting: ./file ./folder' +# No arguments check +[[ "$(nix fmt)" = "Formatting(0):" ]] +# Argument forwarding check +nix fmt ./file ./folder | grep 'Formatting(2): ./file ./folder' nix flake check nix flake show | grep -P "package 'formatter'" diff --git a/tests/functional/fmt.simple.sh b/tests/functional/fmt.simple.sh index 4c8c67ebb..e53f6c9be 100755 --- a/tests/functional/fmt.simple.sh +++ b/tests/functional/fmt.simple.sh @@ -1 +1,2 @@ -echo Formatting: "${@}" +#!/usr/bin/env bash +echo "Formatting(${#}):" "${@}" From ebebe626ff4ec6da98c0a043c64b35efe1c05bc3 Mon Sep 17 00:00:00 2001 From: Artturin Date: Wed, 11 Sep 2024 00:17:03 +0300 Subject: [PATCH 908/910] Fix making the build directory kept by `keep-failed` readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caused by 1d3696f0fb88d610abc234a60e0d6d424feafdf1 Without this fix the kept build directory is readable only by root ``` $ sudo ls -ld /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5 drwx------ root root 60 B Wed Sep 11 00:09:48 2024  /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5/ $ sudo ls -ld /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5/build drwxr-xr-x nixbld1 nixbld 80 B Wed Sep 11 00:09:58 2024  /comp-temp/nix-build-openssh-static-x86_64-unknown-linux-musl-9.8p1.drv-5/build/ ``` --- src/libstore/unix/build/local-derivation-goal.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index d55278a52..08b973cd1 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -2997,6 +2997,7 @@ void LocalDerivationGoal::deleteTmpDir(bool force) might have privileged stuff (like a copy of netrc). */ if (settings.keepFailed && !force && !drv->isBuiltin()) { printError("note: keeping build directory '%s'", tmpDir); + chmod(topTmpDir.c_str(), 0755); chmod(tmpDir.c_str(), 0755); } else From f2e7e996dafe069a8671f8c5a6796174c415da44 Mon Sep 17 00:00:00 2001 From: "Travis A. Everett" Date: Tue, 10 Sep 2024 20:54:09 -0500 Subject: [PATCH 909/910] sequoia-nixbld-user-migration: disable trace mode Was hoping to leave this enabled for a little while as core community members test this script out, but Apple's aggressive release timeline for macOS 15 Sequoia has caught us off-guard here. It's probably not ideal for a general audience if the script spews all of this output--and people can still force bash to run in trace mode if we really need to debug a problem. --- scripts/sequoia-nixbld-user-migration.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/sequoia-nixbld-user-migration.sh b/scripts/sequoia-nixbld-user-migration.sh index ecf19fc87..12c736d8e 100755 --- a/scripts/sequoia-nixbld-user-migration.sh +++ b/scripts/sequoia-nixbld-user-migration.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -x - ((NEW_NIX_FIRST_BUILD_UID=351)) ((TEMP_NIX_FIRST_BUILD_UID=31000)) From 38bfbb297c380f8b07d8a20ffdeb72da71c1567c Mon Sep 17 00:00:00 2001 From: Noam Yorav-Raphael Date: Wed, 11 Sep 2024 13:36:46 +0300 Subject: [PATCH 910/910] Use envvars NIX_CACHE_HOME, NIX_CONFIG_HOME, NIX_DATA_HOME, NIX_STATE_HOME if defined (#11351) --- doc/manual/rl-next/add-nix-state-home.md | 14 ++++++ doc/manual/src/command-ref/env-common.md | 15 ++++++- scripts/nix-profile.sh.in | 46 ++++++++++--------- src/libcmd/repl.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval-settings.cc | 2 +- src/libfetchers/cache.cc | 2 +- src/libfetchers/git-utils.cc | 2 +- src/libfetchers/git.cc | 2 +- src/libfetchers/mercurial.cc | 2 +- src/libfetchers/registry.cc | 4 +- src/libflake/flake/config.cc | 2 +- src/libstore/globals.cc | 2 +- src/libstore/nar-info-disk-cache.cc | 2 +- src/libstore/store-api.cc | 2 +- src/libutil/users.cc | 57 ++++++++++++++++++++---- src/libutil/users.hh | 8 ++-- 17 files changed, 118 insertions(+), 48 deletions(-) create mode 100644 doc/manual/rl-next/add-nix-state-home.md diff --git a/doc/manual/rl-next/add-nix-state-home.md b/doc/manual/rl-next/add-nix-state-home.md new file mode 100644 index 000000000..bbfdd5d38 --- /dev/null +++ b/doc/manual/rl-next/add-nix-state-home.md @@ -0,0 +1,14 @@ +--- +synopsis: Use envvars NIX_CACHE_HOME, NIX_CONFIG_HOME, NIX_DATA_HOME, NIX_STATE_HOME if defined +prs: [11351] +--- + +Added new environment variables: + +- `NIX_CACHE_HOME` +- `NIX_CONFIG_HOME` +- `NIX_DATA_HOME` +- `NIX_STATE_HOME` + +Each, if defined, takes precedence over the corresponding [XDG environment variable](@docroot@/command-ref/env-common.md#xdg-base-directories). +This provides more fine-grained control over where Nix looks for files, and allows to have a stand-alone Nix environment, which only uses files in a specific directory, and doesn't interfere with the user environment. diff --git a/doc/manual/src/command-ref/env-common.md b/doc/manual/src/command-ref/env-common.md index 0b5017882..ee3995111 100644 --- a/doc/manual/src/command-ref/env-common.md +++ b/doc/manual/src/command-ref/env-common.md @@ -138,6 +138,19 @@ The following environment variables are used to determine locations of various s - [`XDG_STATE_HOME`]{#env-XDG_STATE_HOME} (default `~/.local/state`) - [`XDG_CACHE_HOME`]{#env-XDG_CACHE_HOME} (default `~/.cache`) - [XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html [`use-xdg-base-directories`]: @docroot@/command-ref/conf-file.md#conf-use-xdg-base-directories + +In addition, setting the following environment variables overrides the XDG base directories: + +- [`NIX_CONFIG_HOME`]{#env-NIX_CONFIG_HOME} (default `$XDG_CONFIG_HOME/nix`) +- [`NIX_STATE_HOME`]{#env-NIX_STATE_HOME} (default `$XDG_STATE_HOME/nix`) +- [`NIX_CACHE_HOME`]{#env-NIX_CACHE_HOME} (default `$XDG_CACHE_HOME/nix`) + +When [`use-xdg-base-directories`] is enabled, the configuration directory is: + +1. `$NIX_CONFIG_HOME`, if it is defined +2. Otherwise, `$XDG_CONFIG_HOME/nix`, if `XDG_CONFIG_HOME` is defined +3. Otherwise, `~/.config/nix`. + +Likewise for the state and cache directories. diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in index e868399b1..3d0e498f4 100644 --- a/scripts/nix-profile.sh.in +++ b/scripts/nix-profile.sh.in @@ -3,29 +3,33 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then # Set up the per-user profile. - NIX_LINK="$HOME/.nix-profile" - if [ -n "${XDG_STATE_HOME-}" ]; then - NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile" + if [ -n "$NIX_STATE_HOME" ]; then + NIX_LINK="$NIX_STATE_HOME/profile" else - NIX_LINK_NEW="$HOME/.local/state/nix/profile" - fi - if [ -e "$NIX_LINK_NEW" ]; then - if [ -t 2 ] && [ -e "$NIX_LINK" ]; then - warning="\033[1;35mwarning:\033[0m" - printf "$warning Both %s and legacy %s exist; using the former.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 - if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then - printf " Since the profiles match, you can safely delete either of them.\n" 1>&2 - else - # This should be an exceptionally rare occasion: the only way to get it would be to - # 1. Update to newer Nix; - # 2. Remove .nix-profile; - # 3. Set the $NIX_LINK_NEW to something other than the default user profile; - # 4. Roll back to older Nix. - # If someone did all that, they can probably figure out how to migrate the profile. - printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2 - fi + NIX_LINK="$HOME/.nix-profile" + if [ -n "${XDG_STATE_HOME-}" ]; then + NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile" + else + NIX_LINK_NEW="$HOME/.local/state/nix/profile" + fi + if [ -e "$NIX_LINK_NEW" ]; then + if [ -t 2 ] && [ -e "$NIX_LINK" ]; then + warning="\033[1;35mwarning:\033[0m" + printf "$warning Both %s and legacy %s exist; using the former.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 + if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then + printf " Since the profiles match, you can safely delete either of them.\n" 1>&2 + else + # This should be an exceptionally rare occasion: the only way to get it would be to + # 1. Update to newer Nix; + # 2. Remove .nix-profile; + # 3. Set the $NIX_LINK_NEW to something other than the default user profile; + # 4. Roll back to older Nix. + # If someone did all that, they can probably figure out how to migrate the profile. + printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2 + fi + fi + NIX_LINK="$NIX_LINK_NEW" fi - NIX_LINK="$NIX_LINK_NEW" fi # Set up environment. diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 63f6c1bdd..b09ae3d09 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -134,7 +134,7 @@ NixRepl::NixRepl(const LookupPath & lookupPath, nix::ref store, refstaticBaseEnv.get())) , runNixPtr{runNix} - , interacter(make_unique(getDataDir() + "/nix/repl-history")) + , interacter(make_unique(getDataDir() + "/repl-history")) { } diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 9019edc1f..c407cc89a 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -69,7 +69,7 @@ struct AttrDb { auto state(_state->lock()); - Path cacheDir = getCacheDir() + "/nix/eval-cache-v5"; + Path cacheDir = getCacheDir() + "/eval-cache-v5"; createDirs(cacheDir); Path dbPath = cacheDir + "/" + fingerprint.to_string(HashFormat::Base16, false) + ".sqlite"; diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 2846eccbc..4cbcb39b9 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -99,7 +99,7 @@ const std::string & EvalSettings::getCurrentSystem() const Path getNixDefExpr() { return settings.useXDGBaseDirectories - ? getStateDir() + "/nix/defexpr" + ? getStateDir() + "/defexpr" : getHome() + "/.nix-defexpr"; } diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 7019b0325..b0b6cb887 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -36,7 +36,7 @@ struct CacheImpl : Cache { auto state(_state.lock()); - auto dbPath = getCacheDir() + "/nix/fetcher-cache-v2.sqlite"; + auto dbPath = getCacheDir() + "/fetcher-cache-v2.sqlite"; createDirs(dirOf(dbPath)); state->db = SQLite(dbPath); diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 114aa4ec0..31c42008f 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -1083,7 +1083,7 @@ std::vector> GitRepoImpl::getSubmodules ref getTarballCache() { - static auto repoDir = std::filesystem::path(getCacheDir()) / "nix" / "tarball-cache"; + static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache"; return GitRepo::openRepo(repoDir, true, true); } diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 076c757c5..13682231d 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -44,7 +44,7 @@ bool isCacheFileWithinTtl(time_t now, const struct stat & st) Path getCachePath(std::string_view key, bool shallow) { return getCacheDir() - + "/nix/gitv3/" + + "/gitv3/" + hashString(HashAlgorithm::SHA256, key).to_string(HashFormat::Nix32, false) + (shallow ? "-shallow" : ""); } diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 3feb3cb19..2c987f79d 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -263,7 +263,7 @@ struct MercurialInputScheme : InputScheme return makeResult(res->value, res->storePath); } - Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false)); + Path cacheDir = fmt("%s/hg/%s", getCacheDir(), hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false)); /* If this is a commit hash that we already have, we don't have to pull again. */ diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index 3c893c8ea..7f7a09053 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -116,7 +116,7 @@ static std::shared_ptr getSystemRegistry(const Settings & settings) Path getUserRegistryPath() { - return getConfigDir() + "/nix/registry.json"; + return getConfigDir() + "/registry.json"; } std::shared_ptr getUserRegistry(const Settings & settings) @@ -159,7 +159,7 @@ static std::shared_ptr getGlobalRegistry(const Settings & settings, re if (!hasPrefix(path, "/")) { auto storePath = downloadFile(store, path, "flake-registry.json").storePath; if (auto store2 = store.dynamic_pointer_cast()) - store2->addPermRoot(storePath, getCacheDir() + "/nix/flake-registry.json"); + store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json"); path = store->toRealPath(storePath); } diff --git a/src/libflake/flake/config.cc b/src/libflake/flake/config.cc index e526cdddf..4879de463 100644 --- a/src/libflake/flake/config.cc +++ b/src/libflake/flake/config.cc @@ -12,7 +12,7 @@ typedef std::map> TrustedList; Path trustedListPath() { - return getDataDir() + "/nix/trusted-settings.json"; + return getDataDir() + "/trusted-settings.json"; } static TrustedList readTrustedList() diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 52ab35b4c..8958e6997 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -135,7 +135,7 @@ std::vector getUserConfigFiles() std::vector files; auto dirs = getConfigDirs(); for (auto & dir : dirs) { - files.insert(files.end(), dir + "/nix/nix.conf"); + files.insert(files.end(), dir + "/nix.conf"); } return files; } diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 83e63794e..80e8d3414 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -87,7 +87,7 @@ public: Sync _state; - NarInfoDiskCacheImpl(Path dbPath = getCacheDir() + "/nix/binary-cache-v6.sqlite") + NarInfoDiskCacheImpl(Path dbPath = getCacheDir() + "/binary-cache-v6.sqlite") { auto state(_state.lock()); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index fc8d51c39..426a69ae2 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1315,7 +1315,7 @@ ref openStore(StoreReference && storeURI) /* If /nix doesn't exist, there is no daemon socket, and we're not root, then automatically set up a chroot store in ~/.local/share/nix/root. */ - auto chrootStore = getDataDir() + "/nix/root"; + auto chrootStore = getDataDir() + "/root"; if (!pathExists(chrootStore)) { try { createDirs(chrootStore); diff --git a/src/libutil/users.cc b/src/libutil/users.cc index d546e364f..b4bc67cbc 100644 --- a/src/libutil/users.cc +++ b/src/libutil/users.cc @@ -7,15 +7,33 @@ namespace nix { Path getCacheDir() { - auto cacheDir = getEnv("XDG_CACHE_HOME"); - return cacheDir ? *cacheDir : getHome() + "/.cache"; + auto dir = getEnv("NIX_CACHE_HOME"); + if (dir) { + return *dir; + } else { + auto xdgDir = getEnv("XDG_CACHE_HOME"); + if (xdgDir) { + return *xdgDir + "/nix"; + } else { + return getHome() + "/.cache/nix"; + } + } } Path getConfigDir() { - auto configDir = getEnv("XDG_CONFIG_HOME"); - return configDir ? *configDir : getHome() + "/.config"; + auto dir = getEnv("NIX_CONFIG_HOME"); + if (dir) { + return *dir; + } else { + auto xdgDir = getEnv("XDG_CONFIG_HOME"); + if (xdgDir) { + return *xdgDir + "/nix"; + } else { + return getHome() + "/.config/nix"; + } + } } std::vector getConfigDirs() @@ -23,6 +41,9 @@ std::vector getConfigDirs() Path configHome = getConfigDir(); auto configDirs = getEnv("XDG_CONFIG_DIRS").value_or("/etc/xdg"); std::vector result = tokenizeString>(configDirs, ":"); + for (auto& p : result) { + p += "/nix"; + } result.insert(result.begin(), configHome); return result; } @@ -30,19 +51,37 @@ std::vector getConfigDirs() Path getDataDir() { - auto dataDir = getEnv("XDG_DATA_HOME"); - return dataDir ? *dataDir : getHome() + "/.local/share"; + auto dir = getEnv("NIX_DATA_HOME"); + if (dir) { + return *dir; + } else { + auto xdgDir = getEnv("XDG_DATA_HOME"); + if (xdgDir) { + return *xdgDir + "/nix"; + } else { + return getHome() + "/.local/share/nix"; + } + } } Path getStateDir() { - auto stateDir = getEnv("XDG_STATE_HOME"); - return stateDir ? *stateDir : getHome() + "/.local/state"; + auto dir = getEnv("NIX_STATE_HOME"); + if (dir) { + return *dir; + } else { + auto xdgDir = getEnv("XDG_STATE_HOME"); + if (xdgDir) { + return *xdgDir + "/nix"; + } else { + return getHome() + "/.local/state/nix"; + } + } } Path createNixStateDir() { - Path dir = getStateDir() + "/nix"; + Path dir = getStateDir(); createDirs(dir); return dir; } diff --git a/src/libutil/users.hh b/src/libutil/users.hh index 153cc73fd..d22c3311d 100644 --- a/src/libutil/users.hh +++ b/src/libutil/users.hh @@ -24,12 +24,12 @@ Path getHomeOf(uid_t userId); Path getHome(); /** - * @return $XDG_CACHE_HOME or $HOME/.cache. + * @return $NIX_CACHE_HOME or $XDG_CACHE_HOME/nix or $HOME/.cache/nix. */ Path getCacheDir(); /** - * @return $XDG_CONFIG_HOME or $HOME/.config. + * @return $NIX_CONFIG_HOME or $XDG_CONFIG_HOME/nix or $HOME/.config/nix. */ Path getConfigDir(); @@ -39,12 +39,12 @@ Path getConfigDir(); std::vector getConfigDirs(); /** - * @return $XDG_DATA_HOME or $HOME/.local/share. + * @return $NIX_DATA_HOME or $XDG_DATA_HOME/nix or $HOME/.local/share/nix. */ Path getDataDir(); /** - * @return $XDG_STATE_HOME or $HOME/.local/state. + * @return $NIX_STATE_HOME or $XDG_STATE_HOME/nix or $HOME/.local/state/nix. */ Path getStateDir();