mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-26 15:56:18 +02:00
Initial flake support
This commit is contained in:
parent
f216c76c56
commit
7a5cf31060
9 changed files with 282 additions and 47 deletions
3
corepkgs/default-installation-source.nix
Normal file
3
corepkgs/default-installation-source.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
builtins.mapAttrs (flakeName: flakeInfo:
|
||||||
|
(getFlake flakeInfo.uri).${flakeName}.provides.packages or {})
|
||||||
|
builtins.flakeRegistry
|
|
@ -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)))
|
$(foreach file,config.nix $(corepkgs_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/corepkgs)))
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,7 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store)
|
||||||
, sOutputHash(symbols.create("outputHash"))
|
, sOutputHash(symbols.create("outputHash"))
|
||||||
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
|
, sOutputHashAlgo(symbols.create("outputHashAlgo"))
|
||||||
, sOutputHashMode(symbols.create("outputHashMode"))
|
, sOutputHashMode(symbols.create("outputHashMode"))
|
||||||
|
, sDescription(symbols.create("description"))
|
||||||
, repair(NoRepair)
|
, repair(NoRepair)
|
||||||
, store(store)
|
, store(store)
|
||||||
, baseEnv(allocEnv(128))
|
, baseEnv(allocEnv(128))
|
||||||
|
|
|
@ -72,7 +72,8 @@ public:
|
||||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||||
sFile, sLine, sColumn, sFunctor, sToString,
|
sFile, sLine, sColumn, sFunctor, sToString,
|
||||||
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
sRight, sWrong, sStructuredAttrs, sBuilder, sArgs,
|
||||||
sOutputHash, sOutputHashAlgo, sOutputHashMode;
|
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||||
|
sDescription;
|
||||||
Symbol sDerivationNix;
|
Symbol sDerivationNix;
|
||||||
|
|
||||||
/* If set, force copying files to the Nix store even if they
|
/* If set, force copying files to the Nix store even if they
|
||||||
|
@ -311,6 +312,23 @@ private:
|
||||||
friend struct ExprOpConcatLists;
|
friend struct ExprOpConcatLists;
|
||||||
friend struct ExprSelect;
|
friend struct ExprSelect;
|
||||||
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "fetchGit.hh"
|
||||||
#include "primops.hh"
|
#include "primops.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "download.hh"
|
#include "download.hh"
|
||||||
|
@ -15,14 +16,6 @@ using namespace std::string_literals;
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct GitInfo
|
|
||||||
{
|
|
||||||
Path storePath;
|
|
||||||
std::string rev;
|
|
||||||
std::string shortRev;
|
|
||||||
uint64_t revCount = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::regex revRegex("^[0-9a-fA-F]{40}$");
|
std::regex revRegex("^[0-9a-fA-F]{40}$");
|
||||||
|
|
||||||
GitInfo exportGit(ref<Store> store, const std::string & uri,
|
GitInfo exportGit(ref<Store> store, const std::string & uri,
|
||||||
|
|
23
src/libexpr/primops/fetchGit.hh
Normal file
23
src/libexpr/primops/fetchGit.hh
Normal file
|
@ -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;
|
||||||
|
|
||||||
|
}
|
161
src/libexpr/primops/flake.cc
Normal file
161
src/libexpr/primops/flake.cc
Normal file
|
@ -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);
|
||||||
|
|
||||||
|
}
|
65
src/nix/flake.cc
Normal file
65
src/nix/flake.cc
Normal file
|
@ -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>());
|
|
@ -26,47 +26,12 @@ Value * SourceExprCommand::getSourceExpr(EvalState & state)
|
||||||
{
|
{
|
||||||
if (vSourceExpr) return vSourceExpr;
|
if (vSourceExpr) return vSourceExpr;
|
||||||
|
|
||||||
auto sToplevel = state.symbols.create("_toplevel");
|
|
||||||
|
|
||||||
vSourceExpr = state.allocValue();
|
vSourceExpr = state.allocValue();
|
||||||
|
|
||||||
if (file != "")
|
if (file != "")
|
||||||
state.evalFile(lookupFileArg(state, file), *vSourceExpr);
|
state.evalFile(lookupFileArg(state, file), *vSourceExpr);
|
||||||
|
else
|
||||||
else {
|
state.evalFile(lookupFileArg(state, "<nix/default-installation-source.nix>"), *vSourceExpr);
|
||||||
|
|
||||||
/* 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return vSourceExpr;
|
return vSourceExpr;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue