mirror of
https://github.com/NixOS/nix.git
synced 2025-11-17 16:02:43 +01:00
Merge remote-tracking branch 'upstream/master' into trustless-remote-builder-simple
This commit is contained in:
commit
5738b08233
199 changed files with 5472 additions and 1751 deletions
|
|
@ -52,9 +52,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
|||
for (auto & attr : tokens) {
|
||||
|
||||
/* Is i an index (integer) or a normal attribute name? */
|
||||
enum { apAttr, apIndex } apType = apAttr;
|
||||
unsigned int attrIndex;
|
||||
if (string2Int(attr, attrIndex)) apType = apIndex;
|
||||
auto attrIndex = string2Int<unsigned int>(attr);
|
||||
|
||||
/* Evaluate the expression. */
|
||||
Value * vNew = state.allocValue();
|
||||
|
|
@ -65,9 +63,9 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
|||
/* It should evaluate to either a set or an expression,
|
||||
according to what is specified in the attrPath. */
|
||||
|
||||
if (apType == apAttr) {
|
||||
if (!attrIndex) {
|
||||
|
||||
if (v->type != tAttrs)
|
||||
if (v->type() != nAttrs)
|
||||
throw TypeError(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
|
|
@ -82,17 +80,17 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
|||
pos = *a->pos;
|
||||
}
|
||||
|
||||
else if (apType == apIndex) {
|
||||
else {
|
||||
|
||||
if (!v->isList())
|
||||
throw TypeError(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
attrPath,
|
||||
showType(*v));
|
||||
if (attrIndex >= v->listSize())
|
||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", attrIndex, attrPath);
|
||||
if (*attrIndex >= v->listSize())
|
||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
|
||||
|
||||
v = v->listElems()[attrIndex];
|
||||
v = v->listElems()[*attrIndex];
|
||||
pos = noPos;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ void EvalState::mkAttrs(Value & v, size_t capacity)
|
|||
v = vEmptySet;
|
||||
return;
|
||||
}
|
||||
clearValue(v);
|
||||
v.type = tAttrs;
|
||||
v.attrs = allocBindings(capacity);
|
||||
v.mkAttrs(allocBindings(capacity));
|
||||
nrAttrsets++;
|
||||
nrAttrsInAttrsets += capacity;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ MixEvalArgs::MixEvalArgs()
|
|||
{
|
||||
addFlag({
|
||||
.longName = "arg",
|
||||
.description = "argument to be passed to Nix functions",
|
||||
.description = "Pass the value *expr* as the argument *name* to Nix functions.",
|
||||
.labels = {"name", "expr"},
|
||||
.handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "argstr",
|
||||
.description = "string-valued argument to be passed to Nix functions",
|
||||
.description = "Pass the string *string* as the argument *name* to Nix functions.",
|
||||
.labels = {"name", "string"},
|
||||
.handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }},
|
||||
});
|
||||
|
|
@ -29,14 +29,14 @@ MixEvalArgs::MixEvalArgs()
|
|||
addFlag({
|
||||
.longName = "include",
|
||||
.shortName = 'I',
|
||||
.description = "add a path to the list of locations used to look up `<...>` file names",
|
||||
.description = "Add *path* to the list of locations used to look up `<...>` file names.",
|
||||
.labels = {"path"},
|
||||
.handler = {[&](std::string s) { searchPath.push_back(s); }}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "impure",
|
||||
.description = "allow access to mutable paths and repositories",
|
||||
.description = "Allow access to mutable paths and repositories.",
|
||||
.handler = {[&]() {
|
||||
evalSettings.pureEval = false;
|
||||
}},
|
||||
|
|
@ -44,7 +44,7 @@ MixEvalArgs::MixEvalArgs()
|
|||
|
||||
addFlag({
|
||||
.longName = "override-flake",
|
||||
.description = "override a flake registry value",
|
||||
.description = "Override the flake registries, redirecting *original-ref* to *resolved-ref*.",
|
||||
.labels = {"original-ref", "resolved-ref"},
|
||||
.handler = {[&](std::string _from, std::string _to) {
|
||||
auto from = parseFlakeRef(_from, absPath("."));
|
||||
|
|
|
|||
|
|
@ -390,14 +390,14 @@ Value & AttrCursor::forceValue()
|
|||
}
|
||||
|
||||
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
|
||||
if (v.type == tString)
|
||||
if (v.type() == nString)
|
||||
cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context),
|
||||
string_t{v.string.s, {}}};
|
||||
else if (v.type == tPath)
|
||||
cachedValue = {root->db->setString(getKey(), v.path), v.path};
|
||||
else if (v.type == tBool)
|
||||
else if (v.type() == nPath)
|
||||
cachedValue = {root->db->setString(getKey(), v.path), string_t{v.path, {}}};
|
||||
else if (v.type() == nBool)
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
|
||||
else if (v.type == tAttrs)
|
||||
else if (v.type() == nAttrs)
|
||||
; // FIXME: do something?
|
||||
else
|
||||
cachedValue = {root->db->setMisc(getKey()), misc_t()};
|
||||
|
|
@ -442,7 +442,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
|
|||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type != tAttrs)
|
||||
if (v.type() != nAttrs)
|
||||
return nullptr;
|
||||
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
||||
|
||||
|
|
@ -512,10 +512,10 @@ std::string AttrCursor::getString()
|
|||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type != tString && v.type != tPath)
|
||||
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type));
|
||||
if (v.type() != nString && v.type() != nPath)
|
||||
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()));
|
||||
|
||||
return v.type == tString ? v.string.s : v.path;
|
||||
return v.type() == nString ? v.string.s : v.path;
|
||||
}
|
||||
|
||||
string_t AttrCursor::getStringWithContext()
|
||||
|
|
@ -543,12 +543,12 @@ string_t AttrCursor::getStringWithContext()
|
|||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type == tString)
|
||||
if (v.type() == nString)
|
||||
return {v.string.s, v.getContext()};
|
||||
else if (v.type == tPath)
|
||||
else if (v.type() == nPath)
|
||||
return {v.path, {}};
|
||||
else
|
||||
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type));
|
||||
throw TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()));
|
||||
}
|
||||
|
||||
bool AttrCursor::getBool()
|
||||
|
|
@ -567,7 +567,7 @@ bool AttrCursor::getBool()
|
|||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type != tBool)
|
||||
if (v.type() != nBool)
|
||||
throw TypeError("'%s' is not a Boolean", getAttrPathStr());
|
||||
|
||||
return v.boolean;
|
||||
|
|
@ -589,7 +589,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
|||
|
||||
auto & v = forceValue();
|
||||
|
||||
if (v.type != tAttrs)
|
||||
if (v.type() != nAttrs)
|
||||
throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
||||
|
||||
std::vector<Symbol> attrs;
|
||||
|
|
|
|||
|
|
@ -32,23 +32,21 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
|
|||
|
||||
void EvalState::forceValue(Value & v, const Pos & pos)
|
||||
{
|
||||
if (v.type == tThunk) {
|
||||
if (v.isThunk()) {
|
||||
Env * env = v.thunk.env;
|
||||
Expr * expr = v.thunk.expr;
|
||||
try {
|
||||
v.type = tBlackhole;
|
||||
v.mkBlackhole();
|
||||
//checkInterrupt();
|
||||
expr->eval(*this, *env, v);
|
||||
} catch (...) {
|
||||
v.type = tThunk;
|
||||
v.thunk.env = env;
|
||||
v.thunk.expr = expr;
|
||||
v.mkThunk(env, expr);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (v.type == tApp)
|
||||
else if (v.isApp())
|
||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||
else if (v.type == tBlackhole)
|
||||
else if (v.isBlackhole())
|
||||
throwEvalError(pos, "infinite recursion encountered");
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +54,7 @@ void EvalState::forceValue(Value & v, const Pos & pos)
|
|||
inline void EvalState::forceAttrs(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tAttrs)
|
||||
if (v.type() != nAttrs)
|
||||
throwTypeError("value is %1% while a set was expected", v);
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +62,7 @@ inline void EvalState::forceAttrs(Value & v)
|
|||
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tAttrs)
|
||||
if (v.type() != nAttrs)
|
||||
throwTypeError(pos, "value is %1% while a set was expected", v);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ RootValue allocRootValue(Value * v)
|
|||
}
|
||||
|
||||
|
||||
static void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
|
||||
void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ static void printValue(std::ostream & str, std::set<const Value *> & active, con
|
|||
return;
|
||||
}
|
||||
|
||||
switch (v.type) {
|
||||
switch (v.internalType) {
|
||||
case tInt:
|
||||
str << v.integer;
|
||||
break;
|
||||
|
|
@ -158,32 +158,27 @@ std::ostream & operator << (std::ostream & str, const Value & v)
|
|||
|
||||
const Value *getPrimOp(const Value &v) {
|
||||
const Value * primOp = &v;
|
||||
while (primOp->type == tPrimOpApp) {
|
||||
while (primOp->isPrimOpApp()) {
|
||||
primOp = primOp->primOpApp.left;
|
||||
}
|
||||
assert(primOp->type == tPrimOp);
|
||||
assert(primOp->isPrimOp());
|
||||
return primOp;
|
||||
}
|
||||
|
||||
|
||||
string showType(ValueType type)
|
||||
{
|
||||
switch (type) {
|
||||
case tInt: return "an integer";
|
||||
case tBool: return "a Boolean";
|
||||
case tString: return "a string";
|
||||
case tPath: return "a path";
|
||||
case tNull: return "null";
|
||||
case tAttrs: return "a set";
|
||||
case tList1: case tList2: case tListN: return "a list";
|
||||
case tThunk: return "a thunk";
|
||||
case tApp: return "a function application";
|
||||
case tLambda: return "a function";
|
||||
case tBlackhole: return "a black hole";
|
||||
case tPrimOp: return "a built-in function";
|
||||
case tPrimOpApp: return "a partially applied built-in function";
|
||||
case tExternal: return "an external value";
|
||||
case tFloat: return "a float";
|
||||
case nInt: return "an integer";
|
||||
case nBool: return "a Boolean";
|
||||
case nString: return "a string";
|
||||
case nPath: return "a path";
|
||||
case nNull: return "null";
|
||||
case nAttrs: return "a set";
|
||||
case nList: return "a list";
|
||||
case nFunction: return "a function";
|
||||
case nExternal: return "an external value";
|
||||
case nFloat: return "a float";
|
||||
case nThunk: return "a thunk";
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
|
@ -191,15 +186,18 @@ string showType(ValueType type)
|
|||
|
||||
string showType(const Value & v)
|
||||
{
|
||||
switch (v.type) {
|
||||
switch (v.internalType) {
|
||||
case tString: return v.string.context ? "a string with context" : "a string";
|
||||
case tPrimOp:
|
||||
return fmt("the built-in function '%s'", string(v.primOp->name));
|
||||
case tPrimOpApp:
|
||||
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
|
||||
case tExternal: return v.external->showType();
|
||||
case tThunk: return "a thunk";
|
||||
case tApp: return "a function application";
|
||||
case tBlackhole: return "a black hole";
|
||||
default:
|
||||
return showType(v.type);
|
||||
return showType(v.type());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,9 +205,9 @@ string showType(const Value & v)
|
|||
bool Value::isTrivial() const
|
||||
{
|
||||
return
|
||||
type != tApp
|
||||
&& type != tPrimOpApp
|
||||
&& (type != tThunk
|
||||
internalType != tApp
|
||||
&& internalType != tPrimOpApp
|
||||
&& (internalType != tThunk
|
||||
|| (dynamic_cast<ExprAttrs *>(thunk.expr)
|
||||
&& ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(thunk.expr)
|
||||
|
|
@ -404,11 +402,6 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
|||
for (auto & i : evalSettings.nixPath.get()) addToSearchPath(i);
|
||||
}
|
||||
|
||||
try {
|
||||
addToSearchPath("nix=" + canonPath(settings.nixDataDir + "/nix/corepkgs", true));
|
||||
} catch (Error &) {
|
||||
}
|
||||
|
||||
if (evalSettings.restrictEval || evalSettings.pureEval) {
|
||||
allowedPaths = PathSet();
|
||||
|
||||
|
|
@ -432,9 +425,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
|||
}
|
||||
}
|
||||
|
||||
clearValue(vEmptySet);
|
||||
vEmptySet.type = tAttrs;
|
||||
vEmptySet.attrs = allocBindings(0);
|
||||
vEmptySet.mkAttrs(allocBindings(0));
|
||||
|
||||
createBaseEnv();
|
||||
}
|
||||
|
|
@ -461,6 +452,8 @@ Path EvalState::checkSourcePath(const Path & path_)
|
|||
*/
|
||||
Path abspath = canonPath(path_);
|
||||
|
||||
if (hasPrefix(abspath, corepkgsPrefix)) return abspath;
|
||||
|
||||
for (auto & i : *allowedPaths) {
|
||||
if (isDirOrInDir(abspath, i)) {
|
||||
found = true;
|
||||
|
|
@ -550,16 +543,14 @@ Value * EvalState::addPrimOp(const string & name,
|
|||
the primop to a dummy value. */
|
||||
if (arity == 0) {
|
||||
auto vPrimOp = allocValue();
|
||||
vPrimOp->type = tPrimOp;
|
||||
vPrimOp->primOp = new PrimOp { .fun = primOp, .arity = 1, .name = sym };
|
||||
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
|
||||
Value v;
|
||||
mkApp(v, *vPrimOp, *vPrimOp);
|
||||
return addConstant(name, v);
|
||||
}
|
||||
|
||||
Value * v = allocValue();
|
||||
v->type = tPrimOp;
|
||||
v->primOp = new PrimOp { .fun = primOp, .arity = arity, .name = sym };
|
||||
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym });
|
||||
staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl;
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
||||
|
|
@ -574,8 +565,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
|||
if (primOp.arity == 0) {
|
||||
primOp.arity = 1;
|
||||
auto vPrimOp = allocValue();
|
||||
vPrimOp->type = tPrimOp;
|
||||
vPrimOp->primOp = new PrimOp(std::move(primOp));
|
||||
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
|
||||
Value v;
|
||||
mkApp(v, *vPrimOp, *vPrimOp);
|
||||
return addConstant(primOp.name, v);
|
||||
|
|
@ -586,8 +576,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
|||
primOp.name = symbols.create(std::string(primOp.name, 2));
|
||||
|
||||
Value * v = allocValue();
|
||||
v->type = tPrimOp;
|
||||
v->primOp = new PrimOp(std::move(primOp));
|
||||
v->mkPrimOp(new PrimOp(std::move(primOp)));
|
||||
staticBaseEnv.vars[envName] = baseEnvDispl;
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
|
||||
|
|
@ -603,9 +592,9 @@ Value & EvalState::getBuiltin(const string & name)
|
|||
|
||||
std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||
{
|
||||
if (v.type == tPrimOp || v.type == tPrimOpApp) {
|
||||
if (v.isPrimOp() || v.isPrimOpApp()) {
|
||||
auto v2 = &v;
|
||||
while (v2->type == tPrimOpApp)
|
||||
while (v2->isPrimOpApp())
|
||||
v2 = v2->primOpApp.left;
|
||||
if (v2->primOp->doc)
|
||||
return Doc {
|
||||
|
|
@ -668,11 +657,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s))
|
|||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1))
|
||||
{
|
||||
throw TypeError(s, s1);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2))
|
||||
{
|
||||
throw TypeError({
|
||||
|
|
@ -697,6 +681,14 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
|
|||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const string & s1))
|
||||
{
|
||||
throw MissingArgumentError({
|
||||
.hint = hintfmt(s, s1),
|
||||
.errPos = pos
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInline(void addErrorTrace(Error & e, const char * s, const string & s2))
|
||||
{
|
||||
e.addTrace(std::nullopt, s, s2);
|
||||
|
|
@ -710,15 +702,13 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con
|
|||
|
||||
void mkString(Value & v, const char * s)
|
||||
{
|
||||
mkStringNoCopy(v, dupString(s));
|
||||
v.mkString(dupString(s));
|
||||
}
|
||||
|
||||
|
||||
Value & mkString(Value & v, std::string_view s, const PathSet & context)
|
||||
{
|
||||
v.type = tString;
|
||||
v.string.s = dupStringWithLen(s.data(), s.size());
|
||||
v.string.context = 0;
|
||||
v.mkString(dupStringWithLen(s.data(), s.size()));
|
||||
if (!context.empty()) {
|
||||
size_t n = 0;
|
||||
v.string.context = (const char * *)
|
||||
|
|
@ -733,7 +723,7 @@ Value & mkString(Value & v, std::string_view s, const PathSet & context)
|
|||
|
||||
void mkPath(Value & v, const char * s)
|
||||
{
|
||||
mkPathNoCopy(v, dupString(s));
|
||||
v.mkPath(dupString(s));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -794,16 +784,9 @@ Env & EvalState::allocEnv(size_t size)
|
|||
|
||||
void EvalState::mkList(Value & v, size_t size)
|
||||
{
|
||||
clearValue(v);
|
||||
if (size == 1)
|
||||
v.type = tList1;
|
||||
else if (size == 2)
|
||||
v.type = tList2;
|
||||
else {
|
||||
v.type = tListN;
|
||||
v.bigList.size = size;
|
||||
v.bigList.elems = size ? (Value * *) allocBytes(size * sizeof(Value *)) : 0;
|
||||
}
|
||||
v.mkList(size);
|
||||
if (size > 2)
|
||||
v.bigList.elems = (Value * *) allocBytes(size * sizeof(Value *));
|
||||
nrListElems += size;
|
||||
}
|
||||
|
||||
|
|
@ -812,9 +795,7 @@ unsigned long nrThunks = 0;
|
|||
|
||||
static inline void mkThunk(Value & v, Env & env, Expr * expr)
|
||||
{
|
||||
v.type = tThunk;
|
||||
v.thunk.env = &env;
|
||||
v.thunk.expr = expr;
|
||||
v.mkThunk(&env, expr);
|
||||
nrThunks++;
|
||||
}
|
||||
|
||||
|
|
@ -949,7 +930,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
|
|||
{
|
||||
Value v;
|
||||
e->eval(*this, env, v);
|
||||
if (v.type != tBool)
|
||||
if (v.type() != nBool)
|
||||
throwTypeError("value is %1% while a Boolean was expected", v);
|
||||
return v.boolean;
|
||||
}
|
||||
|
|
@ -959,7 +940,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
|
|||
{
|
||||
Value v;
|
||||
e->eval(*this, env, v);
|
||||
if (v.type != tBool)
|
||||
if (v.type() != nBool)
|
||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
||||
return v.boolean;
|
||||
}
|
||||
|
|
@ -968,7 +949,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
|
|||
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
|
||||
{
|
||||
e->eval(*this, env, v);
|
||||
if (v.type != tAttrs)
|
||||
if (v.type() != nAttrs)
|
||||
throwTypeError("value is %1% while a set was expected", v);
|
||||
}
|
||||
|
||||
|
|
@ -1068,7 +1049,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
Value nameVal;
|
||||
i.nameExpr->eval(state, *dynamicEnv, nameVal);
|
||||
state.forceValue(nameVal, i.pos);
|
||||
if (nameVal.type == tNull)
|
||||
if (nameVal.type() == nNull)
|
||||
continue;
|
||||
state.forceStringNoCtx(nameVal);
|
||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
||||
|
|
@ -1153,7 +1134,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
Symbol name = getName(i, state, env);
|
||||
if (def) {
|
||||
state.forceValue(*vAttrs, pos);
|
||||
if (vAttrs->type != tAttrs ||
|
||||
if (vAttrs->type() != nAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
{
|
||||
def->eval(state, env, v);
|
||||
|
|
@ -1193,7 +1174,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|||
state.forceValue(*vAttrs);
|
||||
Bindings::iterator j;
|
||||
Symbol name = getName(i, state, env);
|
||||
if (vAttrs->type != tAttrs ||
|
||||
if (vAttrs->type() != nAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
{
|
||||
mkBool(v, false);
|
||||
|
|
@ -1209,9 +1190,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.type = tLambda;
|
||||
v.lambda.env = &env;
|
||||
v.lambda.fun = this;
|
||||
v.mkLambda(&env, this);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1229,11 +1208,11 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
|
|||
/* Figure out the number of arguments still needed. */
|
||||
size_t argsDone = 0;
|
||||
Value * primOp = &fun;
|
||||
while (primOp->type == tPrimOpApp) {
|
||||
while (primOp->isPrimOpApp()) {
|
||||
argsDone++;
|
||||
primOp = primOp->primOpApp.left;
|
||||
}
|
||||
assert(primOp->type == tPrimOp);
|
||||
assert(primOp->isPrimOp());
|
||||
auto arity = primOp->primOp->arity;
|
||||
auto argsLeft = arity - argsDone;
|
||||
|
||||
|
|
@ -1244,7 +1223,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
|
|||
Value * vArgs[arity];
|
||||
auto n = arity - 1;
|
||||
vArgs[n--] = &arg;
|
||||
for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left)
|
||||
for (Value * arg = &fun; arg->isPrimOpApp(); arg = arg->primOpApp.left)
|
||||
vArgs[n--] = arg->primOpApp.right;
|
||||
|
||||
/* And call the primop. */
|
||||
|
|
@ -1254,9 +1233,7 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos)
|
|||
} else {
|
||||
Value * fun2 = allocValue();
|
||||
*fun2 = fun;
|
||||
v.type = tPrimOpApp;
|
||||
v.primOpApp.left = fun2;
|
||||
v.primOpApp.right = &arg;
|
||||
v.mkPrimOpApp(fun2, &arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1266,12 +1243,12 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
|||
|
||||
forceValue(fun, pos);
|
||||
|
||||
if (fun.type == tPrimOp || fun.type == tPrimOpApp) {
|
||||
if (fun.isPrimOp() || fun.isPrimOpApp()) {
|
||||
callPrimOp(fun, arg, v, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fun.type == tAttrs) {
|
||||
if (fun.type() == nAttrs) {
|
||||
auto found = fun.attrs->find(sFunctor);
|
||||
if (found != fun.attrs->end()) {
|
||||
/* fun may be allocated on the stack of the calling function,
|
||||
|
|
@ -1287,7 +1264,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po
|
|||
}
|
||||
}
|
||||
|
||||
if (fun.type != tLambda)
|
||||
if (!fun.isLambda())
|
||||
throwTypeError(pos, "attempt to call something which is not a function but %1%", fun);
|
||||
|
||||
ExprLambda & lambda(*fun.lambda.fun);
|
||||
|
|
@ -1370,7 +1347,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
{
|
||||
forceValue(fun);
|
||||
|
||||
if (fun.type == tAttrs) {
|
||||
if (fun.type() == nAttrs) {
|
||||
auto found = fun.attrs->find(sFunctor);
|
||||
if (found != fun.attrs->end()) {
|
||||
Value * v = allocValue();
|
||||
|
|
@ -1380,7 +1357,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
}
|
||||
}
|
||||
|
||||
if (fun.type != tLambda || !fun.lambda.fun->matchAttrs) {
|
||||
if (!fun.isLambda() || !fun.lambda.fun->matchAttrs) {
|
||||
res = fun;
|
||||
return;
|
||||
}
|
||||
|
|
@ -1402,7 +1379,13 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
if (j != args.end()) {
|
||||
actualArgs->attrs->push_back(*j);
|
||||
} else if (!i.def) {
|
||||
throwTypeError("cannot auto-call a function that has an argument without a default value ('%1%')", i.name);
|
||||
throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||
|
||||
nix attempted to evaluate a function as a top level expression; in this case it must have its
|
||||
arguments supplied either by default values, or passed explicitly with --arg or --argstr.
|
||||
|
||||
https://nixos.org/manual/nix/stable/#ss-functions)", i.name);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1564,7 +1547,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|||
NixFloat nf = 0;
|
||||
|
||||
bool first = !forceString;
|
||||
ValueType firstType = tString;
|
||||
ValueType firstType = nString;
|
||||
|
||||
for (auto & i : *es) {
|
||||
Value vTmp;
|
||||
|
|
@ -1575,36 +1558,36 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|||
since paths are copied when they are used in a derivation),
|
||||
and none of the strings are allowed to have contexts. */
|
||||
if (first) {
|
||||
firstType = vTmp.type;
|
||||
firstType = vTmp.type();
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (firstType == tInt) {
|
||||
if (vTmp.type == tInt) {
|
||||
if (firstType == nInt) {
|
||||
if (vTmp.type() == nInt) {
|
||||
n += vTmp.integer;
|
||||
} else if (vTmp.type == tFloat) {
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
// Upgrade the type from int to float;
|
||||
firstType = tFloat;
|
||||
firstType = nFloat;
|
||||
nf = n;
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
|
||||
} else if (firstType == tFloat) {
|
||||
if (vTmp.type == tInt) {
|
||||
} else if (firstType == nFloat) {
|
||||
if (vTmp.type() == nInt) {
|
||||
nf += vTmp.integer;
|
||||
} else if (vTmp.type == tFloat) {
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
|
||||
} else
|
||||
s << state.coerceToString(pos, vTmp, context, false, firstType == tString);
|
||||
s << state.coerceToString(pos, vTmp, context, false, firstType == nString);
|
||||
}
|
||||
|
||||
if (firstType == tInt)
|
||||
if (firstType == nInt)
|
||||
mkInt(v, n);
|
||||
else if (firstType == tFloat)
|
||||
else if (firstType == nFloat)
|
||||
mkFloat(v, nf);
|
||||
else if (firstType == tPath) {
|
||||
else if (firstType == nPath) {
|
||||
if (!context.empty())
|
||||
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||
auto path = canonPath(s.str());
|
||||
|
|
@ -1631,7 +1614,7 @@ void EvalState::forceValueDeep(Value & v)
|
|||
|
||||
forceValue(v);
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
if (v.type() == nAttrs) {
|
||||
for (auto & i : *v.attrs)
|
||||
try {
|
||||
recurse(*i.value);
|
||||
|
|
@ -1654,7 +1637,7 @@ void EvalState::forceValueDeep(Value & v)
|
|||
NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tInt)
|
||||
if (v.type() != nInt)
|
||||
throwTypeError(pos, "value is %1% while an integer was expected", v);
|
||||
return v.integer;
|
||||
}
|
||||
|
|
@ -1663,9 +1646,9 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
|||
NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type == tInt)
|
||||
if (v.type() == nInt)
|
||||
return v.integer;
|
||||
else if (v.type != tFloat)
|
||||
else if (v.type() != nFloat)
|
||||
throwTypeError(pos, "value is %1% while a float was expected", v);
|
||||
return v.fpoint;
|
||||
}
|
||||
|
|
@ -1674,7 +1657,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
|||
bool EvalState::forceBool(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tBool)
|
||||
if (v.type() != nBool)
|
||||
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
|
||||
return v.boolean;
|
||||
}
|
||||
|
|
@ -1682,14 +1665,14 @@ bool EvalState::forceBool(Value & v, const Pos & pos)
|
|||
|
||||
bool EvalState::isFunctor(Value & fun)
|
||||
{
|
||||
return fun.type == tAttrs && fun.attrs->find(sFunctor) != fun.attrs->end();
|
||||
return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end();
|
||||
}
|
||||
|
||||
|
||||
void EvalState::forceFunction(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tLambda && v.type != tPrimOp && v.type != tPrimOpApp && !isFunctor(v))
|
||||
if (v.type() != nFunction && !isFunctor(v))
|
||||
throwTypeError(pos, "value is %1% while a function was expected", v);
|
||||
}
|
||||
|
||||
|
|
@ -1697,7 +1680,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
|
|||
string EvalState::forceString(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type != tString) {
|
||||
if (v.type() != nString) {
|
||||
if (pos)
|
||||
throwTypeError(pos, "value is %1% while a string was expected", v);
|
||||
else
|
||||
|
|
@ -1730,7 +1713,7 @@ void copyContext(const Value & v, PathSet & context)
|
|||
std::vector<std::pair<Path, std::string>> Value::getContext()
|
||||
{
|
||||
std::vector<std::pair<Path, std::string>> res;
|
||||
assert(type == tString);
|
||||
assert(internalType == tString);
|
||||
if (string.context)
|
||||
for (const char * * p = string.context; *p; ++p)
|
||||
res.push_back(decodeContext(*p));
|
||||
|
|
@ -1763,11 +1746,11 @@ string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
|||
|
||||
bool EvalState::isDerivation(Value & v)
|
||||
{
|
||||
if (v.type != tAttrs) return false;
|
||||
if (v.type() != nAttrs) return false;
|
||||
Bindings::iterator i = v.attrs->find(sType);
|
||||
if (i == v.attrs->end()) return false;
|
||||
forceValue(*i->value);
|
||||
if (i->value->type != tString) return false;
|
||||
if (i->value->type() != nString) return false;
|
||||
return strcmp(i->value->string.s, "derivation") == 0;
|
||||
}
|
||||
|
||||
|
|
@ -1792,17 +1775,17 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
|||
|
||||
string s;
|
||||
|
||||
if (v.type == tString) {
|
||||
if (v.type() == nString) {
|
||||
copyContext(v, context);
|
||||
return v.string.s;
|
||||
}
|
||||
|
||||
if (v.type == tPath) {
|
||||
if (v.type() == nPath) {
|
||||
Path path(canonPath(v.path));
|
||||
return copyToStore ? copyPathToStore(context, path) : path;
|
||||
}
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
if (v.type() == nAttrs) {
|
||||
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
|
||||
if (maybeString) {
|
||||
return *maybeString;
|
||||
|
|
@ -1812,18 +1795,18 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
|||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
||||
}
|
||||
|
||||
if (v.type == tExternal)
|
||||
if (v.type() == nExternal)
|
||||
return v.external->coerceToString(pos, context, coerceMore, copyToStore);
|
||||
|
||||
if (coerceMore) {
|
||||
|
||||
/* Note that `false' is represented as an empty string for
|
||||
shell scripting convenience, just like `null'. */
|
||||
if (v.type == tBool && v.boolean) return "1";
|
||||
if (v.type == tBool && !v.boolean) return "";
|
||||
if (v.type == tInt) return std::to_string(v.integer);
|
||||
if (v.type == tFloat) return std::to_string(v.fpoint);
|
||||
if (v.type == tNull) return "";
|
||||
if (v.type() == nBool && v.boolean) return "1";
|
||||
if (v.type() == nBool && !v.boolean) return "";
|
||||
if (v.type() == nInt) return std::to_string(v.integer);
|
||||
if (v.type() == nFloat) return std::to_string(v.fpoint);
|
||||
if (v.type() == nNull) return "";
|
||||
|
||||
if (v.isList()) {
|
||||
string result;
|
||||
|
|
@ -1886,40 +1869,38 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||
if (&v1 == &v2) return true;
|
||||
|
||||
// Special case type-compatibility between float and int
|
||||
if (v1.type == tInt && v2.type == tFloat)
|
||||
if (v1.type() == nInt && v2.type() == nFloat)
|
||||
return v1.integer == v2.fpoint;
|
||||
if (v1.type == tFloat && v2.type == tInt)
|
||||
if (v1.type() == nFloat && v2.type() == nInt)
|
||||
return v1.fpoint == v2.integer;
|
||||
|
||||
// All other types are not compatible with each other.
|
||||
if (v1.type != v2.type) return false;
|
||||
if (v1.type() != v2.type()) return false;
|
||||
|
||||
switch (v1.type) {
|
||||
switch (v1.type()) {
|
||||
|
||||
case tInt:
|
||||
case nInt:
|
||||
return v1.integer == v2.integer;
|
||||
|
||||
case tBool:
|
||||
case nBool:
|
||||
return v1.boolean == v2.boolean;
|
||||
|
||||
case tString:
|
||||
case nString:
|
||||
return strcmp(v1.string.s, v2.string.s) == 0;
|
||||
|
||||
case tPath:
|
||||
case nPath:
|
||||
return strcmp(v1.path, v2.path) == 0;
|
||||
|
||||
case tNull:
|
||||
case nNull:
|
||||
return true;
|
||||
|
||||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
case nList:
|
||||
if (v1.listSize() != v2.listSize()) return false;
|
||||
for (size_t n = 0; n < v1.listSize(); ++n)
|
||||
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
|
||||
return true;
|
||||
|
||||
case tAttrs: {
|
||||
case nAttrs: {
|
||||
/* If both sets denote a derivation (type = "derivation"),
|
||||
then compare their outPaths. */
|
||||
if (isDerivation(v1) && isDerivation(v2)) {
|
||||
|
|
@ -1941,15 +1922,13 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
|||
}
|
||||
|
||||
/* Functions are incomparable. */
|
||||
case tLambda:
|
||||
case tPrimOp:
|
||||
case tPrimOpApp:
|
||||
case nFunction:
|
||||
return false;
|
||||
|
||||
case tExternal:
|
||||
case nExternal:
|
||||
return *v1.external == *v2.external;
|
||||
|
||||
case tFloat:
|
||||
case nFloat:
|
||||
return v1.fpoint == v2.fpoint;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -432,4 +432,6 @@ struct EvalSettings : Config
|
|||
|
||||
extern EvalSettings evalSettings;
|
||||
|
||||
static const std::string corepkgsPrefix{"/__corepkgs__/"};
|
||||
|
||||
}
|
||||
|
|
|
|||
41
src/libexpr/fetchurl.nix
Normal file
41
src/libexpr/fetchurl.nix
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{ system ? "" # obsolete
|
||||
, url
|
||||
, hash ? "" # an SRI hash
|
||||
|
||||
# Legacy hash specification
|
||||
, md5 ? "", sha1 ? "", sha256 ? "", sha512 ? ""
|
||||
, outputHash ?
|
||||
if hash != "" then hash else if sha512 != "" then sha512 else if sha1 != "" then sha1 else if md5 != "" then md5 else sha256
|
||||
, outputHashAlgo ?
|
||||
if hash != "" then "" else if sha512 != "" then "sha512" else if sha1 != "" then "sha1" else if md5 != "" then "md5" else "sha256"
|
||||
|
||||
, executable ? false
|
||||
, unpack ? false
|
||||
, name ? baseNameOf (toString url)
|
||||
}:
|
||||
|
||||
derivation {
|
||||
builder = "builtin:fetchurl";
|
||||
|
||||
# New-style output content requirements.
|
||||
inherit outputHashAlgo outputHash;
|
||||
outputHashMode = if unpack || executable then "recursive" else "flat";
|
||||
|
||||
inherit name url executable unpack;
|
||||
|
||||
system = "builtin";
|
||||
|
||||
# No need to double the amount of network traffic
|
||||
preferLocalBuild = true;
|
||||
|
||||
impureEnvVars = [
|
||||
# We borrow these environment variables from the caller to allow
|
||||
# easy proxy configuration. This is impure, but a fixed-output
|
||||
# derivation like fetchurl is allowed to do so since its result is
|
||||
# by definition pure.
|
||||
"http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy"
|
||||
];
|
||||
|
||||
# To make "nix-prefetch-url" work.
|
||||
urls = [ url ];
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
|||
|
||||
static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
|
||||
{
|
||||
if (value.type == tThunk && value.isTrivial())
|
||||
if (value.isThunk() && value.isTrivial())
|
||||
state.forceValue(value, pos);
|
||||
}
|
||||
|
||||
|
|
@ -82,9 +82,9 @@ static void expectType(EvalState & state, ValueType type,
|
|||
Value & value, const Pos & pos)
|
||||
{
|
||||
forceTrivialValue(state, value, pos);
|
||||
if (value.type != type)
|
||||
if (value.type() != type)
|
||||
throw Error("expected %s but got %s at %s",
|
||||
showType(type), showType(value.type), pos);
|
||||
showType(type), showType(value.type()), pos);
|
||||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
|
|
@ -93,7 +93,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
|||
static FlakeInput parseFlakeInput(EvalState & state,
|
||||
const std::string & inputName, Value * value, const Pos & pos)
|
||||
{
|
||||
expectType(state, tAttrs, *value, pos);
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
|
||||
FlakeInput input;
|
||||
|
||||
|
|
@ -108,23 +108,32 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
|||
for (nix::Attr attr : *(value->attrs)) {
|
||||
try {
|
||||
if (attr.name == sUrl) {
|
||||
expectType(state, tString, *attr.value, *attr.pos);
|
||||
expectType(state, nString, *attr.value, *attr.pos);
|
||||
url = attr.value->string.s;
|
||||
attrs.emplace("url", *url);
|
||||
} else if (attr.name == sFlake) {
|
||||
expectType(state, tBool, *attr.value, *attr.pos);
|
||||
expectType(state, nBool, *attr.value, *attr.pos);
|
||||
input.isFlake = attr.value->boolean;
|
||||
} else if (attr.name == sInputs) {
|
||||
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos);
|
||||
} else if (attr.name == sFollows) {
|
||||
expectType(state, tString, *attr.value, *attr.pos);
|
||||
expectType(state, nString, *attr.value, *attr.pos);
|
||||
input.follows = parseInputPath(attr.value->string.s);
|
||||
} else {
|
||||
if (attr.value->type == tString)
|
||||
attrs.emplace(attr.name, attr.value->string.s);
|
||||
else
|
||||
throw TypeError("flake input attribute '%s' is %s while a string is expected",
|
||||
attr.name, showType(*attr.value));
|
||||
switch (attr.value->type()) {
|
||||
case nString:
|
||||
attrs.emplace(attr.name, attr.value->string.s);
|
||||
break;
|
||||
case nBool:
|
||||
attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean });
|
||||
break;
|
||||
case nInt:
|
||||
attrs.emplace(attr.name, (long unsigned int)attr.value->integer);
|
||||
break;
|
||||
default:
|
||||
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||
attr.name, showType(*attr.value));
|
||||
}
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name));
|
||||
|
|
@ -158,7 +167,7 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
|||
{
|
||||
std::map<FlakeId, FlakeInput> inputs;
|
||||
|
||||
expectType(state, tAttrs, *value, pos);
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
|
||||
for (nix::Attr & inputAttr : *(*value).attrs) {
|
||||
inputs.emplace(inputAttr.name,
|
||||
|
|
@ -199,10 +208,10 @@ static Flake getFlake(
|
|||
Value vInfo;
|
||||
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
||||
|
||||
expectType(state, tAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
|
||||
expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
|
||||
|
||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||
expectType(state, tString, *description->value, *description->pos);
|
||||
expectType(state, nString, *description->value, *description->pos);
|
||||
flake.description = description->value->string.s;
|
||||
}
|
||||
|
||||
|
|
@ -214,9 +223,9 @@ static Flake getFlake(
|
|||
auto sOutputs = state.symbols.create("outputs");
|
||||
|
||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||
expectType(state, tLambda, *outputs->value, *outputs->pos);
|
||||
expectType(state, nFunction, *outputs->value, *outputs->pos);
|
||||
|
||||
if (outputs->value->lambda.fun->matchAttrs) {
|
||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->matchAttrs) {
|
||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||
if (formal.name != state.sSelf)
|
||||
flake.inputs.emplace(formal.name, FlakeInput {
|
||||
|
|
@ -231,21 +240,21 @@ static Flake getFlake(
|
|||
auto sNixConfig = state.symbols.create("nixConfig");
|
||||
|
||||
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
||||
expectType(state, tAttrs, *nixConfig->value, *nixConfig->pos);
|
||||
expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos);
|
||||
|
||||
for (auto & setting : *nixConfig->value->attrs) {
|
||||
forceTrivialValue(state, *setting.value, *setting.pos);
|
||||
if (setting.value->type == tString)
|
||||
if (setting.value->type() == nString)
|
||||
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
|
||||
else if (setting.value->type == tInt)
|
||||
else if (setting.value->type() == nInt)
|
||||
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
|
||||
else if (setting.value->type == tBool)
|
||||
else if (setting.value->type() == nBool)
|
||||
flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)});
|
||||
else if (setting.value->isList()) {
|
||||
else if (setting.value->type() == nList) {
|
||||
std::vector<std::string> ss;
|
||||
for (unsigned int n = 0; n < setting.value->listSize(); ++n) {
|
||||
auto elem = setting.value->listElems()[n];
|
||||
if (elem->type != tString)
|
||||
if (elem->type() != nString)
|
||||
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||
setting.name, showType(*setting.value));
|
||||
ss.push_back(state.forceStringNoCtx(*elem, *setting.pos));
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
|||
if (!outTI->isList()) throw errMsg;
|
||||
Outputs result;
|
||||
for (auto i = outTI->listElems(); i != outTI->listElems() + outTI->listSize(); ++i) {
|
||||
if ((*i)->type != tString) throw errMsg;
|
||||
if ((*i)->type() != nString) throw errMsg;
|
||||
auto out = outputs.find((*i)->string.s);
|
||||
if (out == outputs.end()) throw errMsg;
|
||||
result.insert(*out);
|
||||
|
|
@ -172,20 +172,20 @@ StringSet DrvInfo::queryMetaNames()
|
|||
bool DrvInfo::checkMeta(Value & v)
|
||||
{
|
||||
state->forceValue(v);
|
||||
if (v.isList()) {
|
||||
if (v.type() == nList) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
if (!checkMeta(*v.listElems()[n])) return false;
|
||||
return true;
|
||||
}
|
||||
else if (v.type == tAttrs) {
|
||||
else if (v.type() == nAttrs) {
|
||||
Bindings::iterator i = v.attrs->find(state->sOutPath);
|
||||
if (i != v.attrs->end()) return false;
|
||||
for (auto & i : *v.attrs)
|
||||
if (!checkMeta(*i.value)) return false;
|
||||
return true;
|
||||
}
|
||||
else return v.type == tInt || v.type == tBool || v.type == tString ||
|
||||
v.type == tFloat;
|
||||
else return v.type() == nInt || v.type() == nBool || v.type() == nString ||
|
||||
v.type() == nFloat;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -201,7 +201,7 @@ Value * DrvInfo::queryMeta(const string & name)
|
|||
string DrvInfo::queryMetaString(const string & name)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v || v->type != tString) return "";
|
||||
if (!v || v->type() != nString) return "";
|
||||
return v->string.s;
|
||||
}
|
||||
|
||||
|
|
@ -210,12 +210,12 @@ NixInt DrvInfo::queryMetaInt(const string & name, NixInt def)
|
|||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type == tInt) return v->integer;
|
||||
if (v->type == tString) {
|
||||
if (v->type() == nInt) return v->integer;
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
integer meta fields. */
|
||||
NixInt n;
|
||||
if (string2Int(v->string.s, n)) return n;
|
||||
if (auto n = string2Int<NixInt>(v->string.s))
|
||||
return *n;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
|
@ -224,12 +224,12 @@ NixFloat DrvInfo::queryMetaFloat(const string & name, NixFloat def)
|
|||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type == tFloat) return v->fpoint;
|
||||
if (v->type == tString) {
|
||||
if (v->type() == nFloat) return v->fpoint;
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
float meta fields. */
|
||||
NixFloat n;
|
||||
if (string2Float(v->string.s, n)) return n;
|
||||
if (auto n = string2Float<NixFloat>(v->string.s))
|
||||
return *n;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
|
@ -239,8 +239,8 @@ bool DrvInfo::queryMetaBool(const string & name, bool def)
|
|||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type == tBool) return v->boolean;
|
||||
if (v->type == tString) {
|
||||
if (v->type() == nBool) return v->boolean;
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
Boolean meta fields. */
|
||||
if (strcmp(v->string.s, "true") == 0) return true;
|
||||
|
|
@ -331,7 +331,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
/* Process the expression. */
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
|
||||
|
||||
else if (v.type == tAttrs) {
|
||||
else if (v.type() == nAttrs) {
|
||||
|
||||
/* !!! undocumented hackery to support combining channels in
|
||||
nix-env.cc. */
|
||||
|
|
@ -353,7 +353,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
/* If the value of this attribute is itself a set,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (i->value->type == tAttrs) {
|
||||
if (i->value->type() == nAttrs) {
|
||||
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
||||
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
|
||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
|
|
@ -362,7 +362,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
|||
}
|
||||
}
|
||||
|
||||
else if (v.isList()) {
|
||||
else if (v.type() == nList) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
|
|
|
|||
|
|
@ -40,6 +40,6 @@ $(eval $(call install-file-in, $(d)/nix-expr.pc, $(prefix)/lib/pkgconfig, 0644))
|
|||
$(foreach i, $(wildcard src/libexpr/flake/*.hh), \
|
||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644)))
|
||||
|
||||
$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh
|
||||
$(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh
|
||||
|
||||
$(d)/flake/flake.cc: $(d)/flake/call-flake.nix.gen.hh
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ MakeError(ThrownError, AssertionError);
|
|||
MakeError(Abort, EvalError);
|
||||
MakeError(TypeError, EvalError);
|
||||
MakeError(UndefinedVarError, Error);
|
||||
MakeError(MissingArgumentError, Error);
|
||||
MakeError(RestrictedPathError, Error);
|
||||
|
||||
|
||||
|
|
@ -129,7 +130,7 @@ struct ExprPath : Expr
|
|||
{
|
||||
string s;
|
||||
Value v;
|
||||
ExprPath(const string & s) : s(s) { mkPathNoCopy(v, this->s.c_str()); };
|
||||
ExprPath(const string & s) : s(s) { v.mkPath(this->s.c_str()); };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -698,6 +698,10 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
|
|||
Path res = r.second + suffix;
|
||||
if (pathExists(res)) return canonPath(res);
|
||||
}
|
||||
|
||||
if (hasPrefix(path, "nix/"))
|
||||
return corepkgsPrefix + path.substr(4);
|
||||
|
||||
throw ThrownError({
|
||||
.hint = hintfmt(evalSettings.pureEval
|
||||
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||
|
|
|
|||
|
|
@ -164,7 +164,15 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
|||
state.forceFunction(**fun, pos);
|
||||
mkApp(v, **fun, w);
|
||||
state.forceAttrs(v, pos);
|
||||
} else {
|
||||
}
|
||||
|
||||
else if (path == corepkgsPrefix + "fetchurl.nix") {
|
||||
state.eval(state.parseExprFromString(
|
||||
#include "fetchurl.nix.gen.hh"
|
||||
, "/"), v);
|
||||
}
|
||||
|
||||
else {
|
||||
if (!vScope)
|
||||
state.evalFile(realPath, v);
|
||||
else {
|
||||
|
|
@ -356,24 +364,20 @@ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Valu
|
|||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
string t;
|
||||
switch (args[0]->type) {
|
||||
case tInt: t = "int"; break;
|
||||
case tBool: t = "bool"; break;
|
||||
case tString: t = "string"; break;
|
||||
case tPath: t = "path"; break;
|
||||
case tNull: t = "null"; break;
|
||||
case tAttrs: t = "set"; break;
|
||||
case tList1: case tList2: case tListN: t = "list"; break;
|
||||
case tLambda:
|
||||
case tPrimOp:
|
||||
case tPrimOpApp:
|
||||
t = "lambda";
|
||||
break;
|
||||
case tExternal:
|
||||
switch (args[0]->type()) {
|
||||
case nInt: t = "int"; break;
|
||||
case nBool: t = "bool"; break;
|
||||
case nString: t = "string"; break;
|
||||
case nPath: t = "path"; break;
|
||||
case nNull: t = "null"; break;
|
||||
case nAttrs: t = "set"; break;
|
||||
case nList: t = "list"; break;
|
||||
case nFunction: t = "lambda"; break;
|
||||
case nExternal:
|
||||
t = args[0]->external->typeOf();
|
||||
break;
|
||||
case tFloat: t = "float"; break;
|
||||
default: abort();
|
||||
case nFloat: t = "float"; break;
|
||||
case nThunk: abort();
|
||||
}
|
||||
mkString(v, state.symbols.create(t));
|
||||
}
|
||||
|
|
@ -393,7 +397,7 @@ static RegisterPrimOp primop_typeOf({
|
|||
static void prim_isNull(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->type == tNull);
|
||||
mkBool(v, args[0]->type() == nNull);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isNull({
|
||||
|
|
@ -413,18 +417,7 @@ static RegisterPrimOp primop_isNull({
|
|||
static void prim_isFunction(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
bool res;
|
||||
switch (args[0]->type) {
|
||||
case tLambda:
|
||||
case tPrimOp:
|
||||
case tPrimOpApp:
|
||||
res = true;
|
||||
break;
|
||||
default:
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
mkBool(v, res);
|
||||
mkBool(v, args[0]->type() == nFunction);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isFunction({
|
||||
|
|
@ -440,7 +433,7 @@ static RegisterPrimOp primop_isFunction({
|
|||
static void prim_isInt(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->type == tInt);
|
||||
mkBool(v, args[0]->type() == nInt);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isInt({
|
||||
|
|
@ -456,7 +449,7 @@ static RegisterPrimOp primop_isInt({
|
|||
static void prim_isFloat(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->type == tFloat);
|
||||
mkBool(v, args[0]->type() == nFloat);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isFloat({
|
||||
|
|
@ -472,7 +465,7 @@ static RegisterPrimOp primop_isFloat({
|
|||
static void prim_isString(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->type == tString);
|
||||
mkBool(v, args[0]->type() == nString);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isString({
|
||||
|
|
@ -488,7 +481,7 @@ static RegisterPrimOp primop_isString({
|
|||
static void prim_isBool(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->type == tBool);
|
||||
mkBool(v, args[0]->type() == nBool);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isBool({
|
||||
|
|
@ -504,7 +497,7 @@ static RegisterPrimOp primop_isBool({
|
|||
static void prim_isPath(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->type == tPath);
|
||||
mkBool(v, args[0]->type() == nPath);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isPath({
|
||||
|
|
@ -520,20 +513,20 @@ struct CompareValues
|
|||
{
|
||||
bool operator () (const Value * v1, const Value * v2) const
|
||||
{
|
||||
if (v1->type == tFloat && v2->type == tInt)
|
||||
if (v1->type() == nFloat && v2->type() == nInt)
|
||||
return v1->fpoint < v2->integer;
|
||||
if (v1->type == tInt && v2->type == tFloat)
|
||||
if (v1->type() == nInt && v2->type() == nFloat)
|
||||
return v1->integer < v2->fpoint;
|
||||
if (v1->type != v2->type)
|
||||
if (v1->type() != v2->type())
|
||||
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
|
||||
switch (v1->type) {
|
||||
case tInt:
|
||||
switch (v1->type()) {
|
||||
case nInt:
|
||||
return v1->integer < v2->integer;
|
||||
case tFloat:
|
||||
case nFloat:
|
||||
return v1->fpoint < v2->fpoint;
|
||||
case tString:
|
||||
case nString:
|
||||
return strcmp(v1->string.s, v2->string.s) < 0;
|
||||
case tPath:
|
||||
case nPath:
|
||||
return strcmp(v1->path, v2->path) < 0;
|
||||
default:
|
||||
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2));
|
||||
|
|
@ -777,7 +770,7 @@ static RegisterPrimOp primop_deepSeq({
|
|||
static void prim_trace(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type == tString)
|
||||
if (args[0]->type() == nString)
|
||||
printError("trace: %1%", args[0]->string.s);
|
||||
else
|
||||
printError("trace: %1%", *args[0]);
|
||||
|
|
@ -902,7 +895,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
|
||||
if (ignoreNulls) {
|
||||
state.forceValue(*i->value, pos);
|
||||
if (i->value->type == tNull) continue;
|
||||
if (i->value->type() == nNull) continue;
|
||||
}
|
||||
|
||||
if (i->name == state.sContentAddressed) {
|
||||
|
|
@ -1308,7 +1301,7 @@ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value
|
|||
{
|
||||
PathSet context;
|
||||
Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
|
||||
if (args[0]->type == tPath) mkPath(v, dir.c_str()); else mkString(v, dir, context);
|
||||
if (args[0]->type() == nPath) mkPath(v, dir.c_str()); else mkString(v, dir, context);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_dirOf({
|
||||
|
|
@ -1449,7 +1442,7 @@ static void prim_readDir(EvalState & state, const Pos & pos, Value * * args, Val
|
|||
Value * ent_val = state.allocAttr(v, state.symbols.create(ent.name));
|
||||
if (ent.type == DT_UNKNOWN)
|
||||
ent.type = getFileType(path + "/" + ent.name);
|
||||
mkStringNoCopy(*ent_val,
|
||||
ent_val->mkString(
|
||||
ent.type == DT_REG ? "regular" :
|
||||
ent.type == DT_DIR ? "directory" :
|
||||
ent.type == DT_LNK ? "symlink" :
|
||||
|
|
@ -1813,7 +1806,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args
|
|||
});
|
||||
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type != tLambda)
|
||||
if (args[0]->type() != nFunction)
|
||||
throw TypeError({
|
||||
.hint = hintfmt(
|
||||
"first argument in call to 'filterSource' is not a function but %1%",
|
||||
|
|
@ -2079,7 +2072,7 @@ static RegisterPrimOp primop_hasAttr({
|
|||
static void prim_isAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->type == tAttrs);
|
||||
mkBool(v, args[0]->type() == nAttrs);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isAttrs({
|
||||
|
|
@ -2259,11 +2252,11 @@ static RegisterPrimOp primop_catAttrs({
|
|||
static void prim_functionArgs(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
if (args[0]->type == tPrimOpApp || args[0]->type == tPrimOp) {
|
||||
if (args[0]->isPrimOpApp() || args[0]->isPrimOp()) {
|
||||
state.mkAttrs(v, 0);
|
||||
return;
|
||||
}
|
||||
if (args[0]->type != tLambda)
|
||||
if (!args[0]->isLambda())
|
||||
throw TypeError({
|
||||
.hint = hintfmt("'functionArgs' requires a function"),
|
||||
.errPos = pos
|
||||
|
|
@ -2342,7 +2335,7 @@ static RegisterPrimOp primop_mapAttrs({
|
|||
static void prim_isList(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
mkBool(v, args[0]->isList());
|
||||
mkBool(v, args[0]->type() == nList);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_isList({
|
||||
|
|
@ -2694,7 +2687,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
|
|||
auto comparator = [&](Value * a, Value * b) {
|
||||
/* Optimization: if the comparator is lessThan, bypass
|
||||
callFunction. */
|
||||
if (args[0]->type == tPrimOp && args[0]->primOp->fun == prim_lessThan)
|
||||
if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan)
|
||||
return CompareValues()(a, b);
|
||||
|
||||
Value vTmp1, vTmp2;
|
||||
|
|
@ -2836,7 +2829,7 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value &
|
|||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
state.forceValue(*args[1], pos);
|
||||
if (args[0]->type == tFloat || args[1]->type == tFloat)
|
||||
if (args[0]->type() == nFloat || args[1]->type() == nFloat)
|
||||
mkFloat(v, state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos));
|
||||
else
|
||||
mkInt(v, state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos));
|
||||
|
|
@ -2855,7 +2848,7 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value &
|
|||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
state.forceValue(*args[1], pos);
|
||||
if (args[0]->type == tFloat || args[1]->type == tFloat)
|
||||
if (args[0]->type() == nFloat || args[1]->type() == nFloat)
|
||||
mkFloat(v, state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos));
|
||||
else
|
||||
mkInt(v, state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos));
|
||||
|
|
@ -2874,7 +2867,7 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value &
|
|||
{
|
||||
state.forceValue(*args[0], pos);
|
||||
state.forceValue(*args[1], pos);
|
||||
if (args[0]->type == tFloat || args[1]->type == tFloat)
|
||||
if (args[0]->type() == nFloat || args[1]->type() == nFloat)
|
||||
mkFloat(v, state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos));
|
||||
else
|
||||
mkInt(v, state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos));
|
||||
|
|
@ -2901,7 +2894,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
|
|||
.errPos = pos
|
||||
});
|
||||
|
||||
if (args[0]->type == tFloat || args[1]->type == tFloat) {
|
||||
if (args[0]->type() == nFloat || args[1]->type() == nFloat) {
|
||||
mkFloat(v, state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos));
|
||||
} else {
|
||||
NixInt i1 = state.forceInt(*args[0], pos);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
|||
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type == tAttrs) {
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
|
|
|
|||
|
|
@ -85,26 +85,26 @@ static void fetchTree(
|
|||
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type == tAttrs) {
|
||||
if (args[0]->type() == nAttrs) {
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
fetchers::Attrs attrs;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
state.forceValue(*attr.value);
|
||||
if (attr.value->type == tPath || attr.value->type == tString)
|
||||
if (attr.value->type() == nPath || attr.value->type() == nString)
|
||||
addURI(
|
||||
state,
|
||||
attrs,
|
||||
attr.name,
|
||||
state.coerceToString(*attr.pos, *attr.value, context, false, false)
|
||||
);
|
||||
else if (attr.value->type == tString)
|
||||
else if (attr.value->type() == nString)
|
||||
addURI(state, attrs, attr.name, attr.value->string.s);
|
||||
else if (attr.value->type == tBool)
|
||||
else if (attr.value->type() == nBool)
|
||||
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
|
||||
else if (attr.value->type == tInt)
|
||||
attrs.emplace(attr.name, attr.value->integer);
|
||||
else if (attr.value->type() == nInt)
|
||||
attrs.emplace(attr.name, uint64_t(attr.value->integer));
|
||||
else
|
||||
throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
||||
attr.name, showType(*attr.value));
|
||||
|
|
@ -163,7 +163,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
|||
|
||||
state.forceValue(*args[0]);
|
||||
|
||||
if (args[0]->type == tAttrs) {
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
|
|
@ -324,6 +324,11 @@ static RegisterPrimOp primop_fetchGit({
|
|||
A Boolean parameter that specifies whether submodules should be
|
||||
checked out. 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
|
||||
`rev`s from the specified `ref` are supported).
|
||||
|
||||
Here are some examples of how to use `fetchGit`.
|
||||
|
||||
- To fetch a private repository over SSH:
|
||||
|
|
|
|||
|
|
@ -16,30 +16,30 @@ void printValueAsJSON(EvalState & state, bool strict,
|
|||
|
||||
if (strict) state.forceValue(v);
|
||||
|
||||
switch (v.type) {
|
||||
switch (v.type()) {
|
||||
|
||||
case tInt:
|
||||
case nInt:
|
||||
out.write(v.integer);
|
||||
break;
|
||||
|
||||
case tBool:
|
||||
case nBool:
|
||||
out.write(v.boolean);
|
||||
break;
|
||||
|
||||
case tString:
|
||||
case nString:
|
||||
copyContext(v, context);
|
||||
out.write(v.string.s);
|
||||
break;
|
||||
|
||||
case tPath:
|
||||
case nPath:
|
||||
out.write(state.copyPathToStore(context, v.path));
|
||||
break;
|
||||
|
||||
case tNull:
|
||||
case nNull:
|
||||
out.write(nullptr);
|
||||
break;
|
||||
|
||||
case tAttrs: {
|
||||
case nAttrs: {
|
||||
auto maybeString = state.tryAttrsToString(noPos, v, context, false, false);
|
||||
if (maybeString) {
|
||||
out.write(*maybeString);
|
||||
|
|
@ -61,7 +61,7 @@ void printValueAsJSON(EvalState & state, bool strict,
|
|||
break;
|
||||
}
|
||||
|
||||
case tList1: case tList2: case tListN: {
|
||||
case nList: {
|
||||
auto list(out.list());
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
auto placeholder(list.placeholder());
|
||||
|
|
@ -70,15 +70,18 @@ void printValueAsJSON(EvalState & state, bool strict,
|
|||
break;
|
||||
}
|
||||
|
||||
case tExternal:
|
||||
case nExternal:
|
||||
v.external->printValueAsJSON(state, strict, out, context);
|
||||
break;
|
||||
|
||||
case tFloat:
|
||||
case nFloat:
|
||||
out.write(v.fpoint);
|
||||
break;
|
||||
|
||||
default:
|
||||
case nThunk:
|
||||
throw TypeError("cannot convert %1% to JSON", showType(v));
|
||||
|
||||
case nFunction:
|
||||
throw TypeError("cannot convert %1% to JSON", showType(v));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,31 +58,31 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
|
||||
if (strict) state.forceValue(v);
|
||||
|
||||
switch (v.type) {
|
||||
switch (v.type()) {
|
||||
|
||||
case tInt:
|
||||
case nInt:
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % v.integer).str()));
|
||||
break;
|
||||
|
||||
case tBool:
|
||||
case nBool:
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false"));
|
||||
break;
|
||||
|
||||
case tString:
|
||||
case nString:
|
||||
/* !!! show the context? */
|
||||
copyContext(v, context);
|
||||
doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
|
||||
break;
|
||||
|
||||
case tPath:
|
||||
case nPath:
|
||||
doc.writeEmptyElement("path", singletonAttrs("value", v.path));
|
||||
break;
|
||||
|
||||
case tNull:
|
||||
case nNull:
|
||||
doc.writeEmptyElement("null");
|
||||
break;
|
||||
|
||||
case tAttrs:
|
||||
case nAttrs:
|
||||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
|
||||
|
|
@ -92,14 +92,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
a = v.attrs->find(state.sDrvPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (strict) state.forceValue(*a->value);
|
||||
if (a->value->type == tString)
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
||||
}
|
||||
|
||||
a = v.attrs->find(state.sOutPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (strict) state.forceValue(*a->value);
|
||||
if (a->value->type == tString)
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["outPath"] = a->value->string.s;
|
||||
}
|
||||
|
||||
|
|
@ -118,14 +118,19 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
|
||||
break;
|
||||
|
||||
case tList1: case tList2: case tListN: {
|
||||
case nList: {
|
||||
XMLOpenElement _(doc, "list");
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
|
||||
break;
|
||||
}
|
||||
|
||||
case tLambda: {
|
||||
case nFunction: {
|
||||
if (!v.isLambda()) {
|
||||
// FIXME: Serialize primops and primopapps
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
break;
|
||||
}
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
|
@ -143,15 +148,15 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
break;
|
||||
}
|
||||
|
||||
case tExternal:
|
||||
case nExternal:
|
||||
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
|
||||
break;
|
||||
|
||||
case tFloat:
|
||||
case nFloat:
|
||||
doc.writeEmptyElement("float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
|
||||
break;
|
||||
|
||||
default:
|
||||
case nThunk:
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,24 @@ typedef enum {
|
|||
tPrimOpApp,
|
||||
tExternal,
|
||||
tFloat
|
||||
} ValueType;
|
||||
} InternalType;
|
||||
|
||||
// This type abstracts over all actual value types in the language,
|
||||
// grouping together implementation details like tList*, different function
|
||||
// types, and types in non-normal form (so thunks and co.)
|
||||
typedef enum {
|
||||
nThunk,
|
||||
nInt,
|
||||
nFloat,
|
||||
nBool,
|
||||
nString,
|
||||
nPath,
|
||||
nNull,
|
||||
nAttrs,
|
||||
nList,
|
||||
nFunction,
|
||||
nExternal
|
||||
} ValueType;
|
||||
|
||||
class Bindings;
|
||||
struct Env;
|
||||
|
|
@ -90,7 +106,28 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v);
|
|||
|
||||
struct Value
|
||||
{
|
||||
ValueType type;
|
||||
private:
|
||||
InternalType internalType;
|
||||
|
||||
friend std::string showType(const Value & v);
|
||||
friend void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v);
|
||||
|
||||
public:
|
||||
|
||||
// Functions needed to distinguish the type
|
||||
// These should be removed eventually, by putting the functionality that's
|
||||
// needed by callers into methods of this type
|
||||
|
||||
// type() == nThunk
|
||||
inline bool isThunk() const { return internalType == tThunk; };
|
||||
inline bool isApp() const { return internalType == tApp; };
|
||||
inline bool isBlackhole() const { return internalType == tBlackhole; };
|
||||
|
||||
// type() == nFunction
|
||||
inline bool isLambda() const { return internalType == tLambda; };
|
||||
inline bool isPrimOp() const { return internalType == tPrimOp; };
|
||||
inline bool isPrimOpApp() const { return internalType == tPrimOpApp; };
|
||||
|
||||
union
|
||||
{
|
||||
NixInt integer;
|
||||
|
|
@ -147,24 +184,161 @@ struct Value
|
|||
NixFloat fpoint;
|
||||
};
|
||||
|
||||
// Returns the normal type of a Value. This only returns nThunk if the
|
||||
// Value hasn't been forceValue'd
|
||||
inline ValueType type() const
|
||||
{
|
||||
switch (internalType) {
|
||||
case tInt: return nInt;
|
||||
case tBool: return nBool;
|
||||
case tString: return nString;
|
||||
case tPath: return nPath;
|
||||
case tNull: return nNull;
|
||||
case tAttrs: return nAttrs;
|
||||
case tList1: case tList2: case tListN: return nList;
|
||||
case tLambda: case tPrimOp: case tPrimOpApp: return nFunction;
|
||||
case tExternal: return nExternal;
|
||||
case tFloat: return nFloat;
|
||||
case tThunk: case tApp: case tBlackhole: return nThunk;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
/* After overwriting an app node, be sure to clear pointers in the
|
||||
Value to ensure that the target isn't kept alive unnecessarily. */
|
||||
inline void clearValue()
|
||||
{
|
||||
app.left = app.right = 0;
|
||||
}
|
||||
|
||||
inline void mkInt(NixInt n)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tInt;
|
||||
integer = n;
|
||||
}
|
||||
|
||||
inline void mkBool(bool b)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tBool;
|
||||
boolean = b;
|
||||
}
|
||||
|
||||
inline void mkString(const char * s, const char * * context = 0)
|
||||
{
|
||||
internalType = tString;
|
||||
string.s = s;
|
||||
string.context = context;
|
||||
}
|
||||
|
||||
inline void mkPath(const char * s)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tPath;
|
||||
path = s;
|
||||
}
|
||||
|
||||
inline void mkNull()
|
||||
{
|
||||
clearValue();
|
||||
internalType = tNull;
|
||||
}
|
||||
|
||||
inline void mkAttrs(Bindings * a)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tAttrs;
|
||||
attrs = a;
|
||||
}
|
||||
|
||||
inline void mkList(size_t size)
|
||||
{
|
||||
clearValue();
|
||||
if (size == 1)
|
||||
internalType = tList1;
|
||||
else if (size == 2)
|
||||
internalType = tList2;
|
||||
else {
|
||||
internalType = tListN;
|
||||
bigList.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
inline void mkThunk(Env * e, Expr * ex)
|
||||
{
|
||||
internalType = tThunk;
|
||||
thunk.env = e;
|
||||
thunk.expr = ex;
|
||||
}
|
||||
|
||||
inline void mkApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tApp;
|
||||
app.left = l;
|
||||
app.right = r;
|
||||
}
|
||||
|
||||
inline void mkLambda(Env * e, ExprLambda * f)
|
||||
{
|
||||
internalType = tLambda;
|
||||
lambda.env = e;
|
||||
lambda.fun = f;
|
||||
}
|
||||
|
||||
inline void mkBlackhole()
|
||||
{
|
||||
internalType = tBlackhole;
|
||||
// Value will be overridden anyways
|
||||
}
|
||||
|
||||
inline void mkPrimOp(PrimOp * p)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tPrimOp;
|
||||
primOp = p;
|
||||
}
|
||||
|
||||
|
||||
inline void mkPrimOpApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tPrimOpApp;
|
||||
app.left = l;
|
||||
app.right = r;
|
||||
}
|
||||
|
||||
inline void mkExternal(ExternalValueBase * e)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tExternal;
|
||||
external = e;
|
||||
}
|
||||
|
||||
inline void mkFloat(NixFloat n)
|
||||
{
|
||||
clearValue();
|
||||
internalType = tFloat;
|
||||
fpoint = n;
|
||||
}
|
||||
|
||||
bool isList() const
|
||||
{
|
||||
return type == tList1 || type == tList2 || type == tListN;
|
||||
return internalType == tList1 || internalType == tList2 || internalType == tListN;
|
||||
}
|
||||
|
||||
Value * * listElems()
|
||||
{
|
||||
return type == tList1 || type == tList2 ? smallList : bigList.elems;
|
||||
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
|
||||
}
|
||||
|
||||
const Value * const * listElems() const
|
||||
{
|
||||
return type == tList1 || type == tList2 ? smallList : bigList.elems;
|
||||
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
|
||||
}
|
||||
|
||||
size_t listSize() const
|
||||
{
|
||||
return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size;
|
||||
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
|
||||
}
|
||||
|
||||
/* Check whether forcing this value requires a trivial amount of
|
||||
|
|
@ -176,86 +350,42 @@ struct Value
|
|||
};
|
||||
|
||||
|
||||
/* After overwriting an app node, be sure to clear pointers in the
|
||||
Value to ensure that the target isn't kept alive unnecessarily. */
|
||||
static inline void clearValue(Value & v)
|
||||
{
|
||||
v.app.left = v.app.right = 0;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Remove these static functions, replace call sites with v.mk* instead
|
||||
static inline void mkInt(Value & v, NixInt n)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tInt;
|
||||
v.integer = n;
|
||||
v.mkInt(n);
|
||||
}
|
||||
|
||||
|
||||
static inline void mkFloat(Value & v, NixFloat n)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tFloat;
|
||||
v.fpoint = n;
|
||||
v.mkFloat(n);
|
||||
}
|
||||
|
||||
|
||||
static inline void mkBool(Value & v, bool b)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tBool;
|
||||
v.boolean = b;
|
||||
v.mkBool(b);
|
||||
}
|
||||
|
||||
|
||||
static inline void mkNull(Value & v)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tNull;
|
||||
v.mkNull();
|
||||
}
|
||||
|
||||
|
||||
static inline void mkApp(Value & v, Value & left, Value & right)
|
||||
{
|
||||
v.type = tApp;
|
||||
v.app.left = &left;
|
||||
v.app.right = &right;
|
||||
v.mkApp(&left, &right);
|
||||
}
|
||||
|
||||
|
||||
static inline void mkPrimOpApp(Value & v, Value & left, Value & right)
|
||||
{
|
||||
v.type = tPrimOpApp;
|
||||
v.app.left = &left;
|
||||
v.app.right = &right;
|
||||
}
|
||||
|
||||
|
||||
static inline void mkStringNoCopy(Value & v, const char * s)
|
||||
{
|
||||
v.type = tString;
|
||||
v.string.s = s;
|
||||
v.string.context = 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void mkString(Value & v, const Symbol & s)
|
||||
{
|
||||
mkStringNoCopy(v, ((const string &) s).c_str());
|
||||
v.mkString(((const string &) s).c_str());
|
||||
}
|
||||
|
||||
|
||||
void mkString(Value & v, const char * s);
|
||||
|
||||
|
||||
static inline void mkPathNoCopy(Value & v, const char * s)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tPath;
|
||||
v.path = s;
|
||||
}
|
||||
|
||||
|
||||
void mkPath(Value & v, const char * s);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ Attrs jsonToAttrs(const nlohmann::json & json)
|
|||
|
||||
for (auto & i : json.items()) {
|
||||
if (i.value().is_number())
|
||||
attrs.emplace(i.key(), i.value().get<int64_t>());
|
||||
attrs.emplace(i.key(), i.value().get<uint64_t>());
|
||||
else if (i.value().is_string())
|
||||
attrs.emplace(i.key(), i.value().get<std::string>());
|
||||
else if (i.value().is_boolean())
|
||||
attrs.emplace(i.key(), i.value().get<bool>());
|
||||
attrs.emplace(i.key(), Explicit<bool> { i.value().get<bool>() });
|
||||
else
|
||||
throw Error("unsupported input attribute type in lock file");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,12 +59,13 @@ struct GitInputScheme : InputScheme
|
|||
if (maybeGetStrAttr(attrs, "type") != "git") return {};
|
||||
|
||||
for (auto & [name, value] : attrs)
|
||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash")
|
||||
if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "shallow" && name != "submodules" && name != "lastModified" && name != "revCount" && name != "narHash" && name != "allRefs")
|
||||
throw Error("unsupported Git input attribute '%s'", name);
|
||||
|
||||
parseURL(getStrAttr(attrs, "url"));
|
||||
maybeGetBoolAttr(attrs, "shallow");
|
||||
maybeGetBoolAttr(attrs, "submodules");
|
||||
maybeGetBoolAttr(attrs, "allRefs");
|
||||
|
||||
if (auto ref = maybeGetStrAttr(attrs, "ref")) {
|
||||
if (std::regex_search(*ref, badGitRefRegex))
|
||||
|
|
@ -169,10 +170,12 @@ struct GitInputScheme : InputScheme
|
|||
|
||||
bool shallow = maybeGetBoolAttr(input.attrs, "shallow").value_or(false);
|
||||
bool submodules = maybeGetBoolAttr(input.attrs, "submodules").value_or(false);
|
||||
bool allRefs = maybeGetBoolAttr(input.attrs, "allRefs").value_or(false);
|
||||
|
||||
std::string cacheType = "git";
|
||||
if (shallow) cacheType += "-shallow";
|
||||
if (submodules) cacheType += "-submodules";
|
||||
if (allRefs) cacheType += "-all-refs";
|
||||
|
||||
auto getImmutableAttrs = [&]()
|
||||
{
|
||||
|
|
@ -338,11 +341,15 @@ struct GitInputScheme : InputScheme
|
|||
}
|
||||
}
|
||||
} else {
|
||||
/* If the local ref is older than ‘tarball-ttl’ seconds, do a
|
||||
git fetch to update the local ref to the remote ref. */
|
||||
struct stat st;
|
||||
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
|
||||
(uint64_t) st.st_mtime + settings.tarballTtl <= (uint64_t) now;
|
||||
if (allRefs) {
|
||||
doFetch = true;
|
||||
} else {
|
||||
/* If the local ref is older than ‘tarball-ttl’ seconds, do a
|
||||
git fetch to update the local ref to the remote ref. */
|
||||
struct stat st;
|
||||
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
|
||||
(uint64_t) st.st_mtime + settings.tarballTtl <= (uint64_t) now;
|
||||
}
|
||||
}
|
||||
|
||||
if (doFetch) {
|
||||
|
|
@ -352,9 +359,11 @@ struct GitInputScheme : InputScheme
|
|||
// we're using --quiet for now. Should process its stderr.
|
||||
try {
|
||||
auto ref = input.getRef();
|
||||
auto fetchRef = ref->compare(0, 5, "refs/") == 0
|
||||
? *ref
|
||||
: "refs/heads/" + *ref;
|
||||
auto fetchRef = allRefs
|
||||
? "refs/*"
|
||||
: ref->compare(0, 5, "refs/") == 0
|
||||
? *ref
|
||||
: "refs/heads/" + *ref;
|
||||
runProgram("git", true, { "-C", repoDir, "fetch", "--quiet", "--force", "--", actualUrl, fmt("%s:%s", fetchRef, fetchRef) });
|
||||
} catch (Error & e) {
|
||||
if (!pathExists(localRefFile)) throw;
|
||||
|
|
@ -392,6 +401,28 @@ struct GitInputScheme : InputScheme
|
|||
AutoDelete delTmpDir(tmpDir, true);
|
||||
PathFilter filter = defaultPathFilter;
|
||||
|
||||
RunOptions checkCommitOpts(
|
||||
"git",
|
||||
{ "-C", repoDir, "cat-file", "commit", input.getRev()->gitRev() }
|
||||
);
|
||||
checkCommitOpts.searchPath = true;
|
||||
checkCommitOpts.mergeStderrToStdout = true;
|
||||
|
||||
auto result = runProgram(checkCommitOpts);
|
||||
if (WEXITSTATUS(result.first) == 128
|
||||
&& result.second.find("bad file") != std::string::npos
|
||||
) {
|
||||
throw Error(
|
||||
"Cannot find Git revision '%s' in ref '%s' of repository '%s'! "
|
||||
"Please make sure that the " ANSI_BOLD "rev" ANSI_NORMAL " exists on the "
|
||||
ANSI_BOLD "ref" ANSI_NORMAL " you've specified or add " ANSI_BOLD
|
||||
"allRefs = true;" ANSI_NORMAL " to " ANSI_BOLD "fetchGit" ANSI_NORMAL ".",
|
||||
input.getRev()->gitRev(),
|
||||
*input.getRef(),
|
||||
actualUrl
|
||||
);
|
||||
}
|
||||
|
||||
if (submodules) {
|
||||
Path tmpGitDir = createTempDir();
|
||||
AutoDelete delTmpGitDir(tmpGitDir, true);
|
||||
|
|
|
|||
|
|
@ -37,15 +37,29 @@ struct GitArchiveInputScheme : InputScheme
|
|||
std::optional<std::string> ref;
|
||||
std::optional<std::string> host_url;
|
||||
|
||||
if (path.size() == 2) {
|
||||
} else if (path.size() == 3) {
|
||||
auto size = path.size();
|
||||
if (size == 3) {
|
||||
if (std::regex_match(path[2], revRegex))
|
||||
rev = Hash::parseAny(path[2], htSHA1);
|
||||
else if (std::regex_match(path[2], refRegex))
|
||||
ref = path[2];
|
||||
else
|
||||
throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]);
|
||||
} else
|
||||
} else if (size > 3) {
|
||||
std::string rs;
|
||||
for (auto i = std::next(path.begin(), 2); i != path.end(); i++) {
|
||||
rs += *i;
|
||||
if (std::next(i) != path.end()) {
|
||||
rs += "/";
|
||||
}
|
||||
}
|
||||
|
||||
if (std::regex_match(rs, refRegex)) {
|
||||
ref = rs;
|
||||
} else {
|
||||
throw BadURL("in URL '%s', '%s' is not a branch/tag name", url.url, rs);
|
||||
}
|
||||
} else if (size < 2)
|
||||
throw BadURL("URL '%s' is invalid", url.url);
|
||||
|
||||
for (auto &[name, value] : url.query) {
|
||||
|
|
@ -195,14 +209,14 @@ struct GitArchiveInputScheme : InputScheme
|
|||
|
||||
auto [tree, lastModified] = downloadTarball(store, url.url, "source", true, url.headers);
|
||||
|
||||
input.attrs.insert_or_assign("lastModified", lastModified);
|
||||
input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
|
||||
|
||||
getCache()->add(
|
||||
store,
|
||||
immutableAttrs,
|
||||
{
|
||||
{"rev", rev->gitRev()},
|
||||
{"lastModified", lastModified}
|
||||
{"lastModified", uint64_t(lastModified)}
|
||||
},
|
||||
tree.storePath,
|
||||
true);
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ struct MercurialInputScheme : InputScheme
|
|||
|
||||
Attrs infoAttrs({
|
||||
{"rev", input.getRev()->gitRev()},
|
||||
{"revCount", (int64_t) revCount},
|
||||
{"revCount", (uint64_t) revCount},
|
||||
});
|
||||
|
||||
if (!_input.getRev())
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ struct PathInputScheme : InputScheme
|
|||
if (name == "rev" || name == "narHash")
|
||||
input.attrs.insert_or_assign(name, value);
|
||||
else if (name == "revCount" || name == "lastModified") {
|
||||
uint64_t n;
|
||||
if (!string2Int(value, n))
|
||||
if (auto n = string2Int<uint64_t>(value))
|
||||
input.attrs.insert_or_assign(name, *n);
|
||||
else
|
||||
throw Error("path URL '%s' has invalid parameter '%s'", url.to_string(), name);
|
||||
input.attrs.insert_or_assign(name, n);
|
||||
}
|
||||
else
|
||||
throw Error("path URL '%s' has unsupported parameter '%s'", url.to_string(), name);
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ std::pair<Tree, time_t> downloadTarball(
|
|||
}
|
||||
|
||||
Attrs infoAttrs({
|
||||
{"lastModified", lastModified},
|
||||
{"lastModified", uint64_t(lastModified)},
|
||||
{"etag", res.etag},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -10,25 +10,25 @@ MixCommonArgs::MixCommonArgs(const string & programName)
|
|||
addFlag({
|
||||
.longName = "verbose",
|
||||
.shortName = 'v',
|
||||
.description = "increase verbosity level",
|
||||
.description = "Increase the logging verbosity level.",
|
||||
.handler = {[]() { verbosity = (Verbosity) (verbosity + 1); }},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "quiet",
|
||||
.description = "decrease verbosity level",
|
||||
.description = "Decrease the logging verbosity level.",
|
||||
.handler = {[]() { verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; }},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "debug",
|
||||
.description = "enable debug output",
|
||||
.description = "Set the logging verbosity level to 'debug'.",
|
||||
.handler = {[]() { verbosity = lvlDebug; }},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "option",
|
||||
.description = "set a Nix configuration option (overriding `nix.conf`)",
|
||||
.description = "Set the Nix configuration setting *name* to *value* (overriding `nix.conf`).",
|
||||
.labels = {"name", "value"},
|
||||
.handler = {[](std::string name, std::string value) {
|
||||
try {
|
||||
|
|
@ -51,8 +51,7 @@ MixCommonArgs::MixCommonArgs(const string & programName)
|
|||
|
||||
addFlag({
|
||||
.longName = "log-format",
|
||||
.description = "format of log output; `raw`, `internal-json`, `bar` "
|
||||
"or `bar-with-logs`",
|
||||
.description = "Set the format of log output; one of `raw`, `internal-json`, `bar` or `bar-with-logs`.",
|
||||
.labels = {"format"},
|
||||
.handler = {[](std::string format) { setLogFormat(format); }},
|
||||
});
|
||||
|
|
@ -60,7 +59,7 @@ MixCommonArgs::MixCommonArgs(const string & programName)
|
|||
addFlag({
|
||||
.longName = "max-jobs",
|
||||
.shortName = 'j',
|
||||
.description = "maximum number of parallel builds",
|
||||
.description = "The maximum number of parallel builds.",
|
||||
.labels = Strings{"jobs"},
|
||||
.handler = {[=](std::string s) {
|
||||
settings.set("max-jobs", s);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ struct MixDryRun : virtual Args
|
|||
|
||||
MixDryRun()
|
||||
{
|
||||
mkFlag(0, "dry-run", "show what this command would do without doing it", &dryRun);
|
||||
mkFlag(0, "dry-run", "Show what this command would do without doing it.", &dryRun);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ struct MixJSON : virtual Args
|
|||
|
||||
MixJSON()
|
||||
{
|
||||
mkFlag(0, "json", "produce JSON output", &json);
|
||||
mkFlag(0, "json", "Produce output in JSON format, suitable for consumption by another program.", &json);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -126,6 +128,9 @@ void initNix()
|
|||
CRYPTO_set_locking_callback(opensslLockCallback);
|
||||
#endif
|
||||
|
||||
if (sodium_init() == -1)
|
||||
throw Error("could not initialise libsodium");
|
||||
|
||||
loadConfFile();
|
||||
|
||||
startSignalHandlerThread();
|
||||
|
|
@ -181,50 +186,58 @@ LegacyArgs::LegacyArgs(const std::string & programName,
|
|||
addFlag({
|
||||
.longName = "no-build-output",
|
||||
.shortName = 'Q',
|
||||
.description = "do not show build output",
|
||||
.description = "Do not show build output.",
|
||||
.handler = {[&]() {setLogFormat(LogFormat::raw); }},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "keep-failed",
|
||||
.shortName ='K',
|
||||
.description = "keep temporary directories of failed builds",
|
||||
.description = "Keep temporary directories of failed builds.",
|
||||
.handler = {&(bool&) settings.keepFailed, true},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "keep-going",
|
||||
.shortName ='k',
|
||||
.description = "keep going after a build fails",
|
||||
.description = "Keep going after a build fails.",
|
||||
.handler = {&(bool&) settings.keepGoing, true},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "fallback",
|
||||
.description = "build from source if substitution fails",
|
||||
.description = "Build from source if substitution fails.",
|
||||
.handler = {&(bool&) settings.tryFallback, true},
|
||||
});
|
||||
|
||||
auto intSettingAlias = [&](char shortName, const std::string & longName,
|
||||
const std::string & description, const std::string & dest) {
|
||||
mkFlag<unsigned int>(shortName, longName, description, [=](unsigned int n) {
|
||||
settings.set(dest, std::to_string(n));
|
||||
const std::string & description, const std::string & dest)
|
||||
{
|
||||
addFlag({
|
||||
.longName = longName,
|
||||
.shortName = shortName,
|
||||
.description = description,
|
||||
.labels = {"n"},
|
||||
.handler = {[=](std::string s) {
|
||||
auto n = string2IntWithUnitPrefix<uint64_t>(s);
|
||||
settings.set(dest, std::to_string(n));
|
||||
}}
|
||||
});
|
||||
};
|
||||
|
||||
intSettingAlias(0, "cores", "maximum number of CPU cores to use inside a build", "cores");
|
||||
intSettingAlias(0, "max-silent-time", "number of seconds of silence before a build is killed", "max-silent-time");
|
||||
intSettingAlias(0, "timeout", "number of seconds before a build is killed", "timeout");
|
||||
intSettingAlias(0, "cores", "Maximum number of CPU cores to use inside a build.", "cores");
|
||||
intSettingAlias(0, "max-silent-time", "Number of seconds of silence before a build is killed.", "max-silent-time");
|
||||
intSettingAlias(0, "timeout", "Number of seconds before a build is killed.", "timeout");
|
||||
|
||||
mkFlag(0, "readonly-mode", "do not write to the Nix store",
|
||||
mkFlag(0, "readonly-mode", "Do not write to the Nix store.",
|
||||
&settings.readOnlyMode);
|
||||
|
||||
mkFlag(0, "no-gc-warning", "disable warning about not using '--add-root'",
|
||||
mkFlag(0, "no-gc-warning", "Disable warnings about not using `--add-root`.",
|
||||
&gcWarning, false);
|
||||
|
||||
addFlag({
|
||||
.longName = "store",
|
||||
.description = "URI of the Nix store to use",
|
||||
.description = "The URL of the Nix store to use.",
|
||||
.labels = {"store-uri"},
|
||||
.handler = {&(std::string&) settings.storeUri},
|
||||
});
|
||||
|
|
@ -274,9 +287,7 @@ void printVersion(const string & programName)
|
|||
#if HAVE_BOEHMGC
|
||||
cfg.push_back("gc");
|
||||
#endif
|
||||
#if HAVE_SODIUM
|
||||
cfg.push_back("signed-caches");
|
||||
#endif
|
||||
std::cout << "System type: " << settings.thisSystem << "\n";
|
||||
std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n";
|
||||
std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n";
|
||||
|
|
|
|||
|
|
@ -57,23 +57,7 @@ template<class N> N getIntArg(const string & opt,
|
|||
{
|
||||
++i;
|
||||
if (i == end) throw UsageError("'%1%' requires an argument", opt);
|
||||
string s = *i;
|
||||
N multiplier = 1;
|
||||
if (allowUnit && !s.empty()) {
|
||||
char u = std::toupper(*s.rbegin());
|
||||
if (std::isalpha(u)) {
|
||||
if (u == 'K') multiplier = 1ULL << 10;
|
||||
else if (u == 'M') multiplier = 1ULL << 20;
|
||||
else if (u == 'G') multiplier = 1ULL << 30;
|
||||
else if (u == 'T') multiplier = 1ULL << 40;
|
||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||
s.resize(s.size() - 1);
|
||||
}
|
||||
}
|
||||
N n;
|
||||
if (!string2Int(s, n))
|
||||
throw UsageError("'%1%' requires an integer argument", opt);
|
||||
return n * multiplier;
|
||||
return string2IntWithUnitPrefix<N>(*i);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
|
|||
"enable multi-threading compression, available for xz only currently"};
|
||||
};
|
||||
|
||||
class BinaryCacheStore : public Store, public virtual BinaryCacheStoreConfig
|
||||
class BinaryCacheStore : public virtual BinaryCacheStoreConfig, public virtual Store
|
||||
{
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@
|
|||
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
|
||||
#endif
|
||||
|
||||
#if __APPLE__
|
||||
#include <spawn.h>
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
|
|
@ -675,13 +680,9 @@ void DerivationGoal::tryToBuild()
|
|||
}
|
||||
|
||||
void DerivationGoal::tryLocalBuild() {
|
||||
bool buildLocally = buildMode != bmNormal || parsedDrv->willBuildLocally(worker.store);
|
||||
|
||||
/* Make sure that we are allowed to start a build. If this
|
||||
derivation prefers to be done locally, do it even if
|
||||
maxBuildJobs is 0. */
|
||||
/* Make sure that we are allowed to start a build. */
|
||||
unsigned int curBuilds = worker.getNrLocalBuilds();
|
||||
if (curBuilds >= settings.maxBuildJobs && !(buildLocally && curBuilds == 0)) {
|
||||
if (curBuilds >= settings.maxBuildJobs) {
|
||||
worker.waitForBuildSlot(shared_from_this());
|
||||
outputLocks.unlock();
|
||||
return;
|
||||
|
|
@ -1695,12 +1696,10 @@ void DerivationGoal::startBuilder()
|
|||
userNamespaceSync.writeSide = -1;
|
||||
});
|
||||
|
||||
pid_t tmp;
|
||||
auto ss = tokenizeString<std::vector<std::string>>(readLine(builderOut.readSide.get()));
|
||||
assert(ss.size() == 2);
|
||||
usingUserNamespace = ss[0] == "1";
|
||||
if (!string2Int<pid_t>(ss[1], tmp)) abort();
|
||||
pid = tmp;
|
||||
pid = string2Int<pid_t>(ss[1]).value();
|
||||
|
||||
if (usingUserNamespace) {
|
||||
/* Set the UID/GID mapping of the builder's user namespace
|
||||
|
|
@ -1985,7 +1984,7 @@ void DerivationGoal::writeStructuredAttrs()
|
|||
chownToBuilder(tmpDir + "/.attrs.sh");
|
||||
}
|
||||
|
||||
struct RestrictedStoreConfig : LocalFSStoreConfig
|
||||
struct RestrictedStoreConfig : virtual LocalFSStoreConfig
|
||||
{
|
||||
using LocalFSStoreConfig::LocalFSStoreConfig;
|
||||
const std::string name() { return "Restricted Store"; }
|
||||
|
|
@ -1994,14 +1993,19 @@ struct RestrictedStoreConfig : LocalFSStoreConfig
|
|||
/* A wrapper around LocalStore that only allows building/querying of
|
||||
paths that are in the input closures of the build or were added via
|
||||
recursive Nix calls. */
|
||||
struct RestrictedStore : public LocalFSStore, public virtual RestrictedStoreConfig
|
||||
struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore
|
||||
{
|
||||
ref<LocalStore> next;
|
||||
|
||||
DerivationGoal & goal;
|
||||
|
||||
RestrictedStore(const Params & params, ref<LocalStore> next, DerivationGoal & goal)
|
||||
: StoreConfig(params), Store(params), LocalFSStore(params), next(next), goal(goal)
|
||||
: StoreConfig(params)
|
||||
, LocalFSStoreConfig(params)
|
||||
, RestrictedStoreConfig(params)
|
||||
, Store(params)
|
||||
, LocalFSStore(params)
|
||||
, next(next), goal(goal)
|
||||
{ }
|
||||
|
||||
Path getRealStoreDir() override
|
||||
|
|
@ -2852,7 +2856,31 @@ void DerivationGoal::runChild()
|
|||
}
|
||||
}
|
||||
|
||||
#if __APPLE__
|
||||
posix_spawnattr_t attrp;
|
||||
|
||||
if (posix_spawnattr_init(&attrp))
|
||||
throw SysError("failed to initialize builder");
|
||||
|
||||
if (posix_spawnattr_setflags(&attrp, POSIX_SPAWN_SETEXEC))
|
||||
throw SysError("failed to initialize builder");
|
||||
|
||||
if (drv->platform == "aarch64-darwin") {
|
||||
// Unset kern.curproc_arch_affinity so we can escape Rosetta
|
||||
int affinity = 0;
|
||||
sysctlbyname("kern.curproc_arch_affinity", NULL, NULL, &affinity, sizeof(affinity));
|
||||
|
||||
cpu_type_t cpu = CPU_TYPE_ARM64;
|
||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||
} else if (drv->platform == "x86_64-darwin") {
|
||||
cpu_type_t cpu = CPU_TYPE_X86_64;
|
||||
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
|
||||
}
|
||||
|
||||
posix_spawn(NULL, builder, NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||
#else
|
||||
execve(builder, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||
#endif
|
||||
|
||||
throw SysError("executing '%1%'", drv->builder);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,19 @@
|
|||
#include "util.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
#if HAVE_SODIUM
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
static std::pair<std::string, std::string> split(const string & s)
|
||||
static std::pair<std::string_view, std::string_view> split(std::string_view s)
|
||||
{
|
||||
size_t colon = s.find(':');
|
||||
if (colon == std::string::npos || colon == 0)
|
||||
return {"", ""};
|
||||
return {std::string(s, 0, colon), std::string(s, colon + 1)};
|
||||
return {s.substr(0, colon), s.substr(colon + 1)};
|
||||
}
|
||||
|
||||
Key::Key(const string & s)
|
||||
Key::Key(std::string_view s)
|
||||
{
|
||||
auto ss = split(s);
|
||||
|
||||
|
|
@ -29,62 +27,57 @@ Key::Key(const string & s)
|
|||
key = base64Decode(key);
|
||||
}
|
||||
|
||||
SecretKey::SecretKey(const string & s)
|
||||
std::string Key::to_string() const
|
||||
{
|
||||
return name + ":" + base64Encode(key);
|
||||
}
|
||||
|
||||
SecretKey::SecretKey(std::string_view s)
|
||||
: Key(s)
|
||||
{
|
||||
#if HAVE_SODIUM
|
||||
if (key.size() != crypto_sign_SECRETKEYBYTES)
|
||||
throw Error("secret key is not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !HAVE_SODIUM
|
||||
[[noreturn]] static void noSodium()
|
||||
std::string SecretKey::signDetached(std::string_view data) const
|
||||
{
|
||||
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string SecretKey::signDetached(const std::string & data) const
|
||||
{
|
||||
#if HAVE_SODIUM
|
||||
unsigned char sig[crypto_sign_BYTES];
|
||||
unsigned long long sigLen;
|
||||
crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(),
|
||||
(unsigned char *) key.data());
|
||||
return name + ":" + base64Encode(std::string((char *) sig, sigLen));
|
||||
#else
|
||||
noSodium();
|
||||
#endif
|
||||
}
|
||||
|
||||
PublicKey SecretKey::toPublicKey() const
|
||||
{
|
||||
#if HAVE_SODIUM
|
||||
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
|
||||
crypto_sign_ed25519_sk_to_pk(pk, (unsigned char *) key.data());
|
||||
return PublicKey(name, std::string((char *) pk, crypto_sign_PUBLICKEYBYTES));
|
||||
#else
|
||||
noSodium();
|
||||
#endif
|
||||
}
|
||||
|
||||
PublicKey::PublicKey(const string & s)
|
||||
SecretKey SecretKey::generate(std::string_view name)
|
||||
{
|
||||
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_sign_SECRETKEYBYTES];
|
||||
if (crypto_sign_keypair(pk, sk) != 0)
|
||||
throw Error("key generation failed");
|
||||
|
||||
return SecretKey(name, std::string((char *) sk, crypto_sign_SECRETKEYBYTES));
|
||||
}
|
||||
|
||||
PublicKey::PublicKey(std::string_view s)
|
||||
: Key(s)
|
||||
{
|
||||
#if HAVE_SODIUM
|
||||
if (key.size() != crypto_sign_PUBLICKEYBYTES)
|
||||
throw Error("public key is not valid");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool verifyDetached(const std::string & data, const std::string & sig,
|
||||
const PublicKeys & publicKeys)
|
||||
{
|
||||
#if HAVE_SODIUM
|
||||
auto ss = split(sig);
|
||||
|
||||
auto key = publicKeys.find(ss.first);
|
||||
auto key = publicKeys.find(std::string(ss.first));
|
||||
if (key == publicKeys.end()) return false;
|
||||
|
||||
auto sig2 = base64Decode(ss.second);
|
||||
|
|
@ -94,9 +87,6 @@ bool verifyDetached(const std::string & data, const std::string & sig,
|
|||
return crypto_sign_verify_detached((unsigned char *) sig2.data(),
|
||||
(unsigned char *) data.data(), data.size(),
|
||||
(unsigned char *) key->second.key.data()) == 0;
|
||||
#else
|
||||
noSodium();
|
||||
#endif
|
||||
}
|
||||
|
||||
PublicKeys getDefaultPublicKeys()
|
||||
|
|
|
|||
|
|
@ -13,32 +13,40 @@ struct Key
|
|||
|
||||
/* Construct Key from a string in the format
|
||||
‘<name>:<key-in-base64>’. */
|
||||
Key(const std::string & s);
|
||||
Key(std::string_view s);
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
protected:
|
||||
Key(const std::string & name, const std::string & key)
|
||||
: name(name), key(key) { }
|
||||
Key(std::string_view name, std::string && key)
|
||||
: name(name), key(std::move(key)) { }
|
||||
};
|
||||
|
||||
struct PublicKey;
|
||||
|
||||
struct SecretKey : Key
|
||||
{
|
||||
SecretKey(const std::string & s);
|
||||
SecretKey(std::string_view s);
|
||||
|
||||
/* Return a detached signature of the given string. */
|
||||
std::string signDetached(const std::string & s) const;
|
||||
std::string signDetached(std::string_view s) const;
|
||||
|
||||
PublicKey toPublicKey() const;
|
||||
|
||||
static SecretKey generate(std::string_view name);
|
||||
|
||||
private:
|
||||
SecretKey(std::string_view name, std::string && key)
|
||||
: Key(name, std::move(key)) { }
|
||||
};
|
||||
|
||||
struct PublicKey : Key
|
||||
{
|
||||
PublicKey(const std::string & data);
|
||||
PublicKey(std::string_view data);
|
||||
|
||||
private:
|
||||
PublicKey(const std::string & name, const std::string & key)
|
||||
: Key(name, key) { }
|
||||
PublicKey(std::string_view name, std::string && key)
|
||||
: Key(name, std::move(key)) { }
|
||||
friend struct SecretKey;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ struct DummyStoreConfig : virtual StoreConfig {
|
|||
const std::string name() override { return "Dummy Store"; }
|
||||
};
|
||||
|
||||
struct DummyStore : public Store, public virtual DummyStoreConfig
|
||||
struct DummyStore : public virtual DummyStoreConfig, public virtual Store
|
||||
{
|
||||
DummyStore(const std::string scheme, const std::string uri, const Params & params)
|
||||
: DummyStore(params)
|
||||
|
|
@ -17,6 +17,7 @@ struct DummyStore : public Store, public virtual DummyStoreConfig
|
|||
|
||||
DummyStore(const Params & params)
|
||||
: StoreConfig(params)
|
||||
, DummyStoreConfig(params)
|
||||
, Store(params)
|
||||
{ }
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ struct FileTransferRequest
|
|||
std::string mimeType;
|
||||
std::function<void(std::string_view data)> dataCallback;
|
||||
|
||||
FileTransferRequest(const std::string & uri)
|
||||
FileTransferRequest(std::string_view uri)
|
||||
: uri(uri), parentAct(getCurActivity()) { }
|
||||
|
||||
std::string verb()
|
||||
|
|
|
|||
|
|
@ -25,7 +25,14 @@ public:
|
|||
|
||||
virtual StringSet readDirectory(const Path & path) = 0;
|
||||
|
||||
virtual std::string readFile(const Path & path) = 0;
|
||||
/**
|
||||
* Read a file inside the store.
|
||||
*
|
||||
* If `requireValidPath` is set to `true` (the default), the path must be
|
||||
* inside a valid store path, otherwise it just needs to be physically
|
||||
* present (but not necessarily properly registered)
|
||||
*/
|
||||
virtual std::string readFile(const Path & path, bool requireValidPath = true) = 0;
|
||||
|
||||
virtual std::string readLink(const Path & path) = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -131,6 +131,28 @@ StringSet Settings::getDefaultSystemFeatures()
|
|||
return features;
|
||||
}
|
||||
|
||||
StringSet Settings::getDefaultExtraPlatforms()
|
||||
{
|
||||
if (std::string{SYSTEM} == "x86_64-linux" && !isWSL1())
|
||||
return StringSet{"i686-linux"};
|
||||
#if __APPLE__
|
||||
// Rosetta 2 emulation layer can run x86_64 binaries on aarch64
|
||||
// machines. Note that we can’t force processes from executing
|
||||
// x86_64 in aarch64 environments or vice versa since they can
|
||||
// always exec with their own binary preferences.
|
||||
else if (pathExists("/Library/Apple/System/Library/LaunchDaemons/com.apple.oahd.plist")) {
|
||||
if (std::string{SYSTEM} == "x86_64-darwin")
|
||||
return StringSet{"aarch64-darwin"};
|
||||
else if (std::string{SYSTEM} == "aarch64-darwin")
|
||||
return StringSet{"x86_64-darwin"};
|
||||
else
|
||||
return StringSet{};
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return StringSet{};
|
||||
}
|
||||
|
||||
bool Settings::isExperimentalFeatureEnabled(const std::string & name)
|
||||
{
|
||||
auto & f = experimentalFeatures.get();
|
||||
|
|
@ -206,8 +228,12 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s
|
|||
void MaxBuildJobsSetting::set(const std::string & str, bool append)
|
||||
{
|
||||
if (str == "auto") value = std::max(1U, std::thread::hardware_concurrency());
|
||||
else if (!string2Int(str, value))
|
||||
throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
|
||||
else {
|
||||
if (auto n = string2Int<decltype(value)>(str))
|
||||
value = *n;
|
||||
else
|
||||
throw UsageError("configuration setting '%s' should be 'auto' or an integer", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ class Settings : public Config {
|
|||
|
||||
StringSet getDefaultSystemFeatures();
|
||||
|
||||
StringSet getDefaultExtraPlatforms();
|
||||
|
||||
bool isWSL1();
|
||||
|
||||
public:
|
||||
|
|
@ -545,7 +547,7 @@ public:
|
|||
|
||||
Setting<StringSet> extraPlatforms{
|
||||
this,
|
||||
std::string{SYSTEM} == "x86_64-linux" && !isWSL1() ? StringSet{"i686-linux"} : StringSet{},
|
||||
getDefaultExtraPlatforms(),
|
||||
"extra-platforms",
|
||||
R"(
|
||||
Platforms other than the native one which this machine is capable of
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
|||
const std::string name() override { return "Http Binary Cache Store"; }
|
||||
};
|
||||
|
||||
class HttpBinaryCacheStore : public BinaryCacheStore, public HttpBinaryCacheStoreConfig
|
||||
class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore
|
||||
{
|
||||
private:
|
||||
|
||||
|
|
@ -36,6 +36,9 @@ public:
|
|||
const Path & _cacheUri,
|
||||
const Params & params)
|
||||
: StoreConfig(params)
|
||||
, BinaryCacheStoreConfig(params)
|
||||
, HttpBinaryCacheStoreConfig(params)
|
||||
, Store(params)
|
||||
, BinaryCacheStore(params)
|
||||
, cacheUri(scheme + "://" + _cacheUri)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ struct LegacySSHStoreConfig : virtual StoreConfig
|
|||
const std::string name() override { return "Legacy SSH Store"; }
|
||||
};
|
||||
|
||||
struct LegacySSHStore : public Store, public virtual LegacySSHStoreConfig
|
||||
struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Store
|
||||
{
|
||||
// Hack for getting remote build log output.
|
||||
// Intentionally not in `LegacySSHStoreConfig` so that it doesn't appear in
|
||||
|
|
@ -48,6 +48,7 @@ struct LegacySSHStore : public Store, public virtual LegacySSHStoreConfig
|
|||
|
||||
LegacySSHStore(const string & scheme, const string & host, const Params & params)
|
||||
: StoreConfig(params)
|
||||
, LegacySSHStoreConfig(params)
|
||||
, Store(params)
|
||||
, host(host)
|
||||
, connections(make_ref<Pool<Connection>>(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
|||
const std::string name() override { return "Local Binary Cache Store"; }
|
||||
};
|
||||
|
||||
class LocalBinaryCacheStore : public BinaryCacheStore, public virtual LocalBinaryCacheStoreConfig
|
||||
class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, public virtual BinaryCacheStore
|
||||
{
|
||||
private:
|
||||
|
||||
|
|
@ -24,6 +24,9 @@ public:
|
|||
const Path & binaryCacheDir,
|
||||
const Params & params)
|
||||
: StoreConfig(params)
|
||||
, BinaryCacheStoreConfig(params)
|
||||
, LocalBinaryCacheStoreConfig(params)
|
||||
, Store(params)
|
||||
, BinaryCacheStore(params)
|
||||
, binaryCacheDir(binaryCacheDir)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ struct LocalStoreAccessor : public FSAccessor
|
|||
|
||||
LocalStoreAccessor(ref<LocalFSStore> store) : store(store) { }
|
||||
|
||||
Path toRealPath(const Path & path)
|
||||
Path toRealPath(const Path & path, bool requireValidPath = true)
|
||||
{
|
||||
auto storePath = store->toStorePath(path).first;
|
||||
if (!store->isValidPath(storePath))
|
||||
if (requireValidPath && !store->isValidPath(storePath))
|
||||
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
|
||||
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
|
||||
}
|
||||
|
|
@ -61,9 +61,9 @@ struct LocalStoreAccessor : public FSAccessor
|
|||
return res;
|
||||
}
|
||||
|
||||
std::string readFile(const Path & path) override
|
||||
std::string readFile(const Path & path, bool requireValidPath = true) override
|
||||
{
|
||||
return nix::readFile(toRealPath(path));
|
||||
return nix::readFile(toRealPath(path, requireValidPath));
|
||||
}
|
||||
|
||||
std::string readLink(const Path & path) override
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ struct LocalFSStoreConfig : virtual StoreConfig
|
|||
"log", "directory where Nix will store state"};
|
||||
};
|
||||
|
||||
class LocalFSStore : public virtual Store, public virtual LocalFSStoreConfig
|
||||
class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store
|
||||
{
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -66,8 +66,10 @@ int getSchema(Path schemaPath)
|
|||
int curSchema = 0;
|
||||
if (pathExists(schemaPath)) {
|
||||
string s = readFile(schemaPath);
|
||||
if (!string2Int(s, curSchema))
|
||||
auto n = string2Int<int>(s);
|
||||
if (!n)
|
||||
throw Error("'%1%' is corrupt", schemaPath);
|
||||
curSchema = *n;
|
||||
}
|
||||
return curSchema;
|
||||
}
|
||||
|
|
@ -100,6 +102,8 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
|||
|
||||
LocalStore::LocalStore(const Params & params)
|
||||
: StoreConfig(params)
|
||||
, LocalFSStoreConfig(params)
|
||||
, LocalStoreConfig(params)
|
||||
, Store(params)
|
||||
, LocalFSStore(params)
|
||||
, realStoreDir_{this, false, rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
|
||||
|
|
@ -734,57 +738,62 @@ void LocalStore::queryPathInfoUncached(const StorePath & path,
|
|||
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept
|
||||
{
|
||||
try {
|
||||
callback(retrySQLite<std::shared_ptr<ValidPathInfo>>([&]() {
|
||||
callback(retrySQLite<std::shared_ptr<const ValidPathInfo>>([&]() {
|
||||
auto state(_state.lock());
|
||||
|
||||
/* Get the path info. */
|
||||
auto useQueryPathInfo(state->stmts->QueryPathInfo.use()(printStorePath(path)));
|
||||
|
||||
if (!useQueryPathInfo.next())
|
||||
return std::shared_ptr<ValidPathInfo>();
|
||||
|
||||
auto id = useQueryPathInfo.getInt(0);
|
||||
|
||||
auto narHash = Hash::dummy;
|
||||
try {
|
||||
narHash = Hash::parseAnyPrefixed(useQueryPathInfo.getStr(1));
|
||||
} catch (BadHash & e) {
|
||||
throw Error("invalid-path entry for '%s': %s", printStorePath(path), e.what());
|
||||
}
|
||||
|
||||
auto info = std::make_shared<ValidPathInfo>(path, narHash);
|
||||
|
||||
info->id = id;
|
||||
|
||||
info->registrationTime = useQueryPathInfo.getInt(2);
|
||||
|
||||
auto s = (const char *) sqlite3_column_text(state->stmts->QueryPathInfo, 3);
|
||||
if (s) info->deriver = parseStorePath(s);
|
||||
|
||||
/* Note that narSize = NULL yields 0. */
|
||||
info->narSize = useQueryPathInfo.getInt(4);
|
||||
|
||||
info->ultimate = useQueryPathInfo.getInt(5) == 1;
|
||||
|
||||
s = (const char *) sqlite3_column_text(state->stmts->QueryPathInfo, 6);
|
||||
if (s) info->sigs = tokenizeString<StringSet>(s, " ");
|
||||
|
||||
s = (const char *) sqlite3_column_text(state->stmts->QueryPathInfo, 7);
|
||||
if (s) info->ca = parseContentAddressOpt(s);
|
||||
|
||||
/* Get the references. */
|
||||
auto useQueryReferences(state->stmts->QueryReferences.use()(info->id));
|
||||
|
||||
while (useQueryReferences.next())
|
||||
info->references.insert(parseStorePath(useQueryReferences.getStr(0)));
|
||||
|
||||
return info;
|
||||
return queryPathInfoInternal(*state, path);
|
||||
}));
|
||||
|
||||
} catch (...) { callback.rethrow(); }
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const ValidPathInfo> LocalStore::queryPathInfoInternal(State & state, const StorePath & path)
|
||||
{
|
||||
/* Get the path info. */
|
||||
auto useQueryPathInfo(state.stmts->QueryPathInfo.use()(printStorePath(path)));
|
||||
|
||||
if (!useQueryPathInfo.next())
|
||||
return std::shared_ptr<ValidPathInfo>();
|
||||
|
||||
auto id = useQueryPathInfo.getInt(0);
|
||||
|
||||
auto narHash = Hash::dummy;
|
||||
try {
|
||||
narHash = Hash::parseAnyPrefixed(useQueryPathInfo.getStr(1));
|
||||
} catch (BadHash & e) {
|
||||
throw Error("invalid-path entry for '%s': %s", printStorePath(path), e.what());
|
||||
}
|
||||
|
||||
auto info = std::make_shared<ValidPathInfo>(path, narHash);
|
||||
|
||||
info->id = id;
|
||||
|
||||
info->registrationTime = useQueryPathInfo.getInt(2);
|
||||
|
||||
auto s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 3);
|
||||
if (s) info->deriver = parseStorePath(s);
|
||||
|
||||
/* Note that narSize = NULL yields 0. */
|
||||
info->narSize = useQueryPathInfo.getInt(4);
|
||||
|
||||
info->ultimate = useQueryPathInfo.getInt(5) == 1;
|
||||
|
||||
s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 6);
|
||||
if (s) info->sigs = tokenizeString<StringSet>(s, " ");
|
||||
|
||||
s = (const char *) sqlite3_column_text(state.stmts->QueryPathInfo, 7);
|
||||
if (s) info->ca = parseContentAddressOpt(s);
|
||||
|
||||
/* Get the references. */
|
||||
auto useQueryReferences(state.stmts->QueryReferences.use()(info->id));
|
||||
|
||||
while (useQueryReferences.next())
|
||||
info->references.insert(parseStorePath(useQueryReferences.getStr(0)));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
/* Update path info in the database. */
|
||||
void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info)
|
||||
{
|
||||
|
|
@ -905,7 +914,7 @@ LocalStore::queryDerivationOutputMapNoResolve(const StorePath& path_)
|
|||
if (realisation)
|
||||
outputs.insert_or_assign(outputName, realisation->outPath);
|
||||
else
|
||||
outputs.insert_or_assign(outputName, std::nullopt);
|
||||
outputs.insert({outputName, std::nullopt});
|
||||
}
|
||||
|
||||
return outputs;
|
||||
|
|
@ -1606,7 +1615,7 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si
|
|||
|
||||
SQLiteTxn txn(state->db);
|
||||
|
||||
auto info = std::const_pointer_cast<ValidPathInfo>(std::shared_ptr<const ValidPathInfo>(queryPathInfo(storePath)));
|
||||
auto info = std::const_pointer_cast<ValidPathInfo>(queryPathInfoInternal(*state, storePath));
|
||||
|
||||
info->sigs.insert(sigs.begin(), sigs.end());
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig
|
|||
};
|
||||
|
||||
|
||||
class LocalStore : public LocalFSStore, public virtual LocalStoreConfig
|
||||
class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore
|
||||
{
|
||||
private:
|
||||
|
||||
|
|
@ -198,9 +198,7 @@ public:
|
|||
|
||||
void vacuumDB();
|
||||
|
||||
/* Repair the contents of the given path by redownloading it using
|
||||
a substituter (if available). */
|
||||
void repairPath(const StorePath & path);
|
||||
void repairPath(const StorePath & path) override;
|
||||
|
||||
void addSignatures(const StorePath & storePath, const StringSet & sigs) override;
|
||||
|
||||
|
|
@ -235,6 +233,8 @@ private:
|
|||
void verifyPath(const Path & path, const StringSet & store,
|
||||
PathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors);
|
||||
|
||||
std::shared_ptr<const ValidPathInfo> queryPathInfoInternal(State & state, const StorePath & path);
|
||||
|
||||
void updatePathInfo(State & state, const ValidPathInfo & info);
|
||||
|
||||
void upgradeStore6();
|
||||
|
|
|
|||
|
|
@ -80,16 +80,16 @@ string nextComponent(string::const_iterator & p,
|
|||
|
||||
static bool componentsLT(const string & c1, const string & c2)
|
||||
{
|
||||
int n1, n2;
|
||||
bool c1Num = string2Int(c1, n1), c2Num = string2Int(c2, n2);
|
||||
auto n1 = string2Int<int>(c1);
|
||||
auto n2 = string2Int<int>(c2);
|
||||
|
||||
if (c1Num && c2Num) return n1 < n2;
|
||||
else if (c1 == "" && c2Num) return true;
|
||||
if (n1 && n2) return *n1 < *n2;
|
||||
else if (c1 == "" && n2) return true;
|
||||
else if (c1 == "pre" && c2 != "pre") return true;
|
||||
else if (c2 == "pre") return false;
|
||||
/* Assume that `2.3a' < `2.3.1'. */
|
||||
else if (c2Num) return true;
|
||||
else if (c1Num) return false;
|
||||
else if (n2) return true;
|
||||
else if (n1) return false;
|
||||
else return c1 < c2;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ struct NarAccessor : public FSAccessor
|
|||
return res;
|
||||
}
|
||||
|
||||
std::string readFile(const Path & path) override
|
||||
std::string readFile(const Path & path, bool requireValidPath = true) override
|
||||
{
|
||||
auto i = get(path);
|
||||
if (i.type != FSAccessor::Type::tRegular)
|
||||
|
|
|
|||
|
|
@ -109,8 +109,10 @@ public:
|
|||
SQLiteStmt(state->db,
|
||||
"delete from NARs where ((present = 0 and timestamp < ?) or (present = 1 and timestamp < ?))")
|
||||
.use()
|
||||
(now - settings.ttlNegativeNarInfoCache)
|
||||
(now - settings.ttlPositiveNarInfoCache)
|
||||
// Use a minimum TTL to prevent --refresh from
|
||||
// nuking the entire disk cache.
|
||||
(now - std::max(settings.ttlNegativeNarInfoCache.get(), 3600U))
|
||||
(now - std::max(settings.ttlPositiveNarInfoCache.get(), 30 * 24 * 3600U))
|
||||
.exec();
|
||||
|
||||
debug("deleted %d entries from the NAR info disk cache", sqlite3_changes(state->db));
|
||||
|
|
|
|||
|
|
@ -46,14 +46,18 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
|
|||
else if (name == "FileHash")
|
||||
fileHash = parseHashField(value);
|
||||
else if (name == "FileSize") {
|
||||
if (!string2Int(value, fileSize)) throw corrupt();
|
||||
auto n = string2Int<decltype(fileSize)>(value);
|
||||
if (!n) throw corrupt();
|
||||
fileSize = *n;
|
||||
}
|
||||
else if (name == "NarHash") {
|
||||
narHash = parseHashField(value);
|
||||
haveNarHash = true;
|
||||
}
|
||||
else if (name == "NarSize") {
|
||||
if (!string2Int(value, narSize)) throw corrupt();
|
||||
auto n = string2Int<decltype(narSize)>(value);
|
||||
if (!n) throw corrupt();
|
||||
narSize = *n;
|
||||
}
|
||||
else if (name == "References") {
|
||||
auto refs = tokenizeString<Strings>(value, " ");
|
||||
|
|
|
|||
|
|
@ -101,6 +101,10 @@ bool ParsedDerivation::canBuildLocally(Store & localStore) const
|
|||
&& !drv.isBuiltin())
|
||||
return false;
|
||||
|
||||
if (settings.maxBuildJobs.get() == 0
|
||||
&& !drv.isBuiltin())
|
||||
return false;
|
||||
|
||||
for (auto & feature : getRequiredSystemFeatures())
|
||||
if (!localStore.systemFeatures.get().count(feature)) return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@ static std::optional<GenerationNumber> parseName(const string & profileName, con
|
|||
string s = string(name, profileName.size() + 1);
|
||||
string::size_type p = s.find("-link");
|
||||
if (p == string::npos) return {};
|
||||
unsigned int n;
|
||||
if (string2Int(string(s, 0, p), n) && n >= 0)
|
||||
return n;
|
||||
if (auto n = string2Int<unsigned int>(s.substr(0, p)))
|
||||
return *n;
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
|
@ -214,12 +213,12 @@ void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, b
|
|||
{
|
||||
time_t curTime = time(0);
|
||||
string strDays = string(timeSpec, 0, timeSpec.size() - 1);
|
||||
int days;
|
||||
auto days = string2Int<int>(strDays);
|
||||
|
||||
if (!string2Int(strDays, days) || days < 1)
|
||||
if (!days || *days < 1)
|
||||
throw Error("invalid number of days specifier '%1%'", timeSpec);
|
||||
|
||||
time_t oldTime = curTime - days * 24 * 3600;
|
||||
time_t oldTime = curTime - *days * 24 * 3600;
|
||||
|
||||
deleteGenerationsOlderThan(profile, oldTime, dryRun);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,9 +88,6 @@ PathSet scanForReferences(Sink & toTee,
|
|||
TeeSink sink { refsSink, toTee };
|
||||
std::map<string, Path> backMap;
|
||||
|
||||
/* For efficiency (and a higher hit rate), just search for the
|
||||
hash part of the file name. (This assumes that all references
|
||||
have the form `HASH-bla'). */
|
||||
for (auto & i : refs) {
|
||||
auto baseName = std::string(baseNameOf(i));
|
||||
string::size_type pos = baseName.find('-');
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string &
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
|
||||
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, bool requireValidPath)
|
||||
{
|
||||
auto path = canonPath(path_);
|
||||
|
||||
auto [storePath, restPath] = store->toStorePath(path);
|
||||
|
||||
if (!store->isValidPath(storePath))
|
||||
if (requireValidPath && !store->isValidPath(storePath))
|
||||
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
|
||||
|
||||
auto i = nars.find(std::string(storePath.hashPart()));
|
||||
|
|
@ -113,9 +113,9 @@ StringSet RemoteFSAccessor::readDirectory(const Path & path)
|
|||
return res.first->readDirectory(res.second);
|
||||
}
|
||||
|
||||
std::string RemoteFSAccessor::readFile(const Path & path)
|
||||
std::string RemoteFSAccessor::readFile(const Path & path, bool requireValidPath)
|
||||
{
|
||||
auto res = fetch(path);
|
||||
auto res = fetch(path, requireValidPath);
|
||||
return res.first->readFile(res.second);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class RemoteFSAccessor : public FSAccessor
|
|||
|
||||
Path cacheDir;
|
||||
|
||||
std::pair<ref<FSAccessor>, Path> fetch(const Path & path_);
|
||||
std::pair<ref<FSAccessor>, Path> fetch(const Path & path_, bool requireValidPath = true);
|
||||
|
||||
friend class BinaryCacheStore;
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ public:
|
|||
|
||||
StringSet readDirectory(const Path & path) override;
|
||||
|
||||
std::string readFile(const Path & path) override;
|
||||
std::string readFile(const Path & path, bool requireValidPath = true) override;
|
||||
|
||||
std::string readLink(const Path & path) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -77,8 +77,8 @@ void write(const Store & store, Sink & out, const std::optional<ContentAddress>
|
|||
|
||||
/* TODO: Separate these store impls into different files, give them better names */
|
||||
RemoteStore::RemoteStore(const Params & params)
|
||||
: Store(params)
|
||||
, RemoteStoreConfig(params)
|
||||
: RemoteStoreConfig(params)
|
||||
, Store(params)
|
||||
, connections(make_ref<Pool<Connection>>(
|
||||
std::max(1, (int) maxConnections),
|
||||
[this]() {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ struct RemoteStoreConfig : virtual StoreConfig
|
|||
|
||||
/* FIXME: RemoteStore is a misnomer - should be something like
|
||||
DaemonStore. */
|
||||
class RemoteStore : public virtual Store, public virtual RemoteStoreConfig
|
||||
class RemoteStore : public virtual RemoteStoreConfig, public virtual Store
|
||||
{
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,11 @@ S3Helper::FileTransferResult S3Helper::getObject(
|
|||
return res;
|
||||
}
|
||||
|
||||
S3BinaryCacheStore::S3BinaryCacheStore(const Params & params)
|
||||
: BinaryCacheStoreConfig(params)
|
||||
, BinaryCacheStore(params)
|
||||
{ }
|
||||
|
||||
struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
||||
{
|
||||
using BinaryCacheStoreConfig::BinaryCacheStoreConfig;
|
||||
|
|
@ -195,7 +200,7 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig
|
|||
const std::string name() override { return "S3 Binary Cache Store"; }
|
||||
};
|
||||
|
||||
struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore, virtual S3BinaryCacheStoreConfig
|
||||
struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore
|
||||
{
|
||||
std::string bucketName;
|
||||
|
||||
|
|
@ -208,6 +213,10 @@ struct S3BinaryCacheStoreImpl : public S3BinaryCacheStore, virtual S3BinaryCache
|
|||
const std::string & bucketName,
|
||||
const Params & params)
|
||||
: StoreConfig(params)
|
||||
, BinaryCacheStoreConfig(params)
|
||||
, S3BinaryCacheStoreConfig(params)
|
||||
, Store(params)
|
||||
, BinaryCacheStore(params)
|
||||
, S3BinaryCacheStore(params)
|
||||
, bucketName(bucketName)
|
||||
, s3Helper(profile, region, scheme, endpoint)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,11 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
class S3BinaryCacheStore : public BinaryCacheStore
|
||||
class S3BinaryCacheStore : public virtual BinaryCacheStore
|
||||
{
|
||||
protected:
|
||||
|
||||
S3BinaryCacheStore(const Params & params)
|
||||
: BinaryCacheStore(params)
|
||||
{ }
|
||||
S3BinaryCacheStore(const Params & params);
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ struct SSHStoreConfig : virtual RemoteStoreConfig
|
|||
const std::string name() override { return "SSH Store"; }
|
||||
};
|
||||
|
||||
class SSHStore : public virtual RemoteStore, public virtual SSHStoreConfig
|
||||
class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore
|
||||
{
|
||||
public:
|
||||
|
||||
SSHStore(const std::string & scheme, const std::string & host, const Params & params)
|
||||
: StoreConfig(params)
|
||||
, RemoteStoreConfig(params)
|
||||
, SSHStoreConfig(params)
|
||||
, Store(params)
|
||||
, RemoteStore(params)
|
||||
, host(host)
|
||||
|
|
|
|||
|
|
@ -949,19 +949,20 @@ std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istre
|
|||
getline(str, s);
|
||||
auto narHash = Hash::parseAny(s, htSHA256);
|
||||
getline(str, s);
|
||||
uint64_t narSize;
|
||||
if (!string2Int(s, narSize)) throw Error("number expected");
|
||||
hashGiven = { narHash, narSize };
|
||||
auto narSize = string2Int<uint64_t>(s);
|
||||
if (!narSize) throw Error("number expected");
|
||||
hashGiven = { narHash, *narSize };
|
||||
}
|
||||
ValidPathInfo info(store.parseStorePath(path), hashGiven->first);
|
||||
info.narSize = hashGiven->second;
|
||||
std::string deriver;
|
||||
getline(str, deriver);
|
||||
if (deriver != "") info.deriver = store.parseStorePath(deriver);
|
||||
string s; int n;
|
||||
string s;
|
||||
getline(str, s);
|
||||
if (!string2Int(s, n)) throw Error("number expected");
|
||||
while (n--) {
|
||||
auto n = string2Int<int>(s);
|
||||
if (!n) throw Error("number expected");
|
||||
while ((*n)--) {
|
||||
getline(str, s);
|
||||
info.references.insert(store.parseStorePath(s));
|
||||
}
|
||||
|
|
@ -1066,26 +1067,23 @@ Derivation Store::derivationFromPath(const StorePath & drvPath)
|
|||
return readDerivation(drvPath);
|
||||
}
|
||||
|
||||
|
||||
Derivation Store::readDerivation(const StorePath & drvPath)
|
||||
Derivation readDerivationCommon(Store& store, const StorePath& drvPath, bool requireValidPath)
|
||||
{
|
||||
auto accessor = getFSAccessor();
|
||||
auto accessor = store.getFSAccessor();
|
||||
try {
|
||||
return parseDerivation(*this,
|
||||
accessor->readFile(printStorePath(drvPath)),
|
||||
return parseDerivation(store,
|
||||
accessor->readFile(store.printStorePath(drvPath), requireValidPath),
|
||||
Derivation::nameFromPath(drvPath));
|
||||
} catch (FormatError & e) {
|
||||
throw Error("error parsing derivation '%s': %s", printStorePath(drvPath), e.msg());
|
||||
throw Error("error parsing derivation '%s': %s", store.printStorePath(drvPath), e.msg());
|
||||
}
|
||||
}
|
||||
|
||||
Derivation Store::readDerivation(const StorePath & drvPath)
|
||||
{ return readDerivationCommon(*this, drvPath, true); }
|
||||
|
||||
Derivation Store::readInvalidDerivation(const StorePath & drvPath)
|
||||
{
|
||||
return parseDerivation(
|
||||
*this,
|
||||
readFile(Store::toRealPath(drvPath)),
|
||||
Derivation::nameFromPath(drvPath));
|
||||
}
|
||||
{ return readDerivationCommon(*this, drvPath, false); }
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -175,25 +175,7 @@ struct StoreConfig : public Config
|
|||
{
|
||||
using Config::Config;
|
||||
|
||||
/**
|
||||
* When constructing a store implementation, we pass in a map `params` of
|
||||
* parameters that's supposed to initialize the associated config.
|
||||
* To do that, we must use the `StoreConfig(StringMap & params)`
|
||||
* constructor, so we'd like to `delete` its default constructor to enforce
|
||||
* it.
|
||||
*
|
||||
* However, actually deleting it means that all the subclasses of
|
||||
* `StoreConfig` will have their default constructor deleted (because it's
|
||||
* supposed to call the deleted default constructor of `StoreConfig`). But
|
||||
* because we're always using virtual inheritance, the constructors of
|
||||
* child classes will never implicitely call this one, so deleting it will
|
||||
* be more painful than anything else.
|
||||
*
|
||||
* So we `assert(false)` here to ensure at runtime that the right
|
||||
* constructor is always called without having to redefine a custom
|
||||
* constructor for each `*Config` class.
|
||||
*/
|
||||
StoreConfig() { assert(false); }
|
||||
StoreConfig() = delete;
|
||||
|
||||
virtual ~StoreConfig() { }
|
||||
|
||||
|
|
@ -624,6 +606,11 @@ public:
|
|||
virtual ref<FSAccessor> getFSAccessor()
|
||||
{ unsupported("getFSAccessor"); }
|
||||
|
||||
/* Repair the contents of the given path by redownloading it using
|
||||
a substituter (if available). */
|
||||
virtual void repairPath(const StorePath & path)
|
||||
{ unsupported("repairPath"); }
|
||||
|
||||
/* Add signatures to the specified store path. The signatures are
|
||||
not verified. */
|
||||
virtual void addSignatures(const StorePath & storePath, const StringSet & sigs)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ namespace nix {
|
|||
|
||||
UDSRemoteStore::UDSRemoteStore(const Params & params)
|
||||
: StoreConfig(params)
|
||||
, LocalFSStoreConfig(params)
|
||||
, RemoteStoreConfig(params)
|
||||
, UDSRemoteStoreConfig(params)
|
||||
, Store(params)
|
||||
, LocalFSStore(params)
|
||||
, RemoteStore(params)
|
||||
|
|
|
|||
|
|
@ -14,15 +14,10 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon
|
|||
{
|
||||
}
|
||||
|
||||
UDSRemoteStoreConfig()
|
||||
: UDSRemoteStoreConfig(Store::Params({}))
|
||||
{
|
||||
}
|
||||
|
||||
const std::string name() override { return "Local Daemon Store"; }
|
||||
};
|
||||
|
||||
class UDSRemoteStore : public LocalFSStore, public RemoteStore, public virtual UDSRemoteStoreConfig
|
||||
class UDSRemoteStore : public virtual UDSRemoteStoreConfig, public virtual LocalFSStore, public virtual RemoteStore
|
||||
{
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -254,6 +254,8 @@ nlohmann::json Args::toJSON()
|
|||
res["description"] = description();
|
||||
res["flags"] = std::move(flags);
|
||||
res["args"] = std::move(args);
|
||||
auto s = doc();
|
||||
if (s != "") res.emplace("doc", stripIndentation(s));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -351,38 +353,6 @@ void printTable(std::ostream & out, const Table2 & table)
|
|||
}
|
||||
}
|
||||
|
||||
void Command::printHelp(const string & programName, std::ostream & out)
|
||||
{
|
||||
Args::printHelp(programName, out);
|
||||
|
||||
auto exs = examples();
|
||||
if (!exs.empty()) {
|
||||
out << "\n" ANSI_BOLD "Examples:" ANSI_NORMAL "\n";
|
||||
for (auto & ex : exs)
|
||||
out << "\n"
|
||||
<< " " << ex.description << "\n" // FIXME: wrap
|
||||
<< " $ " << ex.command << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json Command::toJSON()
|
||||
{
|
||||
auto exs = nlohmann::json::array();
|
||||
|
||||
for (auto & example : examples()) {
|
||||
auto ex = nlohmann::json::object();
|
||||
ex["description"] = example.description;
|
||||
ex["command"] = chomp(stripIndentation(example.command));
|
||||
exs.push_back(std::move(ex));
|
||||
}
|
||||
|
||||
auto res = Args::toJSON();
|
||||
res["examples"] = std::move(exs);
|
||||
auto s = doc();
|
||||
if (s != "") res.emplace("doc", stripIndentation(s));
|
||||
return res;
|
||||
}
|
||||
|
||||
MultiCommand::MultiCommand(const Commands & commands)
|
||||
: commands(commands)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ public:
|
|||
/* Return a short one-line description of the command. */
|
||||
virtual std::string description() { return ""; }
|
||||
|
||||
/* Return documentation about this command, in Markdown format. */
|
||||
virtual std::string doc() { return ""; }
|
||||
|
||||
protected:
|
||||
|
||||
static const size_t ArityAny = std::numeric_limits<size_t>::max();
|
||||
|
|
@ -65,8 +68,12 @@ protected:
|
|||
, arity(ArityAny)
|
||||
{ }
|
||||
|
||||
template<class T>
|
||||
Handler(T * dest)
|
||||
Handler(std::string * dest)
|
||||
: fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
|
||||
Handler(std::optional<std::string> * dest)
|
||||
: fun([=](std::vector<std::string> ss) { *dest = ss[0]; })
|
||||
, arity(1)
|
||||
{ }
|
||||
|
|
@ -76,6 +83,14 @@ protected:
|
|||
: fun([=](std::vector<std::string> ss) { *dest = val; })
|
||||
, arity(0)
|
||||
{ }
|
||||
|
||||
template<class I>
|
||||
Handler(I * dest)
|
||||
: fun([=](std::vector<std::string> ss) {
|
||||
*dest = string2IntWithUnitPrefix<I>(ss[0]);
|
||||
})
|
||||
, arity(1)
|
||||
{ }
|
||||
};
|
||||
|
||||
/* Flags. */
|
||||
|
|
@ -127,19 +142,6 @@ public:
|
|||
/* Helper functions for constructing flags / positional
|
||||
arguments. */
|
||||
|
||||
void mkFlag1(char shortName, const std::string & longName,
|
||||
const std::string & label, const std::string & description,
|
||||
std::function<void(std::string)> fun)
|
||||
{
|
||||
addFlag({
|
||||
.longName = longName,
|
||||
.shortName = shortName,
|
||||
.description = description,
|
||||
.labels = {label},
|
||||
.handler = {[=](std::string s) { fun(s); }}
|
||||
});
|
||||
}
|
||||
|
||||
void mkFlag(char shortName, const std::string & name,
|
||||
const std::string & description, bool * dest)
|
||||
{
|
||||
|
|
@ -158,33 +160,6 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
template<class I>
|
||||
void mkIntFlag(char shortName, const std::string & longName,
|
||||
const std::string & description, I * dest)
|
||||
{
|
||||
mkFlag<I>(shortName, longName, description, [=](I n) {
|
||||
*dest = n;
|
||||
});
|
||||
}
|
||||
|
||||
template<class I>
|
||||
void mkFlag(char shortName, const std::string & longName,
|
||||
const std::string & description, std::function<void(I)> fun)
|
||||
{
|
||||
addFlag({
|
||||
.longName = longName,
|
||||
.shortName = shortName,
|
||||
.description = description,
|
||||
.labels = {"N"},
|
||||
.handler = {[=](std::string s) {
|
||||
I n;
|
||||
if (!string2Int(s, n))
|
||||
throw UsageError("flag '--%s' requires a integer argument", longName);
|
||||
fun(n);
|
||||
}}
|
||||
});
|
||||
}
|
||||
|
||||
void expectArgs(ExpectedArg && arg)
|
||||
{
|
||||
expectedArgs.emplace_back(std::move(arg));
|
||||
|
|
@ -225,28 +200,11 @@ struct Command : virtual Args
|
|||
virtual void prepare() { };
|
||||
virtual void run() = 0;
|
||||
|
||||
/* Return documentation about this command, in Markdown format. */
|
||||
virtual std::string doc() { return ""; }
|
||||
|
||||
struct Example
|
||||
{
|
||||
std::string description;
|
||||
std::string command;
|
||||
};
|
||||
|
||||
typedef std::list<Example> Examples;
|
||||
|
||||
virtual Examples examples() { return Examples(); }
|
||||
|
||||
typedef int Category;
|
||||
|
||||
static constexpr Category catDefault = 0;
|
||||
|
||||
virtual Category category() { return catDefault; }
|
||||
|
||||
void printHelp(const string & programName, std::ostream & out) override;
|
||||
|
||||
nlohmann::json toJSON() override;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, std::function<ref<Command>()>> Commands;
|
||||
|
|
|
|||
|
|
@ -230,7 +230,9 @@ template<typename T>
|
|||
void BaseSetting<T>::set(const std::string & str, bool append)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "Integer required.");
|
||||
if (!string2Int(str, value))
|
||||
if (auto n = string2Int<T>(str))
|
||||
value = *n;
|
||||
else
|
||||
throw UsageError("setting '%s' has invalid value '%s'", name, str);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,36 +61,31 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
|||
if (errPos.origin == foFile) {
|
||||
LinesOfCode loc;
|
||||
try {
|
||||
// FIXME: when running as the daemon, make sure we don't
|
||||
// open a file to which the client doesn't have access.
|
||||
AutoCloseFD fd = open(errPos.file.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (!fd) {
|
||||
logError(SysError("opening file '%1%'", errPos.file).info());
|
||||
return std::nullopt;
|
||||
}
|
||||
else
|
||||
if (!fd) return {};
|
||||
|
||||
// count the newlines.
|
||||
int count = 0;
|
||||
string line;
|
||||
int pl = errPos.line - 1;
|
||||
do
|
||||
{
|
||||
// count the newlines.
|
||||
int count = 0;
|
||||
string line;
|
||||
int pl = errPos.line - 1;
|
||||
do
|
||||
{
|
||||
line = readLine(fd.get());
|
||||
++count;
|
||||
if (count < pl)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if (count == pl) {
|
||||
loc.prevLineOfCode = line;
|
||||
} else if (count == pl + 1) {
|
||||
loc.errLineOfCode = line;
|
||||
} else if (count == pl + 2) {
|
||||
loc.nextLineOfCode = line;
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
return loc;
|
||||
}
|
||||
line = readLine(fd.get());
|
||||
++count;
|
||||
if (count < pl)
|
||||
;
|
||||
else if (count == pl)
|
||||
loc.prevLineOfCode = line;
|
||||
else if (count == pl + 1)
|
||||
loc.errLineOfCode = line;
|
||||
else if (count == pl + 2) {
|
||||
loc.nextLineOfCode = line;
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
return loc;
|
||||
}
|
||||
catch (EndOfFile & eof) {
|
||||
if (loc.errLineOfCode.has_value())
|
||||
|
|
@ -99,7 +94,6 @@ std::optional<LinesOfCode> getCodeLines(const ErrPos & errPos)
|
|||
return std::nullopt;
|
||||
}
|
||||
catch (std::exception & e) {
|
||||
printError("error reading nix file: %s\n%s", errPos.file, e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace nix {
|
|||
ErrorInfo structs are sent to the logger as part of an exception, or directly with the
|
||||
logError or logWarning macros.
|
||||
|
||||
See the error-demo.cc program for usage examples.
|
||||
See libutil/tests/logging.cc for usage examples.
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace nix {
|
|||
});
|
||||
|
||||
auto str = testing::internal::GetCapturedStderr();
|
||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nopening file '\x1B[33;1mrandom.nix\x1B[0m': \x1B[33;1mNo such file or directory\x1B[0m\n@nix {\"action\":\"msg\",\"column\":13,\"file\":\"random.nix\",\"level\":0,\"line\":2,\"msg\":\"\\u001b[31;1merror:\\u001b[0m\\u001b[34;1m --- error name --- error-unit-test\\u001b[0m\\n\\u001b[34;1mat: \\u001b[33;1m(2:13)\\u001b[34;1m in file: \\u001b[0mrandom.nix\\n\\nerror without any code lines.\\n\\nthis hint has \\u001b[33;1myellow\\u001b[0m templated \\u001b[33;1mvalues\\u001b[0m!!\",\"raw_msg\":\"this hint has \\u001b[33;1myellow\\u001b[0m templated \\u001b[33;1mvalues\\u001b[0m!!\"}\n");
|
||||
ASSERT_STREQ(str.c_str(), "@nix {\"action\":\"msg\",\"column\":13,\"file\":\"random.nix\",\"level\":0,\"line\":2,\"msg\":\"\\u001b[31;1merror:\\u001b[0m\\u001b[34;1m --- error name --- error-unit-test\\u001b[0m\\n\\u001b[34;1mat: \\u001b[33;1m(2:13)\\u001b[34;1m in file: \\u001b[0mrandom.nix\\n\\nerror without any code lines.\\n\\nthis hint has \\u001b[33;1myellow\\u001b[0m templated \\u001b[33;1mvalues\\u001b[0m!!\",\"raw_msg\":\"this hint has \\u001b[33;1myellow\\u001b[0m templated \\u001b[33;1mvalues\\u001b[0m!!\"}\n");
|
||||
}
|
||||
|
||||
TEST(logEI, appendingHintsToPreviousError) {
|
||||
|
|
@ -208,7 +208,7 @@ namespace nix {
|
|||
});
|
||||
|
||||
auto str = testing::internal::GetCapturedStderr();
|
||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nopening file '\x1B[33;1minvalid filename\x1B[0m': \x1B[33;1mNo such file or directory\x1B[0m\n\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m in file: \x1B[0minvalid filename\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m in file: \x1B[0minvalid filename\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
|
||||
}
|
||||
|
||||
TEST(logError, logErrorWithOnlyHintAndName) {
|
||||
|
|
@ -290,7 +290,7 @@ namespace nix {
|
|||
logError(e.info());
|
||||
|
||||
auto str = testing::internal::GetCapturedStderr();
|
||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nopening file '\x1B[33;1minvalid filename\x1B[0m': \x1B[33;1mNo such file or directory\x1B[0m\n\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nshow-traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n\x1B[34;1m---- show-trace ----\x1B[0m\n\x1B[34;1mtrace: \x1B[0mwhile trying to compute \x1B[33;1m42\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(1:19)\x1B[34;1m from stdin\x1B[0m\n\n 1| this is the other problem line of code\n | \x1B[31;1m^\x1B[0m\n\n\x1B[34;1mtrace: \x1B[0mwhile doing something without a \x1B[33;1mpos\x1B[0m\n\x1B[34;1mtrace: \x1B[0mmissing \x1B[33;1mnix file\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(100:1)\x1B[34;1m in file: \x1B[0minvalid filename\n");
|
||||
ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nshow-traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n\x1B[34;1m---- show-trace ----\x1B[0m\n\x1B[34;1mtrace: \x1B[0mwhile trying to compute \x1B[33;1m42\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(1:19)\x1B[34;1m from stdin\x1B[0m\n\n 1| this is the other problem line of code\n | \x1B[31;1m^\x1B[0m\n\n\x1B[34;1mtrace: \x1B[0mwhile doing something without a \x1B[33;1mpos\x1B[0m\n\x1B[34;1mtrace: \x1B[0mmissing \x1B[33;1mnix file\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(100:1)\x1B[34;1m in file: \x1B[0minvalid filename\n");
|
||||
}
|
||||
|
||||
TEST(addTrace, hideTracesWithoutShowTrace) {
|
||||
|
|
|
|||
|
|
@ -320,20 +320,15 @@ namespace nix {
|
|||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(string2Float, emptyString) {
|
||||
double n;
|
||||
ASSERT_EQ(string2Float("", n), false);
|
||||
ASSERT_EQ(string2Float<double>(""), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(string2Float, trivialConversions) {
|
||||
double n;
|
||||
ASSERT_EQ(string2Float("1.0", n), true);
|
||||
ASSERT_EQ(n, 1.0);
|
||||
ASSERT_EQ(string2Float<double>("1.0"), 1.0);
|
||||
|
||||
ASSERT_EQ(string2Float("0.0", n), true);
|
||||
ASSERT_EQ(n, 0.0);
|
||||
ASSERT_EQ(string2Float<double>("0.0"), 0.0);
|
||||
|
||||
ASSERT_EQ(string2Float("-100.25", n), true);
|
||||
ASSERT_EQ(n, (-100.25));
|
||||
ASSERT_EQ(string2Float<double>("-100.25"), -100.25);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
|
@ -341,20 +336,15 @@ namespace nix {
|
|||
* --------------------------------------------------------------------------*/
|
||||
|
||||
TEST(string2Int, emptyString) {
|
||||
double n;
|
||||
ASSERT_EQ(string2Int("", n), false);
|
||||
ASSERT_EQ(string2Int<int>(""), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(string2Int, trivialConversions) {
|
||||
double n;
|
||||
ASSERT_EQ(string2Int("1", n), true);
|
||||
ASSERT_EQ(n, 1);
|
||||
ASSERT_EQ(string2Int<int>("1"), 1);
|
||||
|
||||
ASSERT_EQ(string2Int("0", n), true);
|
||||
ASSERT_EQ(n, 0);
|
||||
ASSERT_EQ(string2Int<int>("0"), 0);
|
||||
|
||||
ASSERT_EQ(string2Int("-100", n), true);
|
||||
ASSERT_EQ(n, (-100));
|
||||
ASSERT_EQ(string2Int<int>("-100"), -100);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
|||
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
||||
|
||||
// A Git ref (i.e. branch or tag name).
|
||||
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.-]*"; // FIXME: check
|
||||
const static std::string refRegexS = "[a-zA-Z0-9][a-zA-Z0-9_.\\/-]*"; // FIXME: check
|
||||
extern std::regex refRegex;
|
||||
|
||||
// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
|
||||
|
|
|
|||
|
|
@ -397,21 +397,49 @@ bool statusOk(int status);
|
|||
|
||||
|
||||
/* Parse a string into an integer. */
|
||||
template<class N> bool string2Int(const string & s, N & n)
|
||||
template<class N>
|
||||
std::optional<N> string2Int(const std::string & s)
|
||||
{
|
||||
if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
||||
return false;
|
||||
if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
|
||||
return std::nullopt;
|
||||
std::istringstream str(s);
|
||||
N n;
|
||||
str >> n;
|
||||
return str && str.get() == EOF;
|
||||
if (str && str.get() == EOF) return n;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or
|
||||
'T' denoting a binary unit prefix. */
|
||||
template<class N>
|
||||
N string2IntWithUnitPrefix(std::string s)
|
||||
{
|
||||
N multiplier = 1;
|
||||
if (!s.empty()) {
|
||||
char u = std::toupper(*s.rbegin());
|
||||
if (std::isalpha(u)) {
|
||||
if (u == 'K') multiplier = 1ULL << 10;
|
||||
else if (u == 'M') multiplier = 1ULL << 20;
|
||||
else if (u == 'G') multiplier = 1ULL << 30;
|
||||
else if (u == 'T') multiplier = 1ULL << 40;
|
||||
else throw UsageError("invalid unit specifier '%1%'", u);
|
||||
s.resize(s.size() - 1);
|
||||
}
|
||||
}
|
||||
if (auto n = string2Int<N>(s))
|
||||
return *n * multiplier;
|
||||
throw UsageError("'%s' is not an integer", s);
|
||||
}
|
||||
|
||||
/* Parse a string into a float. */
|
||||
template<class N> bool string2Float(const string & s, N & n)
|
||||
template<class N>
|
||||
std::optional<N> string2Float(const string & s)
|
||||
{
|
||||
std::istringstream str(s);
|
||||
N n;
|
||||
str >> n;
|
||||
return str && str.get() == EOF;
|
||||
if (str && str.get() == EOF) return n;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -217,9 +217,9 @@ static void main_nix_build(int argc, char * * argv)
|
|||
// read the shebang to understand which packages to read from. Since
|
||||
// this is handled via nix-shell -p, we wrap our ruby script execution
|
||||
// in ruby -e 'load' which ignores the shebangs.
|
||||
envCommand = (format("exec %1% %2% -e 'load(\"%3%\")' -- %4%") % execArgs % interpreter % script % joined.str()).str();
|
||||
envCommand = (format("exec %1% %2% -e 'load(ARGV.shift)' -- %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str();
|
||||
} else {
|
||||
envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % script % joined.str()).str();
|
||||
envCommand = (format("exec %1% %2% %3% %4%") % execArgs % interpreter % shellEscape(script) % joined.str()).str();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1138,38 +1138,38 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
|||
i.queryName(), j)
|
||||
});
|
||||
else {
|
||||
if (v->type == tString) {
|
||||
if (v->type() == nString) {
|
||||
attrs2["type"] = "string";
|
||||
attrs2["value"] = v->string.s;
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type == tInt) {
|
||||
} else if (v->type() == nInt) {
|
||||
attrs2["type"] = "int";
|
||||
attrs2["value"] = (format("%1%") % v->integer).str();
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type == tFloat) {
|
||||
} else if (v->type() == nFloat) {
|
||||
attrs2["type"] = "float";
|
||||
attrs2["value"] = (format("%1%") % v->fpoint).str();
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->type == tBool) {
|
||||
} else if (v->type() == nBool) {
|
||||
attrs2["type"] = "bool";
|
||||
attrs2["value"] = v->boolean ? "true" : "false";
|
||||
xml.writeEmptyElement("meta", attrs2);
|
||||
} else if (v->isList()) {
|
||||
} else if (v->type() == nList) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
for (unsigned int j = 0; j < v->listSize(); ++j) {
|
||||
if (v->listElems()[j]->type != tString) continue;
|
||||
if (v->listElems()[j]->type() != nString) continue;
|
||||
XMLAttrs attrs3;
|
||||
attrs3["value"] = v->listElems()[j]->string.s;
|
||||
xml.writeEmptyElement("string", attrs3);
|
||||
}
|
||||
} else if (v->type == tAttrs) {
|
||||
} else if (v->type() == nAttrs) {
|
||||
attrs2["type"] = "strings";
|
||||
XMLOpenElement m(xml, "meta", attrs2);
|
||||
Bindings & attrs = *v->attrs;
|
||||
for (auto &i : attrs) {
|
||||
Attr & a(*attrs.find(i.name));
|
||||
if(a.value->type != tString) continue;
|
||||
if(a.value->type() != nString) continue;
|
||||
XMLAttrs attrs3;
|
||||
attrs3["type"] = i.name;
|
||||
attrs3["value"] = a.value->string.s;
|
||||
|
|
@ -1250,11 +1250,10 @@ static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArg
|
|||
if (opArgs.size() != 1)
|
||||
throw UsageError("exactly one argument expected");
|
||||
|
||||
GenerationNumber dstGen;
|
||||
if (!string2Int(opArgs.front(), dstGen))
|
||||
if (auto dstGen = string2Int<GenerationNumber>(opArgs.front()))
|
||||
switchGeneration(globals, *dstGen);
|
||||
else
|
||||
throw UsageError("expected a generation number");
|
||||
|
||||
switchGeneration(globals, dstGen);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1308,17 +1307,17 @@ static void opDeleteGenerations(Globals & globals, Strings opFlags, Strings opAr
|
|||
if(opArgs.front().size() < 2)
|
||||
throw Error("invalid number of generations ‘%1%’", opArgs.front());
|
||||
string str_max = string(opArgs.front(), 1, opArgs.front().size());
|
||||
GenerationNumber max;
|
||||
if (!string2Int(str_max, max) || max == 0)
|
||||
auto max = string2Int<GenerationNumber>(str_max);
|
||||
if (!max || *max == 0)
|
||||
throw Error("invalid number of generations to keep ‘%1%’", opArgs.front());
|
||||
deleteGenerationsGreaterThan(globals.profile, max, globals.dryRun);
|
||||
deleteGenerationsGreaterThan(globals.profile, *max, globals.dryRun);
|
||||
} else {
|
||||
std::set<GenerationNumber> gens;
|
||||
for (auto & i : opArgs) {
|
||||
GenerationNumber n;
|
||||
if (!string2Int(i, n))
|
||||
if (auto n = string2Int<GenerationNumber>(i))
|
||||
gens.insert(*n);
|
||||
else
|
||||
throw UsageError("invalid generation number '%1%'", i);
|
||||
gens.insert(n);
|
||||
}
|
||||
deleteGenerations(globals.profile, gens, globals.dryRun);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,12 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
|||
output paths, and optionally the derivation path, as well
|
||||
as the meta attributes. */
|
||||
Path drvPath = keepDerivations ? i.queryDrvPath() : "";
|
||||
DrvInfo::Outputs outputs = i.queryOutputs(true);
|
||||
StringSet metaNames = i.queryMetaNames();
|
||||
|
||||
Value & v(*state.allocValue());
|
||||
manifest.listElems()[n++] = &v;
|
||||
state.mkAttrs(v, 16);
|
||||
state.mkAttrs(v, 7 + outputs.size());
|
||||
|
||||
mkString(*state.allocAttr(v, state.sType), "derivation");
|
||||
mkString(*state.allocAttr(v, state.sName), i.queryName());
|
||||
|
|
@ -68,7 +70,6 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
|||
mkString(*state.allocAttr(v, state.sDrvPath), i.queryDrvPath());
|
||||
|
||||
// Copy each output meant for installation.
|
||||
DrvInfo::Outputs outputs = i.queryOutputs(true);
|
||||
Value & vOutputs = *state.allocAttr(v, state.sOutputs);
|
||||
state.mkList(vOutputs, outputs.size());
|
||||
unsigned int m = 0;
|
||||
|
|
@ -88,8 +89,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
|||
|
||||
// Copy the meta attributes.
|
||||
Value & vMeta = *state.allocAttr(v, state.sMeta);
|
||||
state.mkAttrs(vMeta, 16);
|
||||
StringSet metaNames = i.queryMetaNames();
|
||||
state.mkAttrs(vMeta, metaNames.size());
|
||||
for (auto & j : metaNames) {
|
||||
Value * v = i.queryMeta(j);
|
||||
if (!v) continue;
|
||||
|
|
|
|||
|
|
@ -1,232 +0,0 @@
|
|||
#include "hash.hh"
|
||||
#include "shared.hh"
|
||||
#include "filetransfer.hh"
|
||||
#include "store-api.hh"
|
||||
#include "eval.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "common-eval-args.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "finally.hh"
|
||||
#include "../nix/legacy.hh"
|
||||
#include "progress-bar.hh"
|
||||
#include "tarfile.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
using namespace nix;
|
||||
|
||||
|
||||
/* If ‘uri’ starts with ‘mirror://’, then resolve it using the list of
|
||||
mirrors defined in Nixpkgs. */
|
||||
string resolveMirrorUri(EvalState & state, string uri)
|
||||
{
|
||||
if (string(uri, 0, 9) != "mirror://") return uri;
|
||||
|
||||
string s(uri, 9);
|
||||
auto p = s.find('/');
|
||||
if (p == string::npos) throw Error("invalid mirror URI");
|
||||
string mirrorName(s, 0, p);
|
||||
|
||||
Value vMirrors;
|
||||
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
|
||||
state.forceAttrs(vMirrors);
|
||||
|
||||
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
|
||||
if (mirrorList == vMirrors.attrs->end())
|
||||
throw Error("unknown mirror name '%1%'", mirrorName);
|
||||
state.forceList(*mirrorList->value);
|
||||
|
||||
if (mirrorList->value->listSize() < 1)
|
||||
throw Error("mirror URI '%1%' did not expand to anything", uri);
|
||||
|
||||
string mirror = state.forceString(*mirrorList->value->listElems()[0]);
|
||||
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
|
||||
}
|
||||
|
||||
|
||||
static int main_nix_prefetch_url(int argc, char * * argv)
|
||||
{
|
||||
{
|
||||
HashType ht = htSHA256;
|
||||
std::vector<string> args;
|
||||
bool printPath = getEnv("PRINT_PATH") == "1";
|
||||
bool fromExpr = false;
|
||||
string attrPath;
|
||||
bool unpack = false;
|
||||
bool executable = false;
|
||||
string name;
|
||||
|
||||
struct MyArgs : LegacyArgs, MixEvalArgs
|
||||
{
|
||||
using LegacyArgs::LegacyArgs;
|
||||
};
|
||||
|
||||
MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) {
|
||||
if (*arg == "--help")
|
||||
showManPage("nix-prefetch-url");
|
||||
else if (*arg == "--version")
|
||||
printVersion("nix-prefetch-url");
|
||||
else if (*arg == "--type") {
|
||||
string s = getArg(*arg, arg, end);
|
||||
ht = parseHashType(s);
|
||||
}
|
||||
else if (*arg == "--print-path")
|
||||
printPath = true;
|
||||
else if (*arg == "--attr" || *arg == "-A") {
|
||||
fromExpr = true;
|
||||
attrPath = getArg(*arg, arg, end);
|
||||
}
|
||||
else if (*arg == "--unpack")
|
||||
unpack = true;
|
||||
else if (*arg == "--executable")
|
||||
executable = true;
|
||||
else if (*arg == "--name")
|
||||
name = getArg(*arg, arg, end);
|
||||
else if (*arg != "" && arg->at(0) == '-')
|
||||
return false;
|
||||
else
|
||||
args.push_back(*arg);
|
||||
return true;
|
||||
});
|
||||
|
||||
myArgs.parseCmdline(argvToStrings(argc, argv));
|
||||
|
||||
initPlugins();
|
||||
|
||||
if (args.size() > 2)
|
||||
throw UsageError("too many arguments");
|
||||
|
||||
Finally f([]() { stopProgressBar(); });
|
||||
|
||||
if (isatty(STDERR_FILENO))
|
||||
startProgressBar();
|
||||
|
||||
auto store = openStore();
|
||||
auto state = std::make_unique<EvalState>(myArgs.searchPath, store);
|
||||
|
||||
Bindings & autoArgs = *myArgs.getAutoArgs(*state);
|
||||
|
||||
/* If -A is given, get the URI from the specified Nix
|
||||
expression. */
|
||||
string uri;
|
||||
if (!fromExpr) {
|
||||
if (args.empty())
|
||||
throw UsageError("you must specify a URI");
|
||||
uri = args[0];
|
||||
} else {
|
||||
Path path = resolveExprPath(lookupFileArg(*state, args.empty() ? "." : args[0]));
|
||||
Value vRoot;
|
||||
state->evalFile(path, vRoot);
|
||||
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
||||
state->forceAttrs(v);
|
||||
|
||||
/* Extract the URI. */
|
||||
auto attr = v.attrs->find(state->symbols.create("urls"));
|
||||
if (attr == v.attrs->end())
|
||||
throw Error("attribute set does not contain a 'urls' attribute");
|
||||
state->forceList(*attr->value);
|
||||
if (attr->value->listSize() < 1)
|
||||
throw Error("'urls' list is empty");
|
||||
uri = state->forceString(*attr->value->listElems()[0]);
|
||||
|
||||
/* Extract the hash mode. */
|
||||
attr = v.attrs->find(state->symbols.create("outputHashMode"));
|
||||
if (attr == v.attrs->end())
|
||||
printInfo("warning: this does not look like a fetchurl call");
|
||||
else
|
||||
unpack = state->forceString(*attr->value) == "recursive";
|
||||
|
||||
/* Extract the name. */
|
||||
if (name.empty()) {
|
||||
attr = v.attrs->find(state->symbols.create("name"));
|
||||
if (attr != v.attrs->end())
|
||||
name = state->forceString(*attr->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out a name in the Nix store. */
|
||||
if (name.empty())
|
||||
name = baseNameOf(uri);
|
||||
if (name.empty())
|
||||
throw Error("cannot figure out file name for '%1%'", uri);
|
||||
|
||||
/* If an expected hash is given, the file may already exist in
|
||||
the store. */
|
||||
std::optional<Hash> expectedHash;
|
||||
Hash hash(ht);
|
||||
std::optional<StorePath> storePath;
|
||||
if (args.size() == 2) {
|
||||
expectedHash = Hash::parseAny(args[1], ht);
|
||||
const auto recursive = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||
storePath = store->makeFixedOutputPath(recursive, *expectedHash, name);
|
||||
if (store->isValidPath(*storePath))
|
||||
hash = *expectedHash;
|
||||
else
|
||||
storePath.reset();
|
||||
}
|
||||
|
||||
if (!storePath) {
|
||||
|
||||
auto actualUri = resolveMirrorUri(*state, uri);
|
||||
|
||||
AutoDelete tmpDir(createTempDir(), true);
|
||||
Path tmpFile = (Path) tmpDir + "/tmp";
|
||||
|
||||
/* Download the file. */
|
||||
{
|
||||
auto mode = 0600;
|
||||
if (executable)
|
||||
mode = 0700;
|
||||
|
||||
AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode);
|
||||
if (!fd) throw SysError("creating temporary file '%s'", tmpFile);
|
||||
|
||||
FdSink sink(fd.get());
|
||||
|
||||
FileTransferRequest req(actualUri);
|
||||
req.decompress = false;
|
||||
getFileTransfer()->download(std::move(req), sink);
|
||||
}
|
||||
|
||||
/* Optionally unpack the file. */
|
||||
if (unpack) {
|
||||
printInfo("unpacking...");
|
||||
Path unpacked = (Path) tmpDir + "/unpacked";
|
||||
createDirs(unpacked);
|
||||
unpackTarfile(tmpFile, unpacked);
|
||||
|
||||
/* If the archive unpacks to a single file/directory, then use
|
||||
that as the top-level. */
|
||||
auto entries = readDirectory(unpacked);
|
||||
if (entries.size() == 1)
|
||||
tmpFile = unpacked + "/" + entries[0].name;
|
||||
else
|
||||
tmpFile = unpacked;
|
||||
}
|
||||
|
||||
const auto method = unpack || executable ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||
|
||||
auto info = store->addToStoreSlow(name, tmpFile, method, ht, expectedHash);
|
||||
storePath = info.path;
|
||||
assert(info.ca);
|
||||
hash = getContentAddressHash(*info.ca);
|
||||
}
|
||||
|
||||
stopProgressBar();
|
||||
|
||||
if (!printPath)
|
||||
printInfo("path is '%s'", store->printStorePath(*storePath));
|
||||
|
||||
std::cout << printHash16or32(hash) << std::endl;
|
||||
if (printPath)
|
||||
std::cout << store->printStorePath(*storePath) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static RegisterLegacyCommand r_nix_prefetch_url("nix-prefetch-url", main_nix_prefetch_url);
|
||||
|
|
@ -19,10 +19,6 @@
|
|||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if HAVE_SODIUM
|
||||
#include <sodium.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace nix_store {
|
||||
|
||||
|
|
@ -761,7 +757,7 @@ static void opRepairPath(Strings opFlags, Strings opArgs)
|
|||
throw UsageError("no flags expected");
|
||||
|
||||
for (auto & i : opArgs)
|
||||
ensureLocalStore()->repairPath(store->followLinksToStorePath(i));
|
||||
store->repairPath(store->followLinksToStorePath(i));
|
||||
}
|
||||
|
||||
/* Optimise the disk space usage of the Nix store by hard-linking
|
||||
|
|
@ -980,21 +976,11 @@ static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs)
|
|||
string secretKeyFile = *i++;
|
||||
string publicKeyFile = *i++;
|
||||
|
||||
#if HAVE_SODIUM
|
||||
if (sodium_init() == -1)
|
||||
throw Error("could not initialise libsodium");
|
||||
auto secretKey = SecretKey::generate(keyName);
|
||||
|
||||
unsigned char pk[crypto_sign_PUBLICKEYBYTES];
|
||||
unsigned char sk[crypto_sign_SECRETKEYBYTES];
|
||||
if (crypto_sign_keypair(pk, sk) != 0)
|
||||
throw Error("key generation failed");
|
||||
|
||||
writeFile(publicKeyFile, keyName + ":" + base64Encode(string((char *) pk, crypto_sign_PUBLICKEYBYTES)));
|
||||
writeFile(publicKeyFile, secretKey.toPublicKey().to_string());
|
||||
umask(0077);
|
||||
writeFile(secretKeyFile, keyName + ":" + base64Encode(string((char *) sk, crypto_sign_SECRETKEYBYTES)));
|
||||
#else
|
||||
throw Error("Nix was not compiled with libsodium, required for signed binary cache support");
|
||||
#endif
|
||||
writeFile(secretKeyFile, secretKey.to_string());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
|
|||
addFlag({
|
||||
.longName = "name",
|
||||
.shortName = 'n',
|
||||
.description = "name component of the store path",
|
||||
.description = "Override the name component of the store path. It defaults to the base name of *path*.",
|
||||
.labels = {"name"},
|
||||
.handler = {&namePart},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
|||
addFlag({
|
||||
.longName = "out-link",
|
||||
.shortName = 'o',
|
||||
.description = "path of the symlink to the build result",
|
||||
.description = "Use *path* as prefix for the symlinks to the build results. It defaults to `result`.",
|
||||
.labels = {"path"},
|
||||
.handler = {&outLink},
|
||||
.completer = completePath
|
||||
|
|
@ -27,13 +27,13 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
|||
|
||||
addFlag({
|
||||
.longName = "no-link",
|
||||
.description = "do not create a symlink to the build result",
|
||||
.description = "Do not create symlinks to the build results.",
|
||||
.handler = {&outLink, Path("")},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "rebuild",
|
||||
.description = "rebuild an already built package and compare the result to the existing store paths",
|
||||
.description = "Rebuild an already built package and compare the result to the existing store paths.",
|
||||
.handler = {&buildMode, bmCheck},
|
||||
});
|
||||
}
|
||||
|
|
@ -43,22 +43,11 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
|||
return "build a derivation or fetch a store path";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To build and run GNU Hello from NixOS 17.03:",
|
||||
"nix build -f channel:nixos-17.03 hello; ./result/bin/hello"
|
||||
},
|
||||
Example{
|
||||
"To build the build.x86_64-linux attribute from release.nix:",
|
||||
"nix build -f release.nix build.x86_64-linux"
|
||||
},
|
||||
Example{
|
||||
"To make a profile point at GNU Hello:",
|
||||
"nix build --profile /tmp/profile nixpkgs#hello"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "build.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
|
|
@ -69,7 +58,8 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
|||
|
||||
if (outLink != "")
|
||||
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
||||
for (size_t i = 0; i < buildables.size(); ++i)
|
||||
for (const auto & [_i, buildable] : enumerate(buildables)) {
|
||||
auto i = _i;
|
||||
std::visit(overloaded {
|
||||
[&](BuildableOpaque bo) {
|
||||
std::string symlink = outLink;
|
||||
|
|
@ -85,7 +75,8 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
|||
store2->addPermRoot(output.second, absPath(symlink));
|
||||
}
|
||||
},
|
||||
}, buildables[i]);
|
||||
}, buildable);
|
||||
}
|
||||
|
||||
updateProfile(buildables);
|
||||
|
||||
|
|
|
|||
92
src/nix/build.md
Normal file
92
src/nix/build.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Build the default package from the flake in the current directory:
|
||||
|
||||
```console
|
||||
# nix build
|
||||
```
|
||||
|
||||
* Build and run GNU Hello from the `nixpkgs` flake:
|
||||
|
||||
```console
|
||||
# nix build nixpkgs#hello
|
||||
# ./result/bin/hello
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
* Build GNU Hello and Cowsay, leaving two result symlinks:
|
||||
|
||||
```console
|
||||
# nix build nixpkgs#hello nixpkgs#cowsay
|
||||
# ls -l result*
|
||||
lrwxrwxrwx 1 … result -> /nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10
|
||||
lrwxrwxrwx 1 … result-1 -> /nix/store/rkfrm0z6x6jmi7d3gsmma4j53h15mg33-cowsay-3.03+dfsg2
|
||||
```
|
||||
|
||||
* Build a specific output:
|
||||
|
||||
```console
|
||||
# nix build nixpkgs#glibc.dev
|
||||
# ls -ld ./result-dev
|
||||
lrwxrwxrwx 1 … ./result-dev -> /nix/store/dkm3gwl0xrx0wrw6zi5x3px3lpgjhlw4-glibc-2.32-dev
|
||||
```
|
||||
|
||||
* Build attribute `build.x86_64-linux` from (non-flake) Nix expression
|
||||
`release.nix`:
|
||||
|
||||
```console
|
||||
# nix build -f release.nix build.x86_64-linux
|
||||
```
|
||||
|
||||
* Build a NixOS system configuration from a flake, and make a profile
|
||||
point to the result:
|
||||
|
||||
```console
|
||||
# nix build --profile /nix/var/nix/profiles/system \
|
||||
~/my-configurations#nixosConfigurations.machine.config.system.build.toplevel
|
||||
```
|
||||
|
||||
(This is essentially what `nixos-rebuild` does.)
|
||||
|
||||
* Build an expression specified on the command line:
|
||||
|
||||
```console
|
||||
# nix build --impure --expr \
|
||||
'with import <nixpkgs> {};
|
||||
runCommand "foo" {
|
||||
buildInputs = [ hello ];
|
||||
}
|
||||
"hello > $out"'
|
||||
# cat ./result
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Note that `--impure` is needed because we're using `<nixpkgs>`,
|
||||
which relies on the `$NIX_PATH` environment variable.
|
||||
|
||||
* Fetch a store path from the configured substituters, if it doesn't
|
||||
already exist:
|
||||
|
||||
```console
|
||||
# nix build /nix/store/rkfrm0z6x6jmi7d3gsmma4j53h15mg33-cowsay-3.03+dfsg2
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
`nix build` builds the specified *installables*. Installables that
|
||||
resolve to derivations are built (or substituted if possible). Store
|
||||
path installables are substituted.
|
||||
|
||||
Unless `--no-link` is specified, after a successful build, it creates
|
||||
symlinks to the store paths of the installables. These symlinks have
|
||||
the prefix `./result` by default; this can be overriden using the
|
||||
`--out-link` option. Each symlink has a suffix `-<N>-<outname>`, where
|
||||
*N* is the index of the installable (with the left-most installable
|
||||
having index 0), and *outname* is the symbolic derivation output name
|
||||
(e.g. `bin`, `dev` or `lib`). `-<N>` is omitted if *N* = 0, and
|
||||
`-<outname>` is omitted if *outname* = `out` (denoting the default
|
||||
output).
|
||||
|
||||
)""
|
||||
|
|
@ -16,7 +16,7 @@ struct CmdBundle : InstallableCommand
|
|||
{
|
||||
addFlag({
|
||||
.longName = "bundler",
|
||||
.description = "use custom bundler",
|
||||
.description = fmt("Use a custom bundler instead of the default (`%s`).", bundler),
|
||||
.labels = {"flake-url"},
|
||||
.handler = {&bundler},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
|
|
@ -27,7 +27,7 @@ struct CmdBundle : InstallableCommand
|
|||
addFlag({
|
||||
.longName = "out-link",
|
||||
.shortName = 'o',
|
||||
.description = "path of the symlink to the build result",
|
||||
.description = "Override the name of the symlink to the build result. It defaults to the base name of the app.",
|
||||
.labels = {"path"},
|
||||
.handler = {&outLink},
|
||||
.completer = completePath
|
||||
|
|
@ -40,14 +40,11 @@ struct CmdBundle : InstallableCommand
|
|||
return "bundle an application so that it works outside of the Nix store";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To bundle Hello:",
|
||||
"nix bundle hello"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "bundle.md"
|
||||
;
|
||||
}
|
||||
|
||||
Category category() override { return catSecondary; }
|
||||
|
|
@ -93,7 +90,7 @@ struct CmdBundle : InstallableCommand
|
|||
mkString(*evalState->allocAttr(*arg, evalState->symbols.create("system")), settings.thisSystem.get());
|
||||
|
||||
arg->attrs->sort();
|
||||
|
||||
|
||||
auto vRes = evalState->allocValue();
|
||||
evalState->callFunction(*bundler.toValue(*evalState).first, *arg, *vRes, noPos);
|
||||
|
||||
|
|
|
|||
36
src/nix/bundle.md
Normal file
36
src/nix/bundle.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Bundle Hello:
|
||||
|
||||
```console
|
||||
# nix bundle nixpkgs#hello
|
||||
# ./hello
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
* Bundle a specific version of Nix:
|
||||
|
||||
```console
|
||||
# nix bundle github:NixOS/nix/e3ddffb27e5fc37a209cfd843c6f7f6a9460a8ec
|
||||
# ./nix --version
|
||||
nix (Nix) 2.4pre20201215_e3ddffb
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
`nix bundle` packs the closure of the [Nix app](./nix3-run.md)
|
||||
*installable* into a single self-extracting executable. See the
|
||||
[`nix-bundle` homepage](https://github.com/matthewbauer/nix-bundle)
|
||||
for more details.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This command only works on Linux.
|
||||
|
||||
# Bundler definitions
|
||||
|
||||
TODO
|
||||
|
||||
)""
|
||||
|
|
@ -37,6 +37,13 @@ struct CmdCatStore : StoreCommand, MixCat
|
|||
return "print the contents of a file in the Nix store on stdout";
|
||||
}
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "store-cat.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
cat(store->getFSAccessor());
|
||||
|
|
@ -62,6 +69,13 @@ struct CmdCatNar : StoreCommand, MixCat
|
|||
return "print the contents of a file inside a NAR file on stdout";
|
||||
}
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "nar-cat.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
cat(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
|
||||
|
|
|
|||
|
|
@ -65,18 +65,18 @@ StorePathsCommand::StorePathsCommand(bool recursive)
|
|||
if (recursive)
|
||||
addFlag({
|
||||
.longName = "no-recursive",
|
||||
.description = "apply operation to specified paths only",
|
||||
.description = "Apply operation to specified paths only.",
|
||||
.handler = {&this->recursive, false},
|
||||
});
|
||||
else
|
||||
addFlag({
|
||||
.longName = "recursive",
|
||||
.shortName = 'r',
|
||||
.description = "apply operation to closure of the specified paths",
|
||||
.description = "Apply operation to closure of the specified paths.",
|
||||
.handler = {&this->recursive, true},
|
||||
});
|
||||
|
||||
mkFlag(0, "all", "apply operation to the entire store", &all);
|
||||
mkFlag(0, "all", "Apply the operation to every store path.", &all);
|
||||
}
|
||||
|
||||
void StorePathsCommand::run(ref<Store> store)
|
||||
|
|
@ -133,7 +133,7 @@ MixProfile::MixProfile()
|
|||
{
|
||||
addFlag({
|
||||
.longName = "profile",
|
||||
.description = "profile to update",
|
||||
.description = "The profile to update.",
|
||||
.labels = {"path"},
|
||||
.handler = {&profile},
|
||||
.completer = completePath
|
||||
|
|
@ -190,14 +190,14 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
|
|||
addFlag({
|
||||
.longName = "ignore-environment",
|
||||
.shortName = 'i',
|
||||
.description = "clear the entire environment (except those specified with --keep)",
|
||||
.description = "Clear the entire environment (except those specified with `--keep`).",
|
||||
.handler = {&ignoreEnvironment, true},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "keep",
|
||||
.shortName = 'k',
|
||||
.description = "keep specified environment variable",
|
||||
.description = "Keep the environment variable *name*.",
|
||||
.labels = {"name"},
|
||||
.handler = {[&](std::string s) { keep.insert(s); }},
|
||||
});
|
||||
|
|
@ -205,7 +205,7 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
|
|||
addFlag({
|
||||
.longName = "unset",
|
||||
.shortName = 'u',
|
||||
.description = "unset specified environment variable",
|
||||
.description = "Unset the environment variable *name*.",
|
||||
.labels = {"name"},
|
||||
.handler = {[&](std::string s) { unset.insert(s); }},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace nix {
|
|||
|
||||
extern std::string programPath;
|
||||
|
||||
extern char * * savedArgv;
|
||||
|
||||
class EvalState;
|
||||
struct Pos;
|
||||
class Store;
|
||||
|
|
@ -261,6 +263,8 @@ void completeFlakeRefWithFragment(
|
|||
const Strings & defaultFlakeAttrPaths,
|
||||
std::string_view prefix);
|
||||
|
||||
std::string showVersions(const std::set<std::string> & versions);
|
||||
|
||||
void printClosureDiff(
|
||||
ref<Store> store,
|
||||
const StorePath & beforePath,
|
||||
|
|
|
|||
|
|
@ -21,28 +21,28 @@ struct CmdCopy : StorePathsCommand
|
|||
{
|
||||
addFlag({
|
||||
.longName = "from",
|
||||
.description = "URI of the source Nix store",
|
||||
.description = "URL of the source Nix store.",
|
||||
.labels = {"store-uri"},
|
||||
.handler = {&srcUri},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "to",
|
||||
.description = "URI of the destination Nix store",
|
||||
.description = "URL of the destination Nix store.",
|
||||
.labels = {"store-uri"},
|
||||
.handler = {&dstUri},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "no-check-sigs",
|
||||
.description = "do not require that paths are signed by trusted keys",
|
||||
.description = "Do not require that paths are signed by trusted keys.",
|
||||
.handler = {&checkSigs, NoCheckSigs},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "substitute-on-destination",
|
||||
.shortName = 's',
|
||||
.description = "whether to try substitutes on the destination store (only supported by SSH)",
|
||||
.description = "Whether to try substitutes on the destination store (only supported by SSH stores).",
|
||||
.handler = {&substitute, Substitute},
|
||||
});
|
||||
|
||||
|
|
@ -54,32 +54,11 @@ struct CmdCopy : StorePathsCommand
|
|||
return "copy paths between Nix stores";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To copy Firefox from the local store to a binary cache in file:///tmp/cache:",
|
||||
"nix copy --to file:///tmp/cache $(type -p firefox)"
|
||||
},
|
||||
Example{
|
||||
"To copy the entire current NixOS system closure to another machine via SSH:",
|
||||
"nix copy --to ssh://server /run/current-system"
|
||||
},
|
||||
Example{
|
||||
"To copy a closure from another machine via SSH:",
|
||||
"nix copy --from ssh://server /nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2"
|
||||
},
|
||||
#ifdef ENABLE_S3
|
||||
Example{
|
||||
"To copy Hello to an S3 binary cache:",
|
||||
"nix copy --to s3://my-bucket?region=eu-west-1 nixpkgs#hello"
|
||||
},
|
||||
Example{
|
||||
"To copy Hello to an S3-compatible binary cache:",
|
||||
"nix copy --to s3://my-bucket?region=eu-west-1&endpoint=example.com nixpkgs#hello"
|
||||
},
|
||||
#endif
|
||||
};
|
||||
return
|
||||
#include "copy.md"
|
||||
;
|
||||
}
|
||||
|
||||
Category category() override { return catSecondary; }
|
||||
|
|
|
|||
58
src/nix/copy.md
Normal file
58
src/nix/copy.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Copy Firefox from the local store to a binary cache in `/tmp/cache`:
|
||||
|
||||
```console
|
||||
# nix copy --to file:///tmp/cache $(type -p firefox)
|
||||
```
|
||||
|
||||
Note the `file://` - without this, the destination is a chroot
|
||||
store, not a binary cache.
|
||||
|
||||
* Copy the entire current NixOS system closure to another machine via
|
||||
SSH:
|
||||
|
||||
```console
|
||||
# nix copy -s --to ssh://server /run/current-system
|
||||
```
|
||||
|
||||
The `-s` flag causes the remote machine to try to substitute missing
|
||||
store paths, which may be faster if the link between the local and
|
||||
remote machines is slower than the link between the remote machine
|
||||
and its substituters (e.g. `https://cache.nixos.org`).
|
||||
|
||||
* Copy a closure from another machine via SSH:
|
||||
|
||||
```console
|
||||
# nix copy --from ssh://server /nix/store/a6cnl93nk1wxnq84brbbwr6hxw9gp2w9-blender-2.79-rc2
|
||||
```
|
||||
|
||||
* Copy Hello to a binary cache in an Amazon S3 bucket:
|
||||
|
||||
```console
|
||||
# nix copy --to s3://my-bucket?region=eu-west-1 nixpkgs#hello
|
||||
```
|
||||
|
||||
or to an S3-compatible storage system:
|
||||
|
||||
```console
|
||||
# nix copy --to s3://my-bucket?region=eu-west-1&endpoint=example.com nixpkgs#hello
|
||||
```
|
||||
|
||||
Note that this only works if Nix is built with AWS support.
|
||||
|
||||
* Copy a closure from `/nix/store` to the chroot store `/tmp/nix/nix/store`:
|
||||
|
||||
```console
|
||||
# nix copy --to /tmp/nix nixpkgs#hello --no-check-sigs
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
`nix copy` copies store path closures between two Nix stores. The
|
||||
source store is specified using `--from` and the destination using
|
||||
`--to`. If one of these is omitted, it defaults to the local store.
|
||||
|
||||
)""
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#include "command.hh"
|
||||
#include "shared.hh"
|
||||
#include "local-store.hh"
|
||||
#include "remote-store.hh"
|
||||
|
|
@ -150,7 +151,7 @@ static ref<Store> openUncachedStore()
|
|||
}
|
||||
|
||||
|
||||
static void daemonLoop(char * * argv)
|
||||
static void daemonLoop()
|
||||
{
|
||||
if (chdir("/") == -1)
|
||||
throw SysError("cannot change current directory");
|
||||
|
|
@ -232,9 +233,9 @@ static void daemonLoop(char * * argv)
|
|||
setSigChldAction(false);
|
||||
|
||||
// For debugging, stuff the pid into argv[1].
|
||||
if (peer.pidKnown && argv[1]) {
|
||||
if (peer.pidKnown && savedArgv[1]) {
|
||||
string processName = std::to_string(peer.pid);
|
||||
strncpy(argv[1], processName.c_str(), strlen(argv[1]));
|
||||
strncpy(savedArgv[1], processName.c_str(), strlen(savedArgv[1]));
|
||||
}
|
||||
|
||||
// Handle the connection.
|
||||
|
|
@ -264,6 +265,57 @@ static void daemonLoop(char * * argv)
|
|||
}
|
||||
}
|
||||
|
||||
static void runDaemon(bool stdio, std::optional<TrustedFlag> isTrustedOpt = {})
|
||||
{
|
||||
auto ensureNoTrustedFlag = [&]() {
|
||||
if (isTrustedOpt)
|
||||
throw Error("--trust and --no-trust flags are only for use with --stdio when this nix-daemon process is not proxying another");
|
||||
};
|
||||
|
||||
if (stdio) {
|
||||
if (auto store = openUncachedStore().dynamic_pointer_cast<RemoteStore>()) {
|
||||
ensureNoTrustedFlag();
|
||||
auto conn = store->openConnectionWrapper();
|
||||
int from = conn->from.fd;
|
||||
int to = conn->to.fd;
|
||||
|
||||
auto nfds = std::max(from, STDIN_FILENO) + 1;
|
||||
while (true) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(from, &fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
|
||||
throw SysError("waiting for data from client or server");
|
||||
if (FD_ISSET(from, &fds)) {
|
||||
auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
|
||||
if (res == -1)
|
||||
throw SysError("splicing data from daemon socket to stdout");
|
||||
else if (res == 0)
|
||||
throw EndOfFile("unexpected EOF from daemon socket");
|
||||
}
|
||||
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||
auto res = splice(STDIN_FILENO, nullptr, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
|
||||
if (res == -1)
|
||||
throw SysError("splicing data from stdin to daemon socket");
|
||||
else if (res == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FdSource from(STDIN_FILENO);
|
||||
FdSink to(STDOUT_FILENO);
|
||||
/* Auth hook is empty because in this mode we blindly trust the
|
||||
standard streams. Limiting access to those is explicitly
|
||||
not `nix-daemon`'s responsibility. */
|
||||
auto isTrusted = isTrustedOpt.value_or(Trusted);
|
||||
processConnection(openUncachedStore(), from, to, isTrusted, NotRecursive, [&](Store & _){});
|
||||
}
|
||||
} else {
|
||||
ensureNoTrustedFlag();
|
||||
daemonLoop();
|
||||
}
|
||||
}
|
||||
|
||||
static int main_nix_daemon(int argc, char * * argv)
|
||||
{
|
||||
|
|
@ -292,57 +344,34 @@ static int main_nix_daemon(int argc, char * * argv)
|
|||
|
||||
initPlugins();
|
||||
|
||||
auto ensureNoTrustedFlag = [&]() {
|
||||
if (isTrustedOpt)
|
||||
throw Error("--trust and --no-trust flags are only for use with --stdio when this nix-daemon process is not proxying another");
|
||||
};
|
||||
|
||||
if (stdio) {
|
||||
if (auto store = openUncachedStore().dynamic_pointer_cast<RemoteStore>()) {
|
||||
ensureNoTrustedFlag();
|
||||
auto conn = store->openConnectionWrapper();
|
||||
int from = conn->from.fd;
|
||||
int to = conn->to.fd;
|
||||
|
||||
auto nfds = std::max(from, STDIN_FILENO) + 1;
|
||||
while (true) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(from, &fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
|
||||
throw SysError("waiting for data from client or server");
|
||||
if (FD_ISSET(from, &fds)) {
|
||||
auto res = splice(from, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
|
||||
if (res == -1)
|
||||
throw SysError("splicing data from daemon socket to stdout");
|
||||
else if (res == 0)
|
||||
throw EndOfFile("unexpected EOF from daemon socket");
|
||||
}
|
||||
if (FD_ISSET(STDIN_FILENO, &fds)) {
|
||||
auto res = splice(STDIN_FILENO, nullptr, to, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
|
||||
if (res == -1)
|
||||
throw SysError("splicing data from stdin to daemon socket");
|
||||
else if (res == 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FdSource from(STDIN_FILENO);
|
||||
FdSink to(STDOUT_FILENO);
|
||||
/* Auth hook is empty because in this mode we blindly trust the
|
||||
standard streams. Limitting access to thoses is explicitly
|
||||
not `nix-daemon`'s responsibility. */
|
||||
auto isTrusted = isTrustedOpt.value_or(Trusted);
|
||||
processConnection(openUncachedStore(), from, to, isTrusted, NotRecursive, [&](Store & _){});
|
||||
}
|
||||
} else {
|
||||
ensureNoTrustedFlag();
|
||||
daemonLoop(argv);
|
||||
}
|
||||
runDaemon(stdio, isTrustedOpt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static RegisterLegacyCommand r_nix_daemon("nix-daemon", main_nix_daemon);
|
||||
|
||||
struct CmdDaemon : StoreCommand
|
||||
{
|
||||
std::string description() override
|
||||
{
|
||||
return "daemon to perform store operations on behalf of non-root clients";
|
||||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
||||
std::string doc() override
|
||||
{
|
||||
return
|
||||
#include "daemon.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
{
|
||||
runDaemon(false);
|
||||
}
|
||||
};
|
||||
|
||||
static auto rCmdDaemon = registerCommand2<CmdDaemon>({"daemon"});
|
||||
21
src/nix/daemon.md
Normal file
21
src/nix/daemon.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
R""(
|
||||
|
||||
# Example
|
||||
|
||||
* Run the daemon in the foreground:
|
||||
|
||||
```console
|
||||
# nix daemon
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command runs the Nix daemon, which is a required component in
|
||||
multi-user Nix installations. It performs build actions and other
|
||||
operations on the Nix store on behalf of non-root users. Usually you
|
||||
don't run the daemon directly; instead it's managed by a service
|
||||
management framework such as `systemd`.
|
||||
|
||||
Note that this daemon does not fork into the background.
|
||||
|
||||
)""
|
||||
|
|
@ -204,7 +204,7 @@ struct Common : InstallableCommand, MixProfile
|
|||
{
|
||||
addFlag({
|
||||
.longName = "redirect",
|
||||
.description = "redirect a store path to a mutable location",
|
||||
.description = "Redirect a store path to a mutable location.",
|
||||
.labels = {"installable", "outputs-dir"},
|
||||
.handler = {[&](std::string installable, std::string outputsDir) {
|
||||
redirects.push_back({installable, outputsDir});
|
||||
|
|
@ -334,7 +334,7 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
addFlag({
|
||||
.longName = "command",
|
||||
.shortName = 'c',
|
||||
.description = "command and arguments to be executed instead of an interactive shell",
|
||||
.description = "Instead of starting an interactive shell, start the specified command and arguments.",
|
||||
.labels = {"command", "args"},
|
||||
.handler = {[&](std::vector<std::string> ss) {
|
||||
if (ss.empty()) throw UsageError("--command requires at least one argument");
|
||||
|
|
@ -344,38 +344,38 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
|
||||
addFlag({
|
||||
.longName = "phase",
|
||||
.description = "phase to run (e.g. `build` or `configure`)",
|
||||
.description = "The stdenv phase to run (e.g. `build` or `configure`).",
|
||||
.labels = {"phase-name"},
|
||||
.handler = {&phase},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "configure",
|
||||
.description = "run the configure phase",
|
||||
.description = "Run the `configure` phase.",
|
||||
.handler = {&phase, {"configure"}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "build",
|
||||
.description = "run the build phase",
|
||||
.description = "Run the `build` phase.",
|
||||
.handler = {&phase, {"build"}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "check",
|
||||
.description = "run the check phase",
|
||||
.description = "Run the `check` phase.",
|
||||
.handler = {&phase, {"check"}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "install",
|
||||
.description = "run the install phase",
|
||||
.description = "Run the `install` phase.",
|
||||
.handler = {&phase, {"install"}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "installcheck",
|
||||
.description = "run the installcheck phase",
|
||||
.description = "Run the `installcheck` phase.",
|
||||
.handler = {&phase, {"installCheck"}},
|
||||
});
|
||||
}
|
||||
|
|
@ -385,30 +385,11 @@ struct CmdDevelop : Common, MixEnvironment
|
|||
return "run a bash shell that provides the build environment of a derivation";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To get the build environment of GNU hello:",
|
||||
"nix develop nixpkgs#hello"
|
||||
},
|
||||
Example{
|
||||
"To get the build environment of the default package of flake in the current directory:",
|
||||
"nix develop"
|
||||
},
|
||||
Example{
|
||||
"To store the build environment in a profile:",
|
||||
"nix develop --profile /tmp/my-shell nixpkgs#hello"
|
||||
},
|
||||
Example{
|
||||
"To use a build environment previously recorded in a profile:",
|
||||
"nix develop /tmp/my-shell"
|
||||
},
|
||||
Example{
|
||||
"To replace all occurences of a store path with a writable directory:",
|
||||
"nix develop --redirect nixpkgs#glibc.dev ~/my-glibc/outputs/dev"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "develop.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
|
|
@ -495,14 +476,11 @@ struct CmdPrintDevEnv : Common
|
|||
return "print shell code that can be sourced by bash to reproduce the build environment of a derivation";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To apply the build environment of GNU hello to the current shell:",
|
||||
". <(nix print-dev-env nixpkgs#hello)"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "print-dev-env.md"
|
||||
;
|
||||
}
|
||||
|
||||
Category category() override { return catUtility; }
|
||||
|
|
|
|||
94
src/nix/develop.md
Normal file
94
src/nix/develop.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Start a shell with the build environment of the default package of
|
||||
the flake in the current directory:
|
||||
|
||||
```console
|
||||
# nix develop
|
||||
```
|
||||
|
||||
Typical commands to run inside this shell are:
|
||||
|
||||
```console
|
||||
# configurePhase
|
||||
# buildPhase
|
||||
# installPhase
|
||||
```
|
||||
|
||||
Alternatively, you can run whatever build tools your project uses
|
||||
directly, e.g. for a typical Unix project:
|
||||
|
||||
```console
|
||||
# ./configure --prefix=$out
|
||||
# make
|
||||
# make install
|
||||
```
|
||||
|
||||
* Run a particular build phase directly:
|
||||
|
||||
```console
|
||||
# nix develop --configure
|
||||
# nix develop --build
|
||||
# nix develop --check
|
||||
# nix develop --install
|
||||
# nix develop --installcheck
|
||||
```
|
||||
|
||||
* Start a shell with the build environment of GNU Hello:
|
||||
|
||||
```console
|
||||
# nix develop nixpkgs#hello
|
||||
```
|
||||
|
||||
* Record a build environment in a profile:
|
||||
|
||||
```console
|
||||
# nix develop --profile /tmp/my-build-env nixpkgs#hello
|
||||
```
|
||||
|
||||
* Use a build environment previously recorded in a profile:
|
||||
|
||||
```console
|
||||
# nix develop /tmp/my-build-env
|
||||
```
|
||||
|
||||
* Replace all occurences of the store path corresponding to
|
||||
`glibc.dev` with a writable directory:
|
||||
|
||||
```console
|
||||
# nix develop --redirect nixpkgs#glibc.dev ~/my-glibc/outputs/dev
|
||||
```
|
||||
|
||||
Note that this is useful if you're running a `nix develop` shell for
|
||||
`nixpkgs#glibc` in `~/my-glibc` and want to compile another package
|
||||
against it.
|
||||
|
||||
# Description
|
||||
|
||||
`nix develop` starts a `bash` shell that provides an interactive build
|
||||
environment nearly identical to what Nix would use to build
|
||||
*installable*. Inside this shell, environment variables and shell
|
||||
functions are set up so that you can interactively and incrementally
|
||||
build your package.
|
||||
|
||||
Nix determines the build environment by building a modified version of
|
||||
the derivation *installable* that just records the environment
|
||||
initialised by `stdenv` and exits. This build environment can be
|
||||
recorded into a profile using `--profile`.
|
||||
|
||||
The prompt used by the `bash` shell can be customised by setting the
|
||||
`bash-prompt` and `bash-prompt-suffix` settings in `nix.conf` or in
|
||||
the flake's `nixConfig` attribute.
|
||||
|
||||
# Flake output attributes
|
||||
|
||||
If no flake output attribute is given, `nix run` tries the following
|
||||
flake output attributes:
|
||||
|
||||
* `devShell.<system>`
|
||||
|
||||
* `defaultPackage.<system>`
|
||||
|
||||
)""
|
||||
|
|
@ -121,14 +121,11 @@ struct CmdDiffClosures : SourceExprCommand
|
|||
return "show what packages and versions were added and removed between two closures";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
{
|
||||
"To show what got added and removed between two versions of the NixOS system profile:",
|
||||
"nix store diff-closures /nix/var/nix/profiles/system-655-link /nix/var/nix/profiles/system-658-link",
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "diff-closures.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store) override
|
||||
|
|
|
|||
51
src/nix/diff-closures.md
Normal file
51
src/nix/diff-closures.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Show what got added and removed between two versions of the NixOS
|
||||
system profile:
|
||||
|
||||
```console
|
||||
# nix store diff-closures /nix/var/nix/profiles/system-655-link /nix/var/nix/profiles/system-658-link
|
||||
acpi-call: 2020-04-07-5.8.16 → 2020-04-07-5.8.18
|
||||
baloo-widgets: 20.08.1 → 20.08.2
|
||||
bluez-qt: +12.6 KiB
|
||||
dolphin: 20.08.1 → 20.08.2, +13.9 KiB
|
||||
kdeconnect: 20.08.2 → ∅, -6597.8 KiB
|
||||
kdeconnect-kde: ∅ → 20.08.2, +6599.7 KiB
|
||||
…
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command shows the differences between the two closures *before*
|
||||
and *after* with respect to the addition, removal, or version change
|
||||
of packages, as well as changes in store path sizes.
|
||||
|
||||
For each package name in the two closures (where a package name is
|
||||
defined as the name component of a store path excluding the version),
|
||||
if there is a change in the set of versions of the package, or a
|
||||
change in the size of the store paths of more than 8 KiB, it prints a
|
||||
line like this:
|
||||
|
||||
```console
|
||||
dolphin: 20.08.1 → 20.08.2, +13.9 KiB
|
||||
```
|
||||
|
||||
No size change is shown if it's below the threshold. If the package
|
||||
does not exist in either the *before* or *after* closures, it is
|
||||
represented using `∅` (empty set) on the appropriate side of the
|
||||
arrow. If a package has an empty version string, the version is
|
||||
rendered as `ε` (epsilon).
|
||||
|
||||
There may be multiple versions of a package in each closure. In that
|
||||
case, only the changed versions are shown. Thus,
|
||||
|
||||
```console
|
||||
libfoo: 1.2, 1.3 → 1.4
|
||||
```
|
||||
|
||||
leaves open the possibility that there are other versions (e.g. `1.1`)
|
||||
that exist in both closures.
|
||||
|
||||
)""
|
||||
|
|
@ -11,14 +11,11 @@ struct CmdDumpPath : StorePathCommand
|
|||
return "serialise a store path to stdout in NAR format";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To get a NAR from the binary cache https://cache.nixos.org/:",
|
||||
"nix store dump-path --store https://cache.nixos.org/ /nix/store/7crrmih8c52r8fbnqb933dxrsp44md93-glibc-2.25"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "store-dump-path.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run(ref<Store> store, const StorePath & storePath) override
|
||||
|
|
@ -49,14 +46,11 @@ struct CmdDumpPath2 : Command
|
|||
return "serialise a path to stdout in NAR format";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To serialise directory 'foo' as a NAR:",
|
||||
"nix nar dump-path ./foo"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "nar-dump-path.md"
|
||||
;
|
||||
}
|
||||
|
||||
void run() override
|
||||
|
|
|
|||
|
|
@ -15,14 +15,11 @@ struct CmdEdit : InstallableCommand
|
|||
return "open the Nix expression of a Nix package in $EDITOR";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
Example{
|
||||
"To open the Nix expression of the GNU Hello package:",
|
||||
"nix edit nixpkgs#hello"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "edit.md"
|
||||
;
|
||||
}
|
||||
|
||||
Category category() override { return catSecondary; }
|
||||
|
|
|
|||
31
src/nix/edit.md
Normal file
31
src/nix/edit.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Open the Nix expression of the GNU Hello package:
|
||||
|
||||
```console
|
||||
# nix edit nixpkgs#hello
|
||||
```
|
||||
|
||||
* Get the filename and line number used by `nix edit`:
|
||||
|
||||
```console
|
||||
# nix eval --raw nixpkgs#hello.meta.position
|
||||
/nix/store/fvafw0gvwayzdan642wrv84pzm5bgpmy-source/pkgs/applications/misc/hello/default.nix:15
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command opens the Nix expression of a derivation in an
|
||||
editor. The filename and line number of the derivation are taken from
|
||||
its `meta.position` attribute. Nixpkgs' `stdenv.mkDerivation` sets
|
||||
this attribute to the location of the definition of the
|
||||
`meta.description`, `version` or `name` derivation attributes.
|
||||
|
||||
The editor to invoke is specified by the `EDITOR` environment
|
||||
variable. It defaults to `cat`. If the editor is `emacs`, `nano` or
|
||||
`vim`, it is passed the line number of the derivation using the
|
||||
argument `+<lineno>`.
|
||||
|
||||
)""
|
||||
|
|
@ -18,18 +18,18 @@ struct CmdEval : MixJSON, InstallableCommand
|
|||
|
||||
CmdEval()
|
||||
{
|
||||
mkFlag(0, "raw", "print strings unquoted", &raw);
|
||||
mkFlag(0, "raw", "Print strings without quotes or escaping.", &raw);
|
||||
|
||||
addFlag({
|
||||
.longName = "apply",
|
||||
.description = "apply a function to each argument",
|
||||
.description = "Apply the function *expr* to each argument.",
|
||||
.labels = {"expr"},
|
||||
.handler = {&apply},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "write-to",
|
||||
.description = "write a string or attrset of strings to 'path'",
|
||||
.description = "Write a string or attrset of strings to *path*.",
|
||||
.labels = {"path"},
|
||||
.handler = {&writeTo},
|
||||
});
|
||||
|
|
@ -40,30 +40,11 @@ struct CmdEval : MixJSON, InstallableCommand
|
|||
return "evaluate a Nix expression";
|
||||
}
|
||||
|
||||
Examples examples() override
|
||||
std::string doc() override
|
||||
{
|
||||
return {
|
||||
{
|
||||
"To evaluate a Nix expression given on the command line:",
|
||||
"nix eval --expr '1 + 2'"
|
||||
},
|
||||
{
|
||||
"To evaluate a Nix expression from a file or URI:",
|
||||
"nix eval -f ./my-nixpkgs hello.name"
|
||||
},
|
||||
{
|
||||
"To get the current version of Nixpkgs:",
|
||||
"nix eval --raw nixpkgs#lib.version"
|
||||
},
|
||||
{
|
||||
"To print the store path of the Hello package:",
|
||||
"nix eval --raw nixpkgs#hello"
|
||||
},
|
||||
{
|
||||
"To get a list of checks in the 'nix' flake:",
|
||||
"nix eval nix#checks.x86_64-linux --apply builtins.attrNames"
|
||||
},
|
||||
};
|
||||
return
|
||||
#include "eval.md"
|
||||
;
|
||||
}
|
||||
|
||||
Category category() override { return catSecondary; }
|
||||
|
|
@ -97,10 +78,10 @@ struct CmdEval : MixJSON, InstallableCommand
|
|||
recurse = [&](Value & v, const Pos & pos, const Path & path)
|
||||
{
|
||||
state->forceValue(v);
|
||||
if (v.type == tString)
|
||||
if (v.type() == nString)
|
||||
// FIXME: disallow strings with contexts?
|
||||
writeFile(path, v.string.s);
|
||||
else if (v.type == tAttrs) {
|
||||
else if (v.type() == nAttrs) {
|
||||
if (mkdir(path.c_str(), 0777) == -1)
|
||||
throw SysError("creating directory '%s'", path);
|
||||
for (auto & attr : *v.attrs)
|
||||
|
|
|
|||
74
src/nix/eval.md
Normal file
74
src/nix/eval.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Evaluate a Nix expression given on the command line:
|
||||
|
||||
```console
|
||||
# nix eval --expr '1 + 2'
|
||||
```
|
||||
|
||||
* Evaluate a Nix expression to JSON:
|
||||
|
||||
```console
|
||||
# nix eval --json --expr '{ x = 1; }'
|
||||
{"x":1}
|
||||
```
|
||||
|
||||
* Evaluate a Nix expression from a file:
|
||||
|
||||
```console
|
||||
# nix eval -f ./my-nixpkgs hello.name
|
||||
```
|
||||
|
||||
* Get the current version of the `nixpkgs` flake:
|
||||
|
||||
```console
|
||||
# nix eval --raw nixpkgs#lib.version
|
||||
```
|
||||
|
||||
* Print the store path of the Hello package:
|
||||
|
||||
```console
|
||||
# nix eval --raw nixpkgs#hello
|
||||
```
|
||||
|
||||
* Get a list of checks in the `nix` flake:
|
||||
|
||||
```console
|
||||
# nix eval nix#checks.x86_64-linux --apply builtins.attrNames
|
||||
```
|
||||
|
||||
* Generate a directory with the specified contents:
|
||||
|
||||
```console
|
||||
# nix eval --write-to ./out --expr '{ foo = "bar"; subdir.bla = "123"; }'
|
||||
# cat ./out/foo
|
||||
bar
|
||||
# cat ./out/subdir/bla
|
||||
123
|
||||
|
||||
# Description
|
||||
|
||||
This command evaluates the Nix expression *installable* and prints the
|
||||
result on standard output.
|
||||
|
||||
# Output format
|
||||
|
||||
`nix eval` can produce output in several formats:
|
||||
|
||||
* By default, the evaluation result is printed as a Nix expression.
|
||||
|
||||
* With `--json`, the evaluation result is printed in JSON format. Note
|
||||
that this fails if the result contains values that are not
|
||||
representable as JSON, such as functions.
|
||||
|
||||
* With `--raw`, the evaluation result must be a string, which is
|
||||
printed verbatim, without any quoting.
|
||||
|
||||
* With `--write-to` *path*, the evaluation result must be a string or
|
||||
a nested attribute set whose leaf values are strings. These strings
|
||||
are written to files named *path*/*attrpath*. *path* must not
|
||||
already exist.
|
||||
|
||||
)""
|
||||
29
src/nix/flake-archive.md
Normal file
29
src/nix/flake-archive.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Copy the `dwarffs` flake and its dependencies to a binary cache:
|
||||
|
||||
```console
|
||||
# nix flake archive --to file:///tmp/my-cache dwarffs
|
||||
```
|
||||
|
||||
* Fetch the `dwarffs` flake and its dependencies to the local Nix
|
||||
store:
|
||||
|
||||
```console
|
||||
# nix flake archive dwarffs
|
||||
```
|
||||
|
||||
* Print the store paths of the flake sources of NixOps without
|
||||
fetching them:
|
||||
|
||||
```console
|
||||
# nix flake archive --json --dry-run nixops
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
FIXME
|
||||
|
||||
)""
|
||||
68
src/nix/flake-check.md
Normal file
68
src/nix/flake-check.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
R""(
|
||||
|
||||
# Examples
|
||||
|
||||
* Evaluate the flake in the current directory, and build its checks:
|
||||
|
||||
```console
|
||||
# nix flake check
|
||||
```
|
||||
|
||||
* Verify that the `patchelf` flake evaluates, but don't build its
|
||||
checks:
|
||||
|
||||
```console
|
||||
# nix flake check --no-build github:NixOS/patchelf
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
This command verifies that the flake specified by flake reference
|
||||
*flake-url* can be evaluated successfully (as detailed below), and
|
||||
that the derivations specified by the flake's `checks` output can be
|
||||
built successfully.
|
||||
|
||||
# Evaluation checks
|
||||
|
||||
This following flake output attributes must be derivations:
|
||||
|
||||
* `checks.`*system*`.`*name*
|
||||
* `defaultPackage.`*system*`
|
||||
* `devShell.`*system*`
|
||||
* `nixosConfigurations.`*name*`.config.system.build.toplevel
|
||||
* `packages.`*system*`.`*name*
|
||||
|
||||
The following flake output attributes must be [app
|
||||
definitions](./nix3-run.md):
|
||||
|
||||
* `apps.`*system*`.`*name*
|
||||
* `defaultApp.`*system*`
|
||||
|
||||
The following flake output attributes must be [template
|
||||
definitions](./nix3-flake-init.md):
|
||||
|
||||
* `defaultTemplate`
|
||||
* `templates`.`*name*
|
||||
|
||||
The following flake output attributes must be *Nixpkgs overlays*:
|
||||
|
||||
* `overlay`
|
||||
* `overlays`.`*name*
|
||||
|
||||
The following flake output attributes must be *NixOS modules*:
|
||||
|
||||
* `nixosModule`
|
||||
* `nixosModules`.`*name*
|
||||
|
||||
The following flake output attributes must be
|
||||
[bundlers](./nix3-bundle.md):
|
||||
|
||||
* `bundlers`.`*name*
|
||||
* `defaultBundler`
|
||||
|
||||
In addition, the `hydraJobs` output is evaluated in the same way as
|
||||
Hydra's `hydra-eval-jobs` (i.e. as a arbitrarily deeply nested
|
||||
attribute set of derivations). Similarly, the
|
||||
`legacyPackages`.*system* output is evaluated like `nix-env -qa`.
|
||||
|
||||
)""
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue