From dc4a280318650762a79447dbb299aef887f61d2e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 24 Sep 2020 22:55:30 +0200 Subject: [PATCH] Allow overring module options from the command line 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. --- src/nix/bundle.cc | 2 +- src/nix/develop.cc | 3 +- src/nix/flake.cc | 2 +- src/nix/installables.cc | 65 +++++++++++++++++++++++++++++++++++++++-- src/nix/installables.hh | 12 ++++++-- src/nix/profile.cc | 6 +++- 6 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index fc41da9e4..a757abf1e 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -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); diff --git a/src/nix/develop.cc b/src/nix/develop.cc index f29fa71d2..f2681e14d 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -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"; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 39260e4f9..4186ee955 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -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); diff --git a/src/nix/installables.cc b/src/nix/installables.cc index 028813390..e2ee9f52f 100644 --- a/src/nix/installables.cc +++ b/src/nix/installables.cc @@ -13,6 +13,8 @@ #include "url.hh" #include "registry.hh" +#include "../cpptoml/cpptoml.h" + #include #include @@ -549,8 +551,61 @@ InstallableFlake::getCursors(EvalState & state) std::shared_ptr InstallableFlake::getLockedFlake() const { - if (!_lockedFlake) + if (!_lockedFlake) { _lockedFlake = std::make_shared(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(lockFlake(*state, + parseFlakeRef("path://" + tempDir), lockFlags)); + } + } + return _lockedFlake; } @@ -600,10 +655,14 @@ std::vector> SourceExprCommand::parseInstallables( try { auto [flakeRef, fragment] = parseFlakeRefWithFragment(s, absPath(".")); + auto state = getEvalState(); result.push_back(std::make_shared( - getEvalState(), std::move(flakeRef), + state, + std::move(flakeRef), fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment}, - getDefaultFlakeAttrPathPrefixes(), lockFlags)); + getDefaultFlakeAttrPathPrefixes(), + lockFlags, + getAutoArgs(*state))); continue; } catch (...) { ex = std::current_exception(); diff --git a/src/nix/installables.hh b/src/nix/installables.hh index c7c2f8981..c5f58e1ab 100644 --- a/src/nix/installables.hh +++ b/src/nix/installables.hh @@ -98,11 +98,17 @@ struct InstallableFlake : InstallableValue Strings prefixes; const flake::LockFlags & lockFlags; mutable std::shared_ptr _lockedFlake; + Bindings * options; - InstallableFlake(ref state, FlakeRef && flakeRef, - Strings && attrPaths, Strings && prefixes, const flake::LockFlags & lockFlags) + InstallableFlake( + ref 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(); } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 7ce4dfe4c..251ee8ca7 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -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();