Update instructions in containers.rst
[gromacs.git] / src / gromacs / commandline / cmdlinemodulemanager.cpp
blob066495cd67405afb42cf59e44c136fb4661a2268
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5 * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
36 /*! \internal \file
37 * \brief
38 * Implements gmx::CommandLineModuleManager.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_commandline
43 #include "gmxpre.h"
45 #include "cmdlinemodulemanager.h"
47 #include <cstdio>
49 #include <string>
50 #include <utility>
52 #include "gromacs/commandline/cmdlinehelpcontext.h"
53 #include "gromacs/commandline/cmdlineinit.h"
54 #include "gromacs/commandline/cmdlinemodule.h"
55 #include "gromacs/commandline/cmdlineparser.h"
56 #include "gromacs/commandline/cmdlineprogramcontext.h"
57 #include "gromacs/math/utilities.h"
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/options.h"
60 #include "gromacs/utility/basenetwork.h"
61 #include "gromacs/utility/coolstuff.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/futil.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/stringutil.h"
67 #include "gromacs/utility/sysinfo.h"
69 #include "cmdlinehelpmodule.h"
70 #include "cmdlinemodulemanager_impl.h"
72 namespace gmx
75 namespace
78 //! \addtogroup module_commandline
79 //! \{
81 /********************************************************************
82 * CMainCommandLineModule
85 /*! \brief
86 * Implements a ICommandLineModule, given a function with C/C++ main()
87 * signature.
89 class CMainCommandLineModule : public ICommandLineModule
91 public:
92 //! \copydoc gmx::CommandLineModuleManager::CMainFunction
93 typedef CommandLineModuleManager::CMainFunction CMainFunction;
94 //! \copydoc gmx::CommandLineModuleManager::InitSettingsFunction
95 typedef CommandLineModuleManager::InitSettingsFunction InitSettingsFunction;
97 /*! \brief
98 * Creates a wrapper module for the given main function.
100 * \param[in] name Name for the module.
101 * \param[in] shortDescription One-line description for the module.
102 * \param[in] mainFunction Main function to wrap.
103 * \param[in] settingsFunction Initializer for settings (can be null).
105 * Does not throw. This is essential for correct implementation of
106 * CommandLineModuleManager::runAsMainCMain().
108 CMainCommandLineModule(const char* name,
109 const char* shortDescription,
110 CMainFunction mainFunction,
111 InitSettingsFunction settingsFunction) :
112 name_(name),
113 shortDescription_(shortDescription),
114 mainFunction_(mainFunction),
115 settingsFunction_(settingsFunction)
119 const char* name() const override { return name_; }
120 const char* shortDescription() const override { return shortDescription_; }
122 void init(CommandLineModuleSettings* settings) override
124 if (settingsFunction_ != nullptr)
126 settingsFunction_(settings);
129 int run(int argc, char* argv[]) override { return mainFunction_(argc, argv); }
130 void writeHelp(const CommandLineHelpContext& context) const override
132 writeCommandLineHelpCMain(context, name_, mainFunction_);
135 private:
136 const char* name_;
137 const char* shortDescription_;
138 CMainFunction mainFunction_;
139 InitSettingsFunction settingsFunction_;
142 //! \}
144 } // namespace
146 /********************************************************************
147 * CommandLineCommonOptionsHolder
150 CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder() :
151 bHelp_(false),
152 bHidden_(false),
153 bQuiet_(false),
154 bVersion_(false),
155 bCopyright_(true),
156 niceLevel_(19),
157 bNiceSet_(false),
158 bBackup_(true),
159 bFpexcept_(false),
160 debugLevel_(0)
162 binaryInfoSettings_.copyright(true);
165 CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder() {}
167 void CommandLineCommonOptionsHolder::initOptions()
169 options_.addOption(BooleanOption("h").store(&bHelp_).description("Print help and quit"));
170 options_.addOption(BooleanOption("hidden").store(&bHidden_).hidden().description(
171 "Show hidden options in help"));
172 options_.addOption(BooleanOption("quiet").store(&bQuiet_).description(
173 "Do not print common startup info or quotes"));
174 options_.addOption(
175 BooleanOption("version").store(&bVersion_).description("Print extended version information and quit"));
176 options_.addOption(
177 BooleanOption("copyright").store(&bCopyright_).description("Print copyright information on startup"));
178 options_.addOption(IntegerOption("nice")
179 .store(&niceLevel_)
180 .storeIsSet(&bNiceSet_)
181 .description("Set the nicelevel (default depends on command)"));
182 options_.addOption(BooleanOption("backup").store(&bBackup_).description(
183 "Write backups if output files exist"));
184 options_.addOption(
185 BooleanOption("fpexcept").store(&bFpexcept_).hidden().description("Enable floating-point exceptions"));
186 options_.addOption(IntegerOption("debug")
187 .store(&debugLevel_)
188 .hidden()
189 .defaultValueIfSet(1)
190 .description("Write file with debug information, "
191 "1: short (default), 2: also x and f"));
194 bool CommandLineCommonOptionsHolder::finishOptions()
196 options_.finish();
197 binaryInfoSettings_.extendedInfo(bVersion_);
198 // The latter condition suppresses the copyright with
199 // -quiet -version.
200 binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
201 return !bVersion_;
204 void CommandLineCommonOptionsHolder::adjustFromSettings(const CommandLineModuleSettings& settings)
206 if (!bNiceSet_)
208 niceLevel_ = settings.defaultNiceLevel();
212 /********************************************************************
213 * CommandLineModuleManager::Impl
216 /*! \internal \brief
217 * Private implementation class for CommandLineModuleManager.
219 * \ingroup module_commandline
221 class CommandLineModuleManager::Impl
223 public:
224 /*! \brief
225 * Initializes the implementation class.
227 * \param[in] binaryName Name of the running binary
228 * (without Gromacs binary suffix or .exe on Windows).
229 * \param programContext Program information for the running binary.
231 Impl(const char* binaryName, CommandLineProgramContext* programContext);
233 /*! \brief
234 * Helper method that adds a given module to the module manager.
236 * \throws std::bad_alloc if out of memory.
238 void addModule(CommandLineModulePointer module);
239 /*! \brief
240 * Creates the help module if it does not yet exist.
242 * \throws std::bad_alloc if out of memory.
244 * This method should be called before accessing \a helpModule_.
246 void ensureHelpModuleExists();
248 /*! \brief
249 * Finds a module that matches a name.
251 * \param[in] name Module name to find.
252 * \returns Iterator to the found module, or
253 * \c modules_.end() if not found.
255 * Does not throw.
257 CommandLineModuleMap::const_iterator findModuleByName(const std::string& name) const;
259 /*! \brief
260 * Processes command-line options for the wrapper binary.
262 * \param[in,out] optionsHolder Common options.
263 * \param[in,out] argc On input, argc passed to run().
264 * On output, argc to be passed to the module.
265 * \param[in,out] argv On input, argv passed to run().
266 * On output, argv to be passed to the module.
267 * \throws InvalidInputError if there are invalid options.
268 * \returns The module that should be run.
270 * Handles command-line options that affect the wrapper binary
271 * (potentially changing the members of \c this in response to the
272 * options). Also finds the module that should be run and the
273 * arguments that should be passed to it.
275 ICommandLineModule* processCommonOptions(CommandLineCommonOptionsHolder* optionsHolder,
276 int* argc,
277 char*** argv);
279 //! Prints the footer at the end of execution.
280 static void printThanks(FILE* fp);
282 /*! \brief
283 * Maps module names to module objects.
285 * Owns the contained modules.
287 CommandLineModuleMap modules_;
288 /*! \brief
289 * List of groupings for modules for help output.
291 * Owns the contained module group data objects.
292 * CommandLineModuleGroup objects point to the data objects contained
293 * here.
295 CommandLineModuleGroupList moduleGroups_;
296 //! Information about the currently running program.
297 CommandLineProgramContext& programContext_;
298 //! Name of the binary.
299 std::string binaryName_;
300 /*! \brief
301 * Module that implements help for the binary.
303 * The pointed module is owned by the \a modules_ container.
305 CommandLineHelpModule* helpModule_;
306 //! If non-NULL, run this module in single-module mode.
307 ICommandLineModule* singleModule_;
308 //! Stores the value set with setQuiet().
309 bool bQuiet_;
311 private:
312 GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
315 CommandLineModuleManager::Impl::Impl(const char* binaryName, CommandLineProgramContext* programContext) :
316 programContext_(*programContext),
317 binaryName_(binaryName != nullptr ? binaryName : ""),
318 helpModule_(nullptr),
319 singleModule_(nullptr),
320 bQuiet_(false)
322 GMX_RELEASE_ASSERT(binaryName_.find('-') == std::string::npos,
323 "Help export does not currently work with binary names with dashes");
326 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
328 GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
329 "Attempted to register a duplicate module name");
330 ensureHelpModuleExists();
331 HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
332 modules_.insert(std::make_pair(std::string(module->name()), std::move(module)));
333 helpModule_->addTopic(std::move(helpTopic), false);
336 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
338 if (helpModule_ == nullptr)
340 helpModule_ = new CommandLineHelpModule(programContext_, binaryName_, modules_, moduleGroups_);
341 addModule(CommandLineModulePointer(helpModule_));
345 CommandLineModuleMap::const_iterator CommandLineModuleManager::Impl::findModuleByName(const std::string& name) const
347 // TODO: Accept unambiguous prefixes?
348 return modules_.find(name);
351 ICommandLineModule* CommandLineModuleManager::Impl::processCommonOptions(CommandLineCommonOptionsHolder* optionsHolder,
352 int* argc,
353 char*** argv)
355 // Check if we are directly invoking a certain module.
356 ICommandLineModule* module = singleModule_;
358 // TODO: It would be nice to propagate at least the -quiet option to
359 // the modules so that they can also be quiet in response to this.
361 if (module == nullptr)
363 // If not in single-module mode, process options to the wrapper binary.
364 // TODO: Ideally, this could be done by CommandLineParser.
366 // Find the module name (if any) in the arg list
367 int indexOfModuleName = 1;
368 while (indexOfModuleName < *argc && (*argv)[indexOfModuleName][0] == '-')
370 ++indexOfModuleName;
372 if (indexOfModuleName > 1)
374 // Process options that are provided to the wrapper
375 // binary. These precede the module name, if one exists.
376 int argcForWrapper = indexOfModuleName;
377 CommandLineParser(optionsHolder->options()).parse(&argcForWrapper, *argv);
379 // If no action requested and there is a module specified, process it.
380 if (indexOfModuleName < *argc && !optionsHolder->shouldIgnoreActualModule())
382 const char* moduleName = (*argv)[indexOfModuleName];
383 CommandLineModuleMap::const_iterator moduleIter = findModuleByName(moduleName);
384 if (moduleIter == modules_.end())
386 std::string message = formatString("'%s' is not a GROMACS command.", moduleName);
387 GMX_THROW(InvalidInputError(message));
389 module = moduleIter->second.get();
390 *argc -= indexOfModuleName;
391 *argv += indexOfModuleName;
392 // After this point, argc and argv are the same independent of
393 // which path is taken: (*argv)[0] is the module name.
396 if (module != nullptr)
398 if (singleModule_ == nullptr)
400 programContext_.setDisplayName(binaryName_ + " " + module->name());
402 // Recognize the common options also after the module name.
403 // TODO: It could be nicer to only recognize -h/-hidden if module is not
404 // null.
405 CommandLineParser(optionsHolder->options()).allowPositionalArguments(true).skipUnknown(true).parse(argc, *argv);
407 if (!optionsHolder->finishOptions())
409 return nullptr;
411 // If no module specified and no other action, show the help.
412 // Also explicitly specifying -h for the wrapper binary goes here.
413 if (module == nullptr || optionsHolder->shouldShowHelp())
415 ensureHelpModuleExists();
416 if (module != nullptr)
418 helpModule_->setModuleOverride(*module);
420 *argc = 1;
421 module = helpModule_;
423 if (module == helpModule_)
425 helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
427 return module;
430 void CommandLineModuleManager::Impl::printThanks(FILE* fp)
432 fprintf(fp, "\n%s\n\n", getCoolQuote().c_str());
435 /********************************************************************
436 * CommandLineModuleManager
439 CommandLineModuleManager::CommandLineModuleManager(const char* binaryName,
440 CommandLineProgramContext* programContext) :
441 impl_(new Impl(binaryName, programContext))
445 CommandLineModuleManager::~CommandLineModuleManager() {}
447 void CommandLineModuleManager::setQuiet(bool bQuiet)
449 impl_->bQuiet_ = bQuiet;
452 void CommandLineModuleManager::setOutputRedirector(IFileOutputRedirector* output)
454 impl_->ensureHelpModuleExists();
455 impl_->helpModule_->setOutputRedirector(output);
458 void CommandLineModuleManager::setSingleModule(ICommandLineModule* module)
460 impl_->singleModule_ = module;
463 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
465 impl_->addModule(std::move(module));
468 void CommandLineModuleManager::addModuleCMain(const char* name, const char* shortDescription, CMainFunction mainFunction)
470 CommandLineModulePointer module(
471 new CMainCommandLineModule(name, shortDescription, mainFunction, nullptr));
472 addModule(std::move(module));
475 void CommandLineModuleManager::addModuleCMainWithSettings(const char* name,
476 const char* shortDescription,
477 CMainFunction mainFunction,
478 InitSettingsFunction settingsFunction)
480 CommandLineModulePointer module(
481 new CMainCommandLineModule(name, shortDescription, mainFunction, settingsFunction));
482 addModule(std::move(module));
485 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(const char* title)
487 const char* const binaryName = impl_->binaryName_.c_str();
488 CommandLineModuleGroupDataPointer group(
489 new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
490 impl_->moduleGroups_.push_back(std::move(group));
491 return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
494 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
496 impl_->ensureHelpModuleExists();
497 impl_->helpModule_->addTopic(std::move(topic), true);
500 int CommandLineModuleManager::run(int argc, char* argv[])
502 ICommandLineModule* module;
503 const bool bMaster = (gmx_node_rank() == 0);
504 bool bQuiet = impl_->bQuiet_ || !bMaster;
505 CommandLineCommonOptionsHolder optionsHolder;
508 optionsHolder.initOptions();
509 module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
511 catch (const std::exception&)
513 bQuiet |= optionsHolder.shouldBeQuiet();
514 if (!bQuiet)
516 printBinaryInformation(stderr, impl_->programContext_, optionsHolder.binaryInfoSettings());
518 throw;
520 bQuiet |= optionsHolder.shouldBeQuiet();
521 if (!bQuiet)
523 FILE* out = optionsHolder.startupInfoFile();
524 printBinaryInformation(out, impl_->programContext_, optionsHolder.binaryInfoSettings());
525 fprintf(out, "\n");
527 if (module == nullptr)
529 return 0;
532 CommandLineModuleSettings settings;
533 module->init(&settings);
534 optionsHolder.adjustFromSettings(settings);
536 gmx_set_max_backup_count(optionsHolder.shouldBackup() ? -1 : 0);
538 // Open the debug file.
539 if (optionsHolder.debugLevel() > 0)
541 std::string filename(impl_->programContext_.programName());
542 if (gmx_node_num() > 1)
544 filename.append(formatString("%d", gmx_node_rank()));
546 filename.append(".debug");
548 fprintf(stderr, "Will write debug log file: %s\n", filename.c_str());
549 gmx_init_debug(optionsHolder.debugLevel(), filename.c_str());
551 // Set the nice level unless disabled in the configuration.
552 if (optionsHolder.niceLevel() != 0)
554 static bool bNiceSet = false; // Only set it once.
555 if (!bNiceSet)
557 // TODO: Diagnostic if this fails and the user explicitly requested it.
558 gmx_set_nice(optionsHolder.niceLevel());
559 bNiceSet = true;
562 if (optionsHolder.enableFPExceptions())
564 // TODO: currently it is always enabled for mdrun (verlet) and tests.
565 gmx_feenableexcept();
568 int rc = 0;
569 if (!(module == impl_->helpModule_ && !bMaster))
571 rc = module->run(argc, argv);
573 if (!bQuiet)
575 impl_->printThanks(stderr);
577 return rc;
580 // static
581 int CommandLineModuleManager::runAsMainSingleModule(int argc, char* argv[], ICommandLineModule* module)
583 CommandLineProgramContext& programContext = gmx::initForCommandLine(&argc, &argv);
586 CommandLineModuleManager manager(nullptr, &programContext);
587 manager.setSingleModule(module);
588 int rc = manager.run(argc, argv);
589 gmx::finalizeForCommandLine();
590 return rc;
592 catch (const std::exception& ex)
594 printFatalErrorMessage(stderr, ex);
595 return processExceptionAtExitForCommandLine(ex);
599 // static
600 int CommandLineModuleManager::runAsMainCMain(int argc, char* argv[], CMainFunction mainFunction)
602 CMainCommandLineModule module(argv[0], nullptr, mainFunction, nullptr);
603 return runAsMainSingleModule(argc, argv, &module);
606 // static
607 int CommandLineModuleManager::runAsMainCMainWithSettings(int argc,
608 char* argv[],
609 CMainFunction mainFunction,
610 InitSettingsFunction settingsFunction)
612 CMainCommandLineModule module(argv[0], nullptr, mainFunction, settingsFunction);
613 return runAsMainSingleModule(argc, argv, &module);
616 /********************************************************************
617 * CommandLineModuleGroupData
620 void CommandLineModuleGroupData::addModule(const char* name, const char* description)
622 CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
623 GMX_RELEASE_ASSERT(moduleIter != allModules_.end(), "Non-existent module added to a group");
624 if (description == nullptr)
626 description = moduleIter->second->shortDescription();
627 GMX_RELEASE_ASSERT(description != nullptr, "Module without a description added to a group");
629 std::string tag(formatString("%s-%s", binaryName_, name));
630 modules_.push_back(std::make_pair(tag, description));
633 /********************************************************************
634 * CommandLineModuleGroup
637 void CommandLineModuleGroup::addModule(const char* name)
639 impl_->addModule(name, nullptr);
642 void CommandLineModuleGroup::addModuleWithDescription(const char* name, const char* description)
644 impl_->addModule(name, description);
647 } // namespace gmx