Merge pull request #4486 from shlevy/command-plugins-v2

Command plugins
This commit is contained in:
Eelco Dolstra 2021-02-24 15:28:19 +01:00 committed by GitHub
commit 199081ad00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 74 additions and 25 deletions

View file

@ -0,0 +1,8 @@
# Release 2.4 (202X-XX-XX)
- It is now an error to modify the `plugin-files` setting via a
command-line flag that appears after the first non-flag argument
to any command, including a subcommand to `nix`. For example,
`nix-instantiate default.nix --plugin-files ""` must now become
`nix-instantiate --plugin-files "" default.nix`.
- Plugins that add new `nix` subcommands are now actually respected.

View file

@ -53,6 +53,9 @@ static int main_build_remote(int argc, char * * argv)
unsetenv("DISPLAY"); unsetenv("DISPLAY");
unsetenv("SSH_ASKPASS"); unsetenv("SSH_ASKPASS");
/* If we ever use the common args framework, make sure to
remove initPlugins below and initialize settings first.
*/
if (argc != 2) if (argc != 2)
throw UsageError("called without required arguments"); throw UsageError("called without required arguments");

View file

@ -79,4 +79,11 @@ MixCommonArgs::MixCommonArgs(const string & programName)
hiddenCategories.insert(cat); hiddenCategories.insert(cat);
} }
void MixCommonArgs::initialFlagsProcessed()
{
initPlugins();
pluginsInited();
}
} }

View file

@ -7,10 +7,14 @@ namespace nix {
//static constexpr auto commonArgsCategory = "Miscellaneous common options"; //static constexpr auto commonArgsCategory = "Miscellaneous common options";
static constexpr auto loggingCategory = "Logging-related options"; static constexpr auto loggingCategory = "Logging-related options";
struct MixCommonArgs : virtual Args class MixCommonArgs : public virtual Args
{ {
void initialFlagsProcessed() override;
public:
string programName; string programName;
MixCommonArgs(const string & programName); MixCommonArgs(const string & programName);
protected:
virtual void pluginsInited() {}
}; };
struct MixDryRun : virtual Args struct MixDryRun : virtual Args

View file

@ -243,8 +243,17 @@ void MaxBuildJobsSetting::set(const std::string & str, bool append)
} }
void PluginFilesSetting::set(const std::string & str, bool append)
{
if (pluginsLoaded)
throw UsageError("plugin-files set after plugins were loaded, you may need to move the flag before the subcommand");
BaseSetting<Paths>::set(str, append);
}
void initPlugins() void initPlugins()
{ {
assert(!settings.pluginFiles.pluginsLoaded);
for (const auto & pluginFile : settings.pluginFiles.get()) { for (const auto & pluginFile : settings.pluginFiles.get()) {
Paths pluginFiles; Paths pluginFiles;
try { try {
@ -270,6 +279,9 @@ void initPlugins()
unknown settings. */ unknown settings. */
globalConfig.reapplyUnknownSettings(); globalConfig.reapplyUnknownSettings();
globalConfig.warnUnknownSettings(); globalConfig.warnUnknownSettings();
/* Tell the user if they try to set plugin-files after we've already loaded */
settings.pluginFiles.pluginsLoaded = true;
} }
} }

View file

@ -28,6 +28,23 @@ struct MaxBuildJobsSetting : public BaseSetting<unsigned int>
void set(const std::string & str, bool append = false) override; void set(const std::string & str, bool append = false) override;
}; };
struct PluginFilesSetting : public BaseSetting<Paths>
{
bool pluginsLoaded = false;
PluginFilesSetting(Config * options,
const Paths & def,
const std::string & name,
const std::string & description,
const std::set<std::string> & aliases = {})
: BaseSetting<Paths>(def, name, description, aliases)
{
options->addSetting(this);
}
void set(const std::string & str, bool append = false) override;
};
class Settings : public Config { class Settings : public Config {
unsigned int getDefaultCores(); unsigned int getDefaultCores();
@ -819,7 +836,7 @@ public:
Setting<uint64_t> minFreeCheckInterval{this, 5, "min-free-check-interval", Setting<uint64_t> minFreeCheckInterval{this, 5, "min-free-check-interval",
"Number of seconds between checking free disk space."}; "Number of seconds between checking free disk space."};
Setting<Paths> pluginFiles{ PluginFilesSetting pluginFiles{
this, {}, "plugin-files", this, {}, "plugin-files",
R"( R"(
A list of plugin files to be loaded by Nix. Each of these files will A list of plugin files to be loaded by Nix. Each of these files will

View file

@ -60,6 +60,7 @@ void Args::parseCmdline(const Strings & _cmdline)
verbosity = lvlError; verbosity = lvlError;
} }
bool argsSeen = false;
for (auto pos = cmdline.begin(); pos != cmdline.end(); ) { for (auto pos = cmdline.begin(); pos != cmdline.end(); ) {
auto arg = *pos; auto arg = *pos;
@ -88,6 +89,10 @@ void Args::parseCmdline(const Strings & _cmdline)
throw UsageError("unrecognised flag '%1%'", arg); throw UsageError("unrecognised flag '%1%'", arg);
} }
else { else {
if (!argsSeen) {
argsSeen = true;
initialFlagsProcessed();
}
pos = rewriteArgs(cmdline, pos); pos = rewriteArgs(cmdline, pos);
pendingArgs.push_back(*pos++); pendingArgs.push_back(*pos++);
if (processArgs(pendingArgs, false)) if (processArgs(pendingArgs, false))
@ -96,6 +101,9 @@ void Args::parseCmdline(const Strings & _cmdline)
} }
processArgs(pendingArgs, true); processArgs(pendingArgs, true);
if (!argsSeen)
initialFlagsProcessed();
} }
bool Args::processFlag(Strings::iterator & pos, Strings::iterator end) bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
@ -298,8 +306,8 @@ Strings argvToStrings(int argc, char * * argv)
return args; return args;
} }
MultiCommand::MultiCommand(const Commands & commands) MultiCommand::MultiCommand(const Commands & commands_)
: commands(commands) : commands(commands_)
{ {
expectArgs({ expectArgs({
.label = "subcommand", .label = "subcommand",

View file

@ -132,6 +132,10 @@ protected:
std::set<std::string> hiddenCategories; std::set<std::string> hiddenCategories;
/* Called after all command line flags before the first non-flag
argument (if any) have been processed. */
virtual void initialFlagsProcessed() {}
public: public:
void addFlag(Flag && flag); void addFlag(Flag && flag);

View file

@ -240,8 +240,6 @@ static void main_nix_build(int argc, char * * argv)
myArgs.parseCmdline(args); myArgs.parseCmdline(args);
initPlugins();
if (packages && fromArgs) if (packages && fromArgs)
throw UsageError("'-p' and '-E' are mutually exclusive"); throw UsageError("'-p' and '-E' are mutually exclusive");

View file

@ -196,8 +196,6 @@ static int main_nix_channel(int argc, char ** argv)
return true; return true;
}); });
initPlugins();
switch (cmd) { switch (cmd) {
case cNone: case cNone:
throw UsageError("no command specified"); throw UsageError("no command specified");

View file

@ -74,8 +74,6 @@ static int main_nix_collect_garbage(int argc, char * * argv)
return true; return true;
}); });
initPlugins();
auto profilesDir = settings.nixStateDir + "/profiles"; auto profilesDir = settings.nixStateDir + "/profiles";
if (removeOld) removeOldGenerations(profilesDir); if (removeOld) removeOldGenerations(profilesDir);

