Skip to content

Commit

Permalink
nix run: Use packages/legacyPackages as fallback if there is no app d…
Browse files Browse the repository at this point in the history
…efinition

'nix run' will try to run $out/bin/<name>, where <name> is the
derivation name (excluding the version). This often works well:

  $ nix run nixpkgs#hello
  Hello, world!

  $ nix run nix -- --version
  nix (Nix) 2.4pre20200626_adf2fbb

  $ nix run patchelf -- --version
  patchelf 0.11.20200623.e61654b

  $ nix run nixpkgs#firefox -- --version
  Mozilla Firefox 77.0.1

  $ nix run nixpkgs#gimp -- --version
  GNU Image Manipulation Program version 2.10.14

though not always:

  $ nix run nixpkgs#git
  error: unable to execute '/nix/store/kp7wp760l4gryq9s36x481b2x4rfklcy-git-2.25.4/bin/git-minimal': No such file or directory
  • Loading branch information
edolstra committed Jun 29, 2020
1 parent 50f13b0 commit 26cf0c6
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 22 deletions.
46 changes: 32 additions & 14 deletions src/nix/app.cc
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
#include "installables.hh"
#include "store-api.hh"
#include "eval-inline.hh"
#include "eval-cache.hh"
#include "names.hh"

namespace nix {

App::App(EvalState & state, Value & vApp)
App Installable::toApp(EvalState & state)
{
state.forceAttrs(vApp);
auto [cursor, attrPath] = getCursor(state, true);

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);
auto type = cursor->getAttr("type")->getString();

auto aProgram = vApp.attrs->need(state.symbols.create("program"));
program = state.forceString(*aProgram.value, context, *aProgram.pos);
if (type == "app") {
auto [program, context] = cursor->getAttr("program")->getStringWithContext();

// FIXME: check that 'program' is in the closure of 'context'.
if (!state.store->isInStore(program))
throw Error("app program '%s' is not in the Nix store", program);
}
if (!state.store->isInStore(program))
throw Error("app program '%s' is not in the Nix store", program);

App Installable::toApp(EvalState & state)
{
return App(state, *toValue(state).first);
std::vector<StorePathWithOutputs> context2;
for (auto & [path, name] : context)
context2.push_back({state.store->parseStorePath(path), {name}});

return App {
.context = std::move(context2),
.program = program,
};
}

else if (type == "derivation") {
auto drvPath = cursor->forceDerivation();
auto outPath = cursor->getAttr(state.sOutPath)->getString();
auto outputName = cursor->getAttr(state.sOutputName)->getString();
auto name = cursor->getAttr(state.sName)->getString();
return App {
.context = { { drvPath, {outputName} } },
.program = outPath + "/bin/" + DrvName(name).name,
};
}

else
throw Error("attribute '%s' has unsupported type '%s'", attrPath, type);
}

}
7 changes: 5 additions & 2 deletions src/nix/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,14 @@ struct CmdFlakeCheck : FlakeCommand

auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try {
#if 0
// FIXME
auto app = App(*state, v);
for (auto & i : app.context) {
auto [drvPathS, outputName] = decodeContext(i);
store->parseStorePath(drvPathS);
}
#endif
} catch (Error & e) {
e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
throw;
Expand Down Expand Up @@ -544,9 +547,9 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
Strings{templateName == "" ? "defaultTemplate" : templateName},
Strings(attrsPathPrefixes), lockFlags);

auto cursor = installable.getCursor(*evalState, true);
auto [cursor, attrPath] = installable.getCursor(*evalState, true);

auto templateDir = cursor.first->getAttr("path")->getString();
auto templateDir = cursor->getAttr("path")->getString();

assert(store->isInStore(templateDir));

Expand Down
4 changes: 1 addition & 3 deletions src/nix/installables.hh
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ typedef std::vector<Buildable> Buildables;

struct App
{
PathSet context;
std::vector<StorePathWithOutputs> context;
Path program;
// FIXME: add args, sandbox settings, metadata, ...

App(EvalState & state, Value & vApp);
};

struct Installable
Expand Down
12 changes: 9 additions & 3 deletions src/nix/run.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,18 @@ struct CmdRun : InstallableCommand, RunCommon

Strings getDefaultFlakeAttrPaths() override
{
return {"defaultApp." + settings.thisSystem.get()};
Strings res{"defaultApp." + settings.thisSystem.get()};
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths())
res.push_back(s);
return res;
}

Strings getDefaultFlakeAttrPathPrefixes() override
{
return {"apps." + settings.thisSystem.get() + "."};
Strings res{"apps." + settings.thisSystem.get() + ".", "packages"};
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes())
res.push_back(s);
return res;
}

void run(ref<Store> store) override
Expand All @@ -186,7 +192,7 @@ struct CmdRun : InstallableCommand, RunCommon

auto app = installable->toApp(*state);

state->realiseContext(app.context);
state->store->buildPaths(app.context);

Strings allArgs{app.program};
for (auto & i : args) allArgs.push_back(i);
Expand Down

2 comments on commit 26cf0c6

@FRidh
Copy link
Member

@FRidh FRidh commented on 26cf0c6 Jul 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice feature! Could Nix first check whether the executable exists before running it, and describe what should be done in case the executable does not exist? This may otherwise be a bit too much magic and get confusing. Typically this type of checking is avoided because the file could be created/moved, but with the read-only store we do not have this issue.

@zimbatm
Copy link
Member

@zimbatm zimbatm commented on 26cf0c6 Jul 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding pkgs.gitMinimal, maybe we could move the variants into the version, like git-2.26.2+minimal. Another idea is to introduce a passthru.entrypoint that can be used to override the location of the binary.

Please sign in to comment.