mirror of
https://github.com/NixOS/nix.git
synced 2024-11-29 02:02:27 +00:00
Merge remote-tracking branch 'upstream/master' into list-experimental-features
This commit is contained in:
commit
3f98353f19
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -23,6 +23,7 @@ Maintainers: tick if completed or explain if not relevant
|
|||||||
- unit tests - `src/*/tests`
|
- unit tests - `src/*/tests`
|
||||||
- integration tests - `tests/nixos/*`
|
- integration tests - `tests/nixos/*`
|
||||||
- [ ] documentation in the manual
|
- [ ] documentation in the manual
|
||||||
|
- [ ] documentation in the internal API docs
|
||||||
- [ ] code and comments are self-explanatory
|
- [ ] code and comments are self-explanatory
|
||||||
- [ ] commit message explains why the change was made
|
- [ ] commit message explains why the change was made
|
||||||
- [ ] new feature or incompatible change: updated release notes
|
- [ ] new feature or incompatible change: updated release notes
|
||||||
|
4
local.mk
4
local.mk
@ -1,6 +1,8 @@
|
|||||||
clean-files += Makefile.config
|
clean-files += Makefile.config
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -Wno-deprecated-declarations
|
GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch
|
||||||
|
# Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers.
|
||||||
|
ERROR_SWITCH_ENUM = -Werror=switch-enum
|
||||||
|
|
||||||
$(foreach i, config.h $(wildcard src/lib*/*.hh), \
|
$(foreach i, config.h $(wildcard src/lib*/*.hh), \
|
||||||
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
|
$(eval $(call install-file-in, $(i), $(includedir)/nix, 0644)))
|
||||||
|
@ -69,6 +69,7 @@ Issues on the board progress through the following states:
|
|||||||
2. [security](https://github.com/NixOS/nix/labels/security)
|
2. [security](https://github.com/NixOS/nix/labels/security)
|
||||||
3. [regression](https://github.com/NixOS/nix/labels/regression)
|
3. [regression](https://github.com/NixOS/nix/labels/regression)
|
||||||
4. [bug](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc)
|
4. [bug](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Abug+sort%3Areactions-%2B1-desc)
|
||||||
|
5. [tests of existing functionality](https://github.com/NixOS/nix/issues?q=is%3Aopen+label%3Atests+-label%3Afeature+sort%3Areactions-%2B1-desc)
|
||||||
|
|
||||||
- [oldest pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc)
|
- [oldest pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Acreated-asc)
|
||||||
- [most popular pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Areactions-%2B1-desc)
|
- [most popular pull requests](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+sort%3Areactions-%2B1-desc)
|
||||||
@ -91,7 +92,7 @@ Issues on the board progress through the following states:
|
|||||||
|
|
||||||
Contributors who took the time to implement concrete change proposals should not wait indefinitely.
|
Contributors who took the time to implement concrete change proposals should not wait indefinitely.
|
||||||
|
|
||||||
- Prioritise fixing bugs over documentation, improvements or new features
|
- Prioritise fixing bugs and testing over documentation, improvements or new features
|
||||||
|
|
||||||
The team values stability and accessibility higher than raw functionality.
|
The team values stability and accessibility higher than raw functionality.
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
$(buildprefix)%.o: %.cc
|
$(buildprefix)%.o: %.cc
|
||||||
@mkdir -p "$(dir $@)"
|
@mkdir -p "$(dir $@)"
|
||||||
$(trace-cxx) $(CXX) -o $@ -c $< $(CPPFLAGS) $(GLOBAL_CXXFLAGS_PCH) $(GLOBAL_CXXFLAGS) $(CXXFLAGS) $($@_CXXFLAGS) -MMD -MF $(call filename-to-dep, $@) -MP
|
$(trace-cxx) $(CXX) -o $@ -c $< $(CPPFLAGS) $(GLOBAL_CXXFLAGS_PCH) $(GLOBAL_CXXFLAGS) $(CXXFLAGS) $($@_CXXFLAGS) $(ERROR_SWITCH_ENUM) -MMD -MF $(call filename-to-dep, $@) -MP
|
||||||
|
|
||||||
$(buildprefix)%.o: %.cpp
|
$(buildprefix)%.o: %.cpp
|
||||||
@mkdir -p "$(dir $@)"
|
@mkdir -p "$(dir $@)"
|
||||||
$(trace-cxx) $(CXX) -o $@ -c $< $(CPPFLAGS) $(GLOBAL_CXXFLAGS_PCH) $(GLOBAL_CXXFLAGS) $(CXXFLAGS) $($@_CXXFLAGS) -MMD -MF $(call filename-to-dep, $@) -MP
|
$(trace-cxx) $(CXX) -o $@ -c $< $(CPPFLAGS) $(GLOBAL_CXXFLAGS_PCH) $(GLOBAL_CXXFLAGS) $(CXXFLAGS) $($@_CXXFLAGS) $(ERROR_SWITCH_ENUM) -MMD -MF $(call filename-to-dep, $@) -MP
|
||||||
|
|
||||||
$(buildprefix)%.o: %.c
|
$(buildprefix)%.o: %.c
|
||||||
@mkdir -p "$(dir $@)"
|
@mkdir -p "$(dir $@)"
|
||||||
|
@ -102,6 +102,28 @@ MixFlakeOptions::MixFlakeOptions()
|
|||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "reference-lock-file",
|
||||||
|
.description = "Read the given lock file instead of `flake.lock` within the top-level flake.",
|
||||||
|
.category = category,
|
||||||
|
.labels = {"flake-lock-path"},
|
||||||
|
.handler = {[&](std::string lockFilePath) {
|
||||||
|
lockFlags.referenceLockFilePath = lockFilePath;
|
||||||
|
}},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "output-lock-file",
|
||||||
|
.description = "Write the given lock file instead of `flake.lock` within the top-level flake.",
|
||||||
|
.category = category,
|
||||||
|
.labels = {"flake-lock-path"},
|
||||||
|
.handler = {[&](std::string lockFilePath) {
|
||||||
|
lockFlags.outputLockFilePath = lockFilePath;
|
||||||
|
}},
|
||||||
|
.completer = completePath
|
||||||
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "inputs-from",
|
.longName = "inputs-from",
|
||||||
.description = "Use the inputs of the specified flake as registry entries.",
|
.description = "Use the inputs of the specified flake as registry entries.",
|
||||||
|
@ -252,7 +252,9 @@ void NixRepl::mainLoop()
|
|||||||
el_hist_size = 1000;
|
el_hist_size = 1000;
|
||||||
#endif
|
#endif
|
||||||
read_history(historyFile.c_str());
|
read_history(historyFile.c_str());
|
||||||
|
auto oldRepl = curRepl;
|
||||||
curRepl = this;
|
curRepl = this;
|
||||||
|
Finally restoreRepl([&] { curRepl = oldRepl; });
|
||||||
#ifndef READLINE
|
#ifndef READLINE
|
||||||
rl_set_complete_func(completionCallback);
|
rl_set_complete_func(completionCallback);
|
||||||
rl_set_list_possib_func(listPossibleCallback);
|
rl_set_list_possib_func(listPossibleCallback);
|
||||||
@ -1024,6 +1026,8 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
|||||||
str << v.fpoint;
|
str << v.fpoint;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case nThunk:
|
||||||
|
case nExternal:
|
||||||
default:
|
default:
|
||||||
str << ANSI_RED "«unknown»" ANSI_NORMAL;
|
str << ANSI_RED "«unknown»" ANSI_NORMAL;
|
||||||
break;
|
break;
|
||||||
|
@ -173,7 +173,17 @@ void Value::print(const SymbolTable & symbols, std::ostream & str,
|
|||||||
case tFloat:
|
case tFloat:
|
||||||
str << fpoint;
|
str << fpoint;
|
||||||
break;
|
break;
|
||||||
|
case tBlackhole:
|
||||||
|
// Although we know for sure that it's going to be an infinite recursion
|
||||||
|
// when this value is accessed _in the current context_, it's likely
|
||||||
|
// that the user will misinterpret a simpler «infinite recursion» output
|
||||||
|
// as a definitive statement about the value, while in fact it may be
|
||||||
|
// a valid value after `builtins.trace` and perhaps some other steps
|
||||||
|
// have completed.
|
||||||
|
str << "«potential infinite recursion»";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
printError("Nix evaluator internal error: Value::print(): invalid value type %1%", internalType);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,6 +239,9 @@ std::string_view showType(ValueType type)
|
|||||||
|
|
||||||
std::string showType(const Value & v)
|
std::string showType(const Value & v)
|
||||||
{
|
{
|
||||||
|
// Allow selecting a subset of enum values
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
switch (v.internalType) {
|
switch (v.internalType) {
|
||||||
case tString: return v.string.context ? "a string with context" : "a string";
|
case tString: return v.string.context ? "a string with context" : "a string";
|
||||||
case tPrimOp:
|
case tPrimOp:
|
||||||
@ -242,16 +255,21 @@ std::string showType(const Value & v)
|
|||||||
default:
|
default:
|
||||||
return std::string(showType(v.type()));
|
return std::string(showType(v.type()));
|
||||||
}
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
PosIdx Value::determinePos(const PosIdx pos) const
|
PosIdx Value::determinePos(const PosIdx pos) const
|
||||||
{
|
{
|
||||||
|
// Allow selecting a subset of enum values
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
switch (internalType) {
|
switch (internalType) {
|
||||||
case tAttrs: return attrs->pos;
|
case tAttrs: return attrs->pos;
|
||||||
case tLambda: return lambda.fun->pos;
|
case tLambda: return lambda.fun->pos;
|
||||||
case tApp: return app.left->determinePos(pos);
|
case tApp: return app.left->determinePos(pos);
|
||||||
default: return pos;
|
default: return pos;
|
||||||
}
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Value::isTrivial() const
|
bool Value::isTrivial() const
|
||||||
@ -2327,6 +2345,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||||||
case nFloat:
|
case nFloat:
|
||||||
return v1.fpoint == v2.fpoint;
|
return v1.fpoint == v2.fpoint;
|
||||||
|
|
||||||
|
case nThunk: // Must not be left by forceValue
|
||||||
default:
|
default:
|
||||||
error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow<EvalError>();
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,9 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||||||
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
||||||
input.follows = follows;
|
input.follows = follows;
|
||||||
} else {
|
} else {
|
||||||
|
// Allow selecting a subset of enum values
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
switch (attr.value->type()) {
|
switch (attr.value->type()) {
|
||||||
case nString:
|
case nString:
|
||||||
attrs.emplace(state.symbols[attr.name], attr.value->string.s);
|
attrs.emplace(state.symbols[attr.name], attr.value->string.s);
|
||||||
@ -139,6 +142,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||||||
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||||
state.symbols[attr.name], showType(*attr.value));
|
state.symbols[attr.name], showType(*attr.value));
|
||||||
}
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
}
|
}
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(
|
e.addTrace(
|
||||||
@ -334,10 +338,14 @@ LockedFlake lockFlake(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!fetchSettings.allowDirty && lockFlags.referenceLockFilePath) {
|
||||||
|
throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false");
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: symlink attack
|
// FIXME: symlink attack
|
||||||
auto oldLockFile = LockFile::read(
|
auto oldLockFile = LockFile::read(
|
||||||
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock");
|
lockFlags.referenceLockFilePath.value_or(
|
||||||
|
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock"));
|
||||||
|
|
||||||
debug("old lock file: %s", oldLockFile);
|
debug("old lock file: %s", oldLockFile);
|
||||||
|
|
||||||
@ -619,13 +627,20 @@ LockedFlake lockFlake(
|
|||||||
|
|
||||||
debug("new lock file: %s", newLockFile);
|
debug("new lock file: %s", newLockFile);
|
||||||
|
|
||||||
|
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
|
||||||
|
auto sourcePath = topRef.input.getSourcePath();
|
||||||
|
auto outputLockFilePath = sourcePath ? std::optional{*sourcePath + "/" + relPath} : std::nullopt;
|
||||||
|
if (lockFlags.outputLockFilePath) {
|
||||||
|
outputLockFilePath = lockFlags.outputLockFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check whether we need to / can write the new lock file. */
|
/* Check whether we need to / can write the new lock file. */
|
||||||
if (!(newLockFile == oldLockFile)) {
|
if (newLockFile != oldLockFile || lockFlags.outputLockFilePath) {
|
||||||
|
|
||||||
auto diff = LockFile::diff(oldLockFile, newLockFile);
|
auto diff = LockFile::diff(oldLockFile, newLockFile);
|
||||||
|
|
||||||
if (lockFlags.writeLockFile) {
|
if (lockFlags.writeLockFile) {
|
||||||
if (auto sourcePath = topRef.input.getSourcePath()) {
|
if (outputLockFilePath) {
|
||||||
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
||||||
if (fetchSettings.warnDirty)
|
if (fetchSettings.warnDirty)
|
||||||
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
||||||
@ -633,25 +648,24 @@ LockedFlake lockFlake(
|
|||||||
if (!lockFlags.updateLockFile)
|
if (!lockFlags.updateLockFile)
|
||||||
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
|
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
|
||||||
|
|
||||||
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
|
bool lockFileExists = pathExists(*outputLockFilePath);
|
||||||
|
|
||||||
auto path = *sourcePath + "/" + relPath;
|
|
||||||
|
|
||||||
bool lockFileExists = pathExists(path);
|
|
||||||
|
|
||||||
if (lockFileExists) {
|
if (lockFileExists) {
|
||||||
auto s = chomp(diff);
|
auto s = chomp(diff);
|
||||||
if (s.empty())
|
if (s.empty())
|
||||||
warn("updating lock file '%s'", path);
|
warn("updating lock file '%s'", *outputLockFilePath);
|
||||||
else
|
else
|
||||||
warn("updating lock file '%s':\n%s", path, s);
|
warn("updating lock file '%s':\n%s", *outputLockFilePath, s);
|
||||||
} else
|
} else
|
||||||
warn("creating lock file '%s'", path);
|
warn("creating lock file '%s'", *outputLockFilePath);
|
||||||
|
|
||||||
newLockFile.write(path);
|
newLockFile.write(*outputLockFilePath);
|
||||||
|
|
||||||
std::optional<std::string> commitMessage = std::nullopt;
|
std::optional<std::string> commitMessage = std::nullopt;
|
||||||
if (lockFlags.commitLockFile) {
|
if (lockFlags.commitLockFile) {
|
||||||
|
if (lockFlags.outputLockFilePath) {
|
||||||
|
throw Error("--commit-lock-file and --output-lock-file are currently incompatible");
|
||||||
|
}
|
||||||
std::string cm;
|
std::string cm;
|
||||||
|
|
||||||
cm = fetchSettings.commitLockFileSummary.get();
|
cm = fetchSettings.commitLockFileSummary.get();
|
||||||
|
@ -118,6 +118,12 @@ struct LockFlags
|
|||||||
/* Whether to commit changes to flake.lock. */
|
/* Whether to commit changes to flake.lock. */
|
||||||
bool commitLockFile = false;
|
bool commitLockFile = false;
|
||||||
|
|
||||||
|
/* The path to a lock file to read instead of the `flake.lock` file in the top-level flake */
|
||||||
|
std::optional<std::string> referenceLockFilePath;
|
||||||
|
|
||||||
|
/* The path to a lock file to write to instead of the `flake.lock` file in the top-level flake */
|
||||||
|
std::optional<Path> outputLockFilePath;
|
||||||
|
|
||||||
/* Flake inputs to be overridden. */
|
/* Flake inputs to be overridden. */
|
||||||
std::map<InputPath, FlakeRef> inputOverrides;
|
std::map<InputPath, FlakeRef> inputOverrides;
|
||||||
|
|
||||||
|
@ -46,3 +46,5 @@ $(foreach i, $(wildcard src/libexpr/flake/*.hh), \
|
|||||||
$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh
|
$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh
|
||||||
|
|
||||||
$(d)/flake/flake.cc: $(d)/flake/call-flake.nix.gen.hh
|
$(d)/flake/flake.cc: $(d)/flake/call-flake.nix.gen.hh
|
||||||
|
|
||||||
|
src/libexpr/primops/fromTOML.o: ERROR_SWITCH_ENUM =
|
||||||
|
@ -577,6 +577,9 @@ struct CompareValues
|
|||||||
return v1->integer < v2->fpoint;
|
return v1->integer < v2->fpoint;
|
||||||
if (v1->type() != v2->type())
|
if (v1->type() != v2->type())
|
||||||
state.error("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow<EvalError>();
|
state.error("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow<EvalError>();
|
||||||
|
// Allow selecting a subset of enum values
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
switch (v1->type()) {
|
switch (v1->type()) {
|
||||||
case nInt:
|
case nInt:
|
||||||
return v1->integer < v2->integer;
|
return v1->integer < v2->integer;
|
||||||
@ -599,6 +602,7 @@ struct CompareValues
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
state.error("cannot compare %s with %s; values of that type are incomparable", showType(*v1), showType(*v2)).debugThrow<EvalError>();
|
state.error("cannot compare %s with %s; values of that type are incomparable", showType(*v1), showType(*v2)).debugThrow<EvalError>();
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
}
|
}
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (!errorCtx.empty())
|
if (!errorCtx.empty())
|
||||||
|
@ -53,6 +53,7 @@ struct BuildResult
|
|||||||
case LogLimitExceeded: return "LogLimitExceeded";
|
case LogLimitExceeded: return "LogLimitExceeded";
|
||||||
case NotDeterministic: return "NotDeterministic";
|
case NotDeterministic: return "NotDeterministic";
|
||||||
case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
|
case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
|
||||||
|
case NoSubstituters: return "NoSubstituters";
|
||||||
default: return "Unknown";
|
default: return "Unknown";
|
||||||
};
|
};
|
||||||
}();
|
}();
|
||||||
|
@ -407,6 +407,10 @@ struct curlFileTransfer : public FileTransfer
|
|||||||
err = Misc;
|
err = Misc;
|
||||||
} else {
|
} else {
|
||||||
// Don't bother retrying on certain cURL errors either
|
// Don't bother retrying on certain cURL errors either
|
||||||
|
|
||||||
|
// Allow selecting a subset of enum values
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case CURLE_FAILED_INIT:
|
case CURLE_FAILED_INIT:
|
||||||
case CURLE_URL_MALFORMAT:
|
case CURLE_URL_MALFORMAT:
|
||||||
@ -427,6 +431,7 @@ struct curlFileTransfer : public FileTransfer
|
|||||||
default: // Shut up warnings
|
default: // Shut up warnings
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt++;
|
attempt++;
|
||||||
|
@ -1134,54 +1134,6 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME: move this, it's not specific to LocalStore.
|
|
||||||
void LocalStore::querySubstitutablePathInfos(const StorePathCAMap & paths, SubstitutablePathInfos & infos)
|
|
||||||
{
|
|
||||||
if (!settings.useSubstitutes) return;
|
|
||||||
for (auto & sub : getDefaultSubstituters()) {
|
|
||||||
for (auto & path : paths) {
|
|
||||||
if (infos.count(path.first))
|
|
||||||
// Choose first succeeding substituter.
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto subPath(path.first);
|
|
||||||
|
|
||||||
// Recompute store path so that we can use a different store root.
|
|
||||||
if (path.second) {
|
|
||||||
subPath = makeFixedOutputPathFromCA(path.first.name(), *path.second);
|
|
||||||
if (sub->storeDir == storeDir)
|
|
||||||
assert(subPath == path.first);
|
|
||||||
if (subPath != path.first)
|
|
||||||
debug("replaced path '%s' with '%s' for substituter '%s'", printStorePath(path.first), sub->printStorePath(subPath), sub->getUri());
|
|
||||||
} else if (sub->storeDir != storeDir) continue;
|
|
||||||
|
|
||||||
debug("checking substituter '%s' for path '%s'", sub->getUri(), sub->printStorePath(subPath));
|
|
||||||
try {
|
|
||||||
auto info = sub->queryPathInfo(subPath);
|
|
||||||
|
|
||||||
if (sub->storeDir != storeDir && !(info->isContentAddressed(*sub) && info->references.empty()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
|
|
||||||
std::shared_ptr<const ValidPathInfo>(info));
|
|
||||||
infos.insert_or_assign(path.first, SubstitutablePathInfo{
|
|
||||||
info->deriver,
|
|
||||||
info->references,
|
|
||||||
narInfo ? narInfo->fileSize : 0,
|
|
||||||
info->narSize});
|
|
||||||
} catch (InvalidPath &) {
|
|
||||||
} catch (SubstituterDisabled &) {
|
|
||||||
} catch (Error & e) {
|
|
||||||
if (settings.tryFallback)
|
|
||||||
logError(e.info());
|
|
||||||
else
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::registerValidPath(const ValidPathInfo & info)
|
void LocalStore::registerValidPath(const ValidPathInfo & info)
|
||||||
{
|
{
|
||||||
registerValidPaths({{info.path, info}});
|
registerValidPaths({{info.path, info}});
|
||||||
|
@ -134,9 +134,6 @@ public:
|
|||||||
|
|
||||||
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
|
StorePathSet querySubstitutablePaths(const StorePathSet & paths) override;
|
||||||
|
|
||||||
void querySubstitutablePathInfos(const StorePathCAMap & paths,
|
|
||||||
SubstitutablePathInfos & infos) override;
|
|
||||||
|
|
||||||
bool pathInfoIsUntrusted(const ValidPathInfo &) override;
|
bool pathInfoIsUntrusted(const ValidPathInfo &) override;
|
||||||
bool realisationIsUntrusted(const Realisation & ) override;
|
bool realisationIsUntrusted(const Realisation & ) override;
|
||||||
|
|
||||||
|
@ -275,6 +275,7 @@ json listNar(ref<FSAccessor> accessor, const Path & path, bool recurse)
|
|||||||
obj["type"] = "symlink";
|
obj["type"] = "symlink";
|
||||||
obj["target"] = accessor->readLink(path);
|
obj["target"] = accessor->readLink(path);
|
||||||
break;
|
break;
|
||||||
|
case FSAccessor::Type::tMissing:
|
||||||
default:
|
default:
|
||||||
throw Error("path '%s' does not exist in NAR", path);
|
throw Error("path '%s' does not exist in NAR", path);
|
||||||
}
|
}
|
||||||
|
@ -507,6 +507,54 @@ StorePathSet Store::queryDerivationOutputs(const StorePath & path)
|
|||||||
return outputPaths;
|
return outputPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Store::querySubstitutablePathInfos(const StorePathCAMap & paths, SubstitutablePathInfos & infos)
|
||||||
|
{
|
||||||
|
if (!settings.useSubstitutes) return;
|
||||||
|
for (auto & sub : getDefaultSubstituters()) {
|
||||||
|
for (auto & path : paths) {
|
||||||
|
if (infos.count(path.first))
|
||||||
|
// Choose first succeeding substituter.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto subPath(path.first);
|
||||||
|
|
||||||
|
// Recompute store path so that we can use a different store root.
|
||||||
|
if (path.second) {
|
||||||
|
subPath = makeFixedOutputPathFromCA(path.first.name(), *path.second);
|
||||||
|
if (sub->storeDir == storeDir)
|
||||||
|
assert(subPath == path.first);
|
||||||
|
if (subPath != path.first)
|
||||||
|
debug("replaced path '%s' with '%s' for substituter '%s'", printStorePath(path.first), sub->printStorePath(subPath), sub->getUri());
|
||||||
|
} else if (sub->storeDir != storeDir) continue;
|
||||||
|
|
||||||
|
debug("checking substituter '%s' for path '%s'", sub->getUri(), sub->printStorePath(subPath));
|
||||||
|
try {
|
||||||
|
auto info = sub->queryPathInfo(subPath);
|
||||||
|
|
||||||
|
if (sub->storeDir != storeDir && !(info->isContentAddressed(*sub) && info->references.empty()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto narInfo = std::dynamic_pointer_cast<const NarInfo>(
|
||||||
|
std::shared_ptr<const ValidPathInfo>(info));
|
||||||
|
infos.insert_or_assign(path.first, SubstitutablePathInfo{
|
||||||
|
info->deriver,
|
||||||
|
info->references,
|
||||||
|
narInfo ? narInfo->fileSize : 0,
|
||||||
|
info->narSize});
|
||||||
|
} catch (InvalidPath &) {
|
||||||
|
} catch (SubstituterDisabled &) {
|
||||||
|
} catch (Error & e) {
|
||||||
|
if (settings.tryFallback)
|
||||||
|
logError(e.info());
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Store::isValidPath(const StorePath & storePath)
|
bool Store::isValidPath(const StorePath & storePath)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
@ -456,7 +456,7 @@ public:
|
|||||||
* resulting ‘infos’ map.
|
* resulting ‘infos’ map.
|
||||||
*/
|
*/
|
||||||
virtual void querySubstitutablePathInfos(const StorePathCAMap & paths,
|
virtual void querySubstitutablePathInfos(const StorePathCAMap & paths,
|
||||||
SubstitutablePathInfos & infos) { return; };
|
SubstitutablePathInfos & infos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a path into the store.
|
* Import a path into the store.
|
||||||
|
@ -236,8 +236,6 @@ nlohmann::json Args::toJSON()
|
|||||||
auto flags = nlohmann::json::object();
|
auto flags = nlohmann::json::object();
|
||||||
|
|
||||||
for (auto & [name, flag] : longFlags) {
|
for (auto & [name, flag] : longFlags) {
|
||||||
/* Skip experimental flags when listing flags. */
|
|
||||||
if (!experimentalFeatureSettings.isEnabled(flag->experimentalFeature)) continue;
|
|
||||||
auto j = nlohmann::json::object();
|
auto j = nlohmann::json::object();
|
||||||
if (flag->aliases.count(name)) continue;
|
if (flag->aliases.count(name)) continue;
|
||||||
if (flag->shortName)
|
if (flag->shortName)
|
||||||
@ -249,6 +247,11 @@ nlohmann::json Args::toJSON()
|
|||||||
j["arity"] = flag->handler.arity;
|
j["arity"] = flag->handler.arity;
|
||||||
if (!flag->labels.empty())
|
if (!flag->labels.empty())
|
||||||
j["labels"] = flag->labels;
|
j["labels"] = flag->labels;
|
||||||
|
// TODO With C++23 use `std::optional::tranform`
|
||||||
|
if (auto & xp = flag->experimentalFeature)
|
||||||
|
j["experimental-feature"] = showExperimentalFeature(*xp);
|
||||||
|
else
|
||||||
|
j["experimental-feature"] = nullptr;
|
||||||
flags[name] = std::move(j);
|
flags[name] = std::move(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,6 +348,11 @@ Strings argvToStrings(int argc, char * * argv)
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<ExperimentalFeature> Command::experimentalFeature ()
|
||||||
|
{
|
||||||
|
return { Xp::NixCommand };
|
||||||
|
}
|
||||||
|
|
||||||
MultiCommand::MultiCommand(const Commands & commands_)
|
MultiCommand::MultiCommand(const Commands & commands_)
|
||||||
: commands(commands_)
|
: commands(commands_)
|
||||||
{
|
{
|
||||||
@ -408,6 +416,11 @@ nlohmann::json MultiCommand::toJSON()
|
|||||||
cat["id"] = command->category();
|
cat["id"] = command->category();
|
||||||
cat["description"] = trim(categories[command->category()]);
|
cat["description"] = trim(categories[command->category()]);
|
||||||
j["category"] = std::move(cat);
|
j["category"] = std::move(cat);
|
||||||
|
// TODO With C++23 use `std::optional::tranform`
|
||||||
|
if (auto xp = command->experimentalFeature())
|
||||||
|
cat["experimental-feature"] = showExperimentalFeature(*xp);
|
||||||
|
else
|
||||||
|
cat["experimental-feature"] = nullptr;
|
||||||
cmds[name] = std::move(j);
|
cmds[name] = std::move(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +236,8 @@ struct Command : virtual public Args
|
|||||||
|
|
||||||
static constexpr Category catDefault = 0;
|
static constexpr Category catDefault = 0;
|
||||||
|
|
||||||
|
virtual std::optional<ExperimentalFeature> experimentalFeature ();
|
||||||
|
|
||||||
virtual Category category() { return catDefault; }
|
virtual Category category() { return catDefault; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,9 +65,10 @@ public:
|
|||||||
switch (lvl) {
|
switch (lvl) {
|
||||||
case lvlError: c = '3'; break;
|
case lvlError: c = '3'; break;
|
||||||
case lvlWarn: c = '4'; break;
|
case lvlWarn: c = '4'; break;
|
||||||
case lvlInfo: c = '5'; break;
|
case lvlNotice: case lvlInfo: c = '5'; break;
|
||||||
case lvlTalkative: case lvlChatty: c = '6'; break;
|
case lvlTalkative: case lvlChatty: c = '6'; break;
|
||||||
default: c = '7';
|
case lvlDebug: case lvlVomit: c = '7';
|
||||||
|
default: c = '7'; break; // should not happen, and missing enum case is reported by -Werror=switch-enum
|
||||||
}
|
}
|
||||||
prefix = std::string("<") + c + ">";
|
prefix = std::string("<") + c + ">";
|
||||||
}
|
}
|
||||||
|
@ -277,17 +277,17 @@ static void printTree(const StorePath & path,
|
|||||||
static void opQuery(Strings opFlags, Strings opArgs)
|
static void opQuery(Strings opFlags, Strings opArgs)
|
||||||
{
|
{
|
||||||
enum QueryType
|
enum QueryType
|
||||||
{ qDefault, qOutputs, qRequisites, qReferences, qReferrers
|
{ qOutputs, qRequisites, qReferences, qReferrers
|
||||||
, qReferrersClosure, qDeriver, qBinding, qHash, qSize
|
, qReferrersClosure, qDeriver, qBinding, qHash, qSize
|
||||||
, qTree, qGraph, qGraphML, qResolve, qRoots };
|
, qTree, qGraph, qGraphML, qResolve, qRoots };
|
||||||
QueryType query = qDefault;
|
std::optional<QueryType> query;
|
||||||
bool useOutput = false;
|
bool useOutput = false;
|
||||||
bool includeOutputs = false;
|
bool includeOutputs = false;
|
||||||
bool forceRealise = false;
|
bool forceRealise = false;
|
||||||
std::string bindingName;
|
std::string bindingName;
|
||||||
|
|
||||||
for (auto & i : opFlags) {
|
for (auto & i : opFlags) {
|
||||||
QueryType prev = query;
|
std::optional<QueryType> prev = query;
|
||||||
if (i == "--outputs") query = qOutputs;
|
if (i == "--outputs") query = qOutputs;
|
||||||
else if (i == "--requisites" || i == "-R") query = qRequisites;
|
else if (i == "--requisites" || i == "-R") query = qRequisites;
|
||||||
else if (i == "--references") query = qReferences;
|
else if (i == "--references") query = qReferences;
|
||||||
@ -312,15 +312,15 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
|||||||
else if (i == "--force-realise" || i == "--force-realize" || i == "-f") forceRealise = true;
|
else if (i == "--force-realise" || i == "--force-realize" || i == "-f") forceRealise = true;
|
||||||
else if (i == "--include-outputs") includeOutputs = true;
|
else if (i == "--include-outputs") includeOutputs = true;
|
||||||
else throw UsageError("unknown flag '%1%'", i);
|
else throw UsageError("unknown flag '%1%'", i);
|
||||||
if (prev != qDefault && prev != query)
|
if (prev && prev != query)
|
||||||
throw UsageError("query type '%1%' conflicts with earlier flag", i);
|
throw UsageError("query type '%1%' conflicts with earlier flag", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query == qDefault) query = qOutputs;
|
if (!query) query = qOutputs;
|
||||||
|
|
||||||
RunPager pager;
|
RunPager pager;
|
||||||
|
|
||||||
switch (query) {
|
switch (*query) {
|
||||||
|
|
||||||
case qOutputs: {
|
case qOutputs: {
|
||||||
for (auto & i : opArgs) {
|
for (auto & i : opArgs) {
|
||||||
|
@ -39,6 +39,14 @@ struct CmdDoctor : StoreCommand
|
|||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command is stable before the others
|
||||||
|
*/
|
||||||
|
std::optional<ExperimentalFeature> experimentalFeature() override
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return "check your system for potential problems and print a PASS or FAIL for each check";
|
return "check your system for potential problems and print a PASS or FAIL for each check";
|
||||||
|
@ -83,6 +83,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
|||||||
.description = "Print full build logs on standard error.",
|
.description = "Print full build logs on standard error.",
|
||||||
.category = loggingCategory,
|
.category = loggingCategory,
|
||||||
.handler = {[&]() { logger->setPrintBuildLogs(true); }},
|
.handler = {[&]() { logger->setPrintBuildLogs(true); }},
|
||||||
|
.experimentalFeature = Xp::NixCommand,
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
@ -98,6 +99,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
|||||||
.description = "Disable substituters and consider all previously downloaded files up-to-date.",
|
.description = "Disable substituters and consider all previously downloaded files up-to-date.",
|
||||||
.category = miscCategory,
|
.category = miscCategory,
|
||||||
.handler = {[&]() { useNet = false; }},
|
.handler = {[&]() { useNet = false; }},
|
||||||
|
.experimentalFeature = Xp::NixCommand,
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
@ -105,6 +107,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
|||||||
.description = "Consider all previously downloaded files out-of-date.",
|
.description = "Consider all previously downloaded files out-of-date.",
|
||||||
.category = miscCategory,
|
.category = miscCategory,
|
||||||
.handler = {[&]() { refresh = true; }},
|
.handler = {[&]() { refresh = true; }},
|
||||||
|
.experimentalFeature = Xp::NixCommand,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,10 +423,8 @@ void mainWrapped(int argc, char * * argv)
|
|||||||
if (!args.command)
|
if (!args.command)
|
||||||
throw UsageError("no subcommand specified");
|
throw UsageError("no subcommand specified");
|
||||||
|
|
||||||
if (args.command->first != "repl"
|
experimentalFeatureSettings.require(
|
||||||
&& args.command->first != "doctor"
|
args.command->second->experimentalFeature());
|
||||||
&& args.command->first != "upgrade-nix")
|
|
||||||
experimentalFeatureSettings.require(Xp::NixCommand);
|
|
||||||
|
|
||||||
if (args.useNet && !haveInternet()) {
|
if (args.useNet && !haveInternet()) {
|
||||||
warn("you don't have Internet access; disabling some network-dependent features");
|
warn("you don't have Internet access; disabling some network-dependent features");
|
||||||
|
@ -12,6 +12,14 @@ struct CmdRepl : RawInstallablesCommand
|
|||||||
evalSettings.pureEval = false;
|
evalSettings.pureEval = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command is stable before the others
|
||||||
|
*/
|
||||||
|
std::optional<ExperimentalFeature> experimentalFeature() override
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> files;
|
std::vector<std::string> files;
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPaths() override
|
Strings getDefaultFlakeAttrPaths() override
|
||||||
|
@ -32,6 +32,14 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command is stable before the others
|
||||||
|
*/
|
||||||
|
std::optional<ExperimentalFeature> experimentalFeature() override
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return "upgrade Nix to the stable version declared in Nixpkgs";
|
return "upgrade Nix to the stable version declared in Nixpkgs";
|
||||||
|
@ -15,9 +15,26 @@ function both_ways {
|
|||||||
# Simple case, the configuration effects the running command
|
# Simple case, the configuration effects the running command
|
||||||
both_ways show-config
|
both_ways show-config
|
||||||
|
|
||||||
# Complicated case, earlier args effect later args
|
# Skipping for now, because we actually *do* want these to show up in
|
||||||
|
# the manual, just be marked experimental. Will reenable once the manual
|
||||||
|
# generation takes advantage of the JSON metadata on this.
|
||||||
|
|
||||||
both_ways store gc --help
|
# both_ways store gc --help
|
||||||
|
|
||||||
expect 1 nix --experimental-features 'nix-command' show-config --flake-registry 'https://no'
|
expect 1 nix --experimental-features 'nix-command' show-config --flake-registry 'https://no'
|
||||||
nix --experimental-features 'nix-command flakes' show-config --flake-registry 'https://no'
|
nix --experimental-features 'nix-command flakes' show-config --flake-registry 'https://no'
|
||||||
|
|
||||||
|
# Double check these are stable
|
||||||
|
nix --experimental-features '' --help
|
||||||
|
nix --experimental-features '' doctor --help
|
||||||
|
nix --experimental-features '' repl --help
|
||||||
|
nix --experimental-features '' upgrade-nix --help
|
||||||
|
|
||||||
|
# These 3 arguments are currently given to all commands, which is wrong (as not
|
||||||
|
# all care). To deal with fixing later, we simply make them require the
|
||||||
|
# nix-command experimental features --- it so happens that the commands we wish
|
||||||
|
# stabilizing to do not need them anyways.
|
||||||
|
for arg in '--print-build-logs' '--offline' '--refresh'; do
|
||||||
|
nix --experimental-features 'nix-command' "$arg" --help
|
||||||
|
! nix --experimental-features '' "$arg" --help
|
||||||
|
done
|
||||||
|
@ -96,7 +96,9 @@ json=$(nix flake metadata flake1 --json | jq .)
|
|||||||
hash1=$(echo "$json" | jq -r .revision)
|
hash1=$(echo "$json" | jq -r .revision)
|
||||||
|
|
||||||
echo -n '# foo' >> $flake1Dir/flake.nix
|
echo -n '# foo' >> $flake1Dir/flake.nix
|
||||||
|
flake1OriginalCommit=$(git -C $flake1Dir rev-parse HEAD)
|
||||||
git -C $flake1Dir commit -a -m 'Foo'
|
git -C $flake1Dir commit -a -m 'Foo'
|
||||||
|
flake1NewCommit=$(git -C $flake1Dir rev-parse HEAD)
|
||||||
hash2=$(nix flake metadata flake1 --json --refresh | jq -r .revision)
|
hash2=$(nix flake metadata flake1 --json --refresh | jq -r .revision)
|
||||||
[[ $hash1 != $hash2 ]]
|
[[ $hash1 != $hash2 ]]
|
||||||
|
|
||||||
@ -491,3 +493,14 @@ nix store delete $(nix store add-path $badFlakeDir)
|
|||||||
[[ $(nix-instantiate --eval flake:git+file://$flake3Dir -A x) = 123 ]]
|
[[ $(nix-instantiate --eval flake:git+file://$flake3Dir -A x) = 123 ]]
|
||||||
[[ $(nix-instantiate -I flake3=flake:flake3 --eval '<flake3>' -A x) = 123 ]]
|
[[ $(nix-instantiate -I flake3=flake:flake3 --eval '<flake3>' -A x) = 123 ]]
|
||||||
[[ $(NIX_PATH=flake3=flake:flake3 nix-instantiate --eval '<flake3>' -A x) = 123 ]]
|
[[ $(NIX_PATH=flake3=flake:flake3 nix-instantiate --eval '<flake3>' -A x) = 123 ]]
|
||||||
|
|
||||||
|
# Test alternate lockfile paths.
|
||||||
|
nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2.lock
|
||||||
|
cmp $flake2Dir/flake.lock $TEST_ROOT/flake2.lock >/dev/null # lockfiles should be identical, since we're referencing flake2's original one
|
||||||
|
|
||||||
|
nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2-overridden.lock --override-input flake1 git+file://$flake1Dir?rev=$flake1OriginalCommit
|
||||||
|
expectStderr 1 cmp $flake2Dir/flake.lock $TEST_ROOT/flake2-overridden.lock
|
||||||
|
nix flake metadata $flake2Dir --reference-lock-file $TEST_ROOT/flake2-overridden.lock | grepQuiet $flake1OriginalCommit
|
||||||
|
|
||||||
|
# reference-lock-file can only be used if allow-dirty is set.
|
||||||
|
expectStderr 1 nix flake metadata $flake2Dir --no-allow-dirty --reference-lock-file $TEST_ROOT/flake2-overridden.lock
|
||||||
|
@ -5,12 +5,19 @@ export NIX_REMOTE=dummy://
|
|||||||
export NIX_STORE_DIR=/nix/store
|
export NIX_STORE_DIR=/nix/store
|
||||||
|
|
||||||
nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>&1 | grepQuiet Hello
|
nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>&1 | grepQuiet Hello
|
||||||
|
nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>/dev/null | grepQuiet 123
|
||||||
nix-instantiate --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1
|
nix-instantiate --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1
|
||||||
nix-instantiate --trace-verbose --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuiet Hello
|
nix-instantiate --trace-verbose --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuiet Hello
|
||||||
nix-instantiate --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuietInverse Hello
|
nix-instantiate --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuietInverse Hello
|
||||||
nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grepQuietInverse Hello
|
nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grepQuietInverse Hello
|
||||||
expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' | grepQuiet Hello
|
expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' | grepQuiet Hello
|
||||||
|
|
||||||
|
nix-instantiate --eval -E 'let x = builtins.trace { x = x; } true; in x' \
|
||||||
|
2>&1 | grepQuiet -E 'trace: { x = «potential infinite recursion»; }'
|
||||||
|
|
||||||
|
nix-instantiate --eval -E 'let x = { repeating = x; tracing = builtins.trace x true; }; in x.tracing'\
|
||||||
|
2>&1 | grepQuiet -F 'trace: { repeating = «repeated»; tracing = «potential infinite recursion»; }'
|
||||||
|
|
||||||
set +x
|
set +x
|
||||||
|
|
||||||
fail=0
|
fail=0
|
||||||
|
Loading…
Reference in New Issue
Block a user