View file

@ -43,8 +43,6 @@ static int main_nix_copy_closure(int argc, char ** argv)
return true; return true;
}); });
initPlugins();
if (sshHost.empty()) if (sshHost.empty())
throw UsageError("no host name specified"); throw UsageError("no host name specified");

View file

@ -1420,8 +1420,6 @@ static int main_nix_env(int argc, char * * argv)
myArgs.parseCmdline(argvToStrings(argc, argv)); myArgs.parseCmdline(argvToStrings(argc, argv));
initPlugins();
if (!op) throw UsageError("no operation specified"); if (!op) throw UsageError("no operation specified");
auto store = openStore(); auto store = openStore();

View file

@ -149,8 +149,6 @@ static int main_nix_instantiate(int argc, char * * argv)
myArgs.parseCmdline(argvToStrings(argc, argv)); myArgs.parseCmdline(argvToStrings(argc, argv));
initPlugins();
if (evalOnly && !wantsReadWrite) if (evalOnly && !wantsReadWrite)
settings.readOnlyMode = true; settings.readOnlyMode = true;

View file

@ -1067,8 +1067,6 @@ static int main_nix_store(int argc, char * * argv)
return true; return true;
}); });
initPlugins();
if (!op) throw UsageError("no operation specified"); if (!op) throw UsageError("no operation specified");
if (op != opDump && op != opRestore) /* !!! hack */ if (op != opDump && op != opRestore) /* !!! hack */

View file

@ -326,8 +326,6 @@ static int main_nix_daemon(int argc, char * * argv)
return true; return true;
}); });
initPlugins();
runDaemon(stdio); runDaemon(stdio);
return 0; return 0;

View file

@ -159,6 +159,12 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
#include "nix.md" #include "nix.md"
; ;
} }
// Plugins may add new subcommands.
void pluginsInited() override
{
commands = RegisterCommand::getCommandsFor({});
}
}; };
static void showHelp(std::vector<std::string> subcommand) static void showHelp(std::vector<std::string> subcommand)
@ -283,8 +289,6 @@ void mainWrapped(int argc, char * * argv)
if (completions) return; if (completions) return;
initPlugins();
if (args.showVersion) { if (args.showVersion) {
printVersion(programName); printVersion(programName);
return; return;

View file

@ -171,8 +171,6 @@ static int main_nix_prefetch_url(int argc, char * * argv)
myArgs.parseCmdline(argvToStrings(argc, argv)); myArgs.parseCmdline(argvToStrings(argc, argv));
initPlugins();
if (args.size() > 2) if (args.size() > 2)
throw UsageError("too many arguments"); throw UsageError("too many arguments");

View file

@ -2,6 +2,6 @@ source common.sh
set -o pipefail set -o pipefail
res=$(nix eval --expr builtins.anotherNull --option setting-set true --option plugin-files $PWD/plugins/libplugintest*) res=$(nix --option setting-set true --option plugin-files $PWD/plugins/libplugintest* eval --expr builtins.anotherNull)
[ "$res"x = "nullx" ] [ "$res"x = "nullx" ]