diff options
-rw-r--r-- | scripts/install-nix-from-closure.sh | 8 | ||||
-rw-r--r-- | src/libexpr/eval.cc | 21 | ||||
-rw-r--r-- | src/libexpr/flake/flake.cc | 2 | ||||
-rw-r--r-- | src/libexpr/nixexpr.hh | 1 | ||||
-rw-r--r-- | src/libfetchers/github.cc | 20 | ||||
-rw-r--r-- | src/libstore/local-store.cc | 2 | ||||
-rw-r--r-- | src/libstore/nar-info-disk-cache.cc | 6 | ||||
-rw-r--r-- | src/libutil/error.cc | 2 | ||||
-rw-r--r-- | src/libutil/error.hh | 2 | ||||
-rw-r--r-- | src/libutil/url-parts.hh | 2 | ||||
-rwxr-xr-x | src/nix-build/nix-build.cc | 4 | ||||
-rw-r--r-- | src/nix/build.cc | 6 | ||||
-rw-r--r-- | src/nix/installables.cc | 2 | ||||
-rw-r--r-- | src/nix/main.cc | 5 | ||||
-rw-r--r-- | src/nix/ping-store.md | 3 | ||||
-rw-r--r-- | src/nix/profile.cc | 24 | ||||
-rw-r--r-- | tests/nix-shell.sh | 18 | ||||
-rw-r--r-- | tests/shell.nix | 2 |
18 files changed, 104 insertions, 26 deletions
diff --git a/scripts/install-nix-from-closure.sh b/scripts/install-nix-from-closure.sh index 6352a8fac..0ee7ce5af 100644 --- a/scripts/install-nix-from-closure.sh +++ b/scripts/install-nix-from-closure.sh @@ -166,9 +166,15 @@ fi mkdir -p $dest/store printf "copying Nix to %s..." "${dest}/store" >&2 +# Insert a newline if no progress is shown. +if [ ! -t 0 ]; then + echo "" +fi for i in $(cd "$self/store" >/dev/null && echo ./*); do - printf "." >&2 + if [ -t 0 ]; then + printf "." >&2 + fi i_tmp="$dest/store/$i.$$" if [ -e "$i_tmp" ]; then rm -rf "$i_tmp" diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ead5bf8c7..f3471aac7 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -657,11 +657,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s)) }); } -LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1)) -{ - throw TypeError(s, s1); -} - LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2)) { throw TypeError({ @@ -686,6 +681,14 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * }); } +LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1)) +{ + throw MissingArgumentError({ + .hint = hintfmt(s, s1), + .errPos = pos + }); +} + LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2)) { e.addTrace(std::nullopt, s, s2); @@ -1376,7 +1379,13 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (j != args.end()) { actualArgs->attrs->push_back(*j); } else if (!i.def) { - throwTypeError("cannot auto-call a function that has an argument without a default value ('%1%')", i.name); + throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%') + +nix attempted to evaluate a function as a top level expression; in this case it must have its +arguments supplied either by default values, or passed explicitly with --arg or --argstr. + +https://nixos.org/manual/nix/stable/#ss-functions)", i.name); + } } } diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 61aeae543..0786fef3d 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -128,7 +128,7 @@ static FlakeInput parseFlakeInput(EvalState & state, attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean }); break; case nInt: - attrs.emplace(attr.name, attr.value->integer); + attrs.emplace(attr.name, (long unsigned int)attr.value->integer); break; default: throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected", diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index b80a7de4e..530202ff6 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -17,6 +17,7 @@ MakeError(ThrownError, AssertionError); MakeError(Abort, EvalError); MakeError(TypeError, EvalError); MakeError(UndefinedVarError, Error); +MakeError(MissingArgumentError, Error); MakeError(RestrictedPathError, Error); diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index db1ced5d6..8352ef02d 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -37,15 +37,29 @@ struct GitArchiveInputScheme : InputScheme std::optional<std::string> ref; std::optional<std::string> host_url; - if (path.size() == 2) { - } else if (path.size() == 3) { + auto size = path.size(); + if (size == 3) { if (std::regex_match(path[2], revRegex)) rev = Hash::parseAny(path[2], htSHA1); else if (std::regex_match(path[2], refRegex)) ref = path[2]; else throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]); - } else + } else if (size > 3) { + std::string rs; + for (auto i = std::next(path.begin(), 2); i != path.end(); i++) { + rs += *i; + if (std::next(i) != path.end()) { + rs += "/"; + } + } + + if (std::regex_match(rs, refRegex)) { + ref = rs; + } else { + throw BadURL("in URL '%s', '%s' is not a branch/tag name", url.url, rs); + } + } else if (size < 2) throw BadURL("URL '%s' is invalid", url.url); for (auto &[name, value] : url.query) { diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index d6d74a0b0..71e61cfe3 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -914,7 +914,7 @@ LocalStore::queryDerivationOutputMapNoResolve(const StorePath& path_) if (realisation) outputs.insert_or_assign(outputName, realisation->outPath); else - outputs.insert_or_assign(outputName, std::nullopt); + outputs.insert({outputName, std::nullopt}); } return outputs; diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 8541cc51f..1d8d2d57e 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -109,8 +109,10 @@ public: SQLiteStmt(state->db, "delete from NARs where ((present = 0 and timestamp < ?) or (present = 1 and timestamp < ?))") .use() - (now - settings.ttlNegativeNarInfoCache) - (now - settings.ttlPositiveNarInfoCache) + // Use a minimum TTL to prevent --refresh from + // nuking the entire disk cache. + (now - std::max(settings.ttlNegativeNarInfoCache.get(), 3600U)) + (now - std::max(settings.ttlPositiveNarInfoCache.get(), 30 * 24 * 3600U)) .exec(); debug("deleted %d entries from the NAR info disk cache", sqlite3_changes(state->db)); diff --git a/src/libutil/error.cc b/src/libutil/error.cc index e7dc3f1d3..2a67a730a 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -61,6 +61,8 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos) if (errPos.origin == foFile) { LinesOfCode loc; try { + // FIXME: when running as the daemon, make sure we don't + // open a file to which the client doesn't have access. AutoCloseFD fd = open(errPos.file.c_str(), O_RDONLY | O_CLOEXEC); if (!fd) return {}; diff --git a/src/libutil/error.hh b/src/libutil/error.hh index aa4fadfcc..1e0bde7ea 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -38,7 +38,7 @@ namespace nix { ErrorInfo structs are sent to the logger as part of an exception, or directly with the logError or logWarning macros. - See the error-demo.cc program for usage examples. + See libutil/tests/logging.cc for usage examples. */ diff --git a/src/libutil/url-parts.hh b/src/libutil/url-parts.hh index 5d21b8d1a..862d9fa6e 100644 --- a/src/libutil/url-parts.hh +++ b/src/libutil/url-parts.hh @@ -23,7 +23,7 @@ const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)"; const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)"; // A Git ref (i.e. branch or tag name). -const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check +const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.\\/-]*"; // FIXME: check extern std::regex refRegex; // Instead of defining what a good Git Ref is, we define what a bad Git Ref is diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 74fafd426..38048da52 100755 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -217,9 +217,9 @@ static void main_nix_build(int argc, char * * argv) // read the shebang to understand which packages to read from. Since // this is handled via nix-shell -p, we wrap our ruby script execution // in ruby -e 'load' which ignores the shebangs. - envCommand = (format("exec %1% %2% -e 'load(\"%3%\")' -- %4%") % execArgs % interpreter % script % joined.str()).str(); + envCommand = (format("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str(); } else { - envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % script % joined.str()).str(); + envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str(); } } diff --git a/src/nix/build.cc b/src/nix/build.cc index 4cb8ade08..724ce9d79 100644 --- a/src/nix/build.cc +++ b/src/nix/build.cc @@ -58,7 +58,8 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile if (outLink != "") if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) - for (size_t i = 0; i < buildables.size(); ++i) + for (const auto & [_i, buildable] : enumerate(buildables)) { + auto i = _i; std::visit(overloaded { [&](BuildableOpaque bo) { std::string symlink = outLink; @@ -74,7 +75,8 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile store2->addPermRoot(output.second, absPath(symlink)); } }, - }, buildables[i]); + }, buildable); + } updateProfile(buildables); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 50e3b29c4..34ee238bf 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -501,7 +501,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF auto drvInfo = DerivationInfo{ std::move(drvPath), - state->store->parseStorePath(attr->getAttr(state->sOutPath)->getString()), + state->store->maybeParseStorePath(attr->getAttr(state->sOutPath)->getString()), attr->getAttr(state->sOutputName)->getString() }; diff --git a/src/nix/main.cc b/src/nix/main.cc index 418396280..80422bd24 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -330,8 +330,11 @@ void mainWrapped(int argc, char * * argv) fileTransferSettings.connectTimeout = 1; } - if (args.refresh) + if (args.refresh) { settings.tarballTtl = 0; + settings.ttlNegativeNarInfoCache = 0; + settings.ttlPositiveNarInfoCache = 0; + } args.command->second->prepare(); args.command->second->run(); diff --git a/src/nix/ping-store.md b/src/nix/ping-store.md index 322093091..8c846791b 100644 --- a/src/nix/ping-store.md +++ b/src/nix/ping-store.md @@ -27,4 +27,7 @@ argument `--store` *url*) can be accessed. What this means is dependent on the type of the store. For instance, for an SSH store it means that Nix can connect to the specified machine. +If the command succeeds, Nix returns a exit code of 0 and does not +print any output. + )"" diff --git a/src/nix/profile.cc b/src/nix/profile.cc index ca95817d0..765d6866e 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -252,8 +252,28 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile pathsToBuild.push_back({drv.drvPath, StringSet{"out"}}); // FIXME manifest.elements.emplace_back(std::move(element)); - } else - throw UnimplementedError("'nix profile install' does not support argument '%s'", installable->what()); + } else { + auto buildables = build(store, Realise::Outputs, {installable}, bmNormal); + + for (auto & buildable : buildables) { + ProfileElement element; + + std::visit(overloaded { + [&](BuildableOpaque bo) { + pathsToBuild.push_back({bo.path, {}}); + element.storePaths.insert(bo.path); + }, + [&](BuildableFromDrv bfd) { + for (auto & output : store->queryDerivationOutputMap(bfd.drvPath)) { + pathsToBuild.push_back({bfd.drvPath, {output.first}}); + element.storePaths.insert(output.second); + } + }, + }, buildable); + + manifest.elements.emplace_back(std::move(element)); + } + } } store->buildPaths(pathsToBuild); diff --git a/tests/nix-shell.sh b/tests/nix-shell.sh index 7b2be650a..4775bafb9 100644 --- a/tests/nix-shell.sh +++ b/tests/nix-shell.sh @@ -47,6 +47,14 @@ 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 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 +chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh + +output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.sh abc def) +[ "$output" = "foo bar abc def" ] + # Test nix-shell shebang mode for ruby # This uses a fake interpreter that returns the arguments passed # This, in turn, verifies the `rc` script is valid and the `load()` script (given using `-e`) is as expected. @@ -54,7 +62,15 @@ sed -e "s|@SHELL_PROG@|$(type -p nix-shell)|" shell.shebang.rb > $TEST_ROOT/shel chmod a+rx $TEST_ROOT/shell.shebang.rb output=$($TEST_ROOT/shell.shebang.rb abc ruby) -[ "$output" = '-e load("'"$TEST_ROOT"'/shell.shebang.rb") -- abc ruby' ] +[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/shell.shebang.rb abc ruby' ] + +# Test nix-shell shebang mode for ruby again with metacharacters in the filename. +# Note: fake interpreter only space-separates args without adding escapes to its output. +sed -e "s|@SHELL_PROG@|$(type -p nix-shell)|" shell.shebang.rb > $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb +chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb + +output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.rb abc ruby) +[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/spaced \'\''"shell.shebang.rb abc ruby' ] # Test 'nix develop'. nix develop -f shell.nix shellDrv -c bash -c '[[ -n $stdenv ]]' diff --git a/tests/shell.nix b/tests/shell.nix index 6ce59b416..24ebcc04c 100644 --- a/tests/shell.nix +++ b/tests/shell.nix @@ -50,7 +50,7 @@ let pkgs = rec { # ruby "interpreter" that outputs "$@" ruby = runCommand "ruby" {} '' mkdir -p $out/bin - echo 'printf -- "$*"' > $out/bin/ruby + echo 'printf %s "$*"' > $out/bin/ruby chmod a+rx $out/bin/ruby ''; |