Rename requires -> inputs, provides -> outputs

Issue #2828.
This commit is contained in:
Eelco Dolstra 2019-05-29 23:09:23 +02:00
parent 65e88694c2
commit 094539ef4a
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
8 changed files with 112 additions and 111 deletions

View file

@ -103,12 +103,12 @@ module.
# A list of flake references denoting the flakes that this flake
# depends on. Nix will resolve and fetch these flakes and pass them
# as a function argument to `provides` below.
# as a function argument to `outputs` below.
#
# `flake:nixpkgs` denotes a flake named `nixpkgs` which is looked up
# in the flake registry, or in `flake.lock` inside this flake, if it
# exists.
requires = [ flake:nixpkgs ];
inputs = [ flake:nixpkgs ];
# The stuff provided by this flake. Flakes can provide whatever they
# want (convention over configuration), but some attributes have
@ -117,9 +117,9 @@ module.
# `nixosModules` is used by NixOS to automatically pull in the
# modules provided by a flake.
#
# `provides` takes a single argument named `deps` that contains
# `outputs` takes a single argument named `deps` that contains
# the resolved set of flakes. (See below.)
provides = deps: {
outputs = deps: {
# This is searched by `nix`, so something like `nix install
# dwarffs.dwarffs` resolves to this `packages.dwarffs`.
@ -168,7 +168,7 @@ Similarly, a minimal `flake.nix` for Nixpkgs:
description = "A collection of packages for the Nix package manager";
provides = deps:
outputs = deps:
let pkgs = import ./. {}; in
{
lib = import ./lib;
@ -310,9 +310,9 @@ Example:
```
## `provides`
## `outputs`
The flake attribute `provides` is a function that takes an argument
The flake attribute `outputs` is a function that takes an argument
named `deps` and returns a (mostly) arbitrary attrset of values. Some
of the standard result attributes:
@ -329,13 +329,13 @@ of the standard result attributes:
we need to avoid a situation where `nixos-rebuild` needs to fetch
its own `nixpkgs` just to do `evalModules`.)
* `shell`: A specification of a development environment in some TBD
* `devShell`: A specification of a development environment in some TBD
format.
The function argument `flakes` is an attrset that contains an
attribute for each dependency specified in `requires`. (Should it
attribute for each dependency specified in `inputs`. (Should it
contain transitive dependencies? Probably not.) Each attribute is an
attrset containing the `provides` of the dependency, in addition to
attrset containing the `outputs` of the dependency, in addition to
the following attributes:
* `path`: The path to the flake's source code. Useful when you want to
@ -366,13 +366,13 @@ It may be useful to pull in repositories that are not flakes
(i.e. don't contain a `flake.nix`). This could be done in two ways:
* Allow flakes not to have a `flake.nix` file, in which case it's a
flake with no requires and no provides. The downside of this
flake with no inputs and no outputs. The downside of this
approach is that we can't detect accidental use of a non-flake
repository. (Also, we need to conjure up an identifier somehow.)
* Add a flake attribute to specifiy non-flake dependencies, e.g.
> nonFlakeRequires.foobar = github:foo/bar;
> nonFlakeInputs.foobar = github:foo/bar;
## Flake registry
@ -454,7 +454,7 @@ The default installation source in `nix` is the `packages` from all
flakes in the registry, that is:
```
builtins.mapAttrs (flakeName: flakeInfo:
(getFlake flakeInfo.uri).${flakeName}.provides.packages or {})
(getFlake flakeInfo.uri).${flakeName}.outputs.packages or {})
builtins.flakeRegistry
```
(where `builtins.flakeRegistry` is the global registry with user
@ -476,10 +476,11 @@ in the registry named `hello`.
Maybe the command
> nix shell
> nix dev-shell
should do something like use `provides.shell` to initialize the shell,
but probably we should ditch `nix shell` / `nix-shell` for direnv.
should do something like use `outputs.devShell` to initialize the
shell, but probably we should ditch `nix shell` / `nix-shell` for
direnv.
## Pure evaluation and caching
@ -535,7 +536,7 @@ repositories.
```nix
{
provides = flakes: {
outputs = flakes: {
nixosSystems.default =
flakes.nixpkgs.lib.evalModules {
modules =
@ -549,7 +550,7 @@ repositories.
};
};
requires =
inputs =
[ "nixpkgs/nixos-18.09"
"dwarffs"
"hydra"

View file

@ -1,10 +1,10 @@
{
"nonFlakeRequires": {},
"requires": {
"inputs": {
"nixpkgs": {
"contentHash": "sha256-vy2UmXQM66aS/Kn2tCtjt9RwxfBvV+nQVb5tJQFwi8E=",
"uri": "github:edolstra/nixpkgs/a4d896e89932e873c4117908d558db6210fa3b56"
"narHash": "sha256-rMiWaLXkhizEEMEeMDutUl0Y/c+VEjfjvMkvBwvuQJU=",
"uri": "github:edolstra/nixpkgs/eeeffd24cd7e407cfaa99e98cfbb8f93bf4cc033"
}
},
"nonFlakeInputs": {},
"version": 1
}

View file

@ -5,13 +5,13 @@
epoch = 2019;
requires = [ "nixpkgs" ];
inputs = [ "nixpkgs" ];
provides = deps: rec {
outputs = inputs: rec {
hydraJobs = import ./release.nix {
nix = deps.self;
nixpkgs = deps.nixpkgs;
nix = inputs.self;
nixpkgs = inputs.nixpkgs;
};
checks = {
@ -29,7 +29,7 @@
defaultPackage = packages.nix;
devShell = import ./shell.nix {
nixpkgs = deps.nixpkgs;
nixpkgs = inputs.nixpkgs;
};
};
}

View file

@ -56,21 +56,21 @@ LockFile::FlakeEntry readFlakeEntry(nlohmann::json json)
if (!flakeRef.isImmutable())
throw Error("cannot use mutable flake '%s' in pure mode", flakeRef);
LockFile::FlakeEntry entry(flakeRef, Hash((std::string) json["contentHash"]));
LockFile::FlakeEntry entry(flakeRef, Hash((std::string) json["narHash"]));
auto nonFlakeRequires = json["nonFlakeRequires"];
auto nonFlakeInputs = json["nonFlakeInputs"];
for (auto i = nonFlakeRequires.begin(); i != nonFlakeRequires.end(); ++i) {
for (auto i = nonFlakeInputs.begin(); i != nonFlakeInputs.end(); ++i) {
FlakeRef flakeRef(i->value("uri", ""));
if (!flakeRef.isImmutable())
throw Error("requested to fetch FlakeRef '%s' purely, which is mutable", flakeRef);
LockFile::NonFlakeEntry nonEntry(flakeRef, Hash(i->value("contentHash", "")));
LockFile::NonFlakeEntry nonEntry(flakeRef, Hash(i->value("narHash", "")));
entry.nonFlakeEntries.insert_or_assign(i.key(), nonEntry);
}
auto requires = json["requires"];
auto inputs = json["inputs"];
for (auto i = requires.begin(); i != requires.end(); ++i)
for (auto i = inputs.begin(); i != inputs.end(); ++i)
entry.flakeEntries.insert_or_assign(i.key(), readFlakeEntry(*i));
return entry;
@ -89,19 +89,19 @@ LockFile readLockFile(const Path & path)
if (version != 1)
throw Error("lock file '%s' has unsupported version %d", path, version);
auto nonFlakeRequires = json["nonFlakeRequires"];
auto nonFlakeInputs = json["nonFlakeInputs"];
for (auto i = nonFlakeRequires.begin(); i != nonFlakeRequires.end(); ++i) {
for (auto i = nonFlakeInputs.begin(); i != nonFlakeInputs.end(); ++i) {
FlakeRef flakeRef(i->value("uri", ""));
LockFile::NonFlakeEntry nonEntry(flakeRef, Hash(i->value("contentHash", "")));
LockFile::NonFlakeEntry nonEntry(flakeRef, Hash(i->value("narHash", "")));
if (!flakeRef.isImmutable())
throw Error("found mutable FlakeRef '%s' in lockfile at path %s", flakeRef, path);
lockFile.nonFlakeEntries.insert_or_assign(i.key(), nonEntry);
}
auto requires = json["requires"];
auto inputs = json["inputs"];
for (auto i = requires.begin(); i != requires.end(); ++i)
for (auto i = inputs.begin(); i != inputs.end(); ++i)
lockFile.flakeEntries.insert_or_assign(i.key(), readFlakeEntry(*i));
return lockFile;
@ -111,13 +111,13 @@ nlohmann::json flakeEntryToJson(const LockFile::FlakeEntry & entry)
{
nlohmann::json json;
json["uri"] = entry.ref.to_string();
json["contentHash"] = entry.narHash.to_string(SRI);
json["narHash"] = entry.narHash.to_string(SRI);
for (auto & x : entry.nonFlakeEntries) {
json["nonFlakeRequires"][x.first]["uri"] = x.second.ref.to_string();
json["nonFlakeRequires"][x.first]["contentHash"] = x.second.narHash.to_string(SRI);
json["nonFlakeInputs"][x.first]["uri"] = x.second.ref.to_string();
json["nonFlakeInputs"][x.first]["narHash"] = x.second.narHash.to_string(SRI);
}
for (auto & x : entry.flakeEntries)
json["requires"][x.first.to_string()] = flakeEntryToJson(x.second);
json["inputs"][x.first.to_string()] = flakeEntryToJson(x.second);
return json;
}
@ -125,14 +125,14 @@ void writeLockFile(const LockFile & lockFile, const Path & path)
{
nlohmann::json json;
json["version"] = 1;
json["nonFlakeRequires"] = nlohmann::json::object();
json["nonFlakeInputs"] = nlohmann::json::object();
for (auto & x : lockFile.nonFlakeEntries) {
json["nonFlakeRequires"][x.first]["uri"] = x.second.ref.to_string();
json["nonFlakeRequires"][x.first]["contentHash"] = x.second.narHash.to_string(SRI);
json["nonFlakeInputs"][x.first]["uri"] = x.second.ref.to_string();
json["nonFlakeInputs"][x.first]["narHash"] = x.second.narHash.to_string(SRI);
}
json["requires"] = nlohmann::json::object();
json["inputs"] = nlohmann::json::object();
for (auto & x : lockFile.flakeEntries)
json["requires"][x.first.to_string()] = flakeEntryToJson(x.second);
json["inputs"][x.first.to_string()] = flakeEntryToJson(x.second);
createDirs(dirOf(path));
writeFile(path, json.dump(4) + "\n"); // '4' = indentation in json file
}
@ -319,41 +319,41 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool impureIsAllowe
if (auto description = vInfo.attrs->get(state.sDescription))
flake.description = state.forceStringNoCtx(*(**description).value, *(**description).pos);
auto sRequires = state.symbols.create("requires");
auto sInputs = state.symbols.create("inputs");
if (auto requires = vInfo.attrs->get(sRequires)) {
state.forceList(*(**requires).value, *(**requires).pos);
for (unsigned int n = 0; n < (**requires).value->listSize(); ++n)
flake.requires.push_back(FlakeRef(state.forceStringNoCtx(
*(**requires).value->listElems()[n], *(**requires).pos)));
if (auto inputs = vInfo.attrs->get(sInputs)) {
state.forceList(*(**inputs).value, *(**inputs).pos);
for (unsigned int n = 0; n < (**inputs).value->listSize(); ++n)
flake.inputs.push_back(FlakeRef(state.forceStringNoCtx(
*(**inputs).value->listElems()[n], *(**inputs).pos)));
}
auto sNonFlakeRequires = state.symbols.create("nonFlakeRequires");
auto sNonFlakeInputs = state.symbols.create("nonFlakeInputs");
if (std::optional<Attr *> nonFlakeRequires = vInfo.attrs->get(sNonFlakeRequires)) {
state.forceAttrs(*(**nonFlakeRequires).value, *(**nonFlakeRequires).pos);
for (Attr attr : *(*(**nonFlakeRequires).value).attrs) {
if (std::optional<Attr *> nonFlakeInputs = vInfo.attrs->get(sNonFlakeInputs)) {
state.forceAttrs(*(**nonFlakeInputs).value, *(**nonFlakeInputs).pos);
for (Attr attr : *(*(**nonFlakeInputs).value).attrs) {
std::string myNonFlakeUri = state.forceStringNoCtx(*attr.value, *attr.pos);
FlakeRef nonFlakeRef = FlakeRef(myNonFlakeUri);
flake.nonFlakeRequires.insert_or_assign(attr.name, nonFlakeRef);
flake.nonFlakeInputs.insert_or_assign(attr.name, nonFlakeRef);
}
}
auto sProvides = state.symbols.create("provides");
auto sOutputs = state.symbols.create("outputs");
if (auto provides = vInfo.attrs->get(sProvides)) {
state.forceFunction(*(**provides).value, *(**provides).pos);
flake.vProvides = (**provides).value;
if (auto outputs = vInfo.attrs->get(sOutputs)) {
state.forceFunction(*(**outputs).value, *(**outputs).pos);
flake.vOutputs = (**outputs).value;
} else
throw Error("flake '%s' lacks attribute 'provides'", flakeRef);
throw Error("flake '%s' lacks attribute 'outputs'", flakeRef);
for (auto & attr : *vInfo.attrs) {
if (attr.name != sEpoch &&
attr.name != state.sName &&
attr.name != state.sDescription &&
attr.name != sRequires &&
attr.name != sNonFlakeRequires &&
attr.name != sProvides)
attr.name != sInputs &&
attr.name != sNonFlakeInputs &&
attr.name != sOutputs)
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
flakeRef, attr.name, *attr.pos);
}
@ -436,7 +436,7 @@ ResolvedFlake resolveFlakeFromLockFile(EvalState & state, const FlakeRef & flake
ResolvedFlake deps(flake);
for (auto & nonFlakeInfo : flake.nonFlakeRequires) {
for (auto & nonFlakeInfo : flake.nonFlakeInputs) {
FlakeRef ref = nonFlakeInfo.second;
auto i = lockFile.nonFlakeEntries.find(nonFlakeInfo.first);
if (i != lockFile.nonFlakeEntries.end()) {
@ -451,7 +451,7 @@ ResolvedFlake resolveFlakeFromLockFile(EvalState & state, const FlakeRef & flake
}
}
for (auto newFlakeRef : flake.requires) {
for (auto newFlakeRef : flake.inputs) {
auto i = lockFile.flakeEntries.find(newFlakeRef);
if (i != lockFile.flakeEntries.end()) { // Propagate lockFile downwards if possible
ResolvedFlake newResFlake = resolveFlakeFromLockFile(state, i->second.ref, handleLockFile, entryToLockFile(i->second));
@ -532,8 +532,8 @@ static void emitSourceInfoAttrs(EvalState & state, const SourceInfo & sourceInfo
void callFlake(EvalState & state, const ResolvedFlake & resFlake, Value & v)
{
// Construct the resulting attrset '{description, provides,
// ...}'. This attrset is passed lazily as an argument to 'provides'.
// Construct the resulting attrset '{description, outputs,
// ...}'. This attrset is passed lazily as an argument to 'outputs'.
state.mkAttrs(v, resFlake.flakeDeps.size() + resFlake.nonFlakeDeps.size() + 8);
@ -558,8 +558,8 @@ void callFlake(EvalState & state, const ResolvedFlake & resFlake, Value & v)
emitSourceInfoAttrs(state, resFlake.flake.sourceInfo, v);
auto vProvides = state.allocAttr(v, state.symbols.create("provides"));
mkApp(*vProvides, *resFlake.flake.vProvides, v);
auto vOutputs = state.allocAttr(v, state.symbols.create("outputs"));
mkApp(*vOutputs, *resFlake.flake.vOutputs, v);
v.attrs->push_back(Attr(state.symbols.create("self"), &v));

View file

@ -106,9 +106,9 @@ struct Flake
FlakeRef originalRef;
std::string description;
SourceInfo sourceInfo;
std::vector<FlakeRef> requires;
std::map<FlakeAlias, FlakeRef> nonFlakeRequires;
Value * vProvides; // FIXME: gc
std::vector<FlakeRef> inputs;
std::map<FlakeAlias, FlakeRef> nonFlakeInputs;
Value * vOutputs; // FIXME: gc
unsigned int epoch;
Flake(const FlakeRef & origRef, const SourceInfo & sourceInfo)

View file

@ -199,16 +199,16 @@ struct CmdFlakeUpdate : FlakeCommand
}
};
static void enumerateProvides(EvalState & state, Value & vFlake,
static void enumerateOutputs(EvalState & state, Value & vFlake,
std::function<void(const std::string & name, Value & vProvide)> callback)
{
state.forceAttrs(vFlake);
auto vProvides = (*vFlake.attrs->get(state.symbols.create("provides")))->value;
auto vOutputs = (*vFlake.attrs->get(state.symbols.create("outputs")))->value;
state.forceAttrs(*vProvides);
state.forceAttrs(*vOutputs);
for (auto & attr : *vProvides->attrs)
for (auto & attr : *vOutputs->attrs)
callback(attr.name, *attr.value);
}
@ -237,9 +237,9 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON
auto vFlake = state->allocValue();
flake::callFlake(*state, flake, *vFlake);
auto provides = nlohmann::json::object();
auto outputs = nlohmann::json::object();
enumerateProvides(*state,
enumerateOutputs(*state,
*vFlake,
[&](const std::string & name, Value & vProvide) {
auto provide = nlohmann::json::object();
@ -250,10 +250,10 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON
provide[aCheck.name] = nlohmann::json::object();
}
provides[name] = provide;
outputs[name] = provide;
});
json["provides"] = std::move(provides);
json["outputs"] = std::move(outputs);
std::cout << json.dump() << std::endl;
} else
@ -298,7 +298,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
// FIXME: check meta attributes
return drvInfo->queryDrvPath();
} catch (Error & e) {
e.addPrefix(fmt("while checking flake attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath));
e.addPrefix(fmt("while checking flake output attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath));
throw;
}
};
@ -311,7 +311,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON
auto vFlake = state->allocValue();
flake::callFlake(*state, flake, *vFlake);
enumerateProvides(*state,
enumerateOutputs(*state,
*vFlake,
[&](const std::string & name, Value & vProvide) {
Activity act(*logger, lvlChatty, actUnknown,

View file

@ -230,16 +230,16 @@ struct InstallableFlake : InstallableValue
makeFlakeClosureGCRoot(*state.store, flakeRef, resFlake);
auto vProvides = (*vFlake->attrs->get(state.symbols.create("provides")))->value;
auto vOutputs = (*vFlake->attrs->get(state.symbols.create("outputs")))->value;
state.forceValue(*vProvides);
state.forceValue(*vOutputs);
auto emptyArgs = state.allocBindings(0);
if (searchPackages) {
// As a convenience, look for the attribute in
// 'provides.packages'.
if (auto aPackages = *vProvides->attrs->get(state.symbols.create("packages"))) {
// 'outputs.packages'.
if (auto aPackages = *vOutputs->attrs->get(state.symbols.create("packages"))) {
try {
auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value);
state.forceValue(*v);
@ -250,7 +250,7 @@ struct InstallableFlake : InstallableValue
// As a temporary hack until Nixpkgs is properly converted
// to provide a clean 'packages' set, look in 'legacyPackages'.
if (auto aPackages = *vProvides->attrs->get(state.symbols.create("legacyPackages"))) {
if (auto aPackages = *vOutputs->attrs->get(state.symbols.create("legacyPackages"))) {
try {
auto * v = findAlongAttrPath(state, *attrPaths.begin(), *emptyArgs, *aPackages->value);
state.forceValue(*v);
@ -260,10 +260,10 @@ struct InstallableFlake : InstallableValue
}
}
// Otherwise, look for it in 'provides'.
// Otherwise, look for it in 'outputs'.
for (auto & attrPath : attrPaths) {
try {
auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vProvides);
auto * v = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
state.forceValue(*v);
return v;
} catch (AttrPathNotFound & e) {

View file

@ -33,7 +33,7 @@ cat > $flake1Dir/flake.nix <<EOF
description = "Bla bla";
provides = deps: rec {
outputs = inputs: rec {
packages.foo = import ./simple.nix;
defaultPackage = packages.foo;
};
@ -50,12 +50,12 @@ cat > $flake2Dir/flake.nix <<EOF
epoch = 2019;
requires = [ "flake1" ];
inputs = [ "flake1" ];
description = "Fnord";
provides = deps: rec {
packages.bar = deps.flake1.provides.packages.foo;
outputs = inputs: rec {
packages.bar = inputs.flake1.outputs.packages.foo;
};
}
EOF
@ -69,12 +69,12 @@ cat > $flake3Dir/flake.nix <<EOF
epoch = 2019;
requires = [ "flake2" ];
inputs = [ "flake2" ];
description = "Fnord";
provides = deps: rec {
packages.xyzzy = deps.flake2.provides.packages.bar;
outputs = inputs: rec {
packages.xyzzy = inputs.flake2.outputs.packages.bar;
};
}
EOF
@ -168,13 +168,13 @@ cat > $flake3Dir/flake.nix <<EOF
epoch = 2019;
requires = [ "flake1" "flake2" ];
inputs = [ "flake1" "flake2" ];
description = "Fnord";
provides = deps: rec {
packages.xyzzy = deps.flake2.provides.packages.bar;
packages.sth = deps.flake1.provides.packages.foo;
outputs = inputs: rec {
packages.xyzzy = inputs.flake2.outputs.packages.bar;
packages.sth = inputs.flake1.outputs.packages.foo;
};
}
EOF
@ -209,7 +209,7 @@ nix build -o $TEST_ROOT/result --flake-registry file://$registry file://$flake2D
mv $flake1Dir.tmp $flake1Dir
mv $flake2Dir.tmp $flake2Dir
# Add nonFlakeRequires to flake3.
# Add nonFlakeInputs to flake3.
rm $flake3Dir/flake.nix
cat > $flake3Dir/flake.nix <<EOF
@ -218,23 +218,23 @@ cat > $flake3Dir/flake.nix <<EOF
epoch = 2019;
requires = [ "flake1" "flake2" ];
inputs = [ "flake1" "flake2" ];
nonFlakeRequires = {
nonFlakeInputs = {
nonFlake = "$nonFlakeDir";
};
description = "Fnord";
provides = deps: rec {
packages.xyzzy = deps.flake2.provides.packages.bar;
packages.sth = deps.flake1.provides.packages.foo;
outputs = inputs: rec {
packages.xyzzy = inputs.flake2.outputs.packages.bar;
packages.sth = inputs.flake1.outputs.packages.foo;
};
}
EOF
git -C $flake3Dir add flake.nix
git -C $flake3Dir commit -m 'Add nonFlakeRequires'
git -C $flake3Dir commit -m 'Add nonFlakeInputs'
# Check whether `nix build` works with a lockfile which is missing a nonFlakeRequires
# Check whether `nix build` works with a lockfile which is missing a nonFlakeInputs
nix build -o $TEST_ROOT/result --flake-registry $registry $flake3Dir:sth