2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements gmx::CommandLineModuleManager.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_commandline
44 #include "cmdlinemodulemanager.h"
51 #include "gromacs/commandline/cmdlinehelpcontext.h"
52 #include "gromacs/commandline/cmdlineinit.h"
53 #include "gromacs/commandline/cmdlinemodule.h"
54 #include "gromacs/commandline/cmdlineparser.h"
55 #include "gromacs/commandline/cmdlineprogramcontext.h"
56 #include "gromacs/legacyheaders/copyrite.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/exceptions.h"
62 #include "gromacs/utility/fatalerror.h"
63 #include "gromacs/utility/futil.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/stringutil.h"
66 #include "gromacs/utility/sysinfo.h"
68 #include "cmdlinehelpmodule.h"
69 #include "cmdlinemodulemanager-impl.h"
77 //! \addtogroup module_commandline
80 /********************************************************************
81 * CMainCommandLineModule
85 * Implements a ICommandLineModule, given a function with C/C++ main()
88 class CMainCommandLineModule : public ICommandLineModule
91 //! \copydoc gmx::CommandLineModuleManager::CMainFunction
92 typedef CommandLineModuleManager::CMainFunction CMainFunction;
95 * Creates a wrapper module for the given main function.
97 * \param[in] name Name for the module.
98 * \param[in] shortDescription One-line description for the module.
99 * \param[in] mainFunction Main function to wrap.
101 * Does not throw. This is essential for correct implementation of
102 * CommandLineModuleManager::runAsMainCMain().
104 CMainCommandLineModule(const char *name, const char *shortDescription,
105 CMainFunction mainFunction)
106 : name_(name), shortDescription_(shortDescription),
107 mainFunction_(mainFunction)
111 virtual const char *name() const
115 virtual const char *shortDescription() const
117 return shortDescription_;
120 virtual void init(CommandLineModuleSettings * /*settings*/)
123 virtual int run(int argc, char *argv[])
125 return mainFunction_(argc, argv);
127 virtual void writeHelp(const CommandLineHelpContext &context) const
129 writeCommandLineHelpCMain(context, name_, mainFunction_);
134 const char *shortDescription_;
135 CMainFunction mainFunction_;
142 /********************************************************************
143 * CommandLineCommonOptionsHolder
146 CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder()
147 : options_(NULL, NULL), bHelp_(false), bHidden_(false),
148 bQuiet_(false), bVersion_(false), bCopyright_(true),
149 niceLevel_(19), bBackup_(true), bFpexcept_(false), debugLevel_(0)
151 binaryInfoSettings_.copyright(true);
154 CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder()
158 void CommandLineCommonOptionsHolder::initOptions()
160 options_.addOption(BooleanOption("h").store(&bHelp_)
161 .description("Print help and quit"));
162 options_.addOption(BooleanOption("hidden").store(&bHidden_)
164 .description("Show hidden options in help"));
165 options_.addOption(BooleanOption("quiet").store(&bQuiet_)
166 .description("Do not print common startup info or quotes"));
167 options_.addOption(BooleanOption("version").store(&bVersion_)
168 .description("Print extended version information and quit"));
169 options_.addOption(BooleanOption("copyright").store(&bCopyright_)
170 .description("Print copyright information on startup"));
171 options_.addOption(IntegerOption("nice").store(&niceLevel_)
172 .description("Set the nicelevel (default depends on command)"));
173 options_.addOption(BooleanOption("backup").store(&bBackup_)
174 .description("Write backups if output files exist"));
175 options_.addOption(BooleanOption("fpexcept").store(&bFpexcept_)
176 .hidden().description("Enable floating-point exceptions"));
177 options_.addOption(IntegerOption("debug").store(&debugLevel_)
178 .hidden().defaultValueIfSet(1)
179 .description("Write file with debug information, "
180 "1: short (default), 2: also x and f"));
183 bool CommandLineCommonOptionsHolder::finishOptions()
186 binaryInfoSettings_.extendedInfo(bVersion_);
187 // The latter condition suppresses the copyright with
189 binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
193 void CommandLineCommonOptionsHolder::adjustFromSettings(
194 const CommandLineModuleSettings &settings)
196 if (!options_.isSet("nice"))
198 niceLevel_ = settings.defaultNiceLevel();
202 /********************************************************************
203 * CommandLineModuleManager::Impl
207 * Private implementation class for CommandLineModuleManager.
209 * \ingroup module_commandline
211 class CommandLineModuleManager::Impl
215 * Initializes the implementation class.
217 * \param[in] binaryName Name of the running binary
218 * (without Gromacs binary suffix or .exe on Windows).
219 * \param programContext Program information for the running binary.
221 Impl(const char *binaryName, CommandLineProgramContext *programContext);
224 * Helper method that adds a given module to the module manager.
226 * \throws std::bad_alloc if out of memory.
228 void addModule(CommandLineModulePointer module);
230 * Creates the help module if it does not yet exist.
232 * \throws std::bad_alloc if out of memory.
234 * This method should be called before accessing \a helpModule_.
236 void ensureHelpModuleExists();
239 * Finds a module that matches a name.
241 * \param[in] name Module name to find.
242 * \returns Iterator to the found module, or
243 * \c modules_.end() if not found.
247 CommandLineModuleMap::const_iterator
248 findModuleByName(const std::string &name) const;
251 * Processes command-line options for the wrapper binary.
253 * \param[in,out] optionsHolder Common options.
254 * \param[in,out] argc On input, argc passed to run().
255 * On output, argc to be passed to the module.
256 * \param[in,out] argv On input, argv passed to run().
257 * On output, argv to be passed to the module.
258 * \throws InvalidInputError if there are invalid options.
259 * \returns The module that should be run.
261 * Handles command-line options that affect the wrapper binary
262 * (potentially changing the members of \c this in response to the
263 * options). Also finds the module that should be run and the
264 * arguments that should be passed to it.
267 processCommonOptions(CommandLineCommonOptionsHolder *optionsHolder,
268 int *argc, char ***argv);
271 * Maps module names to module objects.
273 * Owns the contained modules.
275 CommandLineModuleMap modules_;
277 * List of groupings for modules for help output.
279 * Owns the contained module group data objects.
280 * CommandLineModuleGroup objects point to the data objects contained
283 CommandLineModuleGroupList moduleGroups_;
284 //! Information about the currently running program.
285 CommandLineProgramContext &programContext_;
286 //! Name of the binary.
287 std::string binaryName_;
289 * Module that implements help for the binary.
291 * The pointed module is owned by the \a modules_ container.
293 CommandLineHelpModule *helpModule_;
294 //! If non-NULL, run this module in single-module mode.
295 ICommandLineModule *singleModule_;
296 //! Stores the value set with setQuiet().
300 GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
303 CommandLineModuleManager::Impl::Impl(const char *binaryName,
304 CommandLineProgramContext *programContext)
305 : programContext_(*programContext),
306 binaryName_(binaryName != NULL ? binaryName : ""),
307 helpModule_(NULL), singleModule_(NULL),
310 GMX_RELEASE_ASSERT(binaryName_.find('-') == std::string::npos,
311 "Help export does not currently work with binary names with dashes");
314 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
316 GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
317 "Attempted to register a duplicate module name");
318 ensureHelpModuleExists();
319 HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
320 modules_.insert(std::make_pair(std::string(module->name()),
322 helpModule_->addTopic(move(helpTopic), false);
325 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
327 if (helpModule_ == NULL)
329 helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
330 modules_, moduleGroups_);
331 addModule(CommandLineModulePointer(helpModule_));
335 CommandLineModuleMap::const_iterator
336 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
338 // TODO: Accept unambiguous prefixes?
339 return modules_.find(name);
343 CommandLineModuleManager::Impl::processCommonOptions(
344 CommandLineCommonOptionsHolder *optionsHolder, int *argc, char ***argv)
346 // Check if we are directly invoking a certain module.
347 ICommandLineModule *module = singleModule_;
349 // TODO: It would be nice to propagate at least the -quiet option to
350 // the modules so that they can also be quiet in response to this.
354 // If not in single-module mode, process options to the wrapper binary.
355 // TODO: Ideally, this could be done by CommandLineParser.
356 int argcForWrapper = 1;
357 while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
361 if (argcForWrapper > 1)
363 CommandLineParser(optionsHolder->options())
364 .parse(&argcForWrapper, *argv);
366 // If no action requested and there is a module specified, process it.
367 if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
369 const char *moduleName = (*argv)[argcForWrapper];
370 CommandLineModuleMap::const_iterator moduleIter
371 = findModuleByName(moduleName);
372 if (moduleIter == modules_.end())
374 std::string message =
375 formatString("'%s' is not a GROMACS command.", moduleName);
376 GMX_THROW(InvalidInputError(message));
378 module = moduleIter->second.get();
379 *argc -= argcForWrapper;
380 *argv += argcForWrapper;
381 // After this point, argc and argv are the same independent of
382 // which path is taken: (*argv)[0] is the module name.
387 if (singleModule_ == NULL)
389 programContext_.setDisplayName(binaryName_ + " " + module->name());
391 // Recognize the common options also after the module name.
392 // TODO: It could be nicer to only recognize -h/-hidden if module is not
394 CommandLineParser(optionsHolder->options())
395 .skipUnknown(true).parse(argc, *argv);
397 if (!optionsHolder->finishOptions())
401 // If no module specified and no other action, show the help.
402 // Also explicitly specifying -h for the wrapper binary goes here.
403 if (module == NULL || optionsHolder->shouldShowHelp())
405 ensureHelpModuleExists();
408 helpModule_->setModuleOverride(*module);
411 module = helpModule_;
413 if (module == helpModule_)
415 helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
420 /********************************************************************
421 * CommandLineModuleManager
424 CommandLineModuleManager::CommandLineModuleManager(
425 const char *binaryName, CommandLineProgramContext *programContext)
426 : impl_(new Impl(binaryName, programContext))
430 CommandLineModuleManager::~CommandLineModuleManager()
434 void CommandLineModuleManager::setQuiet(bool bQuiet)
436 impl_->bQuiet_ = bQuiet;
439 void CommandLineModuleManager::setOutputRedirector(
440 IFileOutputRedirector *output)
442 impl_->ensureHelpModuleExists();
443 impl_->helpModule_->setOutputRedirector(output);
446 void CommandLineModuleManager::setSingleModule(ICommandLineModule *module)
448 impl_->singleModule_ = module;
451 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
453 impl_->addModule(move(module));
456 void CommandLineModuleManager::addModuleCMain(
457 const char *name, const char *shortDescription,
458 CMainFunction mainFunction)
460 CommandLineModulePointer module(
461 new CMainCommandLineModule(name, shortDescription, mainFunction));
462 addModule(move(module));
465 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
468 const char *const binaryName = impl_->binaryName_.c_str();
469 CommandLineModuleGroupDataPointer group(
470 new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
471 impl_->moduleGroups_.push_back(move(group));
472 return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
475 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
477 impl_->ensureHelpModuleExists();
478 impl_->helpModule_->addTopic(move(topic), true);
481 int CommandLineModuleManager::run(int argc, char *argv[])
483 ICommandLineModule *module;
484 const bool bMaster = (gmx_node_rank() == 0);
485 bool bQuiet = impl_->bQuiet_ || !bMaster;
486 CommandLineCommonOptionsHolder optionsHolder;
489 optionsHolder.initOptions();
490 module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
492 catch (const std::exception &)
494 bQuiet |= optionsHolder.shouldBeQuiet();
497 printBinaryInformation(stderr, impl_->programContext_,
498 optionsHolder.binaryInfoSettings());
502 bQuiet |= optionsHolder.shouldBeQuiet();
505 FILE *out = optionsHolder.startupInfoFile();
506 printBinaryInformation(out, impl_->programContext_,
507 optionsHolder.binaryInfoSettings());
515 CommandLineModuleSettings settings;
516 module->init(&settings);
517 optionsHolder.adjustFromSettings(settings);
519 gmx_set_max_backup_count(optionsHolder.shouldBackup() ? -1 : 0);
521 // Open the debug file.
522 if (optionsHolder.debugLevel() > 0)
524 std::string filename(impl_->programContext_.programName());
525 if (gmx_node_num() > 1)
527 filename.append(formatString("%d", gmx_node_rank()));
529 filename.append(".debug");
531 fprintf(stderr, "Will write debug log file: %s\n", filename.c_str());
532 gmx_init_debug(optionsHolder.debugLevel(), filename.c_str());
534 // Set the nice level unless disabled in the configuration.
535 if (optionsHolder.niceLevel() != 0)
537 static bool bNiceSet = false; // Only set it once.
540 // TODO: Diagnostic if this fails and the user explicitly requested it.
541 gmx_set_nice(optionsHolder.niceLevel());
545 if (optionsHolder.enableFPExceptions())
547 //TODO: currently it is always enabled for mdrun (verlet) and tests.
548 gmx_feenableexcept();
552 if (!(module == impl_->helpModule_ && !bMaster))
554 rc = module->run(argc, argv);
564 int CommandLineModuleManager::runAsMainSingleModule(
565 int argc, char *argv[], ICommandLineModule *module)
567 CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
570 CommandLineModuleManager manager(NULL, &programContext);
571 manager.setSingleModule(module);
572 int rc = manager.run(argc, argv);
573 gmx::finalizeForCommandLine();
576 catch (const std::exception &ex)
578 printFatalErrorMessage(stderr, ex);
579 return processExceptionAtExitForCommandLine(ex);
584 int CommandLineModuleManager::runAsMainCMain(
585 int argc, char *argv[], CMainFunction mainFunction)
587 CMainCommandLineModule module(argv[0], NULL, mainFunction);
588 return runAsMainSingleModule(argc, argv, &module);
591 /********************************************************************
592 * CommandLineModuleGroupData
595 void CommandLineModuleGroupData::addModule(const char *name,
596 const char *description)
598 CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
599 GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
600 "Non-existent module added to a group");
601 if (description == NULL)
603 description = moduleIter->second->shortDescription();
604 GMX_RELEASE_ASSERT(description != NULL,
605 "Module without a description added to a group");
607 std::string tag(formatString("%s-%s", binaryName_, name));
608 modules_.push_back(std::make_pair(tag, description));
611 /********************************************************************
612 * CommandLineModuleGroup
615 void CommandLineModuleGroup::addModule(const char *name)
617 impl_->addModule(name, NULL);
620 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
621 const char *description)
623 impl_->addModule(name, description);