mirror of
https://github.com/NixOS/nix
synced 2025-07-06 21:41:48 +02:00
Merge branch 'master' into parallel-nix-copy
This commit is contained in:
commit
7ed91d6c6a
98 changed files with 1538 additions and 741 deletions
|
@ -120,7 +120,7 @@ ref<EvalState> EvalCommand::getEvalState()
|
|||
;
|
||||
|
||||
if (startReplOnEvalErrors) {
|
||||
evalState->debugRepl = &runRepl;
|
||||
evalState->debugRepl = &runRepl;
|
||||
};
|
||||
}
|
||||
return ref<EvalState>(evalState);
|
||||
|
|
|
@ -58,6 +58,7 @@ struct CopyCommand : virtual StoreCommand
|
|||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||
{
|
||||
bool startReplOnEvalErrors = false;
|
||||
bool ignoreExceptionsDuringTry = false;
|
||||
|
||||
EvalCommand();
|
||||
|
||||
|
@ -77,10 +78,16 @@ struct MixFlakeOptions : virtual Args, EvalCommand
|
|||
{
|
||||
flake::LockFlags lockFlags;
|
||||
|
||||
std::optional<std::string> needsFlakeInputCompletion = {};
|
||||
|
||||
MixFlakeOptions();
|
||||
|
||||
virtual std::optional<FlakeRef> getFlakeRefForCompletion()
|
||||
virtual std::vector<std::string> getFlakesForCompletion()
|
||||
{ return {}; }
|
||||
|
||||
void completeFlakeInput(std::string_view prefix);
|
||||
|
||||
void completionHook() override;
|
||||
};
|
||||
|
||||
struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||
|
@ -116,12 +123,13 @@ struct InstallablesCommand : virtual Args, SourceExprCommand
|
|||
InstallablesCommand();
|
||||
|
||||
void prepare() override;
|
||||
Installables load();
|
||||
|
||||
virtual bool useDefaultInstallables() { return true; }
|
||||
|
||||
std::optional<FlakeRef> getFlakeRefForCompletion() override;
|
||||
std::vector<std::string> getFlakesForCompletion() override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
|
||||
std::vector<std::string> _installables;
|
||||
};
|
||||
|
@ -135,9 +143,9 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
|||
|
||||
void prepare() override;
|
||||
|
||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
||||
std::vector<std::string> getFlakesForCompletion() override
|
||||
{
|
||||
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
|
||||
return {_installable};
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -23,17 +23,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
void completeFlakeInputPath(
|
||||
ref<EvalState> evalState,
|
||||
const FlakeRef & flakeRef,
|
||||
std::string_view prefix)
|
||||
{
|
||||
auto flake = flake::getFlake(*evalState, flakeRef, true);
|
||||
for (auto & input : flake.inputs)
|
||||
if (hasPrefix(input.first, prefix))
|
||||
completions->add(input.first);
|
||||
}
|
||||
|
||||
MixFlakeOptions::MixFlakeOptions()
|
||||
{
|
||||
auto category = "Common flake-related options";
|
||||
|
@ -86,8 +75,7 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
|
||||
}},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
if (auto flakeRef = getFlakeRefForCompletion())
|
||||
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
|
||||
needsFlakeInputCompletion = {std::string(prefix)};
|
||||
}}
|
||||
});
|
||||
|
||||
|
@ -103,12 +91,10 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
parseFlakeRef(flakeRef, absPath("."), true));
|
||||
}},
|
||||
.completer = {[&](size_t n, std::string_view prefix) {
|
||||
if (n == 0) {
|
||||
if (auto flakeRef = getFlakeRefForCompletion())
|
||||
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
|
||||
} else if (n == 1) {
|
||||
if (n == 0)
|
||||
needsFlakeInputCompletion = {std::string(prefix)};
|
||||
else if (n == 1)
|
||||
completeFlakeRef(getEvalState()->store, prefix);
|
||||
}
|
||||
}}
|
||||
});
|
||||
|
||||
|
@ -139,6 +125,24 @@ MixFlakeOptions::MixFlakeOptions()
|
|||
});
|
||||
}
|
||||
|
||||
void MixFlakeOptions::completeFlakeInput(std::string_view prefix)
|
||||
{
|
||||
auto evalState = getEvalState();
|
||||
for (auto & flakeRefS : getFlakesForCompletion()) {
|
||||
auto flakeRef = parseFlakeRefWithFragment(expandTilde(flakeRefS), absPath(".")).first;
|
||||
auto flake = flake::getFlake(*evalState, flakeRef, true);
|
||||
for (auto & input : flake.inputs)
|
||||
if (hasPrefix(input.first, prefix))
|
||||
completions->add(input.first);
|
||||
}
|
||||
}
|
||||
|
||||
void MixFlakeOptions::completionHook()
|
||||
{
|
||||
if (auto & prefix = needsFlakeInputCompletion)
|
||||
completeFlakeInput(*prefix);
|
||||
}
|
||||
|
||||
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
||||
{
|
||||
addFlag({
|
||||
|
@ -146,7 +150,8 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
|||
.shortName = 'f',
|
||||
.description =
|
||||
"Interpret installables as attribute paths relative to the Nix expression stored in *file*. "
|
||||
"If *file* is the character -, then a Nix expression will be read from standard input.",
|
||||
"If *file* is the character -, then a Nix expression will be read from standard input. "
|
||||
"Implies `--impure`.",
|
||||
.category = installablesCategory,
|
||||
.labels = {"file"},
|
||||
.handler = {&file},
|
||||
|
@ -919,6 +924,9 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
|
|||
break;
|
||||
|
||||
case Realise::Outputs: {
|
||||
if (settings.printMissing)
|
||||
printMissing(store, pathsToBuild, lvlInfo);
|
||||
|
||||
for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) {
|
||||
if (!buildResult.success())
|
||||
buildResult.rethrow();
|
||||
|
@ -1032,21 +1040,26 @@ InstallablesCommand::InstallablesCommand()
|
|||
|
||||
void InstallablesCommand::prepare()
|
||||
{
|
||||
installables = load();
|
||||
}
|
||||
|
||||
Installables InstallablesCommand::load() {
|
||||
Installables installables;
|
||||
if (_installables.empty() && useDefaultInstallables())
|
||||
// FIXME: commands like "nix profile install" should not have a
|
||||
// default, probably.
|
||||
_installables.push_back(".");
|
||||
installables = parseInstallables(getStore(), _installables);
|
||||
return parseInstallables(getStore(), _installables);
|
||||
}
|
||||
|
||||
std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion()
|
||||
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
|
||||
{
|
||||
if (_installables.empty()) {
|
||||
if (useDefaultInstallables())
|
||||
return parseFlakeRefWithFragment(".", absPath(".")).first;
|
||||
return {"."};
|
||||
return {};
|
||||
}
|
||||
return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
|
||||
return _installables;
|
||||
}
|
||||
|
||||
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
||||
|
|
|
@ -132,6 +132,8 @@ struct Installable
|
|||
const std::vector<std::shared_ptr<Installable>> & installables);
|
||||
};
|
||||
|
||||
typedef std::vector<std::shared_ptr<Installable>> Installables;
|
||||
|
||||
struct InstallableValue : Installable
|
||||
{
|
||||
ref<EvalState> state;
|
||||
|
|
|
@ -22,6 +22,7 @@ extern "C" {
|
|||
#include "ansicolor.hh"
|
||||
#include "shared.hh"
|
||||
#include "eval.hh"
|
||||
#include "eval-cache.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "store-api.hh"
|
||||
|
@ -54,6 +55,8 @@ struct NixRepl
|
|||
size_t debugTraceIndex;
|
||||
|
||||
Strings loadedFiles;
|
||||
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
|
||||
std::function<AnnotatedValues()> getValues;
|
||||
|
||||
const static int envSize = 32768;
|
||||
std::shared_ptr<StaticEnv> staticEnv;
|
||||
|
@ -63,13 +66,15 @@ struct NixRepl
|
|||
|
||||
const Path historyFile;
|
||||
|
||||
NixRepl(ref<EvalState> state);
|
||||
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues);
|
||||
~NixRepl();
|
||||
void mainLoop(const std::vector<std::string> & files);
|
||||
void mainLoop();
|
||||
StringSet completePrefix(const std::string & prefix);
|
||||
bool getLine(std::string & input, const std::string & prompt);
|
||||
StorePath getDerivationPath(Value & v);
|
||||
bool processLine(std::string line);
|
||||
|
||||
void loadFile(const Path & path);
|
||||
void loadFlake(const std::string & flakeRef);
|
||||
void initEnv();
|
||||
|
@ -96,9 +101,11 @@ std::string removeWhitespace(std::string s)
|
|||
}
|
||||
|
||||
|
||||
NixRepl::NixRepl(ref<EvalState> state)
|
||||
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<NixRepl::AnnotatedValues()> getValues)
|
||||
: state(state)
|
||||
, debugTraceIndex(0)
|
||||
, getValues(getValues)
|
||||
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
|
||||
, historyFile(getDataDir() + "/nix/repl-history")
|
||||
{
|
||||
|
@ -111,23 +118,20 @@ NixRepl::~NixRepl()
|
|||
write_history(historyFile.c_str());
|
||||
}
|
||||
|
||||
std::string runNix(Path program, const Strings & args,
|
||||
void runNix(Path program, const Strings & args,
|
||||
const std::optional<std::string> & input = {})
|
||||
{
|
||||
auto subprocessEnv = getEnv();
|
||||
subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
|
||||
|
||||
auto res = runProgram(RunOptions {
|
||||
runProgram2(RunOptions {
|
||||
.program = settings.nixBinDir+ "/" + program,
|
||||
.args = args,
|
||||
.environment = subprocessEnv,
|
||||
.input = input,
|
||||
});
|
||||
|
||||
if (!statusOk(res.first))
|
||||
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
|
||||
|
||||
return res.second;
|
||||
return;
|
||||
}
|
||||
|
||||
static NixRepl * curRepl; // ugly
|
||||
|
@ -228,18 +232,12 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
|
|||
return out;
|
||||
}
|
||||
|
||||
void NixRepl::mainLoop(const std::vector<std::string> & files)
|
||||
void NixRepl::mainLoop()
|
||||
{
|
||||
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
|
||||
notice("Welcome to Nix " + nixVersion + ". Type :? for help.\n");
|
||||
|
||||
if (!files.empty()) {
|
||||
for (auto & i : files)
|
||||
loadedFiles.push_back(i);
|
||||
}
|
||||
|
||||
loadFiles();
|
||||
if (!loadedFiles.empty()) notice("");
|
||||
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
|
@ -749,7 +747,6 @@ bool NixRepl::processLine(std::string line)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void NixRepl::loadFile(const Path & path)
|
||||
{
|
||||
loadedFiles.remove(path);
|
||||
|
@ -809,13 +806,15 @@ void NixRepl::loadFiles()
|
|||
Strings old = loadedFiles;
|
||||
loadedFiles.clear();
|
||||
|
||||
bool first = true;
|
||||
for (auto & i : old) {
|
||||
if (!first) notice("");
|
||||
first = false;
|
||||
notice("Loading '%1%'...", i);
|
||||
loadFile(i);
|
||||
}
|
||||
|
||||
for (auto & [i, what] : getValues()) {
|
||||
notice("Loading installable '%1%'...", what);
|
||||
addAttrsToScope(*i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1015,7 +1014,17 @@ void runRepl(
|
|||
ref<EvalState>evalState,
|
||||
const ValMap & extraEnv)
|
||||
{
|
||||
auto repl = std::make_unique<NixRepl>(evalState);
|
||||
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
||||
NixRepl::AnnotatedValues values;
|
||||
return values;
|
||||
};
|
||||
const Strings & searchPath = {};
|
||||
auto repl = std::make_unique<NixRepl>(
|
||||
searchPath,
|
||||
openStore(),
|
||||
evalState,
|
||||
getValues
|
||||
);
|
||||
|
||||
repl->initEnv();
|
||||
|
||||
|
@ -1023,20 +1032,40 @@ void runRepl(
|
|||
for (auto & [name, value] : extraEnv)
|
||||
repl->addVarToScope(repl->state->symbols.create(name), *value);
|
||||
|
||||
repl->mainLoop({});
|
||||
repl->mainLoop();
|
||||
}
|
||||
|
||||
struct CmdRepl : StoreCommand, MixEvalArgs
|
||||
struct CmdRepl : InstallablesCommand
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
|
||||
CmdRepl()
|
||||
CmdRepl(){
|
||||
evalSettings.pureEval = false;
|
||||
}
|
||||
void prepare()
|
||||
{
|
||||
expectArgs({
|
||||
.label = "files",
|
||||
.handler = {&files},
|
||||
.completer = completePath
|
||||
});
|
||||
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
|
||||
warn("future versions of Nix will require using `--file` to load a file");
|
||||
if (this->_installables.size() > 1)
|
||||
warn("more than one input file is not currently supported");
|
||||
auto filePath = this->_installables[0].data();
|
||||
file = std::optional(filePath);
|
||||
_installables.front() = _installables.back();
|
||||
_installables.pop_back();
|
||||
}
|
||||
installables = InstallablesCommand::load();
|
||||
}
|
||||
std::vector<std::string> files;
|
||||
Strings getDefaultFlakeAttrPaths() override
|
||||
{
|
||||
return {""};
|
||||
}
|
||||
virtual bool useDefaultInstallables() override
|
||||
{
|
||||
return file.has_value() or expr.has_value();
|
||||
}
|
||||
|
||||
bool forceImpureByDefault() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
|
@ -1053,14 +1082,37 @@ struct CmdRepl : StoreCommand, MixEvalArgs
|
|||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
evalSettings.pureEval = false;
|
||||
|
||||
auto evalState = make_ref<EvalState>(searchPath, store);
|
||||
|
||||
auto repl = std::make_unique<NixRepl>(evalState);
|
||||
auto state = getEvalState();
|
||||
auto getValues = [&]()->NixRepl::AnnotatedValues{
|
||||
auto installables = load();
|
||||
NixRepl::AnnotatedValues values;
|
||||
for (auto & installable: installables){
|
||||
auto what = installable->what();
|
||||
if (file){
|
||||
auto [val, pos] = installable->toValue(*state);
|
||||
auto what = installable->what();
|
||||
state->forceValue(*val, pos);
|
||||
auto autoArgs = getAutoArgs(*state);
|
||||
auto valPost = state->allocValue();
|
||||
state->autoCallFunction(*autoArgs, *val, *valPost);
|
||||
state->forceValue(*valPost, pos);
|
||||
values.push_back( {valPost, what });
|
||||
} else {
|
||||
auto [val, pos] = installable->toValue(*state);
|
||||
values.push_back( {val, what} );
|
||||
}
|
||||
}
|
||||
return values;
|
||||
};
|
||||
auto repl = std::make_unique<NixRepl>(
|
||||
searchPath,
|
||||
openStore(),
|
||||
state,
|
||||
getValues
|
||||
);
|
||||
repl->autoArgs = getAutoArgs(*repl->state);
|
||||
repl->initEnv();
|
||||
repl->mainLoop(files);
|
||||
repl->mainLoop();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ struct AttrDb
|
|||
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
|
||||
if (!queryAttribute.next()) return {};
|
||||
|
||||
auto rowId = (AttrType) queryAttribute.getInt(0);
|
||||
auto rowId = (AttrId) queryAttribute.getInt(0);
|
||||
auto type = (AttrType) queryAttribute.getInt(1);
|
||||
|
||||
switch (type) {
|
||||
|
@ -486,7 +486,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
|||
return nullptr;
|
||||
else if (std::get_if<failed_t>(&attr->second)) {
|
||||
if (forceErrors)
|
||||
debug("reevaluating failed cached attribute '%s'");
|
||||
debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name));
|
||||
else
|
||||
throw CachedEvalError("cached failure of attribute '%s'", getAttrPathStr(name));
|
||||
} else
|
||||
|
|
|
@ -464,9 +464,10 @@ EvalState::EvalState(
|
|||
, emptyBindings(0)
|
||||
, store(store)
|
||||
, buildStore(buildStore ? buildStore : store)
|
||||
, debugRepl(0)
|
||||
, debugRepl(nullptr)
|
||||
, debugStop(false)
|
||||
, debugQuit(false)
|
||||
, trylevel(0)
|
||||
, regexCache(makeRegexCache())
|
||||
#if HAVE_BOEHMGC
|
||||
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||
|
@ -832,7 +833,14 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
|
|||
: nullptr;
|
||||
|
||||
if (error)
|
||||
printError("%s\n\n" ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL, error->what());
|
||||
{
|
||||
printError("%s\n\n", error->what());
|
||||
|
||||
if (trylevel > 0 && error->info().level != lvlInfo)
|
||||
printError("This exception occurred in a 'tryEval' call. Use " ANSI_GREEN "--ignore-try" ANSI_NORMAL " to skip these.\n");
|
||||
|
||||
printError(ANSI_BOLD "Starting REPL to allow you to inspect the current state of the evaluator.\n" ANSI_NORMAL);
|
||||
}
|
||||
|
||||
auto se = getStaticEnv(expr);
|
||||
if (se) {
|
||||
|
|
|
@ -130,6 +130,7 @@ public:
|
|||
void (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
|
||||
bool debugStop;
|
||||
bool debugQuit;
|
||||
int trylevel;
|
||||
std::list<DebugTrace> debugTraces;
|
||||
std::map<const Expr*, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
const std::shared_ptr<const StaticEnv> getStaticEnv(const Expr & expr) const
|
||||
|
@ -646,6 +647,15 @@ struct EvalSettings : Config
|
|||
|
||||
Setting<bool> useEvalCache{this, true, "eval-cache",
|
||||
"Whether to use the flake evaluation cache."};
|
||||
|
||||
Setting<bool> ignoreExceptionsDuringTry{this, false, "ignore-try",
|
||||
R"(
|
||||
If set to true, ignore exceptions inside 'tryEval' calls when evaluating nix expressions in
|
||||
debug mode (using the --debugger flag). By default the debugger will pause on all exceptions.
|
||||
)"};
|
||||
|
||||
Setting<bool> traceVerbose{this, false, "trace-verbose",
|
||||
"Whether `builtins.traceVerbose` should trace its first argument when evaluated."};
|
||||
};
|
||||
|
||||
extern EvalSettings evalSettings;
|
||||
|
|
|
@ -384,6 +384,18 @@ LockedFlake lockFlake(
|
|||
}
|
||||
}
|
||||
|
||||
/* Check whether this input has overrides for a
|
||||
non-existent input. */
|
||||
for (auto [inputPath, inputOverride] : overrides) {
|
||||
auto inputPath2(inputPath);
|
||||
auto follow = inputPath2.back();
|
||||
inputPath2.pop_back();
|
||||
if (inputPath2 == inputPathPrefix && !flakeInputs.count(follow))
|
||||
warn(
|
||||
"input '%s' has an override for a non-existent input '%s'",
|
||||
printInputPath(inputPathPrefix), follow);
|
||||
}
|
||||
|
||||
/* Go over the flake inputs, resolve/fetch them if
|
||||
necessary (i.e. if they're new or the flakeref changed
|
||||
from what's in the lock file). */
|
||||
|
@ -513,6 +525,15 @@ LockedFlake lockFlake(
|
|||
if (!lockFlags.allowMutable && !input.ref->input.isLocked())
|
||||
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
|
||||
|
||||
/* Note: in case of an --override-input, we use
|
||||
the *original* ref (input2.ref) for the
|
||||
"original" field, rather than the
|
||||
override. This ensures that the override isn't
|
||||
nuked the next time we update the lock
|
||||
file. That is, overrides are sticky unless you
|
||||
use --no-write-lock-file. */
|
||||
auto ref = input2.ref ? *input2.ref : *input.ref;
|
||||
|
||||
if (input.isFlake) {
|
||||
Path localPath = parentPath;
|
||||
FlakeRef localRef = *input.ref;
|
||||
|
@ -524,15 +545,7 @@ LockedFlake lockFlake(
|
|||
|
||||
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
|
||||
|
||||
/* Note: in case of an --override-input, we use
|
||||
the *original* ref (input2.ref) for the
|
||||
"original" field, rather than the
|
||||
override. This ensures that the override isn't
|
||||
nuked the next time we update the lock
|
||||
file. That is, overrides are sticky unless you
|
||||
use --no-write-lock-file. */
|
||||
auto childNode = std::make_shared<LockedNode>(
|
||||
inputFlake.lockedRef, input2.ref ? *input2.ref : *input.ref);
|
||||
auto childNode = std::make_shared<LockedNode>(inputFlake.lockedRef, ref);
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
|
||||
|
@ -560,7 +573,7 @@ LockedFlake lockFlake(
|
|||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, *input.ref, useRegistries, flakeCache);
|
||||
node->inputs.insert_or_assign(id,
|
||||
std::make_shared<LockedNode>(lockedRef, *input.ref, false));
|
||||
std::make_shared<LockedNode>(lockedRef, ref, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -520,6 +520,12 @@ path_start
|
|||
$$ = new ExprPath(path);
|
||||
}
|
||||
| HPATH {
|
||||
if (evalSettings.pureEval) {
|
||||
throw Error(
|
||||
"the path '%s' can not be resolved in pure mode",
|
||||
std::string_view($1.p, $1.l)
|
||||
);
|
||||
}
|
||||
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
|
||||
$$ = new ExprPath(path);
|
||||
}
|
||||
|
|
|
@ -851,6 +851,18 @@ static RegisterPrimOp primop_floor({
|
|||
static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
auto attrs = state.buildBindings(2);
|
||||
|
||||
/* increment state.trylevel, and decrement it when this function returns. */
|
||||
MaintainCount trylevel(state.trylevel);
|
||||
|
||||
void (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
|
||||
if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry)
|
||||
{
|
||||
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
|
||||
savedDebugRepl = state.debugRepl;
|
||||
state.debugRepl = nullptr;
|
||||
}
|
||||
|
||||
try {
|
||||
state.forceValue(*args[0], pos);
|
||||
attrs.insert(state.sValue, args[0]);
|
||||
|
@ -859,6 +871,11 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va
|
|||
attrs.alloc(state.sValue).mkBool(false);
|
||||
attrs.alloc("success").mkBool(false);
|
||||
}
|
||||
|
||||
// restore the debugRepl pointer if we saved it earlier.
|
||||
if (savedDebugRepl)
|
||||
state.debugRepl = savedDebugRepl;
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
|
@ -970,6 +987,15 @@ static RegisterPrimOp primop_trace({
|
|||
});
|
||||
|
||||
|
||||
/* Takes two arguments and evaluates to the second one. Used as the
|
||||
* builtins.traceVerbose implementation when --trace-verbose is not enabled
|
||||
*/
|
||||
static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[1], pos);
|
||||
v = *args[1];
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Derivations
|
||||
*************************************************************/
|
||||
|
@ -3926,6 +3952,18 @@ void EvalState::createBaseEnv()
|
|||
addPrimOp("__exec", 1, prim_exec);
|
||||
}
|
||||
|
||||
addPrimOp({
|
||||
.fun = evalSettings.traceVerbose ? prim_trace : prim_second,
|
||||
.arity = 2,
|
||||
.name = "__traceVerbose",
|
||||
.args = { "e1", "e2" },
|
||||
.doc = R"(
|
||||
Evaluate *e1* and print its abstract syntax representation on standard
|
||||
error if `--trace-verbose` is enabled. Then return *e2*. This function
|
||||
is useful for debugging.
|
||||
)",
|
||||
});
|
||||
|
||||
/* Add a value containing the current Nix expression search path. */
|
||||
mkList(v, searchPath.size());
|
||||
int n = 0;
|
||||
|
|
|
@ -364,6 +364,10 @@ static RegisterPrimOp primop_fetchGit({
|
|||
A Boolean parameter that specifies whether submodules should be
|
||||
checked out. Defaults to `false`.
|
||||
|
||||
- shallow\
|
||||
A Boolean parameter that specifies whether fetching a shallow clone
|
||||
is allowed. Defaults to `false`.
|
||||
|
||||
- allRefs\
|
||||
Whether to fetch all refs of the repository. With this argument being
|
||||
true, it's possible to load a `rev` from *any* `ref` (by default only
|
||||
|
|
|
@ -540,22 +540,22 @@ namespace nix {
|
|||
ASSERT_THAT(v, IsStringEq(output));
|
||||
}
|
||||
|
||||
#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " #input), std::string_view(output)))
|
||||
#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " input), std::string_view(output)))
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
toString,
|
||||
ToStringPrimOpTest,
|
||||
testing::Values(
|
||||
CASE("foo", "foo"),
|
||||
CASE(1, "1"),
|
||||
CASE([1 2 3], "1 2 3"),
|
||||
CASE(.123, "0.123000"),
|
||||
CASE(true, "1"),
|
||||
CASE(false, ""),
|
||||
CASE(null, ""),
|
||||
CASE({ v = "bar"; __toString = self: self.v; }, "bar"),
|
||||
CASE({ v = "bar"; __toString = self: self.v; outPath = "foo"; }, "bar"),
|
||||
CASE({ outPath = "foo"; }, "foo"),
|
||||
CASE(./test, "/test")
|
||||
CASE(R"("foo")", "foo"),
|
||||
CASE(R"(1)", "1"),
|
||||
CASE(R"([1 2 3])", "1 2 3"),
|
||||
CASE(R"(.123)", "0.123000"),
|
||||
CASE(R"(true)", "1"),
|
||||
CASE(R"(false)", ""),
|
||||
CASE(R"(null)", ""),
|
||||
CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"),
|
||||
CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"),
|
||||
CASE(R"({ outPath = "foo"; })", "foo"),
|
||||
CASE(R"(./test)", "/test")
|
||||
)
|
||||
);
|
||||
#undef CASE
|
||||
|
|
|
@ -70,7 +70,7 @@ struct FetchSettings : public Config
|
|||
Setting<bool> warnDirty{this, true, "warn-dirty",
|
||||
"Whether to warn about dirty Git/Mercurial trees."};
|
||||
|
||||
Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
|
||||
Setting<std::string> flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry",
|
||||
"Path or URI of the global flake registry."};
|
||||
|
||||
Setting<bool> useRegistries{this, true, "use-registries",
|
||||
|
|
|
@ -85,8 +85,9 @@ std::optional<std::string> readHead(const Path & path)
|
|||
bool storeCachedHead(const std::string& actualUrl, const std::string& headRef)
|
||||
{
|
||||
Path cacheDir = getCachePath(actualUrl);
|
||||
auto gitDir = ".";
|
||||
try {
|
||||
runProgram("git", true, { "-C", cacheDir, "symbolic-ref", "--", "HEAD", headRef });
|
||||
runProgram("git", true, { "-C", cacheDir, "--git-dir", gitDir, "symbolic-ref", "--", "HEAD", headRef });
|
||||
} catch (ExecError &e) {
|
||||
if (!WIFEXITED(e.status)) throw;
|
||||
return false;
|
||||
|
@ -182,7 +183,7 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir)
|
|||
if (hasHead) {
|
||||
// Using git diff is preferrable over lower-level operations here,
|
||||
// because its conceptually simpler and we only need the exit code anyways.
|
||||
auto gitDiffOpts = Strings({ "-C", workdir, "diff", "HEAD", "--quiet"});
|
||||
auto gitDiffOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "diff", "HEAD", "--quiet"});
|
||||
if (!submodules) {
|
||||
// Changes in submodules should only make the tree dirty
|
||||
// when those submodules will be copied as well.
|
||||
|
@ -203,6 +204,7 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir)
|
|||
std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, const Path & workdir, const WorkdirInfo & workdirInfo)
|
||||
{
|
||||
const bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
|
||||
auto gitDir = ".git";
|
||||
|
||||
if (!fetchSettings.allowDirty)
|
||||
throw Error("Git tree '%s' is dirty", workdir);
|
||||
|
@ -210,7 +212,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
|
|||
if (fetchSettings.warnDirty)
|
||||
warn("Git tree '%s' is dirty", workdir);
|
||||
|
||||
auto gitOpts = Strings({ "-C", workdir, "ls-files", "-z" });
|
||||
auto gitOpts = Strings({ "-C", workdir, "--git-dir", gitDir, "ls-files", "-z" });
|
||||
if (submodules)
|
||||
gitOpts.emplace_back("--recurse-submodules");
|
||||
|
||||
|
@ -240,7 +242,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
|
|||
// modified dirty file?
|
||||
input.attrs.insert_or_assign(
|
||||
"lastModified",
|
||||
workdirInfo.hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
|
||||
workdirInfo.hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "--git-dir", gitDir, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
|
||||
|
||||
return {std::move(storePath), input};
|
||||
}
|
||||
|
@ -572,7 +574,7 @@ struct GitInputScheme : InputScheme
|
|||
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "rev-parse", "--is-shallow-repository" })) == "true";
|
||||
|
||||
if (isShallow && !shallow)
|
||||
throw Error("'%s' is a shallow Git repository, but a non-shallow repository is needed", actualUrl);
|
||||
throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified.", actualUrl);
|
||||
|
||||
// FIXME: check whether rev is an ancestor of ref.
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
|||
|
||||
Headers headers = makeHeadersWithAuthTokens(host);
|
||||
|
||||
std::string ref_uri;
|
||||
std::string refUri;
|
||||
if (ref == "HEAD") {
|
||||
auto file = store->toRealPath(
|
||||
downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath);
|
||||
|
@ -393,10 +393,11 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
|||
if (!remoteLine) {
|
||||
throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
|
||||
}
|
||||
ref_uri = remoteLine->target;
|
||||
refUri = remoteLine->target;
|
||||
} else {
|
||||
ref_uri = fmt("refs/(heads|tags)/%s", ref);
|
||||
refUri = fmt("refs/(heads|tags)/%s", ref);
|
||||
}
|
||||
std::regex refRegex(refUri);
|
||||
|
||||
auto file = store->toRealPath(
|
||||
downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath);
|
||||
|
@ -406,7 +407,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
|||
std::optional<std::string> id;
|
||||
while(!id && getline(is, line)) {
|
||||
auto parsedLine = git::parseLsRemoteLine(line);
|
||||
if (parsedLine && parsedLine->reference == ref_uri)
|
||||
if (parsedLine && parsedLine->reference && std::regex_match(*parsedLine->reference, refRegex))
|
||||
id = parsedLine->target;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,22 @@ HookInstance::HookInstance()
|
|||
{
|
||||
debug("starting build hook '%s'", settings.buildHook);
|
||||
|
||||
auto buildHookArgs = tokenizeString<std::list<std::string>>(settings.buildHook.get());
|
||||
|
||||
if (buildHookArgs.empty())
|
||||
throw Error("'build-hook' setting is empty");
|
||||
|
||||
auto buildHook = buildHookArgs.front();
|
||||
buildHookArgs.pop_front();
|
||||
|
||||
Strings args;
|
||||
|
||||
for (auto & arg : buildHookArgs)
|
||||
args.push_back(arg);
|
||||
|
||||
args.push_back(std::string(baseNameOf(settings.buildHook.get())));
|
||||
args.push_back(std::to_string(verbosity));
|
||||
|
||||
/* Create a pipe to get the output of the child. */
|
||||
fromHook.create();
|
||||
|
||||
|
@ -36,14 +52,9 @@ HookInstance::HookInstance()
|
|||
if (dup2(builderOut.readSide.get(), 5) == -1)
|
||||
throw SysError("dupping builder's stdout/stderr");
|
||||
|
||||
Strings args = {
|
||||
std::string(baseNameOf(settings.buildHook.get())),
|
||||
std::to_string(verbosity),
|
||||
};
|
||||
execv(buildHook.c_str(), stringsToCharPtrs(args).data());
|
||||
|
||||
execv(settings.buildHook.get().c_str(), stringsToCharPtrs(args).data());
|
||||
|
||||
throw SysError("executing '%s'", settings.buildHook);
|
||||
throw SysError("executing '%s'", buildHook);
|
||||
});
|
||||
|
||||
pid.setSeparatePG(true);
|
||||
|
|
|
@ -1717,7 +1717,19 @@ void LocalDerivationGoal::runChild()
|
|||
|
||||
for (auto & i : dirsInChroot) {
|
||||
if (i.second.source == "/proc") continue; // backwards compatibility
|
||||
doBind(i.second.source, chrootRootDir + i.first, i.second.optional);
|
||||
|
||||
#if HAVE_EMBEDDED_SANDBOX_SHELL
|
||||
if (i.second.source == "__embedded_sandbox_shell__") {
|
||||
static unsigned char sh[] = {
|
||||
#include "embedded-sandbox-shell.gen.hh"
|
||||
};
|
||||
auto dst = chrootRootDir + i.first;
|
||||
createDirs(dirOf(dst));
|
||||
writeFile(dst, std::string_view((const char *) sh, sizeof(sh)));
|
||||
chmod_(dst, 0555);
|
||||
} else
|
||||
#endif
|
||||
doBind(i.second.source, chrootRootDir + i.first, i.second.optional);
|
||||
}
|
||||
|
||||
/* Bind a new instance of procfs on /proc. */
|
||||
|
|
|
@ -154,7 +154,7 @@ void PathSubstitutionGoal::tryNext()
|
|||
only after we've downloaded the path. */
|
||||
if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info))
|
||||
{
|
||||
warn("the substitute for '%s' from '%s' is not signed by any of the keys in 'trusted-public-keys'",
|
||||
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;
|
||||
|
|
|
@ -135,6 +135,7 @@ void LocalStore::addTempRoot(const StorePath & path)
|
|||
state->fdRootsSocket.close();
|
||||
goto restart;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,6 +154,7 @@ void LocalStore::addTempRoot(const StorePath & path)
|
|||
state->fdRootsSocket.close();
|
||||
goto restart;
|
||||
}
|
||||
throw;
|
||||
} catch (EndOfFile & e) {
|
||||
debug("GC socket disconnected");
|
||||
state->fdRootsSocket.close();
|
||||
|
|
|
@ -36,7 +36,6 @@ Settings::Settings()
|
|||
, nixStateDir(canonPath(getEnv("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
|
||||
, nixConfDir(canonPath(getEnv("NIX_CONF_DIR").value_or(NIX_CONF_DIR)))
|
||||
, nixUserConfFiles(getUserConfigFiles())
|
||||
, nixLibexecDir(canonPath(getEnv("NIX_LIBEXEC_DIR").value_or(NIX_LIBEXEC_DIR)))
|
||||
, nixBinDir(canonPath(getEnv("NIX_BIN_DIR").value_or(NIX_BIN_DIR)))
|
||||
, nixManDir(canonPath(NIX_MAN_DIR))
|
||||
, nixDaemonSocketFile(canonPath(getEnv("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||
|
@ -67,12 +66,13 @@ Settings::Settings()
|
|||
sandboxPaths = tokenizeString<StringSet>("/bin/sh=" SANDBOX_SHELL);
|
||||
#endif
|
||||
|
||||
|
||||
/* chroot-like behavior from Apple's sandbox */
|
||||
/* chroot-like behavior from Apple's sandbox */
|
||||
#if __APPLE__
|
||||
sandboxPaths = tokenizeString<StringSet>("/System/Library/Frameworks /System/Library/PrivateFrameworks /bin/sh /bin/bash /private/tmp /private/var/tmp /usr/lib");
|
||||
allowedImpureHostPrefixes = tokenizeString<StringSet>("/System/Library /usr/lib /dev /bin/sh");
|
||||
#endif
|
||||
|
||||
buildHook = getSelfExe().value_or("nix") + " __build-remote";
|
||||
}
|
||||
|
||||
void loadConfFile()
|
||||
|
|
|
@ -79,9 +79,6 @@ public:
|
|||
/* A list of user configuration files to load. */
|
||||
std::vector<Path> nixUserConfFiles;
|
||||
|
||||
/* The directory where internal helper programs are stored. */
|
||||
Path nixLibexecDir;
|
||||
|
||||
/* The directory where the main programs are stored. */
|
||||
Path nixBinDir;
|
||||
|
||||
|
@ -195,7 +192,7 @@ public:
|
|||
)",
|
||||
{"build-timeout"}};
|
||||
|
||||
PathSetting buildHook{this, true, nixLibexecDir + "/nix/build-remote", "build-hook",
|
||||
PathSetting buildHook{this, true, "", "build-hook",
|
||||
"The path of the helper program that executes builds to remote machines."};
|
||||
|
||||
Setting<std::string> builders{
|
||||
|
@ -802,7 +799,7 @@ public:
|
|||
)"};
|
||||
|
||||
Setting<StringSet> ignoredAcls{
|
||||
this, {"security.selinux", "system.nfs4_acl"}, "ignored-acls",
|
||||
this, {"security.selinux", "system.nfs4_acl", "security.csm"}, "ignored-acls",
|
||||
R"(
|
||||
A list of ACLs that should be ignored, normally Nix attempts to
|
||||
remove all ACLs from files and directories in the Nix store, but
|
||||
|
|
|
@ -69,6 +69,7 @@ protected:
|
|||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOENT)
|
||||
throw NoSuchBinaryCacheFile("file '%s' does not exist in binary cache", path);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,14 +39,23 @@ libstore_CXXFLAGS += \
|
|||
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
||||
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
|
||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
||||
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||
-DNIX_MAN_DIR=\"$(mandir)\" \
|
||||
-DLSOF=\"$(lsof)\"
|
||||
|
||||
ifeq ($(embedded_sandbox_shell),yes)
|
||||
libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\"
|
||||
|
||||
$(d)/build/local-derivation-goal.cc: $(d)/embedded-sandbox-shell.gen.hh
|
||||
|
||||
$(d)/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
|
||||
$(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
else
|
||||
ifneq ($(sandbox_shell),)
|
||||
libstore_CXXFLAGS += -DSANDBOX_SHELL="\"$(sandbox_shell)\""
|
||||
endif
|
||||
endif
|
||||
|
||||
$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
|
||||
|
||||
|
|
|
@ -67,13 +67,26 @@ bool UserLock::findFreeUser() {
|
|||
#if __linux__
|
||||
/* Get the list of supplementary groups of this build user. This
|
||||
is usually either empty or contains a group such as "kvm". */
|
||||
supplementaryGIDs.resize(10);
|
||||
int ngroups = supplementaryGIDs.size();
|
||||
int err = getgrouplist(pw->pw_name, pw->pw_gid,
|
||||
supplementaryGIDs.data(), &ngroups);
|
||||
if (err == -1)
|
||||
throw Error("failed to get list of supplementary groups for '%1%'", pw->pw_name);
|
||||
int ngroups = 32; // arbitrary initial guess
|
||||
supplementaryGIDs.resize(ngroups);
|
||||
|
||||
int err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(),
|
||||
&ngroups);
|
||||
|
||||
// Our initial size of 32 wasn't sufficient, the correct size has
|
||||
// been stored in ngroups, so we try again.
|
||||
if (err == -1) {
|
||||
supplementaryGIDs.resize(ngroups);
|
||||
err = getgrouplist(pw->pw_name, pw->pw_gid, supplementaryGIDs.data(),
|
||||
&ngroups);
|
||||
}
|
||||
|
||||
// If it failed once more, then something must be broken.
|
||||
if (err == -1)
|
||||
throw Error("failed to get list of supplementary groups for '%1%'",
|
||||
pw->pw_name);
|
||||
|
||||
// Finally, trim back the GID list to its real size
|
||||
supplementaryGIDs.resize(ngroups);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -62,6 +62,9 @@ public:
|
|||
/* How often to purge expired entries from the cache. */
|
||||
const int purgeInterval = 24 * 3600;
|
||||
|
||||
/* How long to cache binary cache info (i.e. /nix-cache-info) */
|
||||
const int cacheInfoTtl = 7 * 24 * 3600;
|
||||
|
||||
struct Cache
|
||||
{
|
||||
int id;
|
||||
|
@ -98,7 +101,7 @@ public:
|
|||
"insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)");
|
||||
|
||||
state->queryCache.create(state->db,
|
||||
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?");
|
||||
"select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ? and timestamp > ?");
|
||||
|
||||
state->insertNAR.create(state->db,
|
||||
"insert or replace into NARs(cache, hashPart, namePart, url, compression, fileHash, fileSize, narHash, "
|
||||
|
@ -183,7 +186,7 @@ public:
|
|||
|
||||
auto i = state->caches.find(uri);
|
||||
if (i == state->caches.end()) {
|
||||
auto queryCache(state->queryCache.use()(uri));
|
||||
auto queryCache(state->queryCache.use()(uri)(time(0) - cacheInfoTtl));
|
||||
if (!queryCache.next())
|
||||
return std::nullopt;
|
||||
state->caches.emplace(uri,
|
||||
|
|
|
@ -69,8 +69,6 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
|
|||
if (value != "unknown-deriver")
|
||||
deriver = StorePath(value);
|
||||
}
|
||||
else if (name == "System")
|
||||
system = value;
|
||||
else if (name == "Sig")
|
||||
sigs.insert(value);
|
||||
else if (name == "CA") {
|
||||
|
@ -106,9 +104,6 @@ std::string NarInfo::to_string(const Store & store) const
|
|||
if (deriver)
|
||||
res += "Deriver: " + std::string(deriver->to_string()) + "\n";
|
||||
|
||||
if (!system.empty())
|
||||
res += "System: " + system + "\n";
|
||||
|
||||
for (auto sig : sigs)
|
||||
res += "Sig: " + sig + "\n";
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ struct NarInfo : ValidPathInfo
|
|||
std::string compression;
|
||||
std::optional<Hash> fileHash;
|
||||
uint64_t fileSize = 0;
|
||||
std::string system;
|
||||
|
||||
NarInfo() = delete;
|
||||
NarInfo(StorePath && path, Hash narHash) : ValidPathInfo(std::move(path), narHash) { }
|
||||
|
|
|
@ -98,7 +98,9 @@
|
|||
(allow file*
|
||||
(literal "/private/var/select/sh"))
|
||||
|
||||
; Allow Rosetta 2 to run x86_64 binaries on aarch64-darwin.
|
||||
; Allow Rosetta 2 to run x86_64 binaries on aarch64-darwin (and vice versa).
|
||||
(allow file-read*
|
||||
(subpath "/Library/Apple/usr/libexec/oah")
|
||||
(subpath "/System/Library/Apple/usr/libexec/oah"))
|
||||
(subpath "/System/Library/Apple/usr/libexec/oah")
|
||||
(subpath "/System/Library/LaunchDaemons/com.apple.oahd.plist")
|
||||
(subpath "/Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist"))
|
||||
|
|
|
@ -1320,7 +1320,8 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_
|
|||
return {uri, params};
|
||||
}
|
||||
|
||||
static bool isNonUriPath(const std::string & spec) {
|
||||
static bool isNonUriPath(const std::string & spec)
|
||||
{
|
||||
return
|
||||
// is not a URL
|
||||
spec.find("://") == std::string::npos
|
||||
|
@ -1337,6 +1338,31 @@ std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Para
|
|||
return std::make_shared<LocalStore>(params);
|
||||
else if (pathExists(settings.nixDaemonSocketFile))
|
||||
return std::make_shared<UDSRemoteStore>(params);
|
||||
#if __linux__
|
||||
else if (!pathExists(stateDir)
|
||||
&& params.empty()
|
||||
&& getuid() != 0
|
||||
&& !getEnv("NIX_STORE_DIR").has_value()
|
||||
&& !getEnv("NIX_STATE_DIR").has_value())
|
||||
{
|
||||
/* If /nix doesn't exist, there is no daemon socket, and
|
||||
we're not root, then automatically set up a chroot
|
||||
store in ~/.local/share/nix/root. */
|
||||
auto chrootStore = getDataDir() + "/nix/root";
|
||||
if (!pathExists(chrootStore)) {
|
||||
try {
|
||||
createDirs(chrootStore);
|
||||
} catch (Error & e) {
|
||||
return std::make_shared<LocalStore>(params);
|
||||
}
|
||||
warn("'/nix' does not exist, so Nix will use '%s' as a chroot store", chrootStore);
|
||||
} else
|
||||
debug("'/nix' does not exist, so Nix will use '%s' as a chroot store", chrootStore);
|
||||
Store::Params params2;
|
||||
params2["root"] = chrootStore;
|
||||
return std::make_shared<LocalStore>(params2);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return std::make_shared<LocalStore>(params);
|
||||
} else if (uri == "daemon") {
|
||||
|
|
|
@ -124,7 +124,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
|||
bool anyCompleted = false;
|
||||
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
|
||||
if (pos == end) {
|
||||
if (flag.handler.arity == ArityAny) break;
|
||||
if (flag.handler.arity == ArityAny || anyCompleted) break;
|
||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
||||
}
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
|
@ -362,6 +362,14 @@ bool MultiCommand::processArgs(const Strings & args, bool finish)
|
|||
return Args::processArgs(args, finish);
|
||||
}
|
||||
|
||||
void MultiCommand::completionHook()
|
||||
{
|
||||
if (command)
|
||||
return command->second->completionHook();
|
||||
else
|
||||
return Args::completionHook();
|
||||
}
|
||||
|
||||
nlohmann::json MultiCommand::toJSON()
|
||||
{
|
||||
auto cmds = nlohmann::json::object();
|
||||
|
|
|
@ -25,6 +25,8 @@ public:
|
|||
/* Return a short one-line description of the command. */
|
||||
virtual std::string description() { return ""; }
|
||||
|
||||
virtual bool forceImpureByDefault() { return false; }
|
||||
|
||||
/* Return documentation about this command, in Markdown format. */
|
||||
virtual std::string doc() { return ""; }
|
||||
|
||||
|
@ -146,6 +148,11 @@ protected:
|
|||
argument (if any) have been processed. */
|
||||
virtual void initialFlagsProcessed() {}
|
||||
|
||||
/* Called after the command line has been processed if we need to generate
|
||||
completions. Useful for commands that need to know the whole command line
|
||||
in order to know what completions to generate. */
|
||||
virtual void completionHook() { }
|
||||
|
||||
public:
|
||||
|
||||
void addFlag(Flag && flag);
|
||||
|
@ -221,6 +228,8 @@ public:
|
|||
|
||||
bool processArgs(const Strings & args, bool finish) override;
|
||||
|
||||
void completionHook() override;
|
||||
|
||||
nlohmann::json toJSON() override;
|
||||
};
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ std::map<ExperimentalFeature, std::string> stringifiedXpFeatures = {
|
|||
{ Xp::RecursiveNix, "recursive-nix" },
|
||||
{ Xp::NoUrlLiterals, "no-url-literals" },
|
||||
{ Xp::FetchClosure, "fetch-closure" },
|
||||
{ Xp::ReplFlake, "repl-flake" },
|
||||
};
|
||||
|
||||
const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name)
|
||||
|
|
|
@ -22,6 +22,7 @@ enum struct ExperimentalFeature
|
|||
RecursiveNix,
|
||||
NoUrlLiterals,
|
||||
FetchClosure,
|
||||
ReplFlake,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,9 +8,9 @@ std::string hiliteMatches(
|
|||
std::string_view prefix,
|
||||
std::string_view postfix)
|
||||
{
|
||||
// Avoid copy on zero matches
|
||||
// Avoid extra work on zero matches
|
||||
if (matches.size() == 0)
|
||||
return (std::string) s;
|
||||
return std::string(s);
|
||||
|
||||
std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) {
|
||||
return a.position() < b.position();
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/syscall.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
|
@ -574,6 +575,20 @@ Path getHome()
|
|||
static Path homeDir = []()
|
||||
{
|
||||
auto homeDir = getEnv("HOME");
|
||||
if (homeDir) {
|
||||
// Only use $HOME if doesn't exist or is owned by the current user.
|
||||
struct stat st;
|
||||
int result = stat(homeDir->c_str(), &st);
|
||||
if (result != 0) {
|
||||
if (errno != ENOENT) {
|
||||
warn("couldn't stat $HOME ('%s') for reason other than not existing ('%d'), falling back to the one defined in the 'passwd' file", *homeDir, errno);
|
||||
homeDir.reset();
|
||||
}
|
||||
} else if (st.st_uid != geteuid()) {
|
||||
warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file", *homeDir);
|
||||
homeDir.reset();
|
||||
}
|
||||
}
|
||||
if (!homeDir) {
|
||||
std::vector<char> buf(16384);
|
||||
struct passwd pwbuf;
|
||||
|
@ -619,6 +634,27 @@ Path getDataDir()
|
|||
}
|
||||
|
||||
|
||||
std::optional<Path> getSelfExe()
|
||||
{
|
||||
static auto cached = []() -> std::optional<Path>
|
||||
{
|
||||
#if __linux__
|
||||
return readLink("/proc/self/exe");
|
||||
#elif __APPLE__
|
||||
char buf[1024];
|
||||
uint32_t size = sizeof(buf);
|
||||
if (_NSGetExecutablePath(buf, &size) == 0)
|
||||
return buf;
|
||||
else
|
||||
return std::nullopt;
|
||||
#else
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}();
|
||||
return cached;
|
||||
}
|
||||
|
||||
|
||||
Paths createDirs(const Path & path)
|
||||
{
|
||||
Paths created;
|
||||
|
|
|
@ -149,10 +149,14 @@ std::vector<Path> getConfigDirs();
|
|||
/* Return $XDG_DATA_HOME or $HOME/.local/share. */
|
||||
Path getDataDir();
|
||||
|
||||
/* Return the path of the current executable. */
|
||||
std::optional<Path> getSelfExe();
|
||||
|
||||
/* Create a directory and all its parents, if necessary. Returns the
|
||||
list of created directories, in order of creation. */
|
||||
Paths createDirs(const Path & path);
|
||||
inline Paths createDirs(PathView path) {
|
||||
inline Paths createDirs(PathView path)
|
||||
{
|
||||
return createDirs(Path(path));
|
||||
}
|
||||
|
||||
|
|
|
@ -257,11 +257,12 @@ static void main_nix_build(int argc, char * * argv)
|
|||
|
||||
auto autoArgs = myArgs.getAutoArgs(*state);
|
||||
|
||||
auto autoArgsWithInNixShell = autoArgs;
|
||||
if (runEnv) {
|
||||
auto newArgs = state->buildBindings(autoArgs->size() + 1);
|
||||
auto newArgs = state->buildBindings(autoArgsWithInNixShell->size() + 1);
|
||||
newArgs.alloc("inNixShell").mkBool(true);
|
||||
for (auto & i : *autoArgs) newArgs.insert(i);
|
||||
autoArgs = newArgs.finish();
|
||||
autoArgsWithInNixShell = newArgs.finish();
|
||||
}
|
||||
|
||||
if (packages) {
|
||||
|
@ -316,10 +317,39 @@ static void main_nix_build(int argc, char * * argv)
|
|||
Value vRoot;
|
||||
state->eval(e, vRoot);
|
||||
|
||||
std::function<bool(const Value & v)> takesNixShellAttr;
|
||||
takesNixShellAttr = [&](const Value & v) {
|
||||
if (!runEnv) {
|
||||
return false;
|
||||
}
|
||||
bool add = false;
|
||||
if (v.type() == nFunction && v.lambda.fun->hasFormals()) {
|
||||
for (auto & i : v.lambda.fun->formals->formals) {
|
||||
if (state->symbols[i.name] == "inNixShell") {
|
||||
add = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return add;
|
||||
};
|
||||
|
||||
for (auto & i : attrPaths) {
|
||||
Value & v(*findAlongAttrPath(*state, i, *autoArgs, vRoot).first);
|
||||
Value & v(*findAlongAttrPath(
|
||||
*state,
|
||||
i,
|
||||
takesNixShellAttr(vRoot) ? *autoArgsWithInNixShell : *autoArgs,
|
||||
vRoot
|
||||
).first);
|
||||
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||
getDerivations(*state, v, "", *autoArgs, drvs, false);
|
||||
getDerivations(
|
||||
*state,
|
||||
v,
|
||||
"",
|
||||
takesNixShellAttr(v) ? *autoArgsWithInNixShell : *autoArgs,
|
||||
drvs,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -543,6 +573,8 @@ static void main_nix_build(int argc, char * * argv)
|
|||
|
||||
restoreProcessContext();
|
||||
|
||||
logger->stop();
|
||||
|
||||
execvp(shell->c_str(), argPtrs.data());
|
||||
|
||||
throw SysError("executing shell '%s'", *shell);
|
||||
|
@ -601,6 +633,8 @@ static void main_nix_build(int argc, char * * argv)
|
|||
outPaths.push_back(outputPath);
|
||||
}
|
||||
|
||||
logger->stop();
|
||||
|
||||
for (auto & path : outPaths)
|
||||
std::cout << store->printStorePath(path) << '\n';
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ void removeOldGenerations(std::string dir)
|
|||
link = readLink(path);
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOENT) continue;
|
||||
throw;
|
||||
}
|
||||
if (link.find("link") != std::string::npos) {
|
||||
printInfo(format("removing old generations of profile %1%") % path);
|
||||
|
|
|
@ -1485,7 +1485,7 @@ static int main_nix_env(int argc, char * * argv)
|
|||
if (globals.profile == "")
|
||||
globals.profile = getDefaultProfile();
|
||||
|
||||
op(globals, opFlags, opArgs);
|
||||
op(globals, std::move(opFlags), std::move(opArgs));
|
||||
|
||||
globals.state->printStats();
|
||||
|
||||
|
|
|
@ -1093,7 +1093,7 @@ static int main_nix_store(int argc, char * * argv)
|
|||
if (op != opDump && op != opRestore) /* !!! hack */
|
||||
store = openStore();
|
||||
|
||||
op(opFlags, opArgs);
|
||||
op(std::move(opFlags), std::move(opArgs));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -276,15 +276,27 @@ struct Common : InstallableCommand, MixProfile
|
|||
const BuildEnvironment & buildEnvironment,
|
||||
const Path & outputsDir = absPath(".") + "/outputs")
|
||||
{
|
||||
// A list of colon-separated environment variables that should be
|
||||
// prepended to, rather than overwritten, in order to keep the shell usable.
|
||||
// Please keep this list minimal in order to avoid impurities.
|
||||
static const char * const savedVars[] = {
|
||||
"PATH", // for commands
|
||||
"XDG_DATA_DIRS", // for loadable completion
|
||||
};
|
||||
|
||||
std::ostringstream out;
|
||||
|
||||
out << "unset shellHook\n";
|
||||
|
||||
out << "nix_saved_PATH=\"$PATH\"\n";
|
||||
for (auto & var : savedVars) {
|
||||
out << fmt("%s=${%s:-}\n", var, var);
|
||||
out << fmt("nix_saved_%s=\"$%s\"\n", var, var);
|
||||
}
|
||||
|
||||
buildEnvironment.toBash(out, ignoreVars);
|
||||
|
||||
out << "PATH=\"$PATH:$nix_saved_PATH\"\n";
|
||||
for (auto & var : savedVars)
|
||||
out << fmt("%s=\"$%s:$nix_saved_%s\"\n", var, var, var);
|
||||
|
||||
out << "export NIX_BUILD_TOP=\"$(mktemp -d -t nix-shell.XXXXXX)\"\n";
|
||||
for (auto & i : {"TMP", "TMPDIR", "TEMP", "TEMPDIR"})
|
||||
|
|
|
@ -50,9 +50,9 @@ public:
|
|||
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
|
||||
}
|
||||
|
||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
||||
std::vector<std::string> getFlakesForCompletion() override
|
||||
{
|
||||
return getFlakeRef();
|
||||
return {flakeUrl};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -740,7 +740,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
|||
"If you've set '%s' to a string, try using a path instead.",
|
||||
templateDir, templateDirAttr->getAttrPathStr());
|
||||
|
||||
std::vector<Path> files;
|
||||
std::vector<Path> changedFiles;
|
||||
std::vector<Path> conflictedFiles;
|
||||
|
||||
std::function<void(const Path & from, const Path & to)> copyDir;
|
||||
copyDir = [&](const Path & from, const Path & to)
|
||||
|
@ -757,31 +758,41 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
|||
auto contents = readFile(from2);
|
||||
if (pathExists(to2)) {
|
||||
auto contents2 = readFile(to2);
|
||||
if (contents != contents2)
|
||||
throw Error("refusing to overwrite existing file '%s'", to2);
|
||||
if (contents != contents2) {
|
||||
printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2, from2);
|
||||
conflictedFiles.push_back(to2);
|
||||
} else {
|
||||
notice("skipping identical file: %s", from2);
|
||||
}
|
||||
continue;
|
||||
} else
|
||||
writeFile(to2, contents);
|
||||
}
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
auto target = readLink(from2);
|
||||
if (pathExists(to2)) {
|
||||
if (readLink(to2) != target)
|
||||
throw Error("refusing to overwrite existing symlink '%s'", to2);
|
||||
if (readLink(to2) != target) {
|
||||
printError("refusing to overwrite existing file '%s'\n please merge it manually with '%s'", to2, from2);
|
||||
conflictedFiles.push_back(to2);
|
||||
} else {
|
||||
notice("skipping identical file: %s", from2);
|
||||
}
|
||||
continue;
|
||||
} else
|
||||
createSymlink(target, to2);
|
||||
}
|
||||
else
|
||||
throw Error("file '%s' has unsupported type", from2);
|
||||
files.push_back(to2);
|
||||
changedFiles.push_back(to2);
|
||||
notice("wrote: %s", to2);
|
||||
}
|
||||
};
|
||||
|
||||
copyDir(templateDir, flakeDir);
|
||||
|
||||
if (pathExists(flakeDir + "/.git")) {
|
||||
if (!changedFiles.empty() && pathExists(flakeDir + "/.git")) {
|
||||
Strings args = { "-C", flakeDir, "add", "--intent-to-add", "--force", "--" };
|
||||
for (auto & s : files) args.push_back(s);
|
||||
for (auto & s : changedFiles) args.push_back(s);
|
||||
runProgram("git", true, args);
|
||||
}
|
||||
auto welcomeText = cursor->maybeGetAttr("welcomeText");
|
||||
|
@ -789,6 +800,9 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
|||
notice("\n");
|
||||
notice(renderMarkdownToTerminal(welcomeText->getString()));
|
||||
}
|
||||
|
||||
if (!conflictedFiles.empty())
|
||||
throw Error("Encountered %d conflicts - see above", conflictedFiles.size());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ convert-secret-to-public` to get the corresponding public key for
|
|||
verifying signed store paths.
|
||||
|
||||
The mandatory argument `--key-name` specifies a key name (such as
|
||||
`cache.example.org-1). It is used to look up keys on the client when
|
||||
`cache.example.org-1`). It is used to look up keys on the client when
|
||||
it verifies signatures. It can be anything, but it’s suggested to use
|
||||
the host name of your cache (e.g. `cache.example.org`) with a suffix
|
||||
denoting the number of the key (to be incremented every time you need
|
||||
|
|
|
@ -266,6 +266,11 @@ void mainWrapped(int argc, char * * argv)
|
|||
programPath = argv[0];
|
||||
auto programName = std::string(baseNameOf(programPath));
|
||||
|
||||
if (argc > 0 && std::string_view(argv[0]) == "__build-remote") {
|
||||
programName = "build-remote";
|
||||
argv++; argc--;
|
||||
}
|
||||
|
||||
{
|
||||
auto legacy = (*RegisterLegacyCommand::commands)[programName];
|
||||
if (legacy) return legacy(argc, argv);
|
||||
|
@ -342,7 +347,10 @@ void mainWrapped(int argc, char * * argv)
|
|||
if (!completions) throw;
|
||||
}
|
||||
|
||||
if (completions) return;
|
||||
if (completions) {
|
||||
args.completionHook();
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.showVersion) {
|
||||
printVersion(programName);
|
||||
|
@ -380,6 +388,9 @@ void mainWrapped(int argc, char * * argv)
|
|||
settings.ttlPositiveNarInfoCache = 0;
|
||||
}
|
||||
|
||||
if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) {
|
||||
evalSettings.pureEval = false;
|
||||
}
|
||||
args.command->second->prepare();
|
||||
args.command->second->run();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ highest precedence:
|
|||
can be specified using the NixOS option `nix.registry`.
|
||||
|
||||
* The user registry `~/.config/nix/registry.json`. This registry can
|
||||
be modified by commands such as `nix flake pin`.
|
||||
be modified by commands such as `nix registry pin`.
|
||||
|
||||
* Overrides specified on the command line using the option
|
||||
`--override-flake`.
|
||||
|
|
|
@ -24,10 +24,34 @@ R""(
|
|||
* Interact with Nixpkgs in the REPL:
|
||||
|
||||
```console
|
||||
# nix repl '<nixpkgs>'
|
||||
# nix repl --file example.nix
|
||||
Loading Installable ''...
|
||||
Added 3 variables.
|
||||
|
||||
Loading '<nixpkgs>'...
|
||||
Added 12428 variables.
|
||||
# nix repl --expr '{a={b=3;c=4;};}'
|
||||
Loading Installable ''...
|
||||
Added 1 variables.
|
||||
|
||||
# nix repl --expr '{a={b=3;c=4;};}' a
|
||||
Loading Installable ''...
|
||||
Added 1 variables.
|
||||
|
||||
# nix repl --extra_experimental_features 'flakes repl-flake' nixpkgs
|
||||
Loading Installable 'flake:nixpkgs#'...
|
||||
Added 5 variables.
|
||||
|
||||
nix-repl> legacyPackages.x86_64-linux.emacs.name
|
||||
"emacs-27.1"
|
||||
|
||||
nix-repl> legacyPackages.x86_64-linux.emacs.name
|
||||
"emacs-27.1"
|
||||
|
||||
nix-repl> :q
|
||||
|
||||
# nix repl --expr 'import <nixpkgs>{}'
|
||||
|
||||
Loading Installable ''...
|
||||
Added 12439 variables.
|
||||
|
||||
nix-repl> emacs.name
|
||||
"emacs-27.1"
|
||||
|
|
|
@ -47,7 +47,7 @@ void runProgramInStore(ref<Store> store,
|
|||
Strings helperArgs = { chrootHelperName, store->storeDir, store2->getRealStoreDir(), program };
|
||||
for (auto & arg : args) helperArgs.push_back(arg);
|
||||
|
||||
execv(readLink("/proc/self/exe").c_str(), stringsToCharPtrs(helperArgs).data());
|
||||
execv(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data());
|
||||
|
||||
throw SysError("could not execute chroot helper");
|
||||
}
|
||||
|
|
|
@ -18,16 +18,26 @@ using namespace nix;
|
|||
|
||||
std::string wrap(std::string prefix, std::string s)
|
||||
{
|
||||
return prefix + s + ANSI_NORMAL;
|
||||
return concatStrings(prefix, s, ANSI_NORMAL);
|
||||
}
|
||||
|
||||
struct CmdSearch : InstallableCommand, MixJSON
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
std::vector<std::string> excludeRes;
|
||||
|
||||
CmdSearch()
|
||||
{
|
||||
expectArgs("regex", &res);
|
||||
addFlag(Flag {
|
||||
.longName = "exclude",
|
||||
.shortName = 'e',
|
||||
.description = "Hide packages whose attribute path, name or description contain *regex*.",
|
||||
.labels = {"regex"},
|
||||
.handler = {[this](std::string s) {
|
||||
excludeRes.push_back(s);
|
||||
}},
|
||||
});
|
||||
}
|
||||
|
||||
std::string description() override
|
||||
|
@ -62,11 +72,16 @@ struct CmdSearch : InstallableCommand, MixJSON
|
|||
res.push_back("^");
|
||||
|
||||
std::vector<std::regex> regexes;
|
||||
std::vector<std::regex> excludeRegexes;
|
||||
regexes.reserve(res.size());
|
||||
excludeRegexes.reserve(excludeRes.size());
|
||||
|
||||
for (auto & re : res)
|
||||
regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase));
|
||||
|
||||
for (auto & re : excludeRes)
|
||||
excludeRegexes.emplace_back(re, std::regex::extended | std::regex::icase);
|
||||
|
||||
auto state = getEvalState();
|
||||
|
||||
auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;
|
||||
|
@ -106,6 +121,14 @@ struct CmdSearch : InstallableCommand, MixJSON
|
|||
std::vector<std::smatch> nameMatches;
|
||||
bool found = false;
|
||||
|
||||
for (auto & regex : excludeRegexes) {
|
||||
if (
|
||||
std::regex_search(attrPath2, regex)
|
||||
|| std::regex_search(name.name, regex)
|
||||
|| std::regex_search(description, regex))
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto & regex : regexes) {
|
||||
found = false;
|
||||
auto addAll = [&found](std::sregex_iterator it, std::vector<std::smatch> & vec) {
|
||||
|
@ -133,15 +156,15 @@ struct CmdSearch : InstallableCommand, MixJSON
|
|||
jsonElem.attr("version", name.version);
|
||||
jsonElem.attr("description", description);
|
||||
} else {
|
||||
auto name2 = hiliteMatches(name.name, std::move(nameMatches), ANSI_GREEN, "\e[0;2m");
|
||||
auto name2 = hiliteMatches(name.name, nameMatches, ANSI_GREEN, "\e[0;2m");
|
||||
if (results > 1) logger->cout("");
|
||||
logger->cout(
|
||||
"* %s%s",
|
||||
wrap("\e[0;1m", hiliteMatches(attrPath2, std::move(attrPathMatches), ANSI_GREEN, "\e[0;1m")),
|
||||
wrap("\e[0;1m", hiliteMatches(attrPath2, attrPathMatches, ANSI_GREEN, "\e[0;1m")),
|
||||
name.version != "" ? " (" + name.version + ")" : "");
|
||||
if (description != "")
|
||||
logger->cout(
|
||||
" %s", hiliteMatches(description, std::move(descriptionMatches), ANSI_GREEN, ANSI_NORMAL));
|
||||
" %s", hiliteMatches(description, descriptionMatches, ANSI_GREEN, ANSI_NORMAL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,23 @@ R""(
|
|||
# nix search nixpkgs 'firefox|chromium'
|
||||
```
|
||||
|
||||
* Search for packages containing `git'`and either `frontend` or `gui`:
|
||||
* Search for packages containing `git` and either `frontend` or `gui`:
|
||||
|
||||
```console
|
||||
# nix search nixpkgs git 'frontend|gui'
|
||||
```
|
||||
|
||||
* Search for packages containing `neovim` but hide ones containing either `gui` or `python`:
|
||||
|
||||
```console
|
||||
# nix search nixpkgs neovim -e 'python|gui'
|
||||
```
|
||||
or
|
||||
|
||||
```console
|
||||
# nix search nixpkgs neovim -e 'python' -e 'gui'
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
`nix search` searches *installable* (which must be evaluatable, e.g. a
|
||||
|
|
|
@ -34,7 +34,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
|
|||
|
||||
std::string description() override
|
||||
{
|
||||
return "upgrade Nix to the latest stable version";
|
||||
return "upgrade Nix to the stable version declared in Nixpkgs";
|
||||
}
|
||||
|
||||
std::string doc() override
|
||||
|
|
|
@ -2,7 +2,7 @@ R""(
|
|||
|
||||
# Examples
|
||||
|
||||
* Upgrade Nix to the latest stable version:
|
||||
* Upgrade Nix to the stable version declared in Nixpkgs:
|
||||
|
||||
```console
|
||||
# nix upgrade-nix
|
||||
|
@ -16,8 +16,11 @@ R""(
|
|||
|
||||
# Description
|
||||
|
||||
This command upgrades Nix to the latest version. By default, it
|
||||
locates the directory containing the `nix` binary in the `$PATH`
|
||||
This command upgrades Nix to the stable version declared in Nixpkgs.
|
||||
This stable version is defined in [nix-fallback-paths.nix](https://github.com/NixOS/nixpkgs/raw/master/nixos/modules/installer/tools/nix-fallback-paths.nix)
|
||||
and updated manually. It may not always be the latest tagged release.
|
||||
|
||||
By default, it locates the directory containing the `nix` binary in the `$PATH`
|
||||
environment variable. If that directory is a Nix profile, it will
|
||||
upgrade the `nix` package in that profile to the latest stable binary
|
||||
release.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue