mirror of
https://github.com/NixOS/nix
synced 2025-06-28 17:51:15 +02:00
Merge branch 'path-info' into ca-drv-exotic
This commit is contained in:
commit
ff2a8ccfe1
104 changed files with 1731 additions and 762 deletions
|
@ -21,6 +21,8 @@ struct AttrDb
|
|||
{
|
||||
std::atomic_bool failed{false};
|
||||
|
||||
const Store & cfg;
|
||||
|
||||
struct State
|
||||
{
|
||||
SQLite db;
|
||||
|
@ -33,8 +35,9 @@ struct AttrDb
|
|||
|
||||
std::unique_ptr<Sync<State>> _state;
|
||||
|
||||
AttrDb(const Hash & fingerprint)
|
||||
: _state(std::make_unique<Sync<State>>())
|
||||
AttrDb(const Store & cfg, const Hash & fingerprint)
|
||||
: cfg(cfg)
|
||||
, _state(std::make_unique<Sync<State>>())
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
|
@ -254,10 +257,10 @@ struct AttrDb
|
|||
return {{rowId, attrs}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
std::vector<std::pair<Path, std::string>> context;
|
||||
NixStringContext context;
|
||||
if (!queryAttribute.isNull(3))
|
||||
for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";"))
|
||||
context.push_back(decodeContext(s));
|
||||
context.push_back(decodeContext(cfg, s));
|
||||
return {{rowId, string_t{queryAttribute.getStr(2), context}}};
|
||||
}
|
||||
case AttrType::Bool:
|
||||
|
@ -274,10 +277,10 @@ struct AttrDb
|
|||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(const Hash & fingerprint)
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(const Store & cfg, const Hash & fingerprint)
|
||||
{
|
||||
try {
|
||||
return std::make_shared<AttrDb>(fingerprint);
|
||||
return std::make_shared<AttrDb>(cfg, fingerprint);
|
||||
} catch (SQLiteError &) {
|
||||
ignoreException();
|
||||
return nullptr;
|
||||
|
@ -288,7 +291,7 @@ EvalCache::EvalCache(
|
|||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||
EvalState & state,
|
||||
RootLoader rootLoader)
|
||||
: db(useCache ? makeAttrDb(*useCache) : nullptr)
|
||||
: db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
|
||||
, state(state)
|
||||
, rootLoader(rootLoader)
|
||||
{
|
||||
|
@ -546,7 +549,7 @@ string_t AttrCursor::getStringWithContext()
|
|||
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
||||
bool valid = true;
|
||||
for (auto & c : s->second) {
|
||||
if (!root->state.store->isValidPath(root->state.store->parseStorePath(c.first))) {
|
||||
if (!root->state.store->isValidPath(c.first)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
@ -563,7 +566,7 @@ string_t AttrCursor::getStringWithContext()
|
|||
auto & v = forceValue();
|
||||
|
||||
if (v.type() == nString)
|
||||
return {v.string.s, v.getContext()};
|
||||
return {v.string.s, v.getContext(*root->state.store)};
|
||||
else if (v.type() == nPath)
|
||||
return {v.path, {}};
|
||||
else
|
||||
|
|
|
@ -52,7 +52,7 @@ struct misc_t {};
|
|||
struct failed_t {};
|
||||
typedef uint64_t AttrId;
|
||||
typedef std::pair<AttrId, Symbol> AttrKey;
|
||||
typedef std::pair<std::string, std::vector<std::pair<Path, std::string>>> string_t;
|
||||
typedef std::pair<std::string, NixStringContext> string_t;
|
||||
|
||||
typedef std::variant<
|
||||
std::vector<Symbol>,
|
||||
|
|
|
@ -24,6 +24,81 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
|
|||
}
|
||||
|
||||
|
||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||
[[gnu::always_inline]]
|
||||
inline void * allocBytes(size_t n)
|
||||
{
|
||||
void * p;
|
||||
#if HAVE_BOEHMGC
|
||||
p = GC_MALLOC(n);
|
||||
#else
|
||||
p = calloc(n, 1);
|
||||
#endif
|
||||
if (!p) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Value * EvalState::allocValue()
|
||||
{
|
||||
#if HAVE_BOEHMGC
|
||||
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
|
||||
GC_malloc_many returns a linked list of objects of the given size, where the first word
|
||||
of each object is also the pointer to the next object in the list. This also means that we
|
||||
have to explicitly clear the first word of every object we take. */
|
||||
if (!*valueAllocCache) {
|
||||
*valueAllocCache = GC_malloc_many(sizeof(Value));
|
||||
if (!*valueAllocCache) throw std::bad_alloc();
|
||||
}
|
||||
|
||||
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||
Take the first list item, advance the list to the next item, and clear the next pointer. */
|
||||
void * p = *valueAllocCache;
|
||||
*valueAllocCache = GC_NEXT(p);
|
||||
GC_NEXT(p) = nullptr;
|
||||
#else
|
||||
void * p = allocBytes(sizeof(Value));
|
||||
#endif
|
||||
|
||||
nrValues++;
|
||||
return (Value *) p;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
Env & EvalState::allocEnv(size_t size)
|
||||
{
|
||||
nrEnvs++;
|
||||
nrValuesInEnvs += size;
|
||||
|
||||
Env * env;
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
if (size == 1) {
|
||||
/* see allocValue for explanations. */
|
||||
if (!*env1AllocCache) {
|
||||
*env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
|
||||
if (!*env1AllocCache) throw std::bad_alloc();
|
||||
}
|
||||
|
||||
void * p = *env1AllocCache;
|
||||
*env1AllocCache = GC_NEXT(p);
|
||||
GC_NEXT(p) = nullptr;
|
||||
env = (Env *) p;
|
||||
} else
|
||||
#endif
|
||||
env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
|
||||
|
||||
env->type = Env::Plain;
|
||||
|
||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
||||
|
||||
return *env;
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
void EvalState::forceValue(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, [&]() { return pos; });
|
||||
|
@ -52,6 +127,7 @@ void EvalState::forceValue(Value & v, Callable getPos)
|
|||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
||||
{
|
||||
forceAttrs(v, [&]() { return pos; });
|
||||
|
@ -59,6 +135,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
|||
|
||||
|
||||
template <typename Callable>
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, Callable getPos)
|
||||
{
|
||||
forceValue(v, getPos);
|
||||
|
@ -67,6 +144,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
|
|||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceList(Value & v, const Pos & pos)
|
||||
{
|
||||
forceValue(v, pos);
|
||||
|
@ -74,18 +152,5 @@ inline void EvalState::forceList(Value & v, const Pos & pos)
|
|||
throwTypeError(pos, "value is %1% while a list was expected", v);
|
||||
}
|
||||
|
||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||
inline void * allocBytes(size_t n)
|
||||
{
|
||||
void * p;
|
||||
#if HAVE_BOEHMGC
|
||||
p = GC_MALLOC(n);
|
||||
#else
|
||||
p = calloc(n, 1);
|
||||
#endif
|
||||
if (!p) throw std::bad_alloc();
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -96,20 +96,20 @@ RootValue allocRootValue(Value * v)
|
|||
}
|
||||
|
||||
|
||||
void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v)
|
||||
void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
switch (v.internalType) {
|
||||
switch (internalType) {
|
||||
case tInt:
|
||||
str << v.integer;
|
||||
str << integer;
|
||||
break;
|
||||
case tBool:
|
||||
str << (v.boolean ? "true" : "false");
|
||||
str << (boolean ? "true" : "false");
|
||||
break;
|
||||
case tString:
|
||||
str << "\"";
|
||||
for (const char * i = v.string.s; *i; i++)
|
||||
for (const char * i = string.s; *i; i++)
|
||||
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
||||
else if (*i == '\n') str << "\\n";
|
||||
else if (*i == '\r') str << "\\r";
|
||||
|
@ -119,19 +119,19 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value &
|
|||
str << "\"";
|
||||
break;
|
||||
case tPath:
|
||||
str << v.path; // !!! escaping?
|
||||
str << path; // !!! escaping?
|
||||
break;
|
||||
case tNull:
|
||||
str << "null";
|
||||
break;
|
||||
case tAttrs: {
|
||||
if (!v.attrs->empty() && !seen.insert(v.attrs).second)
|
||||
str << "<REPEAT>";
|
||||
if (seen && !attrs->empty() && !seen->insert(attrs).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "{ ";
|
||||
for (auto & i : v.attrs->lexicographicOrder()) {
|
||||
for (auto & i : attrs->lexicographicOrder()) {
|
||||
str << i->name << " = ";
|
||||
printValue(str, seen, *i->value);
|
||||
i->value->print(str, seen);
|
||||
str << "; ";
|
||||
}
|
||||
str << "}";
|
||||
|
@ -141,12 +141,12 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value &
|
|||
case tList1:
|
||||
case tList2:
|
||||
case tListN:
|
||||
if (v.listSize() && !seen.insert(v.listElems()).second)
|
||||
str << "<REPEAT>";
|
||||
if (seen && listSize() && !seen->insert(listElems()).second)
|
||||
str << "«repeated»";
|
||||
else {
|
||||
str << "[ ";
|
||||
for (auto v2 : v.listItems()) {
|
||||
printValue(str, seen, *v2);
|
||||
for (auto v2 : listItems()) {
|
||||
v2->print(str, seen);
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
|
@ -166,10 +166,10 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value &
|
|||
str << "<PRIMOP-APP>";
|
||||
break;
|
||||
case tExternal:
|
||||
str << *v.external;
|
||||
str << *external;
|
||||
break;
|
||||
case tFloat:
|
||||
str << v.fpoint;
|
||||
str << fpoint;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
|
@ -177,10 +177,16 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value &
|
|||
}
|
||||
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Value & v)
|
||||
void Value::print(std::ostream & str, bool showRepeated) const
|
||||
{
|
||||
std::set<const void *> seen;
|
||||
printValue(str, seen, v);
|
||||
print(str, showRepeated ? nullptr : &seen);
|
||||
}
|
||||
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Value & v)
|
||||
{
|
||||
v.print(str, false);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -449,8 +455,10 @@ EvalState::EvalState(
|
|||
, regexCache(makeRegexCache())
|
||||
#if HAVE_BOEHMGC
|
||||
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||
, env1AllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||
#else
|
||||
, valueAllocCache(std::make_shared<void *>(nullptr))
|
||||
, env1AllocCache(std::make_shared<void *>(nullptr))
|
||||
#endif
|
||||
, baseEnv(allocEnv(128))
|
||||
, staticBaseEnv(false, 0)
|
||||
|
@ -499,23 +507,6 @@ EvalState::~EvalState()
|
|||
}
|
||||
|
||||
|
||||
void EvalState::requireExperimentalFeatureOnEvaluation(
|
||||
const ExperimentalFeature & feature,
|
||||
const std::string_view fName,
|
||||
const Pos & pos)
|
||||
{
|
||||
if (!settings.isExperimentalFeatureEnabled(feature)) {
|
||||
throw EvalError({
|
||||
.msg = hintfmt(
|
||||
"Cannot call '%2%' because experimental Nix feature '%1%' is disabled. You can enable it via '--extra-experimental-features %1%'.",
|
||||
feature,
|
||||
fName
|
||||
),
|
||||
.errPos = pos
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EvalState::allowPath(const Path & path)
|
||||
{
|
||||
if (allowedPaths)
|
||||
|
@ -727,9 +718,18 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2
|
|||
throw EvalError(s, s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2))
|
||||
{
|
||||
throw EvalError(ErrorInfo {
|
||||
.msg = hintfmt(s, s2),
|
||||
.errPos = pos,
|
||||
.suggestions = suggestions,
|
||||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2))
|
||||
{
|
||||
throw EvalError({
|
||||
throw EvalError(ErrorInfo {
|
||||
.msg = hintfmt(s, s2),
|
||||
.errPos = pos
|
||||
});
|
||||
|
@ -773,6 +773,16 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
|
|||
});
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2))
|
||||
{
|
||||
throw TypeError(ErrorInfo {
|
||||
.msg = hintfmt(s, fun.showNamePos(), s2),
|
||||
.errPos = pos,
|
||||
.suggestions = suggestions,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||
{
|
||||
throw TypeError(s, showType(v));
|
||||
|
@ -876,42 +886,6 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
|||
}
|
||||
|
||||
|
||||
Value * EvalState::allocValue()
|
||||
{
|
||||
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
|
||||
GC_malloc_many returns a linked list of objects of the given size, where the first word
|
||||
of each object is also the pointer to the next object in the list. This also means that we
|
||||
have to explicitly clear the first word of every object we take. */
|
||||
if (!*valueAllocCache) {
|
||||
*valueAllocCache = GC_malloc_many(sizeof(Value));
|
||||
if (!*valueAllocCache) throw std::bad_alloc();
|
||||
}
|
||||
|
||||
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||
Take the first list item, advance the list to the next item, and clear the next pointer. */
|
||||
void * p = *valueAllocCache;
|
||||
GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p));
|
||||
GC_NEXT(p) = nullptr;
|
||||
|
||||
nrValues++;
|
||||
auto v = (Value *) p;
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Env & EvalState::allocEnv(size_t size)
|
||||
{
|
||||
nrEnvs++;
|
||||
nrValuesInEnvs += size;
|
||||
Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *));
|
||||
env->type = Env::Plain;
|
||||
|
||||
/* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */
|
||||
|
||||
return *env;
|
||||
}
|
||||
|
||||
|
||||
void EvalState::mkList(Value & v, size_t size)
|
||||
{
|
||||
v.mkList(size);
|
||||
|
@ -1281,8 +1255,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
}
|
||||
} else {
|
||||
state.forceAttrs(*vAttrs, pos);
|
||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
throwEvalError(pos, "attribute '%1%' missing", name);
|
||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
||||
std::set<std::string> allAttrNames;
|
||||
for (auto & attr : *vAttrs->attrs)
|
||||
allAttrNames.insert(attr.name);
|
||||
throwEvalError(
|
||||
pos,
|
||||
Suggestions::bestMatches(allAttrNames, name),
|
||||
"attribute '%1%' missing", name);
|
||||
}
|
||||
}
|
||||
vAttrs = j->value;
|
||||
pos2 = j->pos;
|
||||
|
@ -1398,8 +1379,17 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
for (auto & i : *args[0]->attrs)
|
||||
if (!lambda.formals->has(i.name))
|
||||
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
|
||||
if (!lambda.formals->has(i.name)) {
|
||||
std::set<std::string> formalNames;
|
||||
for (auto & formal : lambda.formals->formals)
|
||||
formalNames.insert(formal.name);
|
||||
throwTypeError(
|
||||
pos,
|
||||
Suggestions::bestMatches(formalNames, i.name),
|
||||
"%1% called with unexpected argument '%2%'",
|
||||
lambda,
|
||||
i.name);
|
||||
}
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
|
@ -1902,13 +1892,22 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos)
|
|||
|
||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||
name>. */
|
||||
std::pair<std::string, std::string> decodeContext(std::string_view s)
|
||||
NixStringContextElem decodeContext(const Store & store, std::string_view s)
|
||||
{
|
||||
if (s.at(0) == '!') {
|
||||
size_t index = s.find("!", 1);
|
||||
return {std::string(s.substr(index + 1)), std::string(s.substr(1, index - 1))};
|
||||
return {
|
||||
store.parseStorePath(s.substr(index + 1)),
|
||||
std::string(s.substr(1, index - 1)),
|
||||
};
|
||||
} else
|
||||
return {s.at(0) == '/' ? std::string(s) : std::string(s.substr(1)), ""};
|
||||
return {
|
||||
store.parseStorePath(
|
||||
s.at(0) == '/'
|
||||
? s
|
||||
: s.substr(1)),
|
||||
"",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -1920,13 +1919,13 @@ void copyContext(const Value & v, PathSet & context)
|
|||
}
|
||||
|
||||
|
||||
std::vector<std::pair<Path, std::string>> Value::getContext()
|
||||
NixStringContext Value::getContext(const Store & store)
|
||||
{
|
||||
std::vector<std::pair<Path, std::string>> res;
|
||||
NixStringContext res;
|
||||
assert(internalType == tString);
|
||||
if (string.context)
|
||||
for (const char * * p = string.context; *p; ++p)
|
||||
res.push_back(decodeContext(*p));
|
||||
res.push_back(decodeContext(store, *p));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,9 +133,14 @@ private:
|
|||
/* Cache used by prim_match(). */
|
||||
std::shared_ptr<RegexCache> regexCache;
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
/* Allocation cache for GC'd Value objects. */
|
||||
std::shared_ptr<void *> valueAllocCache;
|
||||
|
||||
/* Allocation cache for size-1 Env objects. */
|
||||
std::shared_ptr<void *> env1AllocCache;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
EvalState(
|
||||
|
@ -144,12 +149,6 @@ public:
|
|||
std::shared_ptr<Store> buildStore = nullptr);
|
||||
~EvalState();
|
||||
|
||||
void requireExperimentalFeatureOnEvaluation(
|
||||
const ExperimentalFeature &,
|
||||
const std::string_view fName,
|
||||
const Pos & pos
|
||||
);
|
||||
|
||||
void addToSearchPath(const std::string & s);
|
||||
|
||||
SearchPath getSearchPath() { return searchPath; }
|
||||
|
@ -347,8 +346,8 @@ public:
|
|||
void autoCallFunction(Bindings & args, Value & fun, Value & res);
|
||||
|
||||
/* Allocation primitives. */
|
||||
Value * allocValue();
|
||||
Env & allocEnv(size_t size);
|
||||
inline Value * allocValue();
|
||||
inline Env & allocEnv(size_t size);
|
||||
|
||||
Value * allocAttr(Value & vAttrs, const Symbol & name);
|
||||
Value * allocAttr(Value & vAttrs, std::string_view name);
|
||||
|
@ -425,7 +424,7 @@ std::string showType(const Value & v);
|
|||
|
||||
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
|
||||
name>. */
|
||||
std::pair<std::string, std::string> decodeContext(std::string_view s);
|
||||
NixStringContextElem decodeContext(const Store & store, std::string_view s);
|
||||
|
||||
/* If `path' refers to a directory, then append "/default.nix". */
|
||||
Path resolveExprPath(Path path);
|
||||
|
@ -509,3 +508,5 @@ extern EvalSettings evalSettings;
|
|||
static const std::string corepkgsPrefix{"/__corepkgs__/"};
|
||||
|
||||
}
|
||||
|
||||
#include "eval-inline.hh"
|
||||
|
|
|
@ -706,8 +706,6 @@ void callFlake(EvalState & state,
|
|||
|
||||
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
|
||||
|
||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
|
||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
||||
|
@ -723,7 +721,30 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
|
|||
v);
|
||||
}
|
||||
|
||||
static RegisterPrimOp r2("__getFlake", 1, prim_getFlake);
|
||||
static RegisterPrimOp r2({
|
||||
.name = "__getFlake",
|
||||
.args = {"args"},
|
||||
.doc = R"(
|
||||
Fetch a flake from a flake reference, and return its output attributes and some metadata. For example:
|
||||
|
||||
```nix
|
||||
(builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix
|
||||
```
|
||||
|
||||
Unless impure evaluation is allowed (`--impure`), the flake reference
|
||||
must be "locked", e.g. contain a Git revision or content hash. An
|
||||
example of an unlocked usage is:
|
||||
|
||||
```nix
|
||||
(builtins.getFlake "github:edolstra/dwarffs").rev
|
||||
```
|
||||
|
||||
This function is only available if you enable the experimental feature
|
||||
`flakes`.
|
||||
)",
|
||||
.fun = prim_getFlake,
|
||||
.experimentalFeature = Xp::Flakes,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "get-drvs.hh"
|
||||
#include "util.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "derivations.hh"
|
||||
#include "store-api.hh"
|
||||
#include "path-with-outputs.hh"
|
||||
|
||||
|
@ -102,7 +103,7 @@ StorePath DrvInfo::queryOutPath() const
|
|||
}
|
||||
|
||||
|
||||
DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||
DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
|
||||
{
|
||||
if (outputs.empty()) {
|
||||
/* Get the ‘outputs’ list. */
|
||||
|
@ -112,20 +113,24 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
|||
|
||||
/* For each output... */
|
||||
for (auto elem : i->value->listItems()) {
|
||||
/* Evaluate the corresponding set. */
|
||||
std::string name(state->forceStringNoCtx(*elem, *i->pos));
|
||||
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value, *i->pos);
|
||||
std::string output(state->forceStringNoCtx(*elem, *i->pos));
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||
PathSet context;
|
||||
outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
|
||||
if (withPaths) {
|
||||
/* Evaluate the corresponding set. */
|
||||
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value, *i->pos);
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||
PathSet context;
|
||||
outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
|
||||
} else
|
||||
outputs.emplace(output, std::nullopt);
|
||||
}
|
||||
} else
|
||||
outputs.emplace("out", queryOutPath());
|
||||
outputs.emplace("out", withPaths ? std::optional{queryOutPath()} : std::nullopt);
|
||||
}
|
||||
if (!onlyOutputsToInstall || !attrs)
|
||||
return outputs;
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace nix {
|
|||
struct DrvInfo
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, StorePath> Outputs;
|
||||
typedef std::map<std::string, std::optional<StorePath>> Outputs;
|
||||
|
||||
private:
|
||||
EvalState * state;
|
||||
|
@ -46,8 +46,9 @@ public:
|
|||
StorePath requireDrvPath() const;
|
||||
StorePath queryOutPath() const;
|
||||
std::string queryOutputName() const;
|
||||
/** Return the list of outputs. The "outputs to install" are determined by `meta.outputsToInstall`. */
|
||||
Outputs queryOutputs(bool onlyOutputsToInstall = false);
|
||||
/** Return the unordered map of output names to (optional) output paths.
|
||||
* The "outputs to install" are determined by `meta.outputsToInstall`. */
|
||||
Outputs queryOutputs(bool withPaths = true, bool onlyOutputsToInstall = false);
|
||||
|
||||
StringSet queryMetaNames();
|
||||
Value * queryMeta(const std::string & name);
|
||||
|
|
|
@ -28,6 +28,13 @@ using namespace nix;
|
|||
|
||||
namespace nix {
|
||||
|
||||
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||
{
|
||||
return Pos(data->origin, data->file, loc.first_line, loc.first_column);
|
||||
}
|
||||
|
||||
#define CUR_POS makeCurPos(*yylloc, data)
|
||||
|
||||
// backup to recover from yyless(0)
|
||||
YYLTYPE prev_yylloc;
|
||||
|
||||
|
@ -37,7 +44,6 @@ static void initLoc(YYLTYPE * loc)
|
|||
loc->first_column = loc->last_column = 1;
|
||||
}
|
||||
|
||||
|
||||
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
{
|
||||
prev_yylloc = *loc;
|
||||
|
@ -147,14 +153,20 @@ or { return OR_KW; }
|
|||
try {
|
||||
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
||||
} catch (const boost::bad_lexical_cast &) {
|
||||
throw ParseError("invalid integer '%1%'", yytext);
|
||||
throw ParseError({
|
||||
.msg = hintfmt("invalid integer '%1%'", yytext),
|
||||
.errPos = CUR_POS,
|
||||
});
|
||||
}
|
||||
return INT;
|
||||
}
|
||||
{FLOAT} { errno = 0;
|
||||
yylval->nf = strtod(yytext, 0);
|
||||
if (errno != 0)
|
||||
throw ParseError("invalid float '%1%'", yytext);
|
||||
throw ParseError({
|
||||
.msg = hintfmt("invalid float '%1%'", yytext),
|
||||
.errPos = CUR_POS,
|
||||
});
|
||||
return FLOAT;
|
||||
}
|
||||
|
||||
|
@ -280,7 +292,10 @@ or { return OR_KW; }
|
|||
|
||||
<INPATH_SLASH>{ANY} |
|
||||
<INPATH_SLASH><<EOF>> {
|
||||
throw ParseError("path has a trailing slash");
|
||||
throw ParseError({
|
||||
.msg = hintfmt("path has a trailing slash"),
|
||||
.errPos = CUR_POS,
|
||||
});
|
||||
}
|
||||
|
||||
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
|
||||
|
|
|
@ -23,14 +23,13 @@ MakeError(RestrictedPathError, Error);
|
|||
|
||||
struct Pos
|
||||
{
|
||||
FileOrigin origin;
|
||||
Symbol file;
|
||||
unsigned int line, column;
|
||||
|
||||
Pos() : origin(foString), line(0), column(0) { }
|
||||
Pos(FileOrigin origin, const Symbol & file, unsigned int line, unsigned int column)
|
||||
: origin(origin), file(file), line(line), column(column) { }
|
||||
|
||||
uint32_t line;
|
||||
FileOrigin origin:2;
|
||||
uint32_t column:30;
|
||||
Pos() : line(0), origin(foString), column(0) { };
|
||||
Pos(FileOrigin origin, const Symbol & file, uint32_t line, uint32_t column)
|
||||
: file(file), line(line), origin(origin), column(column) { };
|
||||
operator bool() const
|
||||
{
|
||||
return line != 0;
|
||||
|
|
|
@ -43,8 +43,8 @@ StringMap EvalState::realiseContext(const PathSet & context)
|
|||
StringMap res;
|
||||
|
||||
for (auto & i : context) {
|
||||
auto [ctxS, outputName] = decodeContext(i);
|
||||
auto ctx = store->parseStorePath(ctxS);
|
||||
auto [ctx, outputName] = decodeContext(*store, i);
|
||||
auto ctxS = store->printStorePath(ctx);
|
||||
if (!store->isValidPath(ctx))
|
||||
throw InvalidPathError(store->printStorePath(ctx));
|
||||
if (!outputName.empty() && ctx.isDerivation()) {
|
||||
|
@ -694,7 +694,32 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
|
|||
|
||||
static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info {
|
||||
.name = "__genericClosure",
|
||||
.args = {"attrset"},
|
||||
.arity = 1,
|
||||
.doc = R"(
|
||||
Take an *attrset* with values named `startSet` and `operator` in order to
|
||||
return a *list of attrsets* by starting with the `startSet`, recursively
|
||||
applying the `operator` function to each element. The *attrsets* in the
|
||||
`startSet` and produced by the `operator` must each contain value named
|
||||
`key` which are comparable to each other. The result is produced by
|
||||
repeatedly calling the operator for each element encountered with a
|
||||
unique key, terminating when no new elements are produced. For example,
|
||||
|
||||
```
|
||||
builtins.genericClosure {
|
||||
startSet = [ {key = 5;} ];
|
||||
operator = item: [{
|
||||
key = if (item.key / 2 ) * 2 == item.key
|
||||
then item.key / 2
|
||||
else 3 * item.key + 1;
|
||||
}];
|
||||
}
|
||||
```
|
||||
evaluates to
|
||||
```
|
||||
[ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ]
|
||||
```
|
||||
)",
|
||||
.fun = prim_genericClosure,
|
||||
});
|
||||
|
||||
|
@ -1114,7 +1139,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
drv.inputSrcs.insert(j);
|
||||
if (j.isDerivation()) {
|
||||
Derivation jDrv = state.store->readDerivation(j);
|
||||
if(jDrv.type() != DerivationType::CAFloating)
|
||||
if(jDrv.type().hasKnownOutputPaths())
|
||||
drv.inputDrvs[j] = jDrv.outputNames();
|
||||
}
|
||||
}
|
||||
|
@ -1122,8 +1147,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
|
||||
/* Handle derivation outputs of the form ‘!<name>!<path>’. */
|
||||
else if (path.at(0) == '!') {
|
||||
auto ctx = decodeContext(path);
|
||||
drv.inputDrvs[state.store->parseStorePath(ctx.first)].insert(ctx.second);
|
||||
auto ctx = decodeContext(*state.store, path);
|
||||
drv.inputDrvs[ctx.first].insert(ctx.second);
|
||||
}
|
||||
|
||||
/* Otherwise it's a source file. */
|
||||
|
@ -1171,22 +1196,21 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
std::move(h),
|
||||
{});
|
||||
|
||||
DerivationOutputCAFixed dof { .ca = ca };
|
||||
DerivationOutput::CAFixed dof { .ca = ca };
|
||||
|
||||
drv.env["out"] = state.store->printStorePath(dof.path(*state.store, drvName, "out"));
|
||||
drv.outputs.insert_or_assign("out", DerivationOutput { .output = dof });
|
||||
drv.outputs.insert_or_assign("out", dof);
|
||||
}
|
||||
|
||||
else if (contentAddressed) {
|
||||
HashType ht = parseHashType(outputHashAlgo);
|
||||
for (auto & i : outputs) {
|
||||
drv.env[i] = hashPlaceholder(i);
|
||||
drv.outputs.insert_or_assign(i, DerivationOutput {
|
||||
.output = DerivationOutputCAFloating {
|
||||
drv.outputs.insert_or_assign(i,
|
||||
DerivationOutput::CAFloating {
|
||||
.method = ingestionMethod,
|
||||
.hashType = ht,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1200,43 +1224,36 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
for (auto & i : outputs) {
|
||||
drv.env[i] = "";
|
||||
drv.outputs.insert_or_assign(i,
|
||||
DerivationOutput {
|
||||
.output = DerivationOutputInputAddressed {
|
||||
.path = StorePath::dummy,
|
||||
},
|
||||
});
|
||||
DerivationOutput::Deferred { });
|
||||
}
|
||||
|
||||
// Regular, non-CA derivation should always return a single hash and not
|
||||
// hash per output.
|
||||
auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true);
|
||||
auto hashModulo = hashDerivationModulo(*state.store, drv, true);
|
||||
std::visit(overloaded {
|
||||
[&](Hash & h) {
|
||||
for (auto & i : outputs) {
|
||||
auto outPath = state.store->makeOutputPath(i, h, drvName);
|
||||
drv.env[i] = state.store->printStorePath(outPath);
|
||||
drv.outputs.insert_or_assign(i,
|
||||
DerivationOutput {
|
||||
.output = DerivationOutputInputAddressed {
|
||||
.path = std::move(outPath),
|
||||
},
|
||||
});
|
||||
[&](const DrvHash & drvHash) {
|
||||
auto & h = drvHash.hash;
|
||||
switch (drvHash.kind) {
|
||||
case DrvHash::Kind::Deferred:
|
||||
/* Outputs already deferred, nothing to do */
|
||||
break;
|
||||
case DrvHash::Kind::Regular:
|
||||
for (auto & [outputName, output] : drv.outputs) {
|
||||
auto outPath = state.store->makeOutputPath(outputName, h, drvName);
|
||||
drv.env[outputName] = state.store->printStorePath(outPath);
|
||||
output = DerivationOutput::InputAddressed {
|
||||
.path = std::move(outPath),
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
[&](CaOutputHashes &) {
|
||||
[&](const CaOutputHashes &) {
|
||||
// Shouldn't happen as the toplevel derivation is not CA.
|
||||
assert(false);
|
||||
},
|
||||
[&](DeferredHash &) {
|
||||
for (auto & i : outputs) {
|
||||
drv.outputs.insert_or_assign(i,
|
||||
DerivationOutput {
|
||||
.output = DerivationOutputDeferred{},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
hashModulo);
|
||||
hashModulo.raw());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1248,12 +1265,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
|||
|
||||
/* Optimisation, but required in read-only mode! because in that
|
||||
case we don't actually write store derivations, so we can't
|
||||
read them later.
|
||||
|
||||
However, we don't bother doing this for floating CA derivations because
|
||||
their "hash modulo" is indeterminate until built. */
|
||||
if (drv.type() != DerivationType::CAFloating) {
|
||||
auto h = hashDerivationModulo(*state.store, Derivation(drv), false);
|
||||
read them later. */
|
||||
{
|
||||
auto h = hashDerivationModulo(*state.store, drv, false);
|
||||
drvHashes.lock()->insert_or_assign(drvPath, h);
|
||||
}
|
||||
|
||||
|
@ -3811,7 +3825,7 @@ RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
|
|||
.name = name,
|
||||
.args = {},
|
||||
.arity = arity,
|
||||
.fun = fun
|
||||
.fun = fun,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3883,13 +3897,17 @@ void EvalState::createBaseEnv()
|
|||
|
||||
if (RegisterPrimOp::primOps)
|
||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||
addPrimOp({
|
||||
.fun = primOp.fun,
|
||||
.arity = std::max(primOp.args.size(), primOp.arity),
|
||||
.name = symbols.create(primOp.name),
|
||||
.args = primOp.args,
|
||||
.doc = primOp.doc,
|
||||
});
|
||||
if (!primOp.experimentalFeature
|
||||
|| settings.isExperimentalFeatureEnabled(*primOp.experimentalFeature))
|
||||
{
|
||||
addPrimOp({
|
||||
.fun = primOp.fun,
|
||||
.arity = std::max(primOp.args.size(), primOp.arity),
|
||||
.name = symbols.create(primOp.name),
|
||||
.args = primOp.args,
|
||||
.doc = primOp.doc,
|
||||
});
|
||||
}
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
|
|
|
@ -16,6 +16,7 @@ struct RegisterPrimOp
|
|||
size_t arity = 0;
|
||||
const char * doc;
|
||||
PrimOpFun fun;
|
||||
std::optional<ExperimentalFeature> experimentalFeature;
|
||||
};
|
||||
|
||||
typedef std::vector<Info> PrimOps;
|
||||
|
@ -35,6 +36,7 @@ struct RegisterPrimOp
|
|||
/* These primops are disabled without enableNativeCode, but plugins
|
||||
may wish to use them in limited contexts without globally enabling
|
||||
them. */
|
||||
|
||||
/* Load a ValueInitializer from a DSO and return whatever it initializes */
|
||||
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "derivations.hh"
|
||||
#include "store-api.hh"
|
||||
|
||||
namespace nix {
|
||||
|
@ -82,8 +83,8 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
|
|||
drv = std::string(p, 1);
|
||||
path = &drv;
|
||||
} else if (p.at(0) == '!') {
|
||||
std::pair<std::string, std::string> ctx = decodeContext(p);
|
||||
drv = ctx.first;
|
||||
NixStringContextElem ctx = decodeContext(*state.store, p);
|
||||
drv = state.store->printStorePath(ctx.first);
|
||||
output = ctx.second;
|
||||
path = &drv;
|
||||
}
|
||||
|
|
154
src/libexpr/primops/fetchClosure.cc
Normal file
154
src/libexpr/primops/fetchClosure.cc
Normal file
|
@ -0,0 +1,154 @@
|
|||
#include "primops.hh"
|
||||
#include "store-api.hh"
|
||||
#include "make-content-addressed.hh"
|
||||
#include "url.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0], pos);
|
||||
|
||||
std::optional<std::string> fromStoreUrl;
|
||||
std::optional<StorePath> fromPath;
|
||||
bool toCA = false;
|
||||
std::optional<StorePath> toPath;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
if (attr.name == "fromPath") {
|
||||
PathSet context;
|
||||
fromPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
|
||||
}
|
||||
|
||||
else if (attr.name == "toPath") {
|
||||
state.forceValue(*attr.value, *attr.pos);
|
||||
toCA = true;
|
||||
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
|
||||
PathSet context;
|
||||
toPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
|
||||
}
|
||||
}
|
||||
|
||||
else if (attr.name == "fromStore")
|
||||
fromStoreUrl = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||
|
||||
else
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name),
|
||||
.errPos = pos
|
||||
});
|
||||
}
|
||||
|
||||
if (!fromPath)
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
if (!fromStoreUrl)
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
auto parsedURL = parseURL(*fromStoreUrl);
|
||||
|
||||
if (parsedURL.scheme != "http" &&
|
||||
parsedURL.scheme != "https" &&
|
||||
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
||||
throw Error({
|
||||
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
auto fromStore = openStore(parsedURL.to_string());
|
||||
|
||||
if (toCA) {
|
||||
if (!toPath || !state.store->isValidPath(*toPath)) {
|
||||
auto remappings = makeContentAddressed(*fromStore, *state.store, { *fromPath });
|
||||
auto i = remappings.find(*fromPath);
|
||||
assert(i != remappings.end());
|
||||
if (toPath && *toPath != i->second)
|
||||
throw Error({
|
||||
.msg = hintfmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
|
||||
state.store->printStorePath(*fromPath),
|
||||
state.store->printStorePath(i->second),
|
||||
state.store->printStorePath(*toPath)),
|
||||
.errPos = pos
|
||||
});
|
||||
if (!toPath)
|
||||
throw Error({
|
||||
.msg = hintfmt(
|
||||
"rewriting '%s' to content-addressed form yielded '%s'; "
|
||||
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
|
||||
state.store->printStorePath(*fromPath),
|
||||
state.store->printStorePath(i->second)),
|
||||
.errPos = pos
|
||||
});
|
||||
}
|
||||
} else {
|
||||
copyClosure(*fromStore, *state.store, RealisedPath::Set { *fromPath });
|
||||
toPath = fromPath;
|
||||
}
|
||||
|
||||
/* In pure mode, require a CA path. */
|
||||
if (evalSettings.pureEval) {
|
||||
auto info = state.store->queryPathInfo(*toPath);
|
||||
if (!info->isContentAddressed(*state.store))
|
||||
throw Error({
|
||||
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
|
||||
state.store->printStorePath(*toPath)),
|
||||
.errPos = pos
|
||||
});
|
||||
}
|
||||
|
||||
auto toPathS = state.store->printStorePath(*toPath);
|
||||
v.mkString(toPathS, {toPathS});
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_fetchClosure({
|
||||
.name = "__fetchClosure",
|
||||
.args = {"args"},
|
||||
.doc = R"(
|
||||
Fetch a Nix store closure from a binary cache, rewriting it into
|
||||
content-addressed form. For example,
|
||||
|
||||
```nix
|
||||
builtins.fetchClosure {
|
||||
fromStore = "https://cache.nixos.org";
|
||||
fromPath = /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1;
|
||||
toPath = /nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1;
|
||||
}
|
||||
```
|
||||
|
||||
fetches `/nix/store/r2jd...` from the specified binary cache,
|
||||
and rewrites it into the content-addressed store path
|
||||
`/nix/store/ldbh...`.
|
||||
|
||||
If `fromPath` is already content-addressed, or if you are
|
||||
allowing impure evaluation (`--impure`), then `toPath` may be
|
||||
omitted.
|
||||
|
||||
To find out the correct value for `toPath` given a `fromPath`,
|
||||
you can use `nix store make-content-addressed`:
|
||||
|
||||
```console
|
||||
# nix store make-content-addressed --from https://cache.nixos.org /nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1
|
||||
rewrote '/nix/store/r2jd6ygnmirm2g803mksqqjm4y39yi6i-git-2.33.1' to '/nix/store/ldbhlwhh39wha58rm61bkiiwm6j7211j-git-2.33.1'
|
||||
```
|
||||
|
||||
This function is similar to `builtins.storePath` in that it
|
||||
allows you to use a previously built store path in a Nix
|
||||
expression. However, it is more reproducible because it requires
|
||||
specifying a binary cache from which the path can be fetched.
|
||||
Also, requiring a content-addressed final store path avoids the
|
||||
need for users to configure binary cache public keys.
|
||||
|
||||
This function is only available if you enable the experimental
|
||||
feature `fetch-closure`.
|
||||
)",
|
||||
.fun = prim_fetchClosure,
|
||||
.experimentalFeature = Xp::FetchClosure,
|
||||
});
|
||||
|
||||
}
|
|
@ -145,7 +145,7 @@ static void fetchTree(
|
|||
if (!params.allowNameArgument)
|
||||
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||
throw Error({
|
||||
.msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
|
||||
.msg = hintfmt("attribute 'name' isn't supported in call to 'fetchTree'"),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
|
@ -334,7 +334,7 @@ static RegisterPrimOp primop_fetchTarball({
|
|||
.fun = prim_fetchTarball,
|
||||
});
|
||||
|
||||
static void prim_fetchGit(EvalState &state, const Pos &pos, Value **args, Value &v)
|
||||
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ struct ExprLambda;
|
|||
struct PrimOp;
|
||||
class Symbol;
|
||||
struct Pos;
|
||||
class StorePath;
|
||||
class Store;
|
||||
class EvalState;
|
||||
class XMLWriter;
|
||||
class JSONPlaceholder;
|
||||
|
@ -64,6 +66,8 @@ class JSONPlaceholder;
|
|||
|
||||
typedef int64_t NixInt;
|
||||
typedef double NixFloat;
|
||||
typedef std::pair<StorePath, std::string> NixStringContextElem;
|
||||
typedef std::vector<NixStringContextElem> NixStringContext;
|
||||
|
||||
/* External values must descend from ExternalValueBase, so that
|
||||
* type-agnostic nix functions (e.g. showType) can be implemented
|
||||
|
@ -115,10 +119,13 @@ private:
|
|||
InternalType internalType;
|
||||
|
||||
friend std::string showType(const Value & v);
|
||||
friend void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v);
|
||||
|
||||
void print(std::ostream & str, std::set<const void *> * seen) const;
|
||||
|
||||
public:
|
||||
|
||||
void print(std::ostream & str, bool showRepeated = false) const;
|
||||
|
||||
// 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
|
||||
|
@ -368,7 +375,7 @@ public:
|
|||
non-trivial. */
|
||||
bool isTrivial() const;
|
||||
|
||||
std::vector<std::pair<Path, std::string>> getContext();
|
||||
NixStringContext getContext(const Store &);
|
||||
|
||||
auto listItems()
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue