From 2467c9837500b26aab5c1dcd3cac12cda44898ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 16:58:59 +0200 Subject: [PATCH 1/6] nix app: Search for installable in the 'apps' output I.e. you can write $ nix app blender-bin:blender_2_80 which is equivalent to $ nix app blender-bin:apps.blender_2_80 --- src/nix/command.hh | 12 ++++++++++++ src/nix/installables.cc | 27 +++++++++++++-------------- src/nix/run.cc | 5 +++++ 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/nix/command.hh b/src/nix/command.hh index 659b724c3..3dad64947 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -102,6 +102,18 @@ struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions { return {"defaultPackage"}; } + + virtual Strings getDefaultFlakeAttrPathPrefixes() + { + return { + // As a convenience, look for the attribute in + // 'outputs.packages'. + "packages.", + // As a temporary hack until Nixpkgs is properly converted + // to provide a clean 'packages' set, look in 'legacyPackages'. + "legacyPackages." + }; + } }; enum RealiseMode { Build, NoBuild, DryRun }; diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 1c7debf4e..d7dd95606 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -257,14 +257,16 @@ struct InstallableFlake : InstallableValue { FlakeRef flakeRef; Strings attrPaths; - bool searchPackages = false; + Strings prefixes; InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, Strings attrPaths) : InstallableValue(cmd), flakeRef(flakeRef), attrPaths(std::move(attrPaths)) { } - InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, std::string attrPath) - : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, searchPackages(true) + InstallableFlake(SourceExprCommand & cmd, FlakeRef && flakeRef, + std::string attrPath, Strings && prefixes) + : InstallableValue(cmd), flakeRef(flakeRef), attrPaths{attrPath}, + prefixes(prefixes) { } std::string what() override { return flakeRef.to_string() + ":" + *attrPaths.begin(); } @@ -273,15 +275,8 @@ struct InstallableFlake : InstallableValue { std::vector res; - if (searchPackages) { - // As a convenience, look for the attribute in - // 'outputs.packages'. - res.push_back("packages." + *attrPaths.begin()); - - // As a temporary hack until Nixpkgs is properly converted - // to provide a clean 'packages' set, look in 'legacyPackages'. - res.push_back("legacyPackages." + *attrPaths.begin()); - } + for (auto & prefix : prefixes) + res.push_back(prefix + *attrPaths.begin()); for (auto & s : attrPaths) res.push_back(s); @@ -421,7 +416,11 @@ std::vector> SourceExprCommand::parseInstallables( else if ((colon = s.rfind(':')) != std::string::npos) { auto flakeRef = std::string(s, 0, colon); auto attrPath = std::string(s, colon + 1); - result.push_back(std::make_shared(*this, FlakeRef(flakeRef, true), attrPath)); + result.push_back(std::make_shared( + *this, + FlakeRef(flakeRef, true), + attrPath, + getDefaultFlakeAttrPathPrefixes())); } else if (s.find('/') != std::string::npos || s == ".") { @@ -437,7 +436,7 @@ std::vector> SourceExprCommand::parseInstallables( } else - result.push_back(std::make_shared(*this, FlakeRef("nixpkgs"), s)); + throw Error("unsupported argument '%s'", s); } } diff --git a/src/nix/run.cc b/src/nix/run.cc index 00a682832..62aae12f6 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -225,6 +225,11 @@ struct CmdApp : InstallableCommand, RunCommon return {"defaultApp"}; } + Strings getDefaultFlakeAttrPathPrefixes() override + { + return {"apps."}; + } + void run(ref store) override { auto state = getEvalState(); From 0d69f7f3f012aceb4c494f3c1cc866b378c5eac1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 17:05:37 +0200 Subject: [PATCH 2/6] nix app: Accept arguments Example: $ nix app blender-bin -- --version Blender 2.80 (sub 74) --- src/nix/run.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/nix/run.cc b/src/nix/run.cc index 62aae12f6..d30851d47 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -196,8 +196,11 @@ static RegisterCommand r1(make_ref()); struct CmdApp : InstallableCommand, RunCommon { + std::vector args; + CmdApp() { + expectArgs("args", &args); } std::string name() override @@ -238,7 +241,10 @@ struct CmdApp : InstallableCommand, RunCommon state->realiseContext(app.context); - runProgram(store, app.program, {app.program}); + Strings allArgs{app.program}; + for (auto & i : args) allArgs.push_back(i); + + runProgram(store, app.program, allArgs); } }; From 3b2ebd029cac0b1ce715d8aca44204130c93a869 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 17:31:34 +0200 Subject: [PATCH 3/6] nix flake info --json: Revive enumerating the outputs --- src/nix/flake.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index af1a361b3..6f6d3c130 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -234,8 +234,8 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON if (json) { auto json = flakeToJson(flake); -#if 0 auto state = getEvalState(); + auto flake = resolveFlake(); auto vFlake = state->allocValue(); flake::callFlake(*state, flake, *vFlake); @@ -257,7 +257,6 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON }); json["outputs"] = std::move(outputs); -#endif std::cout << json.dump() << std::endl; } else From 9d1207c02c091a00454fcb7266653d18a6023923 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 17:59:57 +0200 Subject: [PATCH 4/6] nix flake check: Check apps --- src/libexpr/primops.cc | 14 +++++++------- src/nix/command.hh | 2 ++ src/nix/flake.cc | 29 +++++++++++++++++++++++++++-- src/nix/installables.cc | 23 +++++++++++------------ 4 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a9a1a1e35..10ce1abf5 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -51,21 +51,20 @@ void EvalState::realiseContext(const PathSet & context) PathSet drvs; for (auto & i : context) { - std::pair decoded = decodeContext(i); - Path ctx = decoded.first; + auto [ctx, outputName] = decodeContext(i); assert(store->isStorePath(ctx)); if (!store->isValidPath(ctx)) throw InvalidPathError(ctx); - if (!decoded.second.empty() && nix::isDerivation(ctx)) { - drvs.insert(decoded.first + "!" + decoded.second); + if (!outputName.empty() && nix::isDerivation(ctx)) { + drvs.insert(ctx + "!" + outputName); /* Add the output of this derivation to the allowed paths. */ if (allowedPaths) { - auto drv = store->derivationFromPath(decoded.first); - DerivationOutputs::iterator i = drv.outputs.find(decoded.second); + auto drv = store->derivationFromPath(ctx); + DerivationOutputs::iterator i = drv.outputs.find(outputName); if (i == drv.outputs.end()) - throw Error("derivation '%s' does not have an output named '%s'", decoded.first, decoded.second); + throw Error("derivation '%s' does not have an output named '%s'", ctx, outputName); allowedPaths->insert(i->second.path); } } @@ -80,6 +79,7 @@ void EvalState::realiseContext(const PathSet & context) PathSet willBuild, willSubstitute, unknown; unsigned long long downloadSize, narSize; store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize); + store->buildPaths(drvs); } diff --git a/src/nix/command.hh b/src/nix/command.hh index 3dad64947..0ffbe46f5 100644 --- a/src/nix/command.hh +++ b/src/nix/command.hh @@ -43,6 +43,8 @@ struct App PathSet context; Path program; // FIXME: add args, sandbox settings, metadata, ... + + App(EvalState & state, Value & vApp); }; struct Installable diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6f6d3c130..6f6d1a0aa 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -7,6 +7,7 @@ #include "flake/flake.hh" #include "get-drvs.hh" #include "store-api.hh" +#include "derivations.hh" #include #include @@ -301,13 +302,27 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON // FIXME: check meta attributes return drvInfo->queryDrvPath(); } catch (Error & e) { - e.addPrefix(fmt("while checking flake output attribute '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + e.addPrefix(fmt("while checking the derivation '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); throw; } }; PathSet drvPaths; + auto checkApp = [&](const std::string & attrPath, Value & v) { + try { + auto app = App(*state, v); + for (auto & i : app.context) { + auto [drvPath, outputName] = decodeContext(i); + if (!outputName.empty() && nix::isDerivation(drvPath)) + drvPaths.insert(drvPath + "!" + outputName); + } + } catch (Error & e) { + e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", attrPath)); + throw; + } + }; + { Activity act(*logger, lvlInfo, actUnknown, "evaluating flake"); @@ -337,9 +352,19 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON name + "." + (std::string) aCheck.name, *aCheck.value); } + else if (name == "apps") { + state->forceAttrs(vProvide); + for (auto & aCheck : *vProvide.attrs) + checkApp( + name + "." + (std::string) aCheck.name, *aCheck.value); + } + else if (name == "defaultPackage" || name == "devShell") checkDerivation(name, vProvide); + else if (name == "defaultApp") + checkApp(name, vProvide); + } catch (Error & e) { e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name)); throw; @@ -347,7 +372,7 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON }); } - if (build) { + if (build && !drvPaths.empty()) { Activity act(*logger, lvlInfo, actUnknown, "running flake checks"); store->buildPaths(drvPaths); } diff --git a/src/nix/installables.cc b/src/nix/installables.cc index d7dd95606..feaf57f0c 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -69,26 +69,25 @@ Buildable Installable::toBuildable() return std::move(buildables[0]); } -App Installable::toApp(EvalState & state) +App::App(EvalState & state, Value & vApp) { - auto v = toValue(state); + state.forceAttrs(vApp); - state.forceAttrs(*v); - - auto aType = v->attrs->need(state.sType); + auto aType = vApp.attrs->need(state.sType); if (state.forceStringNoCtx(*aType.value, *aType.pos) != "app") throw Error("value does not have type 'app', at %s", *aType.pos); - App app; - - auto aProgram = v->attrs->need(state.symbols.create("program")); - app.program = state.forceString(*aProgram.value, app.context, *aProgram.pos); + auto aProgram = vApp.attrs->need(state.symbols.create("program")); + program = state.forceString(*aProgram.value, context, *aProgram.pos); // FIXME: check that 'program' is in the closure of 'context'. - if (!state.store->isInStore(app.program)) - throw Error("app program '%s' is not in the Nix store", app.program); + if (!state.store->isInStore(program)) + throw Error("app program '%s' is not in the Nix store", program); +} - return app; +App Installable::toApp(EvalState & state) +{ + return App(state, *toValue(state)); } struct InstallableStorePath : Installable From f2fcc163fa6c38566f3a78cd3757ab22f39821fc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 17 Jun 2019 18:05:32 +0200 Subject: [PATCH 5/6] nix flake check: Warn about unknown flake outputs --- src/nix/flake.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6f6d1a0aa..8248a5d7b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -365,6 +365,9 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON else if (name == "defaultApp") checkApp(name, vProvide); + else + warn("unknown flake output '%s'", name); + } catch (Error & e) { e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name)); throw; From 556f33422d0a064ef100fe232f60b42b34bf40e2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 18 Jun 2019 09:45:14 +0200 Subject: [PATCH 6/6] nix flake check: Ignore legacyPackages --- src/nix/flake.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 8248a5d7b..b98cce078 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -365,6 +365,10 @@ struct CmdFlakeCheck : FlakeCommand, MixJSON else if (name == "defaultApp") checkApp(name, vProvide); + else if (name == "legacyPackages") + // FIXME: do getDerivations? + ; + else warn("unknown flake output '%s'", name);