From 04023360edbeb3477286783b3101a5b53f78021b Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Tue, 3 Aug 2021 15:25:15 -0500 Subject: [PATCH 001/299] 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 002/299] 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 003/299] 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 25b0242ca6aee3484111739e95b6788ea56d1a64 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 9 Jun 2024 19:49:39 +0530 Subject: [PATCH 004/299] `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 7a21432e7774617731556d844d05686a8b3ff46d Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Mon, 10 Jun 2024 11:30:39 +0530 Subject: [PATCH 005/299] 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 4809e59b7ee0681d420f83a285d52e9424f2fad8 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 10 Jun 2024 09:31:21 -0400 Subject: [PATCH 006/299] 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 007/299] 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 35bdb9cee7dbb7f8733cab1b1fe327f525496773 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 11 Jun 2024 16:05:57 +0200 Subject: [PATCH 008/299] 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 bd37a70d8f0ed43c21ce3cf746e9a20bede221ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 11 Jun 2024 19:39:42 +0200 Subject: [PATCH 009/299] 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 010/299] 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 68b8a28bc4b09cdd88f28bf0ba102a274a461b0c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 11:39:06 +0200 Subject: [PATCH 011/299] 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 012/299] 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 a58ca342cae1d6bee488a42b6fc054c1072cfb8f Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 11:01:29 -0700 Subject: [PATCH 013/299] 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 b11cf8166f63fa061d98cd5812a94234fe974845 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 13:12:28 -0700 Subject: [PATCH 014/299] 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 4662e7d8568f55f7f56c55e7976b68a25824d1a3 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 14:57:57 -0700 Subject: [PATCH 015/299] 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 016/299] 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 017/299] 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 fcb92b4fa4f260086aafdb7d9e8e51a26360b46e Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 22:14:38 -0700 Subject: [PATCH 018/299] 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 019/299] 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 dc720f89f2689ddfb4c1e0d0ea0d442b6be6c006 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 12:22:42 +0200 Subject: [PATCH 020/299] 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 021/299] 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 022/299] 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 023/299] 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 024/299] 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 025/299] 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 026/299] 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 027/299] 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 028/299] 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 029/299] 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 030/299] 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 031/299] 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: Mon, 8 Apr 2024 14:51:54 +0200 Subject: [PATCH 032/299] 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 033/299] 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 034/299] 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 035/299] 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 036/299] 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 037/299] 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 038/299] 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 039/299] 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 040/299] 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 041/299] 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 042/299] 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 043/299] 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 044/299] 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 045/299] 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 046/299] 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 047/299] 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 048/299] 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 049/299] 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 050/299] 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 051/299] 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 052/299] 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 053/299] 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 054/299] 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 055/299] 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 056/299] 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 057/299] 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 058/299] 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 059/299] 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 060/299] 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 061/299] 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 062/299] 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 063/299] 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 064/299] 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 065/299] 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 066/299] 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 067/299] 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 068/299] 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 069/299] 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 070/299] 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 071/299] 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 072/299] 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 073/299] 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 074/299] 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 075/299] 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 076/299] 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 077/299] 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 078/299] 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 079/299] 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 080/299] 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 081/299] 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 082/299] 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 083/299] 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 084/299] 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 085/299] 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 086/299] 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 087/299] 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 088/299] 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 089/299] 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 090/299] 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 091/299] 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 092/299] 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 093/299] 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 094/299] 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 095/299] 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 096/299] 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 097/299] 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 098/299] 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 099/299] 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 100/299] 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 101/299] 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 102/299] 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 103/299] 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 104/299] 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 105/299] 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 106/299] 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 107/299] 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 108/299] 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 109/299] 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 110/299] 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 111/299] 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 112/299] 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 113/299] 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 114/299] 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 115/299] 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 116/299] 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 117/299] 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 118/299] 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 119/299] 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 120/299] 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 121/299] 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 122/299] 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 123/299] 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 124/299] 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 125/299] 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 126/299] 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 127/299] 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 128/299] 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 129/299] 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 130/299] 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 131/299] 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 132/299] 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 133/299] 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 134/299] 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 135/299] 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 136/299] 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 137/299] 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 138/299] 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 139/299] 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 140/299] 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 141/299] 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 a713476790c62a4c3b22ebc17bbd46ef75e96eb8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 3 Jul 2024 09:03:41 +0200 Subject: [PATCH 142/299] 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 143/299] 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 144/299] 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 145/299] 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 146/299] 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 147/299] 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 148/299] 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 149/299] 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 150/299] 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 151/299] 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 152/299] 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 153/299] 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 154/299] 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 155/299] 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 156/299] 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 157/299] 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 6ef00a503a62dc5554139804d22968b25ba4bf3f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Jul 2024 18:32:34 +0200 Subject: [PATCH 158/299] 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 159/299] 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 160/299] 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 161/299] 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 162/299] 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 163/299] 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 164/299] 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 165/299] 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 166/299] 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 167/299] 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 168/299] 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 169/299] 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 170/299] 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 171/299] 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 172/299] 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 173/299] 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 174/299] 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 175/299] 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 176/299] 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 177/299] 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 178/299] 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 179/299] 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 180/299] 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 181/299] 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 182/299] 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 183/299] 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 184/299] 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 185/299] 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 186/299] 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 187/299] 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 188/299] 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 189/299] 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 190/299] 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 191/299] `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 192/299] 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 193/299] 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 194/299] 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 195/299] 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 196/299] 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 c4e3e2dc27da93cae22cd35a11ee1ef87e23eb57 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 16:24:31 +0200 Subject: [PATCH 197/299] 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 4fd8f19ecfd8ced21c0f43bb3f3e3567d1d38bcd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 12:14:48 +0200 Subject: [PATCH 198/299] 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 6f5f741157ff14e8a67608be9bee2bfc8d5778a8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 13:51:03 +0200 Subject: [PATCH 199/299] 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 200/299] 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 201/299] 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 202/299] 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 203/299] 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 204/299] 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 205/299] 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 206/299] 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 207/299] 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 208/299] 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 209/299] 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 210/299] 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 211/299] 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 212/299] 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 213/299] 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 214/299] 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 215/299] 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 216/299] 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 217/299] 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 218/299] 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 219/299] 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 220/299] 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 221/299] 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 222/299] 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 223/299] 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 224/299] 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 225/299] 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 226/299] 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 227/299] 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 228/299] 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 229/299] 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 230/299] 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 231/299] 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 232/299] 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 233/299] 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 234/299] 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 235/299] 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 236/299] 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 237/299] 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 238/299] 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 239/299] 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 240/299] 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 241/299] 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 242/299] 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 243/299] 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 244/299] 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 245/299] 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 246/299] 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 247/299] 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 248/299] 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 249/299] 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 250/299] 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 251/299] 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 252/299] 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 253/299] 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 254/299] 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 255/299] 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 256/299] 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 257/299] 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 258/299] 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 259/299] 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 260/299] 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 261/299] 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 262/299] 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 263/299] 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 264/299] 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 265/299] 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 266/299] 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 267/299] 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 268/299] 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 269/299] 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 270/299] 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 6c9d62dcebbb042e24cb7e07c5cf5369f1db6ba0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:04:37 +0200 Subject: [PATCH 271/299] 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 272/299] 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 273/299] 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 d0e9878389cc00caff84d0bbaa37fe008af638ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 22:22:15 +0200 Subject: [PATCH 274/299] 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 0a1a116f4b2fd6840a61d8f85494cade6fd6a306 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Tue, 16 Jul 2024 13:51:52 -0700 Subject: [PATCH 275/299] 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 276/299] 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 277/299] 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 278/299] 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 279/299] 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 280/299] 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 281/299] 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 282/299] 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 283/299] 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 284/299] 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 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 285/299] 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 286/299] 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 287/299] 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 288/299] 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 289/299] 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 290/299] 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 291/299] 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 292/299] 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 293/299] 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 294/299] 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 295/299] 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 296/299] 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 297/299] 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 298/299] 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 299/299] 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