2019-02-12 17:23:11 +00:00
|
|
|
#include "flake.hh"
|
2019-06-04 18:01:21 +00:00
|
|
|
#include "lockfile.hh"
|
2018-11-29 18:18:36 +00:00
|
|
|
#include "primops.hh"
|
|
|
|
#include "eval-inline.hh"
|
2020-01-21 15:27:53 +00:00
|
|
|
#include "store-api.hh"
|
|
|
|
#include "fetchers/fetchers.hh"
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
#include "fetchers/regex.hh"
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2019-02-21 05:53:01 +00:00
|
|
|
#include <iostream>
|
2019-05-28 18:34:02 +00:00
|
|
|
#include <ctime>
|
|
|
|
#include <iomanip>
|
2018-11-29 18:18:36 +00:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
2019-05-29 13:31:07 +00:00
|
|
|
using namespace flake;
|
|
|
|
|
|
|
|
namespace flake {
|
|
|
|
|
2019-09-18 21:59:45 +00:00
|
|
|
/* If 'allowLookup' is true, then resolve 'flakeRef' using the
|
|
|
|
registries. */
|
|
|
|
static FlakeRef maybeLookupFlake(
|
2019-06-21 17:04:58 +00:00
|
|
|
EvalState & state,
|
|
|
|
const FlakeRef & flakeRef,
|
|
|
|
bool allowLookup)
|
2018-11-29 18:18:36 +00:00
|
|
|
{
|
2019-06-21 17:04:58 +00:00
|
|
|
if (!flakeRef.isDirect()) {
|
|
|
|
if (allowLookup)
|
2020-01-21 15:27:53 +00:00
|
|
|
return flakeRef.resolve(state.store);
|
2019-06-21 17:04:58 +00:00
|
|
|
else
|
|
|
|
throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", flakeRef);
|
|
|
|
} else
|
|
|
|
return flakeRef;
|
|
|
|
}
|
2019-05-01 09:38:48 +00:00
|
|
|
|
2019-09-18 21:59:45 +00:00
|
|
|
typedef std::vector<std::pair<FlakeRef, FlakeRef>> RefMap;
|
|
|
|
|
|
|
|
static FlakeRef lookupInRefMap(
|
|
|
|
const RefMap & refMap,
|
|
|
|
const FlakeRef & flakeRef)
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
#if 0
|
2019-09-18 21:59:45 +00:00
|
|
|
// FIXME: inefficient.
|
|
|
|
for (auto & i : refMap) {
|
|
|
|
if (flakeRef.contains(i.first)) {
|
|
|
|
debug("mapping '%s' to previously seen input '%s' -> '%s",
|
|
|
|
flakeRef, i.first, i.second);
|
|
|
|
return i.second;
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 15:27:53 +00:00
|
|
|
#endif
|
2019-09-18 21:59:45 +00:00
|
|
|
|
|
|
|
return flakeRef;
|
|
|
|
}
|
2019-06-21 17:04:58 +00:00
|
|
|
|
2019-09-09 14:34:44 +00:00
|
|
|
static void expectType(EvalState & state, ValueType type,
|
|
|
|
Value & value, const Pos & pos)
|
|
|
|
{
|
2019-09-09 15:34:38 +00:00
|
|
|
if (value.type == tThunk && value.isTrivial())
|
2019-09-09 14:34:44 +00:00
|
|
|
state.forceValue(value, pos);
|
|
|
|
if (value.type != type)
|
|
|
|
throw Error("expected %s but got %s at %s",
|
|
|
|
showType(type), showType(value.type), pos);
|
|
|
|
}
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
static InputPath parseInputPath(std::string_view s, const Pos & pos)
|
|
|
|
{
|
|
|
|
InputPath path;
|
|
|
|
|
|
|
|
for (auto & elem : tokenizeString<std::vector<std::string>>(s, "/")) {
|
|
|
|
if (!std::regex_match(elem, fetchers::flakeIdRegex))
|
|
|
|
throw Error("invalid flake input path element '%s' at %s", elem, pos);
|
|
|
|
path.push_back(elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.empty())
|
|
|
|
throw Error("flake input path is empty at %s", pos);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
|
|
|
EvalState & state, Value * value, const Pos & pos);
|
|
|
|
|
|
|
|
static FlakeInput parseFlakeInput(EvalState & state,
|
|
|
|
const std::string & inputName, Value * value, const Pos & pos)
|
|
|
|
{
|
|
|
|
expectType(state, tAttrs, *value, pos);
|
|
|
|
|
|
|
|
FlakeInput input {
|
|
|
|
.ref = parseFlakeRef(inputName)
|
|
|
|
};
|
|
|
|
|
|
|
|
auto sInputs = state.symbols.create("inputs");
|
|
|
|
auto sUrl = state.symbols.create("url");
|
|
|
|
auto sUri = state.symbols.create("uri"); // FIXME: remove soon
|
|
|
|
auto sFlake = state.symbols.create("flake");
|
|
|
|
auto sFollows = state.symbols.create("follows");
|
|
|
|
|
|
|
|
for (Attr attr : *(value->attrs)) {
|
|
|
|
if (attr.name == sUrl || attr.name == sUri) {
|
|
|
|
expectType(state, tString, *attr.value, *attr.pos);
|
|
|
|
input.ref = parseFlakeRef(attr.value->string.s);
|
|
|
|
} else if (attr.name == sFlake) {
|
|
|
|
expectType(state, tBool, *attr.value, *attr.pos);
|
|
|
|
input.isFlake = attr.value->boolean;
|
|
|
|
} else if (attr.name == sInputs) {
|
|
|
|
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos);
|
|
|
|
} else if (attr.name == sFollows) {
|
|
|
|
expectType(state, tString, *attr.value, *attr.pos);
|
|
|
|
input.follows = parseInputPath(attr.value->string.s, *attr.pos);
|
|
|
|
} else
|
|
|
|
throw Error("flake input '%s' has an unsupported attribute '%s', at %s",
|
|
|
|
inputName, attr.name, *attr.pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return input;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
|
|
|
EvalState & state, Value * value, const Pos & pos)
|
|
|
|
{
|
|
|
|
std::map<FlakeId, FlakeInput> inputs;
|
|
|
|
|
|
|
|
expectType(state, tAttrs, *value, pos);
|
|
|
|
|
|
|
|
for (Attr & inputAttr : *(*value).attrs) {
|
|
|
|
inputs.emplace(inputAttr.name,
|
|
|
|
parseFlakeInput(state,
|
|
|
|
inputAttr.name,
|
|
|
|
inputAttr.value,
|
|
|
|
*inputAttr.pos));
|
|
|
|
}
|
|
|
|
|
|
|
|
return inputs;
|
|
|
|
}
|
|
|
|
|
2019-09-18 21:59:45 +00:00
|
|
|
static Flake getFlake(EvalState & state, const FlakeRef & originalRef,
|
|
|
|
bool allowLookup, RefMap & refMap)
|
2018-11-30 15:11:15 +00:00
|
|
|
{
|
2019-09-18 21:59:45 +00:00
|
|
|
auto flakeRef = lookupInRefMap(refMap,
|
|
|
|
maybeLookupFlake(state,
|
|
|
|
lookupInRefMap(refMap, originalRef), allowLookup));
|
2019-09-18 19:17:27 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
auto [sourceInfo, resolvedInput] = flakeRef.input->fetchTree(state.store);
|
|
|
|
|
|
|
|
FlakeRef resolvedRef(resolvedInput, flakeRef.subdir);
|
2019-05-01 08:34:23 +00:00
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
debug("got flake source '%s' from '%s'",
|
2020-01-21 15:27:53 +00:00
|
|
|
state.store->printStorePath(sourceInfo.storePath), resolvedRef);
|
2019-02-25 15:20:50 +00:00
|
|
|
|
2019-09-18 21:59:45 +00:00
|
|
|
refMap.push_back({originalRef, resolvedRef});
|
|
|
|
refMap.push_back({flakeRef, resolvedRef});
|
|
|
|
|
2019-02-12 19:35:03 +00:00
|
|
|
if (state.allowedPaths)
|
2020-01-21 15:27:53 +00:00
|
|
|
state.allowedPaths->insert(sourceInfo.actualPath);
|
2019-02-12 19:35:03 +00:00
|
|
|
|
2019-05-01 18:38:41 +00:00
|
|
|
// Guard against symlink attacks.
|
2020-01-21 15:27:53 +00:00
|
|
|
auto flakeFile = canonPath(sourceInfo.actualPath + "/" + resolvedRef.subdir + "/flake.nix");
|
|
|
|
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
|
|
|
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
|
|
|
resolvedRef, state.store->printStorePath(sourceInfo.storePath));
|
|
|
|
|
|
|
|
Flake flake {
|
|
|
|
.originalRef = originalRef,
|
|
|
|
.resolvedRef = resolvedRef,
|
|
|
|
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
|
|
|
};
|
2019-05-01 09:38:48 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
if (!pathExists(flakeFile))
|
2019-05-01 16:07:36 +00:00
|
|
|
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", resolvedRef, resolvedRef.subdir);
|
2019-04-19 09:43:56 +00:00
|
|
|
|
2018-11-29 18:18:36 +00:00
|
|
|
Value vInfo;
|
2020-01-21 15:27:53 +00:00
|
|
|
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
expectType(state, tAttrs, vInfo, Pos(state.symbols.create(flakeFile), 0, 0));
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2019-07-11 11:54:53 +00:00
|
|
|
auto sEdition = state.symbols.create("edition");
|
|
|
|
auto sEpoch = state.symbols.create("epoch"); // FIXME: remove soon
|
|
|
|
|
|
|
|
auto edition = vInfo.attrs->get(sEdition);
|
|
|
|
if (!edition)
|
|
|
|
edition = vInfo.attrs->get(sEpoch);
|
|
|
|
|
|
|
|
if (edition) {
|
2019-09-09 14:34:44 +00:00
|
|
|
expectType(state, tInt, *(**edition).value, *(**edition).pos);
|
|
|
|
flake.edition = (**edition).value->integer;
|
2019-08-30 09:22:34 +00:00
|
|
|
if (flake.edition > 201909)
|
2019-07-11 11:54:53 +00:00
|
|
|
throw Error("flake '%s' requires unsupported edition %d; please upgrade Nix", flakeRef, flake.edition);
|
2019-08-30 09:22:34 +00:00
|
|
|
if (flake.edition < 201909)
|
|
|
|
throw Error("flake '%s' has illegal edition %d", flakeRef, flake.edition);
|
2019-05-22 12:31:40 +00:00
|
|
|
} else
|
2019-07-11 11:54:53 +00:00
|
|
|
throw Error("flake '%s' lacks attribute 'edition'", flakeRef);
|
2019-05-22 12:31:40 +00:00
|
|
|
|
2019-09-09 14:34:44 +00:00
|
|
|
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
|
|
|
expectType(state, tString, *(**description).value, *(**description).pos);
|
|
|
|
flake.description = (**description).value->string.s;
|
|
|
|
}
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2019-05-29 21:09:23 +00:00
|
|
|
auto sInputs = state.symbols.create("inputs");
|
2019-05-29 13:12:22 +00:00
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
if (std::optional<Attr *> inputs = vInfo.attrs->get(sInputs))
|
|
|
|
flake.inputs = parseFlakeInputs(state, (**inputs).value, *(**inputs).pos);
|
2019-03-21 08:30:16 +00:00
|
|
|
|
2019-05-29 21:09:23 +00:00
|
|
|
auto sOutputs = state.symbols.create("outputs");
|
2019-05-29 13:12:22 +00:00
|
|
|
|
2019-05-29 21:09:23 +00:00
|
|
|
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
2019-09-09 14:34:44 +00:00
|
|
|
expectType(state, tLambda, *(**outputs).value, *(**outputs).pos);
|
2019-05-29 21:09:23 +00:00
|
|
|
flake.vOutputs = (**outputs).value;
|
2019-08-30 09:22:34 +00:00
|
|
|
|
|
|
|
if (flake.vOutputs->lambda.fun->matchAttrs) {
|
|
|
|
for (auto & formal : flake.vOutputs->lambda.fun->formals->formals) {
|
2019-08-30 14:27:51 +00:00
|
|
|
if (formal.name != state.sSelf)
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
flake.inputs.emplace(formal.name, FlakeInput {
|
|
|
|
.ref = parseFlakeRef(formal.name)
|
|
|
|
});
|
2019-08-30 09:22:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-29 18:18:36 +00:00
|
|
|
} else
|
2019-05-29 21:09:23 +00:00
|
|
|
throw Error("flake '%s' lacks attribute 'outputs'", flakeRef);
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2019-05-29 13:12:22 +00:00
|
|
|
for (auto & attr : *vInfo.attrs) {
|
2019-07-11 11:54:53 +00:00
|
|
|
if (attr.name != sEdition &&
|
|
|
|
attr.name != sEpoch &&
|
2019-05-29 13:12:22 +00:00
|
|
|
attr.name != state.sDescription &&
|
2019-05-29 21:09:23 +00:00
|
|
|
attr.name != sInputs &&
|
|
|
|
attr.name != sOutputs)
|
2019-05-29 13:12:22 +00:00
|
|
|
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
|
|
|
flakeRef, attr.name, *attr.pos);
|
|
|
|
}
|
|
|
|
|
2018-11-29 18:18:36 +00:00
|
|
|
return flake;
|
|
|
|
}
|
|
|
|
|
2019-09-18 21:59:45 +00:00
|
|
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
|
|
|
{
|
|
|
|
RefMap refMap;
|
|
|
|
return getFlake(state, originalRef, allowLookup, refMap);
|
|
|
|
}
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
static std::pair<fetchers::Tree, FlakeRef> getNonFlake(
|
|
|
|
EvalState & state,
|
|
|
|
const FlakeRef & originalRef,
|
|
|
|
bool allowLookup,
|
|
|
|
RefMap & refMap)
|
2019-03-21 08:30:16 +00:00
|
|
|
{
|
2019-09-18 21:59:45 +00:00
|
|
|
auto flakeRef = lookupInRefMap(refMap,
|
|
|
|
maybeLookupFlake(state,
|
|
|
|
lookupInRefMap(refMap, originalRef), allowLookup));
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
auto [sourceInfo, resolvedInput] = flakeRef.input->fetchTree(state.store);
|
2019-03-21 08:30:16 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
FlakeRef resolvedRef(resolvedInput, flakeRef.subdir);
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
debug("got non-flake source '%s' from '%s'",
|
2020-01-21 15:27:53 +00:00
|
|
|
state.store->printStorePath(sourceInfo.storePath), resolvedRef);
|
2019-03-21 08:30:16 +00:00
|
|
|
|
2019-09-18 21:59:45 +00:00
|
|
|
refMap.push_back({originalRef, resolvedRef});
|
|
|
|
refMap.push_back({flakeRef, resolvedRef});
|
|
|
|
|
2019-05-01 09:38:48 +00:00
|
|
|
if (state.allowedPaths)
|
2020-01-21 15:27:53 +00:00
|
|
|
state.allowedPaths->insert(sourceInfo.actualPath);
|
2019-03-21 08:30:16 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
return std::make_pair(std::move(sourceInfo), resolvedRef);
|
2019-03-21 08:30:16 +00:00
|
|
|
}
|
|
|
|
|
2020-01-24 12:07:03 +00:00
|
|
|
bool allowedToUseRegistries(LockFileMode handle, bool isTopRef)
|
2019-05-14 09:34:45 +00:00
|
|
|
{
|
|
|
|
if (handle == AllPure) return false;
|
|
|
|
else if (handle == TopRefUsesRegistries) return isTopRef;
|
|
|
|
else if (handle == UpdateLockFile) return true;
|
|
|
|
else if (handle == UseUpdatedLockFile) return true;
|
|
|
|
else if (handle == RecreateLockFile) return true;
|
|
|
|
else if (handle == UseNewLockFile) return true;
|
|
|
|
else assert(false);
|
|
|
|
}
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
static void flattenLockFile(
|
|
|
|
const LockedInputs & inputs,
|
|
|
|
const InputPath & prefix,
|
|
|
|
std::map<InputPath, const LockedInput *> & res)
|
2019-05-01 09:38:48 +00:00
|
|
|
{
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
for (auto &[id, input] : inputs.inputs) {
|
|
|
|
auto inputPath(prefix);
|
|
|
|
inputPath.push_back(id);
|
|
|
|
res.emplace(inputPath, &input);
|
|
|
|
flattenLockFile(input, inputPath, res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string diffLockFiles(const LockedInputs & oldLocks, const LockedInputs & newLocks)
|
|
|
|
{
|
|
|
|
std::map<InputPath, const LockedInput *> oldFlat, newFlat;
|
|
|
|
flattenLockFile(oldLocks, {}, oldFlat);
|
|
|
|
flattenLockFile(newLocks, {}, newFlat);
|
|
|
|
|
|
|
|
auto i = oldFlat.begin();
|
|
|
|
auto j = newFlat.begin();
|
|
|
|
std::string res;
|
|
|
|
|
|
|
|
while (i != oldFlat.end() || j != newFlat.end()) {
|
|
|
|
if (j != newFlat.end() && (i == oldFlat.end() || i->first > j->first)) {
|
|
|
|
res += fmt(" added '%s': '%s'\n", concatStringsSep("/", j->first), j->second->ref);
|
|
|
|
++j;
|
|
|
|
} else if (i != oldFlat.end() && (j == newFlat.end() || i->first < j->first)) {
|
|
|
|
res += fmt(" removed '%s'\n", concatStringsSep("/", i->first));
|
|
|
|
++i;
|
2019-05-01 09:38:48 +00:00
|
|
|
} else {
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
if (!(i->second->ref == j->second->ref))
|
|
|
|
res += fmt(" updated '%s': '%s' -> '%s'\n",
|
|
|
|
concatStringsSep("/", i->first),
|
|
|
|
i->second->ref,
|
|
|
|
j->second->ref);
|
|
|
|
++i;
|
|
|
|
++j;
|
2019-05-01 09:38:48 +00:00
|
|
|
}
|
2019-04-16 14:18:47 +00:00
|
|
|
}
|
2019-02-12 20:05:44 +00:00
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
return res;
|
2019-03-29 15:18:25 +00:00
|
|
|
}
|
2019-02-12 20:55:43 +00:00
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
/* Compute an in-memory lock file for the specified top-level flake,
|
2019-06-04 19:07:55 +00:00
|
|
|
and optionally write it to file, it the flake is writable. */
|
2020-01-22 19:59:59 +00:00
|
|
|
LockedFlake lockFlake(
|
|
|
|
EvalState & state,
|
|
|
|
const FlakeRef & topRef,
|
2020-01-24 12:07:03 +00:00
|
|
|
LockFileMode lockFileMode)
|
2019-05-01 09:38:48 +00:00
|
|
|
{
|
2019-10-16 15:45:09 +00:00
|
|
|
settings.requireExperimentalFeature("flakes");
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
RefMap refMap;
|
|
|
|
|
2019-09-18 19:17:27 +00:00
|
|
|
auto flake = getFlake(state, topRef,
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
allowedToUseRegistries(lockFileMode, true), refMap);
|
2019-06-04 17:10:35 +00:00
|
|
|
|
2019-05-21 12:55:43 +00:00
|
|
|
LockFile oldLockFile;
|
2019-05-01 09:38:48 +00:00
|
|
|
|
2020-01-24 12:07:03 +00:00
|
|
|
if (lockFileMode != RecreateLockFile && lockFileMode != UseNewLockFile) {
|
2019-05-01 09:38:48 +00:00
|
|
|
// If recreateLockFile, start with an empty lockfile
|
2019-05-31 18:12:59 +00:00
|
|
|
// FIXME: symlink attack
|
2019-06-04 18:01:21 +00:00
|
|
|
oldLockFile = LockFile::read(
|
2020-01-21 15:27:53 +00:00
|
|
|
flake.sourceInfo->actualPath + "/" + flake.resolvedRef.subdir + "/flake.lock");
|
2019-05-01 09:38:48 +00:00
|
|
|
}
|
2019-05-01 09:38:48 +00:00
|
|
|
|
2019-10-21 12:57:01 +00:00
|
|
|
debug("old lock file: %s", oldLockFile);
|
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
LockFile newLockFile, prevLockFile;
|
|
|
|
std::vector<InputPath> prevUnresolved;
|
|
|
|
|
|
|
|
// FIXME: check whether all overrides are used.
|
|
|
|
std::map<InputPath, FlakeInput> overrides;
|
|
|
|
|
|
|
|
/* Compute the new lock file. This is dones as a fixpoint
|
|
|
|
iteration: we repeat until the new lock file no longer changes
|
|
|
|
and there are no unresolved "follows" inputs. */
|
|
|
|
while (true) {
|
|
|
|
std::vector<InputPath> unresolved;
|
|
|
|
|
|
|
|
/* Recurse into the flake inputs. */
|
|
|
|
std::function<void(
|
|
|
|
const FlakeInputs & flakeInputs,
|
|
|
|
const LockedInputs & oldLocks,
|
|
|
|
LockedInputs & newLocks,
|
|
|
|
const InputPath & inputPathPrefix)>
|
|
|
|
updateLocks;
|
|
|
|
|
|
|
|
updateLocks = [&](
|
|
|
|
const FlakeInputs & flakeInputs,
|
|
|
|
const LockedInputs & oldLocks,
|
|
|
|
LockedInputs & newLocks,
|
|
|
|
const InputPath & inputPathPrefix)
|
|
|
|
{
|
|
|
|
/* Get the overrides (i.e. attributes of the form
|
|
|
|
'inputs.nixops.inputs.nixpkgs.url = ...'). */
|
|
|
|
for (auto & [id, input] : flake.inputs) {
|
|
|
|
for (auto & [idOverride, inputOverride] : input.overrides) {
|
|
|
|
auto inputPath(inputPathPrefix);
|
|
|
|
inputPath.push_back(id);
|
|
|
|
inputPath.push_back(idOverride);
|
|
|
|
overrides.insert_or_assign(inputPath, inputOverride);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Go over the flake inputs, resolve/fetch them if
|
|
|
|
necessary (i.e. if they're new or the flakeref changed
|
|
|
|
from what's in the lock file). */
|
|
|
|
for (auto & [id, input2] : flakeInputs) {
|
|
|
|
auto inputPath(inputPathPrefix);
|
|
|
|
inputPath.push_back(id);
|
|
|
|
auto inputPathS = concatStringsSep("/", inputPath);
|
|
|
|
|
|
|
|
/* Do we have an override for this input from one of
|
|
|
|
the ancestors? */
|
|
|
|
auto i = overrides.find(inputPath);
|
|
|
|
bool hasOverride = i != overrides.end();
|
|
|
|
auto & input = hasOverride ? i->second : input2;
|
|
|
|
|
|
|
|
if (input.follows) {
|
|
|
|
/* This is a "follows" input
|
|
|
|
(i.e. 'inputs.nixpkgs.follows =
|
|
|
|
"dwarffs/nixpkgs"). Resolve the source and copy
|
|
|
|
its inputs. Note that the source is normally
|
|
|
|
relative to the current node of the lock file
|
|
|
|
(e.g. "dwarffs/nixpkgs" refers to the nixpkgs
|
|
|
|
input of the dwarffs input of the root flake),
|
|
|
|
but if it's from an override, it's relative to
|
|
|
|
the *root* of the lock file. */
|
|
|
|
auto follows = (hasOverride ? newLockFile : newLocks).findInput(*input.follows);
|
|
|
|
if (follows)
|
|
|
|
newLocks.inputs.insert_or_assign(id, **follows);
|
|
|
|
else
|
|
|
|
/* We haven't processed the source of the
|
|
|
|
"follows" yet (e.g. "dwarffs/nixpkgs"). So
|
|
|
|
we'll need another round of the fixpoint
|
|
|
|
iteration. */
|
|
|
|
unresolved.push_back(inputPath);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto oldLock = oldLocks.inputs.find(id);
|
|
|
|
|
|
|
|
if (oldLock != oldLocks.inputs.end() && oldLock->second.originalRef == input.ref && !hasOverride) {
|
|
|
|
/* Copy the input from the old lock file if its
|
|
|
|
flakeref didn't change and there is no override
|
|
|
|
from a higher level flake. */
|
|
|
|
newLocks.inputs.insert_or_assign(id, oldLock->second);
|
|
|
|
|
|
|
|
/* However there may be new overrides on the
|
|
|
|
inputs of this flake, so we need to check those
|
|
|
|
(without fetching this flake - we need to be
|
|
|
|
lazy). */
|
|
|
|
FlakeInputs fakeInputs;
|
|
|
|
|
|
|
|
for (auto & i : oldLock->second.inputs) {
|
|
|
|
fakeInputs.emplace(i.first, FlakeInput {
|
|
|
|
.ref = i.second.originalRef
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
updateLocks(fakeInputs,
|
|
|
|
oldLock->second,
|
|
|
|
newLocks.inputs.find(id)->second,
|
|
|
|
inputPath);
|
2019-09-18 21:59:45 +00:00
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
} else {
|
|
|
|
/* We need to update/create a new lock file
|
|
|
|
entry. So fetch the flake/non-flake. */
|
|
|
|
if (lockFileMode == AllPure || lockFileMode == TopRefUsesRegistries)
|
|
|
|
throw Error("cannot update flake input '%s' in pure mode", inputPathS);
|
|
|
|
|
|
|
|
if (input.isFlake) {
|
|
|
|
auto inputFlake = getFlake(state, input.ref,
|
|
|
|
allowedToUseRegistries(lockFileMode, false), refMap);
|
|
|
|
|
|
|
|
newLocks.inputs.insert_or_assign(id,
|
|
|
|
LockedInput(inputFlake.resolvedRef, inputFlake.originalRef, inputFlake.sourceInfo->narHash));
|
|
|
|
|
|
|
|
/* Recursively process the inputs of this
|
|
|
|
flake. Also, unless we already have this
|
|
|
|
flake in the top-level lock file, use this
|
|
|
|
flake's own lock file. */
|
|
|
|
updateLocks(inputFlake.inputs,
|
|
|
|
oldLock != oldLocks.inputs.end()
|
|
|
|
? (const LockedInputs &) oldLock->second
|
|
|
|
: LockFile::read(
|
|
|
|
inputFlake.sourceInfo->actualPath + "/" + inputFlake.resolvedRef.subdir + "/flake.lock"),
|
|
|
|
newLocks.inputs.find(id)->second,
|
|
|
|
inputPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
auto [sourceInfo, resolvedRef] = getNonFlake(state, input.ref,
|
|
|
|
allowedToUseRegistries(lockFileMode, false), refMap);
|
|
|
|
newLocks.inputs.insert_or_assign(id,
|
|
|
|
LockedInput(resolvedRef, input.ref, sourceInfo.narHash));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
updateLocks(flake.inputs, oldLockFile, newLockFile, {});
|
|
|
|
|
|
|
|
/* Check if there is a cycle in the "follows" inputs. */
|
|
|
|
if (!unresolved.empty() && unresolved == prevUnresolved) {
|
|
|
|
std::vector<std::string> ss;
|
|
|
|
for (auto & i : unresolved)
|
|
|
|
ss.push_back(concatStringsSep("/", i));
|
|
|
|
throw Error("cycle or missing input detected in flake inputs: %s", concatStringsSep(", ", ss));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::swap(unresolved, prevUnresolved);
|
|
|
|
|
|
|
|
/* Done with the fixpoint iteration? */
|
|
|
|
if (newLockFile == prevLockFile) break;
|
|
|
|
prevLockFile = newLockFile;
|
|
|
|
};
|
2019-02-12 21:43:22 +00:00
|
|
|
|
2020-01-24 12:07:03 +00:00
|
|
|
debug("new lock file: %s", newLockFile);
|
2019-10-21 12:57:01 +00:00
|
|
|
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
/* Check whether we need to / can write the new lock file. */
|
2020-01-24 12:07:03 +00:00
|
|
|
if (!(newLockFile == oldLockFile)) {
|
Respect lock files of inputs + fine-grained lock file control
When computing a lock file, we now respect the lock files of flake
inputs. This is important for usability / reproducibility. For
example, the 'nixops' flake depends on the 'nixops-aws' and
'nixops-hetzner' repositories. So when the 'nixops' flake is used in
another flake, we want the versions of 'nixops-aws' and
'nixops-hetzner' locked by the the 'nixops' flake because those
presumably have been tested.
This can lead to a proliferation of versions of flakes like 'nixpkgs'
(since every flake's lock file could depend on a different version of
'nixpkgs'). This is not a major issue when using Nixpkgs overlays or
NixOS modules, since then the top-level flake composes those
overlays/modules into *its* version of Nixpkgs and all other versions
are ignored. Lock file computation has been made a bit more lazy so it
won't try to fetch all those versions of 'nixpkgs'.
However, in case it's necessary to minimize flake versions, there now
are two input attributes that allow this. First, you can copy an input
from another flake, as follows:
inputs.nixpkgs.follows = "dwarffs/nixpkgs";
This states that the calling flake's 'nixpkgs' input shall be the same
as the 'nixpkgs' input of the 'dwarffs' input.
Second, you can override inputs of inputs:
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
or equivalently, using 'follows':
inputs.nixpkgs.url = github:edolstra/nixpkgs/<hash>;
inputs.nixops.inputs.nixpkgs.follows = "nixpkgs";
This states that the 'nixpkgs' input of the 'nixops' input shall be
the same as the calling flake's 'nixpkgs' input.
Finally, at '-v' Nix now prints the changes to the lock file, e.g.
$ nix flake update ~/Misc/eelco-configurations/hagbard
inputs of flake 'git+file:///home/eelco/Misc/eelco-configurations?subdir=hagbard' changed:
updated 'nixpkgs': 'github:edolstra/nixpkgs/7845bf5f4b3013df1cf036e9c9c3a55a30331db9' -> 'github:edolstra/nixpkgs/03f3def66a104a221aac8b751eeb7075374848fd'
removed 'nixops'
removed 'nixops/nixops-aws'
removed 'nixops/nixops-hetzner'
removed 'nixops/nixpkgs'
2020-01-24 21:05:11 +00:00
|
|
|
|
|
|
|
if (!(oldLockFile == LockFile()))
|
|
|
|
printInfo("inputs of flake '%s' changed:\n%s", topRef, chomp(diffLockFiles(oldLockFile, newLockFile)));
|
|
|
|
|
2020-01-24 12:07:03 +00:00
|
|
|
if (lockFileMode == UpdateLockFile || lockFileMode == RecreateLockFile) {
|
2020-01-21 15:27:53 +00:00
|
|
|
if (auto sourcePath = topRef.input->getSourcePath()) {
|
2020-01-24 12:07:03 +00:00
|
|
|
if (!newLockFile.isImmutable()) {
|
2020-01-21 15:27:53 +00:00
|
|
|
if (settings.warnDirty)
|
|
|
|
warn("will not write lock file of flake '%s' because it has a mutable input", topRef);
|
2019-09-20 12:29:49 +00:00
|
|
|
} else {
|
2020-01-27 12:45:49 +00:00
|
|
|
auto path = *sourcePath + (topRef.subdir == "" ? "" : "/" + topRef.subdir) + "/flake.lock";
|
|
|
|
|
|
|
|
if (pathExists(path))
|
|
|
|
warn("updating lock file '%s'", path);
|
|
|
|
else
|
|
|
|
warn("creating lock file '%s'", path);
|
|
|
|
|
|
|
|
newLockFile.write(path);
|
2020-01-21 15:27:53 +00:00
|
|
|
|
|
|
|
// FIXME: rewriting the lockfile changed the
|
|
|
|
// top-level repo, so we should re-read it.
|
|
|
|
|
|
|
|
#if 0
|
2019-07-12 11:29:54 +00:00
|
|
|
// Hack: Make sure that flake.lock is visible to Git, so it ends up in the Nix store.
|
|
|
|
runProgram("git", true,
|
2020-01-21 15:27:53 +00:00
|
|
|
{ "-C", *sourcePath, "add",
|
2019-07-12 11:29:54 +00:00
|
|
|
"--force",
|
|
|
|
"--intent-to-add",
|
|
|
|
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock" });
|
2020-01-21 15:27:53 +00:00
|
|
|
#endif
|
2019-07-12 11:29:54 +00:00
|
|
|
}
|
2019-05-21 13:03:54 +00:00
|
|
|
} else
|
2019-07-12 11:29:54 +00:00
|
|
|
warn("cannot write lock file of remote flake '%s'", topRef);
|
2020-01-24 12:07:03 +00:00
|
|
|
} else if (lockFileMode != AllPure && lockFileMode != TopRefUsesRegistries)
|
2019-07-12 11:29:54 +00:00
|
|
|
warn("using updated lock file without writing it to file");
|
2019-05-21 12:55:43 +00:00
|
|
|
}
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2020-01-24 12:07:03 +00:00
|
|
|
return LockedFlake { .flake = std::move(flake), .lockFile = std::move(newLockFile) };
|
2019-02-21 05:53:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
static void emitSourceInfoAttrs(EvalState & state, const fetchers::Tree & sourceInfo, Value & vAttrs)
|
2019-05-28 12:01:08 +00:00
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
assert(state.store->isValidPath(sourceInfo.storePath));
|
|
|
|
auto pathS = state.store->printStorePath(sourceInfo.storePath);
|
|
|
|
mkString(*state.allocAttr(vAttrs, state.sOutPath), pathS, {pathS});
|
2019-05-28 12:01:08 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
if (sourceInfo.rev) {
|
2019-05-28 12:01:08 +00:00
|
|
|
mkString(*state.allocAttr(vAttrs, state.symbols.create("rev")),
|
2020-01-21 15:27:53 +00:00
|
|
|
sourceInfo.rev->gitRev());
|
2019-05-28 12:01:08 +00:00
|
|
|
mkString(*state.allocAttr(vAttrs, state.symbols.create("shortRev")),
|
2020-01-21 15:27:53 +00:00
|
|
|
sourceInfo.rev->gitShortRev());
|
2019-05-28 12:01:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sourceInfo.revCount)
|
|
|
|
mkInt(*state.allocAttr(vAttrs, state.symbols.create("revCount")), *sourceInfo.revCount);
|
2019-05-28 18:34:02 +00:00
|
|
|
|
|
|
|
if (sourceInfo.lastModified)
|
|
|
|
mkString(*state.allocAttr(vAttrs, state.symbols.create("lastModified")),
|
2020-01-21 15:27:53 +00:00
|
|
|
fmt("%s", std::put_time(std::gmtime(&*sourceInfo.lastModified), "%Y%m%d%H%M%S")));
|
2019-05-28 12:01:08 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
struct LazyInput
|
|
|
|
{
|
|
|
|
bool isFlake;
|
|
|
|
LockedInput lockedInput;
|
|
|
|
};
|
|
|
|
|
2019-06-04 17:10:35 +00:00
|
|
|
/* Helper primop to make callFlake (below) fetch/call its inputs
|
|
|
|
lazily. Note that this primop cannot be called by user code since
|
|
|
|
it doesn't appear in 'builtins'. */
|
|
|
|
static void prim_callFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
|
|
|
{
|
2019-08-30 14:27:51 +00:00
|
|
|
auto lazyInput = (LazyInput *) args[0]->attrs;
|
2019-06-21 17:04:58 +00:00
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
if (lazyInput->isFlake) {
|
2019-09-18 19:17:27 +00:00
|
|
|
auto flake = getFlake(state, lazyInput->lockedInput.ref, false);
|
2019-06-04 18:34:44 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
if (flake.sourceInfo->narHash != lazyInput->lockedInput.narHash)
|
2019-10-21 12:57:01 +00:00
|
|
|
throw Error("the content hash of flake '%s' doesn't match the hash recorded in the referring lockfile",
|
|
|
|
lazyInput->lockedInput.ref);
|
2019-06-04 18:34:44 +00:00
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
callFlake(state, flake, lazyInput->lockedInput, v);
|
|
|
|
} else {
|
2019-09-18 21:59:45 +00:00
|
|
|
RefMap refMap;
|
2020-01-21 15:27:53 +00:00
|
|
|
auto [sourceInfo, resolvedRef] = getNonFlake(state, lazyInput->lockedInput.ref, false, refMap);
|
2019-06-21 17:04:58 +00:00
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
if (sourceInfo.narHash != lazyInput->lockedInput.narHash)
|
2019-10-21 12:57:01 +00:00
|
|
|
throw Error("the content hash of repository '%s' doesn't match the hash recorded in the referring lockfile",
|
|
|
|
lazyInput->lockedInput.ref);
|
2019-06-04 18:56:13 +00:00
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
state.mkAttrs(v, 8);
|
2019-06-04 18:56:13 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
assert(state.store->isValidPath(sourceInfo.storePath));
|
2019-06-04 18:56:13 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
auto pathS = state.store->printStorePath(sourceInfo.storePath);
|
|
|
|
|
|
|
|
mkString(*state.allocAttr(v, state.sOutPath), pathS, {pathS});
|
2019-06-04 18:56:13 +00:00
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
emitSourceInfoAttrs(state, sourceInfo, v);
|
2019-09-26 15:51:51 +00:00
|
|
|
|
|
|
|
v.attrs->sort();
|
2019-08-30 14:27:51 +00:00
|
|
|
}
|
2019-06-04 18:56:13 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 17:10:35 +00:00
|
|
|
void callFlake(EvalState & state,
|
|
|
|
const Flake & flake,
|
2019-08-30 14:27:51 +00:00
|
|
|
const LockedInputs & lockedInputs,
|
2019-10-02 20:08:19 +00:00
|
|
|
Value & vResFinal)
|
2018-11-29 18:18:36 +00:00
|
|
|
{
|
2019-10-02 20:08:19 +00:00
|
|
|
auto & vRes = *state.allocValue();
|
2019-08-30 11:06:23 +00:00
|
|
|
auto & vInputs = *state.allocValue();
|
2019-06-04 20:35:43 +00:00
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
state.mkAttrs(vInputs, flake.inputs.size() + 1);
|
2019-06-04 17:10:35 +00:00
|
|
|
|
2019-08-30 14:27:51 +00:00
|
|
|
for (auto & [inputId, input] : flake.inputs) {
|
|
|
|
auto vFlake = state.allocAttr(vInputs, inputId);
|
2019-06-04 17:10:35 +00:00
|
|
|
auto vPrimOp = state.allocValue();
|
|
|
|
static auto primOp = new PrimOp(prim_callFlake, 1, state.symbols.create("callFlake"));
|
|
|
|
vPrimOp->type = tPrimOp;
|
|
|
|
vPrimOp->primOp = primOp;
|
|
|
|
auto vArg = state.allocValue();
|
|
|
|
vArg->type = tNull;
|
2019-08-30 14:27:51 +00:00
|
|
|
auto lockedInput = lockedInputs.inputs.find(inputId);
|
|
|
|
assert(lockedInput != lockedInputs.inputs.end());
|
2019-06-04 17:10:35 +00:00
|
|
|
// FIXME: leak
|
2019-08-30 14:27:51 +00:00
|
|
|
vArg->attrs = (Bindings *) new LazyInput{input.isFlake, lockedInput->second};
|
2019-06-04 17:10:35 +00:00
|
|
|
mkApp(*vFlake, *vPrimOp, *vArg);
|
2019-04-16 11:56:08 +00:00
|
|
|
}
|
2019-04-08 20:46:25 +00:00
|
|
|
|
2019-08-30 11:06:23 +00:00
|
|
|
auto & vSourceInfo = *state.allocValue();
|
|
|
|
state.mkAttrs(vSourceInfo, 8);
|
2020-01-21 15:27:53 +00:00
|
|
|
emitSourceInfoAttrs(state, *flake.sourceInfo, vSourceInfo);
|
2019-08-30 15:27:51 +00:00
|
|
|
vSourceInfo.attrs->sort();
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2019-08-30 11:06:23 +00:00
|
|
|
vInputs.attrs->push_back(Attr(state.sSelf, &vRes));
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2019-08-30 11:06:23 +00:00
|
|
|
vInputs.attrs->sort();
|
2019-06-04 20:35:43 +00:00
|
|
|
|
|
|
|
/* For convenience, put the outputs directly in the result, so you
|
|
|
|
can refer to an output of an input as 'inputs.foo.bar' rather
|
|
|
|
than 'inputs.foo.outputs.bar'. */
|
2019-08-30 11:06:23 +00:00
|
|
|
auto vCall = *state.allocValue();
|
|
|
|
state.eval(state.parseExprFromString(
|
|
|
|
"outputsFun: inputs: sourceInfo: let outputs = outputsFun inputs; in "
|
|
|
|
"outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; }", "/"), vCall);
|
|
|
|
|
|
|
|
auto vCall2 = *state.allocValue();
|
|
|
|
auto vCall3 = *state.allocValue();
|
|
|
|
state.callFunction(vCall, *flake.vOutputs, vCall2, noPos);
|
|
|
|
state.callFunction(vCall2, vInputs, vCall3, noPos);
|
|
|
|
state.callFunction(vCall3, vSourceInfo, vRes, noPos);
|
2019-10-02 20:08:19 +00:00
|
|
|
|
|
|
|
vResFinal = vRes;
|
2019-04-16 11:56:08 +00:00
|
|
|
}
|
2019-02-12 20:55:43 +00:00
|
|
|
|
2019-06-04 17:10:35 +00:00
|
|
|
void callFlake(EvalState & state,
|
2020-01-22 19:59:59 +00:00
|
|
|
const LockedFlake & lockedFlake,
|
2019-06-04 17:10:35 +00:00
|
|
|
Value & v)
|
|
|
|
{
|
2020-01-22 19:59:59 +00:00
|
|
|
callFlake(state, lockedFlake.flake, lockedFlake.lockFile, v);
|
2019-06-04 17:10:35 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 15:18:25 +00:00
|
|
|
// This function is exposed to be used in nix files.
|
2019-02-12 20:55:43 +00:00
|
|
|
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
|
|
|
{
|
2020-01-22 19:59:59 +00:00
|
|
|
callFlake(state, lockFlake(state, parseFlakeRef(state.forceStringNoCtx(*args[0], pos)),
|
2019-05-29 13:44:48 +00:00
|
|
|
evalSettings.pureEval ? AllPure : UseUpdatedLockFile), v);
|
2018-11-29 18:18:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static RegisterPrimOp r2("getFlake", 1, prim_getFlake);
|
|
|
|
|
2019-05-29 13:31:07 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 19:59:59 +00:00
|
|
|
Fingerprint LockedFlake::getFingerprint() const
|
2019-06-07 20:25:48 +00:00
|
|
|
{
|
2019-07-12 11:29:54 +00:00
|
|
|
// FIXME: as an optimization, if the flake contains a lock file
|
|
|
|
// and we haven't changed it, then it's sufficient to use
|
2019-06-07 20:25:48 +00:00
|
|
|
// flake.sourceInfo.storePath for the fingerprint.
|
|
|
|
return hashString(htSHA256,
|
2019-11-06 11:01:37 +00:00
|
|
|
fmt("%s;%d;%d;%s",
|
2020-01-21 15:27:53 +00:00
|
|
|
flake.sourceInfo->storePath.to_string(),
|
|
|
|
flake.sourceInfo->revCount.value_or(0),
|
|
|
|
flake.sourceInfo->lastModified.value_or(0),
|
2019-11-06 11:01:37 +00:00
|
|
|
lockFile));
|
2019-06-07 20:25:48 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
Flake::~Flake() { }
|
|
|
|
|
2019-05-29 13:31:07 +00:00
|
|
|
}
|