2020-02-11 22:53:46 +00:00
|
|
|
#include "primops.hh"
|
|
|
|
#include "eval-inline.hh"
|
|
|
|
#include "store-api.hh"
|
2020-03-30 12:03:28 +00:00
|
|
|
#include "fetchers.hh"
|
2020-04-08 12:12:22 +00:00
|
|
|
#include "filetransfer.hh"
|
2020-04-07 12:29:45 +00:00
|
|
|
#include "registry.hh"
|
2020-02-11 22:53:46 +00:00
|
|
|
|
2020-03-09 14:28:41 +00:00
|
|
|
#include <ctime>
|
|
|
|
#include <iomanip>
|
|
|
|
|
2020-02-11 22:53:46 +00:00
|
|
|
namespace nix {
|
|
|
|
|
2020-03-09 14:28:41 +00:00
|
|
|
void emitTreeAttrs(
|
|
|
|
EvalState & state,
|
|
|
|
const fetchers::Tree & tree,
|
|
|
|
std::shared_ptr<const fetchers::Input> input,
|
|
|
|
Value & v)
|
|
|
|
{
|
|
|
|
state.mkAttrs(v, 8);
|
|
|
|
|
|
|
|
auto storePath = state.store->printStorePath(tree.storePath);
|
|
|
|
|
|
|
|
mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
|
|
|
|
|
|
|
|
assert(tree.info.narHash);
|
|
|
|
mkString(*state.allocAttr(v, state.symbols.create("narHash")),
|
|
|
|
tree.info.narHash.to_string(SRI));
|
|
|
|
|
|
|
|
if (input->getRev()) {
|
|
|
|
mkString(*state.allocAttr(v, state.symbols.create("rev")), input->getRev()->gitRev());
|
|
|
|
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), input->getRev()->gitShortRev());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tree.info.revCount)
|
|
|
|
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *tree.info.revCount);
|
|
|
|
|
2020-04-02 16:39:41 +00:00
|
|
|
if (tree.info.lastModified) {
|
|
|
|
mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *tree.info.lastModified);
|
|
|
|
mkString(*state.allocAttr(v, state.symbols.create("lastModifiedDate")),
|
2020-03-09 14:28:41 +00:00
|
|
|
fmt("%s", std::put_time(std::gmtime(&*tree.info.lastModified), "%Y%m%d%H%M%S")));
|
2020-04-02 16:39:41 +00:00
|
|
|
}
|
2020-03-09 14:28:41 +00:00
|
|
|
|
|
|
|
v.attrs->sort();
|
|
|
|
}
|
|
|
|
|
2020-02-11 22:53:46 +00:00
|
|
|
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
|
|
|
{
|
2020-03-09 14:28:41 +00:00
|
|
|
settings.requireExperimentalFeature("flakes");
|
2020-02-20 12:36:16 +00:00
|
|
|
|
2020-02-11 22:53:46 +00:00
|
|
|
std::shared_ptr<const fetchers::Input> input;
|
|
|
|
PathSet context;
|
|
|
|
|
|
|
|
state.forceValue(*args[0]);
|
|
|
|
|
|
|
|
if (args[0]->type == tAttrs) {
|
|
|
|
state.forceAttrs(*args[0], pos);
|
|
|
|
|
2020-03-17 19:54:36 +00:00
|
|
|
fetchers::Attrs attrs;
|
2020-02-11 22:53:46 +00:00
|
|
|
|
|
|
|
for (auto & attr : *args[0]->attrs) {
|
|
|
|
state.forceValue(*attr.value);
|
|
|
|
if (attr.value->type == tString)
|
|
|
|
attrs.emplace(attr.name, attr.value->string.s);
|
2020-03-17 20:34:38 +00:00
|
|
|
else if (attr.value->type == tBool)
|
|
|
|
attrs.emplace(attr.name, attr.value->boolean);
|
2020-04-02 14:51:56 +00:00
|
|
|
else if (attr.value->type == tInt)
|
|
|
|
attrs.emplace(attr.name, attr.value->integer);
|
2020-02-11 22:53:46 +00:00
|
|
|
else
|
2020-04-02 14:51:56 +00:00
|
|
|
throw TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
2020-03-09 14:28:41 +00:00
|
|
|
attr.name, showType(*attr.value));
|
2020-02-11 22:53:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!attrs.count("type"))
|
|
|
|
throw Error("attribute 'type' is missing in call to 'fetchTree', at %s", pos);
|
|
|
|
|
|
|
|
input = fetchers::inputFromAttrs(attrs);
|
|
|
|
} else
|
|
|
|
input = fetchers::inputFromURL(state.coerceToString(pos, *args[0], context, false, false));
|
|
|
|
|
|
|
|
if (!evalSettings.pureEval && !input->isDirect())
|
2020-02-20 21:14:44 +00:00
|
|
|
input = lookupInRegistries(state.store, input).first;
|
2020-02-11 22:53:46 +00:00
|
|
|
|
|
|
|
if (evalSettings.pureEval && !input->isImmutable())
|
2020-04-02 17:03:32 +00:00
|
|
|
throw Error("in pure evaluation mode, 'fetchTree' requires an immutable input, at %s", pos);
|
2020-02-11 22:53:46 +00:00
|
|
|
|
2020-03-10 18:21:47 +00:00
|
|
|
// FIXME: use fetchOrSubstituteTree
|
2020-02-11 22:53:46 +00:00
|
|
|
auto [tree, input2] = input->fetchTree(state.store);
|
|
|
|
|
|
|
|
if (state.allowedPaths)
|
|
|
|
state.allowedPaths->insert(tree.actualPath);
|
2020-03-09 14:28:41 +00:00
|
|
|
|
|
|
|
emitTreeAttrs(state, tree, input2, v);
|
2020-02-11 22:53:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static RegisterPrimOp r("fetchTree", 1, prim_fetchTree);
|
|
|
|
|
2020-03-18 16:23:56 +00:00
|
|
|
static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
|
|
|
const string & who, bool unpack, std::string name)
|
|
|
|
{
|
|
|
|
std::optional<std::string> url;
|
|
|
|
std::optional<Hash> expectedHash;
|
|
|
|
|
|
|
|
state.forceValue(*args[0]);
|
|
|
|
|
|
|
|
if (args[0]->type == tAttrs) {
|
|
|
|
|
|
|
|
state.forceAttrs(*args[0], pos);
|
|
|
|
|
|
|
|
for (auto & attr : *args[0]->attrs) {
|
|
|
|
string n(attr.name);
|
|
|
|
if (n == "url")
|
|
|
|
url = state.forceStringNoCtx(*attr.value, *attr.pos);
|
|
|
|
else if (n == "sha256")
|
|
|
|
expectedHash = Hash(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
|
|
|
else if (n == "name")
|
|
|
|
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
|
|
|
else
|
|
|
|
throw EvalError("unsupported argument '%s' to '%s', at %s",
|
2020-04-29 20:48:29 +00:00
|
|
|
attr.name, who, *attr.pos);
|
2020-03-18 16:23:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!url)
|
|
|
|
throw EvalError("'url' argument required, at %s", pos);
|
|
|
|
|
|
|
|
} else
|
|
|
|
url = state.forceStringNoCtx(*args[0], pos);
|
|
|
|
|
|
|
|
url = resolveUri(*url);
|
|
|
|
|
|
|
|
state.checkURI(*url);
|
|
|
|
|
|
|
|
if (name == "")
|
|
|
|
name = baseNameOf(*url);
|
|
|
|
|
|
|
|
if (evalSettings.pureEval && !expectedHash)
|
|
|
|
throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
|
|
|
|
|
|
|
|
auto storePath =
|
|
|
|
unpack
|
|
|
|
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).storePath
|
|
|
|
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
|
|
|
|
|
|
|
|
auto path = state.store->toRealPath(storePath);
|
|
|
|
|
|
|
|
if (expectedHash) {
|
|
|
|
auto hash = unpack
|
|
|
|
? state.store->queryPathInfo(storePath)->narHash
|
|
|
|
: hashFile(htSHA256, path);
|
|
|
|
if (hash != *expectedHash)
|
|
|
|
throw Error((unsigned int) 102, "hash mismatch in file downloaded from '%s':\n wanted: %s\n got: %s",
|
|
|
|
*url, expectedHash->to_string(), hash.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.allowedPaths)
|
|
|
|
state.allowedPaths->insert(path);
|
|
|
|
|
|
|
|
mkString(v, path, PathSet({path}));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
|
|
|
{
|
|
|
|
fetch(state, pos, args, v, "fetchurl", false, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
|
|
|
{
|
|
|
|
fetch(state, pos, args, v, "fetchTarball", true, "source");
|
|
|
|
}
|
|
|
|
|
|
|
|
static RegisterPrimOp r2("__fetchurl", 1, prim_fetchurl);
|
|
|
|
static RegisterPrimOp r3("fetchTarball", 1, prim_fetchTarball);
|
|
|
|
|
2020-02-11 22:53:46 +00:00
|
|
|
}
|