mirror of
https://github.com/NixOS/nix.git
synced 2025-12-07 17:41:00 +01:00
Merge remote-tracking branch 'origin/master' into handle-missing-gc-socket
This commit is contained in:
commit
302625e83b
68 changed files with 1721 additions and 556 deletions
|
|
@ -93,9 +93,17 @@ struct NixRepl
|
|||
void evalString(std::string s, Value & v);
|
||||
void loadDebugTraceEnv(DebugTrace & dt);
|
||||
|
||||
typedef std::set<Value *> ValuesSeen;
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth);
|
||||
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
|
||||
void printValue(std::ostream & str,
|
||||
Value & v,
|
||||
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
|
||||
{
|
||||
::nix::printValue(*state, str, v, PrintOptions {
|
||||
.ansiColors = true,
|
||||
.force = true,
|
||||
.derivationPaths = true,
|
||||
.maxDepth = maxDepth
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
std::string removeWhitespace(std::string s)
|
||||
|
|
@ -246,7 +254,7 @@ void NixRepl::mainLoop()
|
|||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
#ifndef USE_READLINE
|
||||
|
|
@ -708,7 +716,8 @@ bool NixRepl::processLine(std::string line)
|
|||
else if (command == ":p" || command == ":print") {
|
||||
Value v;
|
||||
evalString(arg, v);
|
||||
printValue(std::cout, v, 1000000000) << std::endl;
|
||||
printValue(std::cout, v);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
else if (command == ":q" || command == ":quit") {
|
||||
|
|
@ -770,7 +779,8 @@ bool NixRepl::processLine(std::string line)
|
|||
} else {
|
||||
Value v;
|
||||
evalString(line, v);
|
||||
printValue(std::cout, v, 1) << std::endl;
|
||||
printValue(std::cout, v, 1);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -892,144 +902,6 @@ void NixRepl::evalString(std::string s, Value & v)
|
|||
}
|
||||
|
||||
|
||||
std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth)
|
||||
{
|
||||
ValuesSeen seen;
|
||||
return printValue(str, v, maxDepth, seen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: lot of cut&paste from Nix's eval.cc.
|
||||
std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen)
|
||||
{
|
||||
str.flush();
|
||||
checkInterrupt();
|
||||
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
str << ANSI_CYAN << v.integer << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
str << ANSI_CYAN;
|
||||
printLiteralBool(str, v.boolean);
|
||||
str << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nString:
|
||||
str << ANSI_WARNING;
|
||||
printLiteralString(str, v.string_view());
|
||||
str << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
str << ANSI_GREEN << v.path().to_string() << ANSI_NORMAL; // !!! escaping?
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
str << ANSI_CYAN "null" ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
case nAttrs: {
|
||||
seen.insert(&v);
|
||||
|
||||
bool isDrv = state->isDerivation(v);
|
||||
|
||||
if (isDrv) {
|
||||
str << "«derivation ";
|
||||
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
||||
NixStringContext context;
|
||||
if (i != v.attrs->end())
|
||||
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
else
|
||||
str << "???";
|
||||
str << "»";
|
||||
}
|
||||
|
||||
else if (maxDepth > 0) {
|
||||
str << "{ ";
|
||||
|
||||
typedef std::map<std::string, Value *> Sorted;
|
||||
Sorted sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace(state->symbols[i.name], i.value);
|
||||
|
||||
for (auto & i : sorted) {
|
||||
printAttributeName(str, i.first);
|
||||
str << " = ";
|
||||
if (seen.count(i.second))
|
||||
str << "«repeated»";
|
||||
else
|
||||
try {
|
||||
printValue(str, *i.second, maxDepth - 1, seen);
|
||||
} catch (AssertionError & e) {
|
||||
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
||||
}
|
||||
str << "; ";
|
||||
}
|
||||
|
||||
str << "}";
|
||||
} else
|
||||
str << "{ ... }";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nList:
|
||||
seen.insert(&v);
|
||||
|
||||
str << "[ ";
|
||||
if (maxDepth > 0)
|
||||
for (auto elem : v.listItems()) {
|
||||
if (seen.count(elem))
|
||||
str << "«repeated»";
|
||||
else
|
||||
try {
|
||||
printValue(str, *elem, maxDepth - 1, seen);
|
||||
} catch (AssertionError & e) {
|
||||
str << ANSI_RED "«error: " << e.msg() << "»" ANSI_NORMAL;
|
||||
}
|
||||
str << " ";
|
||||
}
|
||||
else
|
||||
str << "... ";
|
||||
str << "]";
|
||||
break;
|
||||
|
||||
case nFunction:
|
||||
if (v.isLambda()) {
|
||||
std::ostringstream s;
|
||||
s << state->positions[v.lambda.fun->pos];
|
||||
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
||||
} else if (v.isPrimOp()) {
|
||||
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
||||
} else if (v.isPrimOpApp()) {
|
||||
str << ANSI_BLUE "«primop-app»" ANSI_NORMAL;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
str << v.fpoint;
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
case nExternal:
|
||||
default:
|
||||
str << ANSI_RED "«unknown»" ANSI_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues)
|
||||
|
|
|
|||
|
|
@ -105,117 +105,23 @@ RootValue allocRootValue(Value * v)
|
|||
#endif
|
||||
}
|
||||
|
||||
void Value::print(const SymbolTable &symbols, std::ostream &str,
|
||||
std::set<const void *> *seen, int depth) const
|
||||
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (depth <= 0) {
|
||||
str << "«too deep»";
|
||||
return;
|
||||
}
|
||||
switch (internalType) {
|
||||
case tInt:
|
||||
str << integer;
|
||||
break;
|
||||
case tBool:
|
||||
printLiteralBool(str, boolean);
|
||||
break;
|
||||
case tString:
|
||||
printLiteralString(str, string_view());
|
||||
break;
|
||||
case tPath:
|
||||
str << path().to_string(); // !!! escaping?
|
||||
break;
|
||||
case tNull:
|
||||
str << "null";
|
||||
break;
|
||||
case tAttrs: {
|
||||
if (seen && !attrs->empty() && !seen->insert(attrs).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "{ ";
|
||||
for (auto & i : attrs->lexicographicOrder(symbols)) {
|
||||
str << symbols[i->name] << " = ";
|
||||
i->value->print(symbols, str, seen, depth - 1);
|
||||
str << "; ";
|
||||
}
|
||||
str << "}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
if (seen && listSize() && !seen->insert(listElems()).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "[ ";
|
||||
for (auto v2 : listItems()) {
|
||||
if (v2)
|
||||
v2->print(symbols, str, seen, depth - 1);
|
||||
else
|
||||
str << "(nullptr)";
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
}
|
||||
break;
|
||||
case tThunk:
|
||||
case tApp:
|
||||
if (!isBlackhole()) {
|
||||
str << "<CODE>";
|
||||
} else {
|
||||
// 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;
|
||||
case tLambda:
|
||||
str << "<LAMBDA>";
|
||||
break;
|
||||
case tPrimOp:
|
||||
str << "<PRIMOP>";
|
||||
break;
|
||||
case tPrimOpApp:
|
||||
str << "<PRIMOP-APP>";
|
||||
break;
|
||||
case tExternal:
|
||||
str << *external;
|
||||
break;
|
||||
case tFloat:
|
||||
str << fpoint;
|
||||
break;
|
||||
default:
|
||||
printError("Nix evaluator internal error: Value::print(): invalid value type %1%", internalType);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void Value::print(const SymbolTable &symbols, std::ostream &str,
|
||||
bool showRepeated, int depth) const {
|
||||
std::set<const void *> seen;
|
||||
print(symbols, str, showRepeated ? nullptr : &seen, depth);
|
||||
}
|
||||
|
||||
// Pretty print types for assertion errors
|
||||
std::ostream & operator << (std::ostream & os, const ValueType t) {
|
||||
os << showType(t);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string printValue(const EvalState & state, const Value & v)
|
||||
std::string printValue(EvalState & state, Value & v)
|
||||
{
|
||||
std::ostringstream out;
|
||||
v.print(state.symbols, out);
|
||||
v.print(state, out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
|
||||
{
|
||||
printValue(state, str, *this, options);
|
||||
}
|
||||
|
||||
const Value * getPrimOp(const Value &v) {
|
||||
const Value * primOp = &v;
|
||||
|
|
@ -710,6 +616,26 @@ void PrimOp::check()
|
|||
}
|
||||
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp)
|
||||
{
|
||||
output << "primop " << primOp.name;
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
PrimOp * Value::primOpAppPrimOp() const
|
||||
{
|
||||
Value * left = primOpApp.left;
|
||||
while (left && !left->isPrimOp()) {
|
||||
left = left->primOpApp.left;
|
||||
}
|
||||
|
||||
if (!left)
|
||||
return nullptr;
|
||||
return left->primOp;
|
||||
}
|
||||
|
||||
|
||||
void Value::mkPrimOp(PrimOp * p)
|
||||
{
|
||||
p->check();
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ struct PrimOp
|
|||
void check();
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp);
|
||||
|
||||
/**
|
||||
* Info about a constant
|
||||
*/
|
||||
|
|
@ -127,7 +129,7 @@ std::unique_ptr<ValMap> mapStaticEnvBindings(const SymbolTable & st, const Stati
|
|||
void copyContext(const Value & v, NixStringContext & context);
|
||||
|
||||
|
||||
std::string printValue(const EvalState & state, const Value & v);
|
||||
std::string printValue(EvalState & state, Value & v);
|
||||
std::ostream & operator << (std::ostream & os, const ValueType t);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ else { return ELSE; }
|
|||
assert { return ASSERT; }
|
||||
with { return WITH; }
|
||||
let { return LET; }
|
||||
in { return IN; }
|
||||
in { return IN_KW; }
|
||||
rec { return REC; }
|
||||
inherit { return INHERIT; }
|
||||
or { return OR_KW; }
|
||||
|
|
@ -156,7 +156,7 @@ or { return OR_KW; }
|
|||
.errPos = data->state.positions[CUR_POS],
|
||||
});
|
||||
}
|
||||
return INT;
|
||||
return INT_LIT;
|
||||
}
|
||||
{FLOAT} { errno = 0;
|
||||
yylval->nf = strtod(yytext, 0);
|
||||
|
|
@ -165,7 +165,7 @@ or { return OR_KW; }
|
|||
.msg = hintfmt("invalid float '%1%'", yytext),
|
||||
.errPos = data->state.positions[CUR_POS],
|
||||
});
|
||||
return FLOAT;
|
||||
return FLOAT_LIT;
|
||||
}
|
||||
|
||||
\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||
|
|
|
|||
|
|
@ -365,11 +365,11 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
|||
%type <id> attr
|
||||
%token <id> ID
|
||||
%token <str> STR IND_STR
|
||||
%token <n> INT
|
||||
%token <nf> FLOAT
|
||||
%token <n> INT_LIT
|
||||
%token <nf> FLOAT_LIT
|
||||
%token <path> PATH HPATH SPATH PATH_END
|
||||
%token <uri> URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||
%token IF THEN ELSE ASSERT WITH LET IN_KW REC INHERIT EQ NEQ AND OR IMPL OR_KW
|
||||
%token DOLLAR_CURLY /* == ${ */
|
||||
%token IND_STRING_OPEN IND_STRING_CLOSE
|
||||
%token ELLIPSIS
|
||||
|
|
@ -412,7 +412,7 @@ expr_function
|
|||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||
| WITH expr ';' expr_function
|
||||
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
||||
| LET binds IN expr_function
|
||||
| LET binds IN_KW expr_function
|
||||
{ if (!$2->dynamicAttrs.empty())
|
||||
throw ParseError({
|
||||
.msg = hintfmt("dynamic attributes not allowed in let"),
|
||||
|
|
@ -482,8 +482,8 @@ expr_simple
|
|||
else
|
||||
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
|
||||
}
|
||||
| INT { $$ = new ExprInt($1); }
|
||||
| FLOAT { $$ = new ExprFloat($1); }
|
||||
| INT_LIT { $$ = new ExprInt($1); }
|
||||
| FLOAT_LIT { $$ = new ExprFloat($1); }
|
||||
| '"' string_parts '"' { $$ = $2; }
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(CUR_POS, data->symbols, std::move(*$2));
|
||||
|
|
|
|||
|
|
@ -3712,9 +3712,6 @@ static RegisterPrimOp primop_toString({
|
|||
static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring");
|
||||
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
|
||||
|
||||
if (start < 0)
|
||||
state.debugThrowLastTrace(EvalError({
|
||||
|
|
@ -3722,6 +3719,22 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args,
|
|||
.errPos = state.positions[pos]
|
||||
}));
|
||||
|
||||
|
||||
int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring");
|
||||
|
||||
// Special-case on empty substring to avoid O(n) strlen
|
||||
// This allows for the use of empty substrings to efficently capture string context
|
||||
if (len == 0) {
|
||||
state.forceValue(*args[2], pos);
|
||||
if (args[2]->type() == nString) {
|
||||
v.mkString("", args[2]->context());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NixStringContext context;
|
||||
auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring");
|
||||
|
||||
v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
|
||||
}
|
||||
|
||||
|
|
|
|||
100
src/libexpr/print-ambiguous.cc
Normal file
100
src/libexpr/print-ambiguous.cc
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#include "print-ambiguous.hh"
|
||||
#include "print.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
// See: https://github.com/NixOS/nix/issues/9730
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (depth <= 0) {
|
||||
str << "«too deep»";
|
||||
return;
|
||||
}
|
||||
switch (v.type()) {
|
||||
case nInt:
|
||||
str << v.integer;
|
||||
break;
|
||||
case nBool:
|
||||
printLiteralBool(str, v.boolean);
|
||||
break;
|
||||
case nString:
|
||||
printLiteralString(str, v.string_view());
|
||||
break;
|
||||
case nPath:
|
||||
str << v.path().to_string(); // !!! escaping?
|
||||
break;
|
||||
case nNull:
|
||||
str << "null";
|
||||
break;
|
||||
case nAttrs: {
|
||||
if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "{ ";
|
||||
for (auto & i : v.attrs->lexicographicOrder(symbols)) {
|
||||
str << symbols[i->name] << " = ";
|
||||
printAmbiguous(*i->value, symbols, str, seen, depth - 1);
|
||||
str << "; ";
|
||||
}
|
||||
str << "}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nList:
|
||||
if (seen && v.listSize() && !seen->insert(v.listElems()).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "[ ";
|
||||
for (auto v2 : v.listItems()) {
|
||||
if (v2)
|
||||
printAmbiguous(*v2, symbols, str, seen, depth - 1);
|
||||
else
|
||||
str << "(nullptr)";
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
}
|
||||
break;
|
||||
case nThunk:
|
||||
if (!v.isBlackhole()) {
|
||||
str << "<CODE>";
|
||||
} else {
|
||||
// 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;
|
||||
case nFunction:
|
||||
if (v.isLambda()) {
|
||||
str << "<LAMBDA>";
|
||||
} else if (v.isPrimOp()) {
|
||||
str << "<PRIMOP>";
|
||||
} else if (v.isPrimOpApp()) {
|
||||
str << "<PRIMOP-APP>";
|
||||
}
|
||||
break;
|
||||
case nExternal:
|
||||
str << *v.external;
|
||||
break;
|
||||
case nFloat:
|
||||
str << v.fpoint;
|
||||
break;
|
||||
default:
|
||||
printError("Nix evaluator internal error: printAmbiguous: invalid value type");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
24
src/libexpr/print-ambiguous.hh
Normal file
24
src/libexpr/print-ambiguous.hh
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "value.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Print a value in the deprecated format used by `nix-instantiate --eval` and
|
||||
* `nix-env` (for manifests).
|
||||
*
|
||||
* This output can't be changed because it's part of the `nix-instantiate` API,
|
||||
* but it produces ambiguous output; unevaluated thunks and lambdas (and a few
|
||||
* other types) are printed as Nix path syntax like `<CODE>`.
|
||||
*
|
||||
* See: https://github.com/NixOS/nix/issues/9730
|
||||
*/
|
||||
void printAmbiguous(
|
||||
Value &v,
|
||||
const SymbolTable &symbols,
|
||||
std::ostream &str,
|
||||
std::set<const void *> *seen,
|
||||
int depth);
|
||||
|
||||
}
|
||||
52
src/libexpr/print-options.hh
Normal file
52
src/libexpr/print-options.hh
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
/**
|
||||
* @file
|
||||
* @brief Options for printing Nix values.
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Options for printing Nix values.
|
||||
*/
|
||||
struct PrintOptions
|
||||
{
|
||||
/**
|
||||
* If true, output ANSI color sequences.
|
||||
*/
|
||||
bool ansiColors = false;
|
||||
/**
|
||||
* If true, force values.
|
||||
*/
|
||||
bool force = false;
|
||||
/**
|
||||
* If true and `force` is set, print derivations as
|
||||
* `«derivation /nix/store/...»` instead of as attribute sets.
|
||||
*/
|
||||
bool derivationPaths = false;
|
||||
/**
|
||||
* If true, track which values have been printed and skip them on
|
||||
* subsequent encounters. Useful for self-referential values.
|
||||
*/
|
||||
bool trackRepeated = true;
|
||||
/**
|
||||
* Maximum depth to evaluate to.
|
||||
*/
|
||||
size_t maxDepth = std::numeric_limits<size_t>::max();
|
||||
/**
|
||||
* Maximum number of attributes in an attribute set to print.
|
||||
*/
|
||||
size_t maxAttrs = std::numeric_limits<size_t>::max();
|
||||
/**
|
||||
* Maximum number of list items to print.
|
||||
*/
|
||||
size_t maxListItems = std::numeric_limits<size_t>::max();
|
||||
/**
|
||||
* Maximum string length to print.
|
||||
*/
|
||||
size_t maxStringLength = std::numeric_limits<size_t>::max();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +1,66 @@
|
|||
#include "print.hh"
|
||||
#include <limits>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "print.hh"
|
||||
#include "ansicolor.hh"
|
||||
#include "signals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "terminal.hh"
|
||||
#include "english.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string)
|
||||
void printElided(
|
||||
std::ostream & output,
|
||||
unsigned int value,
|
||||
const std::string_view single,
|
||||
const std::string_view plural,
|
||||
bool ansiColors)
|
||||
{
|
||||
if (ansiColors)
|
||||
output << ANSI_FAINT;
|
||||
output << " «";
|
||||
pluralize(output, value, single, plural);
|
||||
output << " elided»";
|
||||
if (ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
|
||||
{
|
||||
size_t charsPrinted = 0;
|
||||
if (ansiColors)
|
||||
str << ANSI_MAGENTA;
|
||||
str << "\"";
|
||||
for (auto i = string.begin(); i != string.end(); ++i) {
|
||||
if (charsPrinted >= maxLength) {
|
||||
str << "\"";
|
||||
printElided(str, string.length() - charsPrinted, "byte", "bytes", ansiColors);
|
||||
return str;
|
||||
}
|
||||
|
||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
||||
else if (*i == '\n') str << "\\n";
|
||||
else if (*i == '\r') str << "\\r";
|
||||
else if (*i == '\t') str << "\\t";
|
||||
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
|
||||
else str << *i;
|
||||
charsPrinted++;
|
||||
}
|
||||
str << "\"";
|
||||
if (ansiColors)
|
||||
str << ANSI_NORMAL;
|
||||
return str;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralString(std::ostream & str, const std::string_view string)
|
||||
{
|
||||
return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
printLiteralBool(std::ostream & str, bool boolean)
|
||||
{
|
||||
|
|
@ -90,5 +132,373 @@ printAttributeName(std::ostream & str, std::string_view name) {
|
|||
return str;
|
||||
}
|
||||
|
||||
bool isImportantAttrName(const std::string& attrName)
|
||||
{
|
||||
return attrName == "type" || attrName == "_type";
|
||||
}
|
||||
|
||||
typedef std::pair<std::string, Value *> AttrPair;
|
||||
|
||||
struct ImportantFirstAttrNameCmp
|
||||
{
|
||||
|
||||
bool operator()(const AttrPair& lhs, const AttrPair& rhs) const
|
||||
{
|
||||
auto lhsIsImportant = isImportantAttrName(lhs.first);
|
||||
auto rhsIsImportant = isImportantAttrName(rhs.first);
|
||||
return std::forward_as_tuple(!lhsIsImportant, lhs.first)
|
||||
< std::forward_as_tuple(!rhsIsImportant, rhs.first);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<Value *> ValuesSeen;
|
||||
|
||||
class Printer
|
||||
{
|
||||
private:
|
||||
std::ostream & output;
|
||||
EvalState & state;
|
||||
PrintOptions options;
|
||||
std::optional<ValuesSeen> seen;
|
||||
|
||||
void printRepeated()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_MAGENTA;
|
||||
output << "«repeated»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printNullptr()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_MAGENTA;
|
||||
output << "«nullptr»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printElided(unsigned int value, const std::string_view single, const std::string_view plural)
|
||||
{
|
||||
::nix::printElided(output, value, single, plural, options.ansiColors);
|
||||
}
|
||||
|
||||
void printInt(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.integer;
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printFloat(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << v.fpoint;
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printBool(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
printLiteralBool(output, v.boolean);
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printString(Value & v)
|
||||
{
|
||||
printLiteralString(output, v.string_view(), options.maxStringLength, options.ansiColors);
|
||||
}
|
||||
|
||||
void printPath(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_GREEN;
|
||||
output << v.path().to_string(); // !!! escaping?
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printNull()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_CYAN;
|
||||
output << "null";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printDerivation(Value & v)
|
||||
{
|
||||
try {
|
||||
Bindings::iterator i = v.attrs->find(state.sDrvPath);
|
||||
NixStringContext context;
|
||||
std::string storePath;
|
||||
if (i != v.attrs->end())
|
||||
storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
|
||||
if (options.ansiColors)
|
||||
output << ANSI_GREEN;
|
||||
output << "«derivation";
|
||||
if (!storePath.empty()) {
|
||||
output << " " << storePath;
|
||||
}
|
||||
output << "»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
} catch (BaseError & e) {
|
||||
printError_(e);
|
||||
}
|
||||
}
|
||||
|
||||
void printAttrs(Value & v, size_t depth)
|
||||
{
|
||||
if (seen && !seen->insert(&v).second) {
|
||||
printRepeated();
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.force && options.derivationPaths && state.isDerivation(v)) {
|
||||
printDerivation(v);
|
||||
} else if (depth < options.maxDepth) {
|
||||
output << "{ ";
|
||||
|
||||
std::vector<std::pair<std::string, Value *>> sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
|
||||
|
||||
if (options.maxAttrs == std::numeric_limits<size_t>::max())
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
else
|
||||
std::sort(sorted.begin(), sorted.end(), ImportantFirstAttrNameCmp());
|
||||
|
||||
size_t attrsPrinted = 0;
|
||||
for (auto & i : sorted) {
|
||||
if (attrsPrinted >= options.maxAttrs) {
|
||||
printElided(sorted.size() - attrsPrinted, "attribute", "attributes");
|
||||
break;
|
||||
}
|
||||
|
||||
printAttributeName(output, i.first);
|
||||
output << " = ";
|
||||
print(*i.second, depth + 1);
|
||||
output << "; ";
|
||||
attrsPrinted++;
|
||||
}
|
||||
|
||||
output << "}";
|
||||
} else
|
||||
output << "{ ... }";
|
||||
}
|
||||
|
||||
void printList(Value & v, size_t depth)
|
||||
{
|
||||
if (seen && v.listSize() && !seen->insert(&v).second) {
|
||||
printRepeated();
|
||||
return;
|
||||
}
|
||||
|
||||
output << "[ ";
|
||||
if (depth < options.maxDepth) {
|
||||
size_t listItemsPrinted = 0;
|
||||
for (auto elem : v.listItems()) {
|
||||
if (listItemsPrinted >= options.maxListItems) {
|
||||
printElided(v.listSize() - listItemsPrinted, "item", "items");
|
||||
break;
|
||||
}
|
||||
|
||||
if (elem) {
|
||||
print(*elem, depth + 1);
|
||||
} else {
|
||||
printNullptr();
|
||||
}
|
||||
output << " ";
|
||||
listItemsPrinted++;
|
||||
}
|
||||
}
|
||||
else
|
||||
output << "... ";
|
||||
output << "]";
|
||||
}
|
||||
|
||||
void printFunction(Value & v)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_BLUE;
|
||||
output << "«";
|
||||
|
||||
if (v.isLambda()) {
|
||||
output << "lambda";
|
||||
if (v.lambda.fun) {
|
||||
if (v.lambda.fun->name) {
|
||||
output << " " << state.symbols[v.lambda.fun->name];
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << state.positions[v.lambda.fun->pos];
|
||||
output << " @ " << filterANSIEscapes(s.str());
|
||||
}
|
||||
} else if (v.isPrimOp()) {
|
||||
if (v.primOp)
|
||||
output << *v.primOp;
|
||||
else
|
||||
output << "primop";
|
||||
} else if (v.isPrimOpApp()) {
|
||||
output << "partially applied ";
|
||||
auto primOp = v.primOpAppPrimOp();
|
||||
if (primOp)
|
||||
output << *primOp;
|
||||
else
|
||||
output << "primop";
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
output << "»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printThunk(Value & v)
|
||||
{
|
||||
if (v.isBlackhole()) {
|
||||
// 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.
|
||||
if (options.ansiColors)
|
||||
output << ANSI_RED;
|
||||
output << "«potential infinite recursion»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
} else if (v.isThunk() || v.isApp()) {
|
||||
if (options.ansiColors)
|
||||
output << ANSI_MAGENTA;
|
||||
output << "«thunk»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void printExternal(Value & v)
|
||||
{
|
||||
v.external->print(output);
|
||||
}
|
||||
|
||||
void printUnknown()
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_RED;
|
||||
output << "«unknown»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void printError_(BaseError & e)
|
||||
{
|
||||
if (options.ansiColors)
|
||||
output << ANSI_RED;
|
||||
output << "«" << e.msg() << "»";
|
||||
if (options.ansiColors)
|
||||
output << ANSI_NORMAL;
|
||||
}
|
||||
|
||||
void print(Value & v, size_t depth)
|
||||
{
|
||||
output.flush();
|
||||
checkInterrupt();
|
||||
|
||||
if (options.force) {
|
||||
try {
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
} catch (BaseError & e) {
|
||||
printError_(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
case nInt:
|
||||
printInt(v);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
printFloat(v);
|
||||
break;
|
||||
|
||||
case nBool:
|
||||
printBool(v);
|
||||
break;
|
||||
|
||||
case nString:
|
||||
printString(v);
|
||||
break;
|
||||
|
||||
case nPath:
|
||||
printPath(v);
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
printNull();
|
||||
break;
|
||||
|
||||
case nAttrs:
|
||||
printAttrs(v, depth);
|
||||
break;
|
||||
|
||||
case nList:
|
||||
printList(v, depth);
|
||||
break;
|
||||
|
||||
case nFunction:
|
||||
printFunction(v);
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
printThunk(v);
|
||||
break;
|
||||
|
||||
case nExternal:
|
||||
printExternal(v);
|
||||
break;
|
||||
|
||||
default:
|
||||
printUnknown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Printer(std::ostream & output, EvalState & state, PrintOptions options)
|
||||
: output(output), state(state), options(options) { }
|
||||
|
||||
void print(Value & v)
|
||||
{
|
||||
if (options.trackRepeated) {
|
||||
seen.emplace();
|
||||
} else {
|
||||
seen.reset();
|
||||
}
|
||||
|
||||
ValuesSeen seen;
|
||||
print(v, 0);
|
||||
}
|
||||
};
|
||||
|
||||
void printValue(EvalState & state, std::ostream & output, Value & v, PrintOptions options)
|
||||
{
|
||||
Printer(output, state, options).print(v);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,46 +9,54 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include "eval.hh"
|
||||
#include "print-options.hh"
|
||||
|
||||
namespace nix {
|
||||
/**
|
||||
* Print a string as a Nix string literal.
|
||||
*
|
||||
* Quotes and fairly minimal escaping are added.
|
||||
*
|
||||
* @param s The logical string
|
||||
*/
|
||||
std::ostream & printLiteralString(std::ostream & o, std::string_view s);
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
|
||||
/** Print `true` or `false`. */
|
||||
std::ostream & printLiteralBool(std::ostream & o, bool b);
|
||||
|
||||
/**
|
||||
* Print a string as an attribute name in the Nix expression language syntax.
|
||||
*
|
||||
* Prints a quoted string if necessary.
|
||||
*/
|
||||
std::ostream & printAttributeName(std::ostream & o, std::string_view s);
|
||||
|
||||
/**
|
||||
* Returns `true' is a string is a reserved keyword which requires quotation
|
||||
* when printing attribute set field names.
|
||||
*/
|
||||
bool isReservedKeyword(const std::string_view str);
|
||||
|
||||
/**
|
||||
* Print a string as an identifier in the Nix expression language syntax.
|
||||
*
|
||||
* FIXME: "identifier" is ambiguous. Identifiers do not have a single
|
||||
* textual representation. They can be used in variable references,
|
||||
* let bindings, left-hand sides or attribute names in a select
|
||||
* expression, or something else entirely, like JSON. Use one of the
|
||||
* `print*` functions instead.
|
||||
*/
|
||||
std::ostream & printIdentifier(std::ostream & o, std::string_view s);
|
||||
/**
|
||||
* Print a string as a Nix string literal.
|
||||
*
|
||||
* Quotes and fairly minimal escaping are added.
|
||||
*
|
||||
* @param o The output stream to print to
|
||||
* @param s The logical string
|
||||
*/
|
||||
std::ostream & printLiteralString(std::ostream & o, std::string_view s);
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const char * s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
inline std::ostream & printLiteralString(std::ostream & o, const std::string & s) {
|
||||
return printLiteralString(o, std::string_view(s));
|
||||
}
|
||||
|
||||
/** Print `true` or `false`. */
|
||||
std::ostream & printLiteralBool(std::ostream & o, bool b);
|
||||
|
||||
/**
|
||||
* Print a string as an attribute name in the Nix expression language syntax.
|
||||
*
|
||||
* Prints a quoted string if necessary.
|
||||
*/
|
||||
std::ostream & printAttributeName(std::ostream & o, std::string_view s);
|
||||
|
||||
/**
|
||||
* Returns `true' is a string is a reserved keyword which requires quotation
|
||||
* when printing attribute set field names.
|
||||
*/
|
||||
bool isReservedKeyword(const std::string_view str);
|
||||
|
||||
/**
|
||||
* Print a string as an identifier in the Nix expression language syntax.
|
||||
*
|
||||
* FIXME: "identifier" is ambiguous. Identifiers do not have a single
|
||||
* textual representation. They can be used in variable references,
|
||||
* let bindings, left-hand sides or attribute names in a select
|
||||
* expression, or something else entirely, like JSON. Use one of the
|
||||
* `print*` functions instead.
|
||||
*/
|
||||
std::ostream & printIdentifier(std::ostream & o, std::string_view s);
|
||||
|
||||
void printValue(EvalState & state, std::ostream & str, Value & v, PrintOptions options = PrintOptions {});
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "value/context.hh"
|
||||
#include "input-accessor.hh"
|
||||
#include "source-path.hh"
|
||||
#include "print-options.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#include <gc/gc_allocator.h>
|
||||
|
|
@ -70,7 +71,7 @@ struct Pos;
|
|||
class StorePath;
|
||||
class EvalState;
|
||||
class XMLWriter;
|
||||
|
||||
class Printer;
|
||||
|
||||
typedef int64_t NixInt;
|
||||
typedef double NixFloat;
|
||||
|
|
@ -82,6 +83,7 @@ typedef double NixFloat;
|
|||
class ExternalValueBase
|
||||
{
|
||||
friend std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
|
||||
friend class Printer;
|
||||
protected:
|
||||
/**
|
||||
* Print out the value
|
||||
|
|
@ -139,11 +141,9 @@ private:
|
|||
|
||||
friend std::string showType(const Value & v);
|
||||
|
||||
void print(const SymbolTable &symbols, std::ostream &str, std::set<const void *> *seen, int depth) const;
|
||||
|
||||
public:
|
||||
|
||||
void print(const SymbolTable &symbols, std::ostream &str, bool showRepeated = false, int depth = INT_MAX) const;
|
||||
void print(EvalState &state, std::ostream &str, PrintOptions options = PrintOptions {});
|
||||
|
||||
// Functions needed to distinguish the type
|
||||
// These should be removed eventually, by putting the functionality that's
|
||||
|
|
@ -364,10 +364,15 @@ public:
|
|||
inline void mkPrimOpApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tPrimOpApp;
|
||||
app.left = l;
|
||||
app.right = r;
|
||||
primOpApp.left = l;
|
||||
primOpApp.right = r;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a `tPrimOpApp` value, get the original `PrimOp` value.
|
||||
*/
|
||||
PrimOp * primOpAppPrimOp() const;
|
||||
|
||||
inline void mkExternal(ExternalValueBase * e)
|
||||
{
|
||||
clearValue();
|
||||
|
|
|
|||
|
|
@ -1495,7 +1495,7 @@ void LocalDerivationGoal::startDaemon()
|
|||
daemon::processConnection(store, from, to,
|
||||
NotTrusted, daemon::Recursive);
|
||||
debug("terminated daemon connection");
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
ignoreException();
|
||||
}
|
||||
});
|
||||
|
|
@ -1707,7 +1707,7 @@ void LocalDerivationGoal::runChild()
|
|||
try {
|
||||
if (drv->isBuiltin() && drv->builder == "builtin:fetchurl")
|
||||
netrcData = readFile(settings.netrcFile);
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
|
||||
#if __linux__
|
||||
if (useChroot) {
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ void Worker::waitForInput()
|
|||
} else {
|
||||
printMsg(lvlVomit, "%1%: read %2% bytes",
|
||||
goal->getName(), rd);
|
||||
std::string data((char *) buffer.data(), rd);
|
||||
std::string_view data((char *) buffer.data(), rd);
|
||||
j->lastOutput = after;
|
||||
goal->handleChildOutput(k, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
|
|||
auto env_end = std::sregex_iterator{};
|
||||
for (auto i = std::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i)
|
||||
unchecked[i->str()].emplace(envFile);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
if (errno == ENOENT || errno == EACCES || errno == ESRCH)
|
||||
continue;
|
||||
throw;
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ void loadConfFile()
|
|||
try {
|
||||
std::string contents = readFile(path);
|
||||
globalConfig.applyConfig(contents, path);
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
};
|
||||
|
||||
applyConfigFile(settings.nixConfDir + "/nix.conf");
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ PublicKeys getDefaultPublicKeys()
|
|||
try {
|
||||
SecretKey secretKey(readFile(secretKeyFile));
|
||||
publicKeys.emplace(secretKey.name, secretKey.toPublicKey());
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
/* Ignore unreadable key files. That's normal in a
|
||||
multi-user installation. */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ LocalStore::LocalStore(const Params & params)
|
|||
[[gnu::unused]] auto res2 = ftruncate(fd.get(), settings.reservedSize);
|
||||
}
|
||||
}
|
||||
} catch (SysError & e) { /* don't care about errors */
|
||||
} catch (SystemError & e) { /* don't care about errors */
|
||||
}
|
||||
|
||||
/* Acquire the big fat lock in shared mode to make sure that no
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
|||
/* Atomically replace the old file with the new hard link. */
|
||||
try {
|
||||
renameFile(tempLink, path);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
if (unlink(tempLink.c_str()) == -1)
|
||||
printError("unable to unlink '%1%'", tempLink);
|
||||
if (errno == EMLINK) {
|
||||
|
|
|
|||
|
|
@ -87,13 +87,13 @@ std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPat
|
|||
nars.emplace(storePath.hashPart(), narAccessor);
|
||||
return {narAccessor, restPath};
|
||||
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
|
||||
try {
|
||||
auto narAccessor = makeNarAccessor(nix::readFile(cacheFile));
|
||||
nars.emplace(storePath.hashPart(), narAccessor);
|
||||
return {narAccessor, restPath};
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
}
|
||||
|
||||
StringSink sink;
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
for (auto pos = savedArgs.begin(); pos != savedArgs.end();pos++)
|
||||
cmdline.push_back(*pos);
|
||||
}
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
}
|
||||
for (auto pos = cmdline.begin(); pos != cmdline.end(); ) {
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ namespace nix {
|
|||
CanonPath CanonPath::root = CanonPath("/");
|
||||
|
||||
CanonPath::CanonPath(std::string_view raw)
|
||||
: path(absPath((Path) raw, "/"))
|
||||
: path(absPath(raw, "/"))
|
||||
{ }
|
||||
|
||||
CanonPath::CanonPath(std::string_view raw, const CanonPath & root)
|
||||
: path(absPath((Path) raw, root.abs()))
|
||||
: path(absPath(raw, root.abs()))
|
||||
{ }
|
||||
|
||||
CanonPath::CanonPath(const std::vector<std::string> & elems)
|
||||
|
|
@ -22,7 +22,7 @@ CanonPath::CanonPath(const std::vector<std::string> & elems)
|
|||
|
||||
CanonPath CanonPath::fromCwd(std::string_view path)
|
||||
{
|
||||
return CanonPath(unchecked_t(), absPath((Path) path));
|
||||
return CanonPath(unchecked_t(), absPath(path));
|
||||
}
|
||||
|
||||
std::optional<CanonPath> CanonPath::parent() const
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats)
|
|||
using namespace std::string_literals;
|
||||
warn("killing stray builder process %d (%s)...",
|
||||
pid, trim(replaceStrings(cmdline, "\0"s, " ")));
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
}
|
||||
}
|
||||
// FIXME: pid wraparound
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p
|
|||
try {
|
||||
std::string includedContents = readFile(path);
|
||||
applyConfigInner(includedContents, p, parsedContents);
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
// TODO: Do we actually want to ignore this? Or is it better to fail?
|
||||
}
|
||||
} else if (!ignoreMissing) {
|
||||
|
|
|
|||
18
src/libutil/english.cc
Normal file
18
src/libutil/english.cc
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include "english.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::ostream & pluralize(
|
||||
std::ostream & output,
|
||||
unsigned int count,
|
||||
const std::string_view single,
|
||||
const std::string_view plural)
|
||||
{
|
||||
if (count == 1)
|
||||
output << "1 " << single;
|
||||
else
|
||||
output << count << " " << plural;
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
18
src/libutil/english.hh
Normal file
18
src/libutil/english.hh
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Pluralize a given value.
|
||||
*
|
||||
* If `count == 1`, prints `1 {single}` to `output`, otherwise prints `{count} {plural}`.
|
||||
*/
|
||||
std::ostream & pluralize(
|
||||
std::ostream & output,
|
||||
unsigned int count,
|
||||
const std::string_view single,
|
||||
const std::string_view plural);
|
||||
|
||||
}
|
||||
|
|
@ -178,20 +178,50 @@ MakeError(Error, BaseError);
|
|||
MakeError(UsageError, Error);
|
||||
MakeError(UnimplementedError, Error);
|
||||
|
||||
class SysError : public Error
|
||||
/**
|
||||
* To use in catch-blocks.
|
||||
*/
|
||||
MakeError(SystemError, Error);
|
||||
|
||||
/**
|
||||
* POSIX system error, created using `errno`, `strerror` friends.
|
||||
*
|
||||
* Throw this, but prefer not to catch this, and catch `SystemError`
|
||||
* instead. This allows implementations to freely switch between this
|
||||
* and `WinError` without breaking catch blocks.
|
||||
*
|
||||
* However, it is permissible to catch this and rethrow so long as
|
||||
* certain conditions are not met (e.g. to catch only if `errNo =
|
||||
* EFooBar`). In that case, try to also catch the equivalent `WinError`
|
||||
* code.
|
||||
*
|
||||
* @todo Rename this to `PosixError` or similar. At this point Windows
|
||||
* support is too WIP to justify the code churn, but if it is finished
|
||||
* then a better identifier becomes moe worth it.
|
||||
*/
|
||||
class SysError : public SystemError
|
||||
{
|
||||
public:
|
||||
int errNo;
|
||||
|
||||
/**
|
||||
* Construct using the explicitly-provided error number. `strerror`
|
||||
* will be used to try to add additional information to the message.
|
||||
*/
|
||||
template<typename... Args>
|
||||
SysError(int errNo_, const Args & ... args)
|
||||
: Error("")
|
||||
SysError(int errNo, const Args & ... args)
|
||||
: SystemError(""), errNo(errNo)
|
||||
{
|
||||
errNo = errNo_;
|
||||
auto hf = hintfmt(args...);
|
||||
err.msg = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct using the ambient `errno`.
|
||||
*
|
||||
* Be sure to not perform another `errno`-modifying operation before
|
||||
* calling this constructor!
|
||||
*/
|
||||
template<typename... Args>
|
||||
SysError(const Args & ... args)
|
||||
: SysError(errno, args ...)
|
||||
|
|
@ -199,7 +229,9 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/** Throw an exception for the purpose of checking that exception handling works; see 'initLibUtil()'.
|
||||
/**
|
||||
* Throw an exception for the purpose of checking that exception
|
||||
* handling works; see 'initLibUtil()'.
|
||||
*/
|
||||
void throwExceptionSelfCheck();
|
||||
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ void closeMostFDs(const std::set<int> & exceptions)
|
|||
}
|
||||
}
|
||||
return;
|
||||
} catch (SysError &) {
|
||||
} catch (SystemError &) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,16 @@ namespace fs = std::filesystem;
|
|||
|
||||
namespace nix {
|
||||
|
||||
Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
|
||||
Path absPath(PathView path, std::optional<PathView> dir, bool resolveSymlinks)
|
||||
{
|
||||
std::string scratch;
|
||||
|
||||
if (path[0] != '/') {
|
||||
// In this case we need to call `canonPath` on a newly-created
|
||||
// string. We set `scratch` to that string first, and then set
|
||||
// `path` to `scratch`. This ensures the newly-created string
|
||||
// lives long enough for the call to `canonPath`, and allows us
|
||||
// to just accept a `std::string_view`.
|
||||
if (!dir) {
|
||||
#ifdef __GNU__
|
||||
/* GNU (aka. GNU/Hurd) doesn't have any limitation on path
|
||||
|
|
@ -35,12 +42,13 @@ Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
|
|||
if (!getcwd(buf, sizeof(buf)))
|
||||
#endif
|
||||
throw SysError("cannot get cwd");
|
||||
path = concatStrings(buf, "/", path);
|
||||
scratch = concatStrings(buf, "/", path);
|
||||
#ifdef __GNU__
|
||||
free(buf);
|
||||
#endif
|
||||
} else
|
||||
path = concatStrings(*dir, "/", path);
|
||||
scratch = concatStrings(*dir, "/", path);
|
||||
path = scratch;
|
||||
}
|
||||
return canonPath(path, resolveSymlinks);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ struct Source;
|
|||
* specified directory, or the current directory otherwise. The path
|
||||
* is also canonicalised.
|
||||
*/
|
||||
Path absPath(Path path,
|
||||
Path absPath(PathView path,
|
||||
std::optional<PathView> dir = {},
|
||||
bool resolveSymlinks = false);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ void writeToStderr(std::string_view s)
|
|||
{
|
||||
try {
|
||||
writeFull(STDERR_FILENO, s, false);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
/* Ignore failing writes to stderr. We need to ignore write
|
||||
errors to ensure that cleanup code that logs to stderr runs
|
||||
to completion if the other side of stderr has been closed
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ void FdSink::writeUnbuffered(std::string_view data)
|
|||
written += data.size();
|
||||
try {
|
||||
writeFull(fd, data);
|
||||
} catch (SysError & e) {
|
||||
} catch (SystemError & e) {
|
||||
_good = false;
|
||||
throw;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ void initLibUtil() {
|
|||
// When exception handling fails, the message tends to be printed by the
|
||||
// C++ runtime, followed by an abort.
|
||||
// For example on macOS we might see an error such as
|
||||
// libc++abi: terminating with uncaught exception of type nix::SysError: error: C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.
|
||||
// libc++abi: terminating with uncaught exception of type nix::SystemError: error: C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.
|
||||
bool caught = false;
|
||||
try {
|
||||
throwExceptionSelfCheck();
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ static void main_nix_build(int argc, char * * argv)
|
|||
args.push_back(word);
|
||||
}
|
||||
}
|
||||
} catch (SysError &) { }
|
||||
} catch (SystemError &) { }
|
||||
}
|
||||
|
||||
struct MyArgs : LegacyArgs, MixEvalArgs
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include "eval.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "profiles.hh"
|
||||
#include "print-ambiguous.hh"
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
|
@ -106,7 +108,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
|||
environment. */
|
||||
auto manifestFile = ({
|
||||
std::ostringstream str;
|
||||
manifest.print(state.symbols, str, true);
|
||||
printAmbiguous(manifest, state.symbols, str, nullptr, std::numeric_limits<int>::max());
|
||||
// TODO with C++20 we can use str.view() instead and avoid copy.
|
||||
std::string str2 = str.str();
|
||||
StringSource source { str2 };
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
#include "globals.hh"
|
||||
#include "print-ambiguous.hh"
|
||||
#include "shared.hh"
|
||||
#include "eval.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "get-drvs.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "signals.hh"
|
||||
#include "value-to-xml.hh"
|
||||
#include "value-to-json.hh"
|
||||
#include "store-api.hh"
|
||||
|
|
@ -24,7 +26,6 @@ static int rootNr = 0;
|
|||
|
||||
enum OutputKind { okPlain, okXML, okJSON };
|
||||
|
||||
|
||||
void processExpr(EvalState & state, const Strings & attrPaths,
|
||||
bool parseOnly, bool strict, Bindings & autoArgs,
|
||||
bool evalOnly, OutputKind output, bool location, Expr * e)
|
||||
|
|
@ -56,7 +57,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
|||
std::cout << std::endl;
|
||||
} else {
|
||||
if (strict) state.forceValueDeep(vRes);
|
||||
vRes.print(state.symbols, std::cout);
|
||||
std::set<const void *> seen;
|
||||
printAmbiguous(vRes, state.symbols, std::cout, &seen, std::numeric_limits<int>::max());
|
||||
std::cout << std::endl;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ struct CmdConfigCheck : StoreCommand
|
|||
if (profileDir.find("/profiles/") == std::string::npos)
|
||||
dirs.insert(dir);
|
||||
}
|
||||
} catch (SysError &) {}
|
||||
} catch (SystemError &) {}
|
||||
}
|
||||
|
||||
if (!dirs.empty()) {
|
||||
|
|
|
|||
|
|
@ -297,7 +297,6 @@ struct Common : InstallableCommand, MixProfile
|
|||
"NIX_LOG_FD",
|
||||
"NIX_REMOTE",
|
||||
"PPID",
|
||||
"SHELL",
|
||||
"SHELLOPTS",
|
||||
"SSL_CERT_FILE", // FIXME: only want to ignore /no-cert-file.crt
|
||||
"TEMP",
|
||||
|
|
@ -604,7 +603,7 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
|
||||
setEnviron();
|
||||
// prevent garbage collection until shell exits
|
||||
setenv("NIX_GCROOT", gcroot.data(), 1);
|
||||
setenv("NIX_GCROOT", gcroot.c_str(), 1);
|
||||
|
||||
Path shell = "bash";
|
||||
|
||||
|
|
@ -647,6 +646,10 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
ignoreException();
|
||||
}
|
||||
|
||||
// Override SHELL with the one chosen for this environment.
|
||||
// This is to make sure the system shell doesn't leak into the build environment.
|
||||
setenv("SHELL", shell.c_str(), 1);
|
||||
|
||||
// If running a phase or single command, don't want an interactive shell running after
|
||||
// Ctrl-C, so don't pass --rcfile
|
||||
auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath}
|
||||
|
|
|
|||
|
|
@ -7,14 +7,12 @@ R""(
|
|||
```console
|
||||
# nix profile list
|
||||
Name: gdb
|
||||
Index: 0
|
||||
Flake attribute: legacyPackages.x86_64-linux.gdb
|
||||
Original flake URL: flake:nixpkgs
|
||||
Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
|
||||
Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
|
||||
|
||||
Name: blender-bin
|
||||
Index: 1
|
||||
Flake attribute: packages.x86_64-linux.default
|
||||
Original flake URL: flake:blender-bin
|
||||
Locked flake URL: github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender
|
||||
|
|
|
|||
|
|
@ -8,13 +8,6 @@ R""(
|
|||
# nix profile remove hello
|
||||
```
|
||||
|
||||
* Remove a package by index
|
||||
*(deprecated, will be removed in a future version)*:
|
||||
|
||||
```console
|
||||
# nix profile remove 3
|
||||
```
|
||||
|
||||
* Remove all packages:
|
||||
|
||||
```console
|
||||
|
|
|
|||
|
|
@ -15,13 +15,6 @@ R""(
|
|||
# nix profile upgrade hello
|
||||
```
|
||||
|
||||
* Upgrade a specific package by index
|
||||
*(deprecated, will be removed in a future version)*:
|
||||
|
||||
```console
|
||||
# nix profile upgrade 0
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command upgrades a previously installed package in a Nix profile,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ const int defaultPriority = 5;
|
|||
struct ProfileElement
|
||||
{
|
||||
StorePathSet storePaths;
|
||||
std::string name;
|
||||
std::optional<ProfileElementSource> source;
|
||||
bool active = true;
|
||||
int priority = defaultPriority;
|
||||
|
|
@ -82,11 +81,6 @@ struct ProfileElement
|
|||
return showVersions(versions);
|
||||
}
|
||||
|
||||
bool operator < (const ProfileElement & other) const
|
||||
{
|
||||
return std::tuple(identifier(), storePaths) < std::tuple(other.identifier(), other.storePaths);
|
||||
}
|
||||
|
||||
void updateStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
|
|
@ -109,7 +103,9 @@ struct ProfileElement
|
|||
|
||||
struct ProfileManifest
|
||||
{
|
||||
std::vector<ProfileElement> elements;
|
||||
using ProfileElementName = std::string;
|
||||
|
||||
std::map<ProfileElementName, ProfileElement> elements;
|
||||
|
||||
ProfileManifest() { }
|
||||
|
||||
|
|
@ -119,8 +115,6 @@ struct ProfileManifest
|
|||
|
||||
if (pathExists(manifestPath)) {
|
||||
auto json = nlohmann::json::parse(readFile(manifestPath));
|
||||
/* Keep track of already found names to allow preventing duplicates. */
|
||||
std::set<std::string> foundNames;
|
||||
|
||||
auto version = json.value("version", 0);
|
||||
std::string sUrl;
|
||||
|
|
@ -131,6 +125,7 @@ struct ProfileManifest
|
|||
sOriginalUrl = "originalUri";
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
sUrl = "url";
|
||||
sOriginalUrl = "originalUrl";
|
||||
break;
|
||||
|
|
@ -138,7 +133,9 @@ struct ProfileManifest
|
|||
throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version);
|
||||
}
|
||||
|
||||
for (auto & e : json["elements"]) {
|
||||
auto elems = json["elements"];
|
||||
for (auto & elem : elems.items()) {
|
||||
auto & e = elem.value();
|
||||
ProfileElement element;
|
||||
for (auto & p : e["storePaths"])
|
||||
element.storePaths.insert(state.store->parseStorePath((std::string) p));
|
||||
|
|
@ -155,25 +152,14 @@ struct ProfileManifest
|
|||
};
|
||||
}
|
||||
|
||||
std::string nameCandidate = element.identifier();
|
||||
if (e.contains("name")) {
|
||||
nameCandidate = e["name"];
|
||||
}
|
||||
else if (element.source) {
|
||||
auto url = parseURL(element.source->to_string());
|
||||
auto name = getNameFromURL(url);
|
||||
if (name)
|
||||
nameCandidate = *name;
|
||||
}
|
||||
std::string name =
|
||||
elems.is_object()
|
||||
? elem.key()
|
||||
: element.source
|
||||
? getNameFromURL(parseURL(element.source->to_string())).value_or(element.identifier())
|
||||
: element.identifier();
|
||||
|
||||
auto finalName = nameCandidate;
|
||||
for (int i = 1; foundNames.contains(finalName); ++i) {
|
||||
finalName = nameCandidate + std::to_string(i);
|
||||
}
|
||||
element.name = finalName;
|
||||
foundNames.insert(element.name);
|
||||
|
||||
elements.emplace_back(std::move(element));
|
||||
addElement(name, std::move(element));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,16 +173,34 @@ struct ProfileManifest
|
|||
for (auto & drvInfo : drvInfos) {
|
||||
ProfileElement element;
|
||||
element.storePaths = {drvInfo.queryOutPath()};
|
||||
element.name = element.identifier();
|
||||
elements.emplace_back(std::move(element));
|
||||
addElement(std::move(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addElement(std::string_view nameCandidate, ProfileElement element)
|
||||
{
|
||||
std::string finalName(nameCandidate);
|
||||
for (int i = 1; elements.contains(finalName); ++i)
|
||||
finalName = nameCandidate + "-" + std::to_string(i);
|
||||
|
||||
elements.insert_or_assign(finalName, std::move(element));
|
||||
}
|
||||
|
||||
void addElement(ProfileElement element)
|
||||
{
|
||||
auto name =
|
||||
element.source
|
||||
? getNameFromURL(parseURL(element.source->to_string()))
|
||||
: std::nullopt;
|
||||
auto name2 = name ? *name : element.identifier();
|
||||
addElement(name2, std::move(element));
|
||||
}
|
||||
|
||||
nlohmann::json toJSON(Store & store) const
|
||||
{
|
||||
auto array = nlohmann::json::array();
|
||||
for (auto & element : elements) {
|
||||
auto es = nlohmann::json::object();
|
||||
for (auto & [name, element] : elements) {
|
||||
auto paths = nlohmann::json::array();
|
||||
for (auto & path : element.storePaths)
|
||||
paths.push_back(store.printStorePath(path));
|
||||
|
|
@ -210,11 +214,11 @@ struct ProfileManifest
|
|||
obj["attrPath"] = element.source->attrPath;
|
||||
obj["outputs"] = element.source->outputs;
|
||||
}
|
||||
array.push_back(obj);
|
||||
es[name] = obj;
|
||||
}
|
||||
nlohmann::json json;
|
||||
json["version"] = 2;
|
||||
json["elements"] = array;
|
||||
json["version"] = 3;
|
||||
json["elements"] = es;
|
||||
return json;
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +229,7 @@ struct ProfileManifest
|
|||
StorePathSet references;
|
||||
|
||||
Packages pkgs;
|
||||
for (auto & element : elements) {
|
||||
for (auto & [name, element] : elements) {
|
||||
for (auto & path : element.storePaths) {
|
||||
if (element.active)
|
||||
pkgs.emplace_back(store->printStorePath(path), true, element.priority);
|
||||
|
|
@ -267,33 +271,27 @@ struct ProfileManifest
|
|||
|
||||
static void printDiff(const ProfileManifest & prev, const ProfileManifest & cur, std::string_view indent)
|
||||
{
|
||||
auto prevElems = prev.elements;
|
||||
std::sort(prevElems.begin(), prevElems.end());
|
||||
|
||||
auto curElems = cur.elements;
|
||||
std::sort(curElems.begin(), curElems.end());
|
||||
|
||||
auto i = prevElems.begin();
|
||||
auto j = curElems.begin();
|
||||
auto i = prev.elements.begin();
|
||||
auto j = cur.elements.begin();
|
||||
|
||||
bool changes = false;
|
||||
|
||||
while (i != prevElems.end() || j != curElems.end()) {
|
||||
if (j != curElems.end() && (i == prevElems.end() || i->identifier() > j->identifier())) {
|
||||
logger->cout("%s%s: ∅ -> %s", indent, j->identifier(), j->versions());
|
||||
while (i != prev.elements.end() || j != cur.elements.end()) {
|
||||
if (j != cur.elements.end() && (i == prev.elements.end() || i->first > j->first)) {
|
||||
logger->cout("%s%s: ∅ -> %s", indent, j->second.identifier(), j->second.versions());
|
||||
changes = true;
|
||||
++j;
|
||||
}
|
||||
else if (i != prevElems.end() && (j == curElems.end() || i->identifier() < j->identifier())) {
|
||||
logger->cout("%s%s: %s -> ∅", indent, i->identifier(), i->versions());
|
||||
else if (i != prev.elements.end() && (j == cur.elements.end() || i->first < j->first)) {
|
||||
logger->cout("%s%s: %s -> ∅", indent, i->second.identifier(), i->second.versions());
|
||||
changes = true;
|
||||
++i;
|
||||
}
|
||||
else {
|
||||
auto v1 = i->versions();
|
||||
auto v2 = j->versions();
|
||||
auto v1 = i->second.versions();
|
||||
auto v2 = j->second.versions();
|
||||
if (v1 != v2) {
|
||||
logger->cout("%s%s: %s -> %s", indent, i->identifier(), v1, v2);
|
||||
logger->cout("%s%s: %s -> %s", indent, i->second.identifier(), v1, v2);
|
||||
changes = true;
|
||||
}
|
||||
++i;
|
||||
|
|
@ -392,7 +390,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
|||
|
||||
element.updateStorePaths(getEvalStore(), store, res);
|
||||
|
||||
manifest.elements.push_back(std::move(element));
|
||||
manifest.addElement(std::move(element));
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -402,7 +400,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
|||
// See https://github.com/NixOS/nix/compare/3efa476c5439f8f6c1968a6ba20a31d1239c2f04..1fe5d172ece51a619e879c4b86f603d9495cc102
|
||||
auto findRefByFilePath = [&]<typename Iterator>(Iterator begin, Iterator end) {
|
||||
for (auto it = begin; it != end; it++) {
|
||||
auto profileElement = *it;
|
||||
auto & profileElement = it->second;
|
||||
for (auto & storePath : profileElement.storePaths) {
|
||||
if (conflictError.fileA.starts_with(store->printStorePath(storePath))) {
|
||||
return std::pair(conflictError.fileA, profileElement.toInstallables(*store));
|
||||
|
|
@ -470,43 +468,35 @@ public:
|
|||
std::string pattern;
|
||||
std::regex reg;
|
||||
};
|
||||
typedef std::variant<size_t, Path, RegexPattern> Matcher;
|
||||
typedef std::variant<Path, RegexPattern> Matcher;
|
||||
|
||||
std::vector<Matcher> getMatchers(ref<Store> store)
|
||||
{
|
||||
std::vector<Matcher> res;
|
||||
|
||||
auto anyIndexMatchers = false;
|
||||
|
||||
for (auto & s : _matchers) {
|
||||
if (auto n = string2Int<size_t>(s)) {
|
||||
res.push_back(*n);
|
||||
anyIndexMatchers = true;
|
||||
}
|
||||
if (auto n = string2Int<size_t>(s))
|
||||
throw Error("'nix profile' no longer supports indices ('%d')", *n);
|
||||
else if (store->isStorePath(s))
|
||||
res.push_back(s);
|
||||
else
|
||||
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
|
||||
}
|
||||
|
||||
if (anyIndexMatchers) {
|
||||
warn("Indices are deprecated and will be removed in a future version!\n"
|
||||
" Refer to packages by their `Name` as printed by `nix profile list`.\n"
|
||||
" See https://github.com/NixOS/nix/issues/9171 for more information.");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool matches(const Store & store, const ProfileElement & element, size_t pos, const std::vector<Matcher> & matchers)
|
||||
bool matches(
|
||||
const Store & store,
|
||||
const std::string & name,
|
||||
const ProfileElement & element,
|
||||
const std::vector<Matcher> & matchers)
|
||||
{
|
||||
for (auto & matcher : matchers) {
|
||||
if (auto n = std::get_if<size_t>(&matcher)) {
|
||||
if (*n == pos) return true;
|
||||
} else if (auto path = std::get_if<Path>(&matcher)) {
|
||||
if (auto path = std::get_if<Path>(&matcher)) {
|
||||
if (element.storePaths.count(store.parseStorePath(*path))) return true;
|
||||
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
|
||||
if (std::regex_match(element.name, regex->reg))
|
||||
if (std::regex_match(name, regex->reg))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -537,10 +527,9 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
|
|||
|
||||
ProfileManifest newManifest;
|
||||
|
||||
for (size_t i = 0; i < oldManifest.elements.size(); ++i) {
|
||||
auto & element(oldManifest.elements[i]);
|
||||
if (!matches(*store, element, i, matchers)) {
|
||||
newManifest.elements.push_back(std::move(element));
|
||||
for (auto & [name, element] : oldManifest.elements) {
|
||||
if (!matches(*store, name, element, matchers)) {
|
||||
newManifest.elements.insert_or_assign(name, std::move(element));
|
||||
} else {
|
||||
notice("removing '%s'", element.identifier());
|
||||
}
|
||||
|
|
@ -553,11 +542,9 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
|
|||
|
||||
if (removedCount == 0) {
|
||||
for (auto matcher: matchers) {
|
||||
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
||||
warn("'%d' is not a valid index", *index);
|
||||
} else if (const Path * path = std::get_if<Path>(&matcher)){
|
||||
if (const Path * path = std::get_if<Path>(&matcher)) {
|
||||
warn("'%s' does not match any paths", *path);
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)) {
|
||||
warn("'%s' does not match any packages", regex->pattern);
|
||||
}
|
||||
}
|
||||
|
|
@ -588,14 +575,13 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
|||
auto matchers = getMatchers(store);
|
||||
|
||||
Installables installables;
|
||||
std::vector<size_t> indices;
|
||||
std::vector<ProfileElement *> elems;
|
||||
|
||||
auto matchedCount = 0;
|
||||
auto upgradedCount = 0;
|
||||
|
||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||
auto & element(manifest.elements[i]);
|
||||
if (!matches(*store, element, i, matchers)) {
|
||||
for (auto & [name, element] : manifest.elements) {
|
||||
if (!matches(*store, name, element, matchers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -651,17 +637,15 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
|||
};
|
||||
|
||||
installables.push_back(installable);
|
||||
indices.push_back(i);
|
||||
elems.push_back(&element);
|
||||
}
|
||||
|
||||
if (upgradedCount == 0) {
|
||||
if (matchedCount == 0) {
|
||||
for (auto & matcher : matchers) {
|
||||
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
||||
warn("'%d' is not a valid index", *index);
|
||||
} else if (const Path * path = std::get_if<Path>(&matcher)){
|
||||
if (const Path * path = std::get_if<Path>(&matcher)) {
|
||||
warn("'%s' does not match any paths", *path);
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)){
|
||||
} else if (const RegexPattern * regex = std::get_if<RegexPattern>(&matcher)) {
|
||||
warn("'%s' does not match any packages", regex->pattern);
|
||||
}
|
||||
}
|
||||
|
|
@ -677,7 +661,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
|||
|
||||
for (size_t i = 0; i < installables.size(); ++i) {
|
||||
auto & installable = installables.at(i);
|
||||
auto & element = manifest.elements[indices.at(i)];
|
||||
auto & element = *elems.at(i);
|
||||
element.updateStorePaths(
|
||||
getEvalStore(),
|
||||
store,
|
||||
|
|
@ -709,13 +693,12 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
|
|||
if (json) {
|
||||
std::cout << manifest.toJSON(*store).dump() << "\n";
|
||||
} else {
|
||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||
auto & element(manifest.elements[i]);
|
||||
for (const auto & [i, e] : enumerate(manifest.elements)) {
|
||||
auto & [name, element] = e;
|
||||
if (i) logger->cout("");
|
||||
logger->cout("Name: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
|
||||
element.name,
|
||||
name,
|
||||
element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
|
||||
logger->cout("Index: %s", i);
|
||||
if (element.source) {
|
||||
logger->cout("Flake attribute: %s%s", element.source->attrPath, element.source->outputs.to_string());
|
||||
logger->cout("Original flake URL: %s", element.source->originalRef.to_string());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue