Skip to content

Commit

Permalink
Allow overring module options from the command line
Browse files Browse the repository at this point in the history
E.g.

  $ nix build github:tweag/nix-ux/configs?dir=configs#hello --argstr who Everybody
  $ ./result/bin/hello
  Hello Everybody

This works by generating a new flake that imports the specified one
and sets the specified module options.
  • Loading branch information
edolstra committed Sep 24, 2020
1 parent b068f96 commit dc4a280
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/nix/bundle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ struct CmdBundle : InstallableCommand
auto bundler = InstallableFlake(
evalState, std::move(bundlerFlakeRef),
Strings{bundlerName == "" ? "defaultBundler" : bundlerName},
Strings({"bundlers."}), lockFlags);
Strings({"bundlers."}), lockFlags, nullptr);

Value * arg = evalState->allocValue();
evalState->mkAttrs(*arg, 2);
Expand Down
3 changes: 2 additions & 1 deletion src/nix/develop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ struct CmdDevelop : Common, MixEnvironment
installable->nixpkgsFlakeRef(),
Strings{"bashInteractive"},
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
lockFlags);
lockFlags,
nullptr);

shell = state->store->printStorePath(
toStorePath(state->store, Realise::Outputs, OperateOn::Output, bashInstallable)) + "/bin/bash";
Expand Down
2 changes: 1 addition & 1 deletion src/nix/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
auto installable = InstallableFlake(
evalState, std::move(templateFlakeRef),
Strings{templateName == "" ? "defaultTemplate" : templateName},
Strings(attrsPathPrefixes), lockFlags);
Strings(attrsPathPrefixes), lockFlags, nullptr);

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

Expand Down
65 changes: 62 additions & 3 deletions src/nix/installables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "url.hh"
#include "registry.hh"

#include "../cpptoml/cpptoml.h"

#include <regex>
#include <queue>

Expand Down Expand Up @@ -549,8 +551,61 @@ InstallableFlake::getCursors(EvalState & state)

std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{
if (!_lockedFlake)
if (!_lockedFlake) {
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlags));

if (options && options->size()) {
/* Get the modules defined by this flake. */
auto cache = openEvalCache(*state, _lockedFlake);
auto root = cache->getRoot();
auto aModules = root->maybeGetAttr(state->symbols.create("modules"));
if (!aModules)
throw Error("flake '%s' has no modules, so --arg cannot override anything", flakeRef);

auto toml = cpptoml::make_table();

auto base = cpptoml::make_table();
base->insert("type", "path");
base->insert("path", _lockedFlake->flake.sourceInfo->actualPath);
if (_lockedFlake->flake.lockedRef.subdir != "")
base->insert("dir", _lockedFlake->flake.lockedRef.subdir);
// FIXME: copy rev etc.
auto inputs = cpptoml::make_table();
inputs->insert("base", base);

toml->insert("inputs", inputs);

for (auto & moduleName : aModules->getAttrs()) {
auto module = cpptoml::make_table();
auto extends = cpptoml::make_array();
extends->push_back("base#" + (std::string) moduleName);
module->insert("extends", extends);
for (auto & option : options->lexicographicOrder()) {
state->forceValue(*option->value);
if (option->value->type == tString)
module->insert(option->name, state->forceString(*option->value));
else if (option->value->type == tInt)
module->insert(option->name, option->value->integer);
else
throw Error("option '%s' is %s which is not supported",
option->name, showType(*option->value));
}
toml->insert(moduleName, module);
}

auto tempDir = createTempDir();
AutoDelete cleanup(tempDir);

std::ostringstream str;
str << *toml;
debug("writing temporary flake:\n%s", str.str());
writeFile(tempDir + "/nix.toml", str.str());

_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state,
parseFlakeRef("path://" + tempDir), lockFlags));
}
}

return _lockedFlake;
}

Expand Down Expand Up @@ -600,10 +655,14 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(

try {
auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath("."));
auto state = getEvalState();
result.push_back(std::make_shared<InstallableFlake>(
getEvalState(), std::move(flakeRef),
state,
std::move(flakeRef),
fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
getDefaultFlakeAttrPathPrefixes(), lockFlags));
getDefaultFlakeAttrPathPrefixes(),
lockFlags,
getAutoArgs(*state)));
continue;
} catch (...) {
ex = std::current_exception();
Expand Down
14 changes: 10 additions & 4 deletions src/nix/installables.hh
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,17 @@ struct InstallableFlake : InstallableValue
Strings prefixes;
const flake::LockFlags & lockFlags;
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;

InstallableFlake(ref<EvalState> state, FlakeRef && flakeRef,
Strings && attrPaths, Strings && prefixes, const flake::LockFlags & lockFlags)
Bindings * options;

InstallableFlake(
ref<EvalState> state,
FlakeRef && flakeRef,
Strings && attrPaths,
Strings && prefixes, const
flake::LockFlags & lockFlags,
Bindings * options)
: InstallableValue(state), flakeRef(flakeRef), attrPaths(attrPaths),
prefixes(prefixes), lockFlags(lockFlags)
prefixes(prefixes), lockFlags(lockFlags), options(options)
{ }

std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
Expand Down
6 changes: 5 additions & 1 deletion src/nix/profile.cc
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,11 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
Activity act(*logger, lvlChatty, actUnknown,
fmt("checking '%s' for updates", element.source->attrPath));

InstallableFlake installable(getEvalState(), FlakeRef(element.source->originalRef), {element.source->attrPath}, {}, lockFlags);
InstallableFlake installable(
getEvalState(),
FlakeRef(element.source->originalRef),
{element.source->attrPath},
{}, lockFlags, nullptr);

auto [attrPath, resolvedRef, drv] = installable.toDerivation();

Expand Down

0 comments on commit dc4a280

Please sign in to comment.