aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--corepkgs/default-installation-source.nix3
-rw-r--r--corepkgs/local.mk8
-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
-rw-r--r--src/nix/flake.cc65
-rw-r--r--src/nix/installables.cc39
9 files changed, 282 insertions, 47 deletions
diff --git a/corepkgs/default-installation-source.nix b/corepkgs/default-installation-source.nix
new file mode 100644
index 000000000..71ba04452
--- /dev/null
+++ b/corepkgs/default-installation-source.nix
@@ -0,0 +1,3 @@
+builtins.mapAttrs (flakeName: flakeInfo:
+ (getFlake flakeInfo.uri).${flakeName}.provides.packages or {})
+ builtins.flakeRegistry
diff --git a/corepkgs/local.mk b/corepkgs/local.mk
index 362c8eb61..41aaec63b 100644
--- a/corepkgs/local.mk
+++ b/corepkgs/local.mk
@@ -1,4 +1,10 @@
-corepkgs_FILES = buildenv.nix unpack-channel.nix derivation.nix fetchurl.nix imported-drv-to-derivation.nix
+corepkgs_FILES = \
+ buildenv.nix \
+ unpack-channel.nix \
+ derivation.nix \
+ fetchurl.nix \
+ imported-drv-to-derivation.nix \
+ default-installation-source.nix
$(foreach file,config.nix $(corepkgs_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/corepkgs)))
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);
+
+}
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
new file mode 100644
index 000000000..98cd90c64
--- /dev/null
+++ b/src/nix/flake.cc
@@ -0,0 +1,65 @@
+#include "command.hh"
+#include "common-args.hh"
+#include "shared.hh"
+#include "progress-bar.hh"
+#include "eval.hh"
+
+using namespace nix;
+
+struct CmdFlakeList : StoreCommand, MixEvalArgs
+{
+ std::string name() override
+ {
+ return "list";
+ }
+
+ std::string description() override
+ {
+ return "list available Nix flakes";
+ }
+
+ void run(nix::ref<nix::Store> store) override
+ {
+ auto evalState = std::make_shared<EvalState>(searchPath, store);
+
+ auto registry = evalState->getFlakeRegistry();
+
+ stopProgressBar();
+
+ for (auto & entry : registry.entries) {
+ std::cout << entry.first << " " << entry.second.uri << "\n";
+ }
+ }
+};
+
+struct CmdFlake : virtual MultiCommand, virtual Command
+{
+ CmdFlake()
+ : MultiCommand({make_ref<CmdFlakeList>()})
+ {
+ }
+
+ std::string name() override
+ {
+ return "flake";
+ }
+
+ std::string description() override
+ {
+ return "manage Nix flakes";
+ }
+
+ void run() override
+ {
+ if (!command)
+ throw UsageError("'nix flake' requires a sub-command.");
+ command->run();
+ }
+
+ void printHelp(const string & programName, std::ostream & out) override
+ {
+ MultiCommand::printHelp(programName, out);
+ }
+};
+
+static RegisterCommand r1(make_ref<CmdFlake>());
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
index 0c1ad3ab3..9b7b96c25 100644
--- a/src/nix/installables.cc
+++ b/src/nix/installables.cc
@@ -26,47 +26,12 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state)
{
if (vSourceExpr) return vSourceExpr;
- auto sToplevel = state.symbols.create("_toplevel");
-
vSourceExpr = state.allocValue();
if (file != "")
state.evalFile(lookupFileArg(state, file), *vSourceExpr);
-
- else {
-
- /* Construct the installation source from $NIX_PATH. */
-
- auto searchPath = state.getSearchPath();
-
- state.mkAttrs(*vSourceExpr, searchPath.size() + 1);
-
- mkBool(*state.allocAttr(*vSourceExpr, sToplevel), true);
-
- std::unordered_set<std::string> seen;
-
- for (auto & i : searchPath) {
- if (i.first == "") continue;
- if (seen.count(i.first)) continue;
- seen.insert(i.first);
-#if 0
- auto res = state.resolveSearchPathElem(i);
- if (!res.first) continue;
- if (!pathExists(res.second)) continue;
- mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)),
- state.getBuiltin("import"),
- mkString(*state.allocValue(), res.second));
-#endif
- Value * v1 = state.allocValue();
- mkPrimOpApp(*v1, state.getBuiltin("findFile"), state.getBuiltin("nixPath"));
- Value * v2 = state.allocValue();
- mkApp(*v2, *v1, mkString(*state.allocValue(), i.first));
- mkApp(*state.allocAttr(*vSourceExpr, state.symbols.create(i.first)),
- state.getBuiltin("import"), *v2);
- }
-
- vSourceExpr->attrs->sort();
- }
+ else
+ state.evalFile(lookupFileArg(state, "<nix/default-installation-source.nix>"), *vSourceExpr);
return vSourceExpr;
}