diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh index 2a145e038..99a0f4889 100644 --- a/src/libcmd/command.hh +++ b/src/libcmd/command.hh @@ -100,6 +100,7 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions { std::optional file; std::optional expr; + std::optional callPackageFile; std::optional applyToInstallable; std::optional installableOverrideAttrs; std::optional installableWithPackages; diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 6587717e4..d9f3284f7 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -207,6 +207,19 @@ SourceExprCommand::SourceExprCommand() .handler = {&expr} }); + addFlag({ + .longName = "call-package", + .shortName = 'C', + .description = + "Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the callPackageable Nix expression stored in *file*. " + "The `callPackage` function is taken from ``. " + "Implies `--impure`.", + .category = installablesCategory, + .labels = {"file"}, + .handler = {&callPackageFile}, + .completer = completePath + }); + addFlag({ .longName = "apply-to-installable", .description = "Apply the function *expr* to each installable.", @@ -542,12 +555,12 @@ Installables SourceExprCommand::parseInstallables( auto doModifyInstallable = applyOverrides && ( applyToInstallable || installableOverrideAttrs || installableWithPackages || overrideArgs.size() > 0 ); - if (nestedIsExprOk && (file || expr)) { - if (file && expr) - throw UsageError("'--file' and '--expr' are exclusive"); + if (nestedIsExprOk && (file || expr || callPackageFile)) { + if ((file && expr) || (file && callPackageFile) || (expr && callPackageFile)) + throw UsageError("'--file', '--expr' and '--call-package' are exclusive"); // FIXME: backward compatibility hack - if (file) evalSettings.pureEval = false; + if (file || callPackageFile) evalSettings.pureEval = false; auto state = getEvalState(); auto vFile = state->allocValue(); @@ -558,7 +571,10 @@ Installables SourceExprCommand::parseInstallables( } else if (file) state->evalFile(lookupFileArg(*state, *file), *vFile); - else { + else if (callPackageFile) { + auto e = state->parseExprFromString(fmt("(import {}).callPackage %s {}", CanonPath::fromCwd(*callPackageFile)), state->rootPath(CanonPath::fromCwd())); + state->eval(e, *vFile); + } else { auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd())); state->eval(e, *vFile); }