aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2018-11-29 19:18:36 +0100
committerEelco Dolstra <edolstra@gmail.com>2019-02-11 12:00:13 +0100
commit7a5cf31060289de61370643937277b5d0d5d178c (patch)
tree9b5b2060a0f9b65d3cfae2ad826015a9ba1d77a8 /src/libexpr
parentf216c76c56cdffb5214d074a7d44812843dd174f (diff)
Initial flake support
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/eval.cc1
-rw-r--r--src/libexpr/eval.hh20
-rw-r--r--src/libexpr/primops/fetchGit.cc9
-rw-r--r--src/libexpr/primops/fetchGit.hh23
-rw-r--r--src/libexpr/primops/flake.cc161
5 files changed, 205 insertions, 9 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 3891e8666..e3a264277 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -290,6 +290,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
, sOutputHash(symbols.create("outputHash"))
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
, sOutputHashMode(symbols.create("outputHashMode"))
+ , sDescription(symbols.create("description"))
, repair(NoRepair)
, store(store)
, baseEnv(allocEnv(128))
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 60cf0f87f..674b08f45 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -72,7 +72,8 @@ public:
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
sFile, sLine, sColumn, sFunctor, sToString,
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
- sOutputHash, sOutputHashAlgo, sOutputHashMode;
+ sOutputHash, sOutputHashAlgo, sOutputHashMode,
+ sDescription;
Symbol sDerivationNix;
/* If set, force copying files to the Nix store even if they
@@ -311,6 +312,23 @@ private:
friend struct ExprOpConcatLists;
friend struct ExprSelect;
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
+
+public:
+
+ struct FlakeRegistry
+ {
+ struct Entry
+ {
+ std::string uri;
+ };
+ std::map<std::string, Entry> entries;
+ };
+
+ const FlakeRegistry & getFlakeRegistry();
+
+private:
+ std::unique_ptr<FlakeRegistry> _flakeRegistry;
+ std::once_flag _flakeRegistryInit;
};
diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc
index b46d2f258..6b6ca08d1 100644
--- a/src/libexpr/primops/fetchGit.cc
+++ b/src/libexpr/primops/fetchGit.cc
@@ -1,3 +1,4 @@
+#include "fetchGit.hh"
#include "primops.hh"
#include "eval-inline.hh"
#include "download.hh"
@@ -15,14 +16,6 @@ using namespace std::string_literals;
namespace nix {
-struct GitInfo
-{
- Path storePath;
- std::string rev;
- std::string shortRev;
- uint64_t revCount = 0;
-};
-
std::regex revRegex("^[0-9a-fA-F]{40}$");
GitInfo exportGit(ref<Store> store, const std::string & uri,
diff --git a/src/libexpr/primops/fetchGit.hh b/src/libexpr/primops/fetchGit.hh
new file mode 100644
index 000000000..23ab2fae9
--- /dev/null
+++ b/src/libexpr/primops/fetchGit.hh
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "store-api.hh"
+
+#include <regex>
+
+namespace nix {
+
+struct GitInfo
+{
+ Path storePath;
+ std::string rev;
+ std::string shortRev;
+ uint64_t revCount = 0;
+};
+
+GitInfo exportGit(ref<Store> store, const std::string & uri,
+ std::experimental::optional<std::string> ref, std::string rev,
+ const std::string & name);
+
+extern std::regex revRegex;
+
+}
diff --git a/src/libexpr/primops/flake.cc b/src/libexpr/primops/flake.cc
new file mode 100644
index 000000000..457c30948
--- /dev/null
+++ b/src/libexpr/primops/flake.cc
@@ -0,0 +1,161 @@
+#include "primops.hh"
+#include "eval-inline.hh"
+#include "fetchGit.hh"
+#include "download.hh"
+
+#include <queue>
+#include <nlohmann/json.hpp>
+
+namespace nix {
+
+const EvalState::FlakeRegistry & EvalState::getFlakeRegistry()
+{
+ std::call_once(_flakeRegistryInit, [&]()
+ {
+ _flakeRegistry = std::make_unique<FlakeRegistry>();
+
+ if (!evalSettings.pureEval) {
+
+ auto registryUri = "file:///home/eelco/Dev/gists/nix-flakes/registry.json";
+
+ auto registryFile = getDownloader()->download(DownloadRequest(registryUri));
+
+ auto json = nlohmann::json::parse(*registryFile.data);
+
+ auto version = json.value("version", 0);
+ if (version != 1)
+ throw Error("flake registry '%s' has unsupported version %d", registryUri, version);
+
+ auto flakes = json["flakes"];
+ for (auto i = flakes.begin(); i != flakes.end(); ++i) {
+ FlakeRegistry::Entry entry;
+ entry.uri = i->value("uri", "");
+ if (entry.uri.empty())
+ throw Error("invalid flake registry entry");
+ _flakeRegistry->entries.emplace(i.key(), entry);
+ }
+ }
+ });
+
+ return *_flakeRegistry;
+}
+
+static void prim_flakeRegistry(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ auto registry = state.getFlakeRegistry();
+
+ state.mkAttrs(v, registry.entries.size());
+
+ for (auto & entry : registry.entries) {
+ auto vEntry = state.allocAttr(v, entry.first);
+ state.mkAttrs(*vEntry, 2);
+ mkString(*state.allocAttr(*vEntry, state.symbols.create("uri")), entry.second.uri);
+ vEntry->attrs->sort();
+ }
+
+ v.attrs->sort();
+}
+
+static RegisterPrimOp r1("__flakeRegistry", 0, prim_flakeRegistry);
+
+struct Flake
+{
+ std::string name;
+ std::string description;
+ Path path;
+ std::set<std::string> requires;
+ Value * vProvides; // FIXME: gc
+};
+
+static Flake fetchFlake(EvalState & state, const std::string & flakeUri)
+{
+ Flake flake;
+
+ auto gitInfo = exportGit(state.store, flakeUri, {}, "", "source");
+
+ state.store->assertStorePath(gitInfo.storePath);
+
+ Value vInfo;
+ state.evalFile(gitInfo.storePath + "/flake.nix", vInfo);
+
+ state.forceAttrs(vInfo);
+
+ if (auto name = vInfo.attrs->get(state.sName))
+ flake.name = state.forceStringNoCtx(*(**name).value, *(**name).pos);
+ else
+ throw Error("flake lacks attribute 'name'");
+
+ if (auto description = vInfo.attrs->get(state.sDescription))
+ flake.description = state.forceStringNoCtx(*(**description).value, *(**description).pos);
+
+ if (auto requires = vInfo.attrs->get(state.symbols.create("requires"))) {
+ state.forceList(*(**requires).value, *(**requires).pos);
+ for (unsigned int n = 0; n < (**requires).value->listSize(); ++n)
+ flake.requires.insert(state.forceStringNoCtx(
+ *(**requires).value->listElems()[n], *(**requires).pos));
+ }
+
+ if (auto provides = vInfo.attrs->get(state.symbols.create("provides"))) {
+ state.forceFunction(*(**provides).value, *(**provides).pos);
+ flake.vProvides = (**provides).value;
+ } else
+ throw Error("flake lacks attribute 'provides'");
+
+ return flake;
+}
+
+static std::map<std::string, Flake> resolveFlakes(EvalState & state, const StringSet & flakeUris)
+{
+ auto registry = state.getFlakeRegistry();
+
+ std::map<std::string, Flake> done;
+ std::queue<std::string> todo;
+ for (auto & i : flakeUris) todo.push(i);
+
+ while (!todo.empty()) {
+ auto flakeUri = todo.front();
+ todo.pop();
+ if (done.count(flakeUri)) continue;
+
+ auto flake = fetchFlake(state, flakeUri);
+
+ for (auto & require : flake.requires) {
+ auto i = registry.entries.find(require);
+ if (i == registry.entries.end())
+ throw Error("unknown flake '%s'", require);
+ todo.push(i->second.uri);
+ }
+
+ done.emplace(flake.name, flake);
+ }
+
+ return done;
+}
+
+static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
+{
+ std::string flakeUri = state.forceStringNoCtx(*args[0], pos);
+
+ auto flakes = resolveFlakes(state, {flakeUri});
+
+ auto vResult = state.allocValue();
+
+ state.mkAttrs(*vResult, flakes.size());
+
+ for (auto & flake : flakes) {
+ auto vFlake = state.allocAttr(*vResult, flake.second.name);
+ state.mkAttrs(*vFlake, 2);
+ mkString(*state.allocAttr(*vFlake, state.sDescription), flake.second.description);
+ auto vProvides = state.allocAttr(*vFlake, state.symbols.create("provides"));
+ mkApp(*vProvides, *flake.second.vProvides, *vResult);
+ vFlake->attrs->sort();
+ }
+
+ vResult->attrs->sort();
+
+ v = *vResult;
+}
+
+static RegisterPrimOp r2("getFlake", 1, prim_getFlake);
+
+}