2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014, 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
42 #include "cmdlinemodulemanager.h"
49 #include "gromacs/legacyheaders/copyrite.h"
50 #include "gromacs/legacyheaders/network.h"
52 #include "gromacs/commandline/cmdlinehelpcontext.h"
53 #include "gromacs/commandline/cmdlinehelpmodule.h"
54 #include "gromacs/commandline/cmdlineinit.h"
55 #include "gromacs/commandline/cmdlinemodule.h"
56 #include "gromacs/commandline/cmdlinemodulemanager-impl.h"
57 #include "gromacs/commandline/cmdlineparser.h"
58 #include "gromacs/commandline/cmdlineprogramcontext.h"
59 #include "gromacs/options/basicoptions.h"
60 #include "gromacs/options/options.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/stringutil.h"
65 // For GMX_BINARY_SUFFIX
74 //! \addtogroup module_commandline
77 /********************************************************************
78 * CMainCommandLineModule
82 * Implements a CommandLineModuleInterface, given a function with C/C++ main()
85 class CMainCommandLineModule : public CommandLineModuleInterface
88 //! \copydoc gmx::CommandLineModuleManager::CMainFunction
89 typedef CommandLineModuleManager::CMainFunction CMainFunction;
92 * Creates a wrapper module for the given main function.
94 * \param[in] name Name for the module.
95 * \param[in] shortDescription One-line description for the module.
96 * \param[in] mainFunction Main function to wrap.
98 * Does not throw. This is essential for correct implementation of
99 * CommandLineModuleManager::runAsMainCMain().
101 CMainCommandLineModule(const char *name, const char *shortDescription,
102 CMainFunction mainFunction)
103 : name_(name), shortDescription_(shortDescription),
104 mainFunction_(mainFunction)
108 virtual const char *name() const
112 virtual const char *shortDescription() const
114 return shortDescription_;
117 virtual int run(int argc, char *argv[])
119 return mainFunction_(argc, argv);
121 virtual void writeHelp(const CommandLineHelpContext &context) const
125 // TODO: The constness should not be cast away.
126 argv[0] = const_cast<char *>(name_);
128 GlobalCommandLineHelpContext global(context);
129 mainFunction_(argc, argv);
134 const char *shortDescription_;
135 CMainFunction mainFunction_;
139 /********************************************************************
140 * CommonOptionsHolder
144 * Encapsulates some handling of common options to the wrapper binary.
146 class CommonOptionsHolder
149 CommonOptionsHolder()
150 : options_(NULL, NULL), bHelp_(false), bHidden_(false),
151 bQuiet_(false), bVersion_(false), bCopyright_(true)
153 binaryInfoSettings_.copyright(true);
156 //! Initializes the common options.
159 options_.addOption(BooleanOption("h").store(&bHelp_)
160 .description("Print help and quit"));
161 options_.addOption(BooleanOption("hidden").store(&bHidden_)
163 .description("Show hidden options in help"));
164 options_.addOption(BooleanOption("quiet").store(&bQuiet_)
165 .description("Do not print common startup info or quotes"));
166 options_.addOption(BooleanOption("version").store(&bVersion_)
167 .description("Print extended version information and quit"));
168 options_.addOption(BooleanOption("copyright").store(&bCopyright_)
169 .description("Print copyright information on startup"));
173 * Finishes option parsing.
175 * \returns `false` if the wrapper binary should quit without executing
181 binaryInfoSettings_.extendedInfo(bVersion_);
182 // The latter condition suppresses the copyright with
184 binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
188 //! Returns the internal Options object.
189 Options *options() { return &options_; }
190 //! Returns the settings for printing startup information.
191 const BinaryInformationSettings &binaryInfoSettings() const
193 return binaryInfoSettings_;
197 * Returns `true` if common options are set such that the wrapper
198 * binary should quit, without running the actual module.
200 bool shouldIgnoreActualModule() const
202 return bHelp_ || bVersion_;
204 //! Returns whether common options specify showing help.
205 bool shouldShowHelp() const { return bHelp_; }
206 //! Returns whether common options specify showing hidden options in help.
207 bool shouldShowHidden() const { return bHidden_; }
208 //! Returns whether common options specify quiet execution.
209 bool shouldBeQuiet() const
211 return bQuiet_ && !bVersion_;
214 //! Returns the file to which startup information should be printed.
215 FILE *startupInfoFile() const { return (bVersion_ ? stdout : stderr); }
219 //! Settings for what to write in the startup header.
220 BinaryInformationSettings binaryInfoSettings_;
232 /********************************************************************
233 * CommandLineModuleManager::Impl
237 * Private implementation class for CommandLineModuleManager.
239 * \ingroup module_commandline
241 class CommandLineModuleManager::Impl
245 * Initializes the implementation class.
247 * \param[in] binaryName Name of the running binary
248 * (without Gromacs binary suffix or .exe on Windows).
249 * \param programContext Program information for the running binary.
251 Impl(const char *binaryName, CommandLineProgramContext *programContext);
254 * Helper method that adds a given module to the module manager.
256 * \throws std::bad_alloc if out of memory.
258 void addModule(CommandLineModulePointer module);
260 * Creates the help module if it does not yet exist.
262 * \throws std::bad_alloc if out of memory.
264 * This method should be called before accessing \a helpModule_.
266 void ensureHelpModuleExists();
269 * Finds a module that matches a name.
271 * \param[in] name Module name to find.
272 * \returns Iterator to the found module, or
273 * \c modules_.end() if not found.
277 CommandLineModuleMap::const_iterator
278 findModuleByName(const std::string &name) const;
280 * Finds a module that the name of the binary.
282 * \param[in] invokedName Name by which the program was invoked.
283 * \throws std::bad_alloc if out of memory.
284 * \returns Iterator to the found module, or
285 * \c modules_.end() if not found.
287 * Checks whether the program is invoked through a symlink whose name
288 * is different from \a binaryName_, and if so, checks
289 * if a module name matches the name of the symlink.
291 * Note that the \p invokedName parameter is currently not necessary
292 * (as the program context object is also available and provides this
293 * value), but it clarifies the control flow.
295 CommandLineModuleMap::const_iterator
296 findModuleFromBinaryName(const char *invokedName) const;
299 * Processes command-line options for the wrapper binary.
301 * \param[in,out] optionsHolder Common options.
302 * \param[in,out] argc On input, argc passed to run().
303 * On output, argc to be passed to the module.
304 * \param[in,out] argv On input, argv passed to run().
305 * On output, argv to be passed to the module.
306 * \throws InvalidInputError if there are invalid options.
307 * \returns The module that should be run.
309 * Handles command-line options that affect the wrapper binary
310 * (potentially changing the members of \c this in response to the
311 * options). Also finds the module that should be run and the
312 * arguments that should be passed to it.
314 CommandLineModuleInterface *
315 processCommonOptions(CommonOptionsHolder *optionsHolder,
316 int *argc, char ***argv);
319 * Maps module names to module objects.
321 * Owns the contained modules.
323 CommandLineModuleMap modules_;
325 * List of groupings for modules for help output.
327 * Owns the contained module group data objects.
328 * CommandLineModuleGroup objects point to the data objects contained
331 CommandLineModuleGroupList moduleGroups_;
332 //! Information about the currently running program.
333 CommandLineProgramContext &programContext_;
334 //! Name of the binary.
335 std::string binaryName_;
337 * Module that implements help for the binary.
339 * The pointed module is owned by the \a modules_ container.
341 CommandLineHelpModule *helpModule_;
342 //! If non-NULL, run this module in single-module mode.
343 CommandLineModuleInterface *singleModule_;
344 //! Stores the value set with setQuiet().
348 GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
351 CommandLineModuleManager::Impl::Impl(const char *binaryName,
352 CommandLineProgramContext *programContext)
353 : programContext_(*programContext),
354 binaryName_(binaryName != NULL ? binaryName : ""),
355 helpModule_(NULL), singleModule_(NULL),
360 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
362 GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
363 "Attempted to register a duplicate module name");
364 ensureHelpModuleExists();
365 HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
366 modules_.insert(std::make_pair(std::string(module->name()),
368 helpModule_->addTopic(move(helpTopic));
371 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
373 if (helpModule_ == NULL)
375 helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
376 modules_, moduleGroups_);
377 addModule(CommandLineModulePointer(helpModule_));
381 CommandLineModuleMap::const_iterator
382 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
384 // TODO: Accept unambiguous prefixes?
385 return modules_.find(name);
388 CommandLineModuleMap::const_iterator
389 CommandLineModuleManager::Impl::findModuleFromBinaryName(
390 const char *invokedName) const
392 std::string moduleName = invokedName;
393 #ifdef GMX_BINARY_SUFFIX
394 moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
396 if (moduleName == binaryName_)
398 return modules_.end();
400 if (startsWith(moduleName, "g_"))
402 moduleName.erase(0, 2);
404 if (startsWith(moduleName, "gmx"))
406 moduleName.erase(0, 3);
408 return findModuleByName(moduleName);
411 CommandLineModuleInterface *
412 CommandLineModuleManager::Impl::processCommonOptions(
413 CommonOptionsHolder *optionsHolder, int *argc, char ***argv)
415 // Check if we are directly invoking a certain module.
416 CommandLineModuleInterface *module = singleModule_;
419 // Also check for invokation through named symlinks.
420 CommandLineModuleMap::const_iterator moduleIter
421 = findModuleFromBinaryName(programContext_.programName());
422 if (moduleIter != modules_.end())
424 module = moduleIter->second.get();
428 // TODO: It would be nice to propagate at least the -quiet option to
429 // the modules so that they can also be quiet in response to this.
433 // If not in single-module mode, process options to the wrapper binary.
434 // TODO: Ideally, this could be done by CommandLineParser.
435 int argcForWrapper = 1;
436 while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
440 if (argcForWrapper > 1)
442 CommandLineParser(optionsHolder->options())
443 .parse(&argcForWrapper, *argv);
445 // If no action requested and there is a module specified, process it.
446 if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
448 const char *moduleName = (*argv)[argcForWrapper];
449 CommandLineModuleMap::const_iterator moduleIter
450 = findModuleByName(moduleName);
451 if (moduleIter == modules_.end())
453 std::string message =
454 formatString("'%s' is not a GROMACS command.", moduleName);
455 GMX_THROW(InvalidInputError(message));
457 module = moduleIter->second.get();
458 *argc -= argcForWrapper;
459 *argv += argcForWrapper;
460 // After this point, argc and argv are the same independent of
461 // which path is taken: (*argv)[0] is the module name.
466 if (singleModule_ == NULL)
468 programContext_.setDisplayName(binaryName_ + " " + module->name());
470 // Recognize the common options also after the module name.
471 // TODO: It could be nicer to only recognize -h/-hidden if module is not
473 CommandLineParser(optionsHolder->options())
474 .skipUnknown(true).parse(argc, *argv);
476 if (!optionsHolder->finishOptions())
480 // If no module specified and no other action, show the help.
481 // Also explicitly specifying -h for the wrapper binary goes here.
482 if (module == NULL || optionsHolder->shouldShowHelp())
484 ensureHelpModuleExists();
487 helpModule_->setModuleOverride(*module);
490 module = helpModule_;
492 if (module == helpModule_)
494 helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
495 helpModule_->setCommonOptions(optionsHolder->options());
500 /********************************************************************
501 * CommandLineModuleManager
504 CommandLineModuleManager::CommandLineModuleManager(
505 const char *binaryName, CommandLineProgramContext *programContext)
506 : impl_(new Impl(binaryName, programContext))
510 CommandLineModuleManager::~CommandLineModuleManager()
514 void CommandLineModuleManager::setQuiet(bool bQuiet)
516 impl_->bQuiet_ = bQuiet;
519 void CommandLineModuleManager::setOutputRedirect(File *output)
521 impl_->ensureHelpModuleExists();
522 impl_->helpModule_->setOutputRedirect(output);
525 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
527 impl_->singleModule_ = module;
530 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
532 impl_->addModule(move(module));
535 void CommandLineModuleManager::addModuleCMain(
536 const char *name, const char *shortDescription,
537 CMainFunction mainFunction)
539 CommandLineModulePointer module(
540 new CMainCommandLineModule(name, shortDescription, mainFunction));
541 addModule(move(module));
544 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
547 const char *const binaryName = impl_->binaryName_.c_str();
548 CommandLineModuleGroupDataPointer group(
549 new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
550 impl_->moduleGroups_.push_back(move(group));
551 return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
554 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
556 impl_->ensureHelpModuleExists();
557 impl_->helpModule_->addTopic(move(topic));
560 int CommandLineModuleManager::run(int argc, char *argv[])
562 CommandLineModuleInterface *module;
563 const bool bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
564 bool bQuiet = impl_->bQuiet_ || !bMaster;
565 CommonOptionsHolder optionsHolder;
568 optionsHolder.initOptions();
569 module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
571 catch (const std::exception &)
573 bQuiet |= optionsHolder.shouldBeQuiet();
576 printBinaryInformation(stderr, impl_->programContext_,
577 optionsHolder.binaryInfoSettings());
581 bQuiet |= optionsHolder.shouldBeQuiet();
584 FILE *out = optionsHolder.startupInfoFile();
585 printBinaryInformation(out, impl_->programContext_,
586 optionsHolder.binaryInfoSettings());
593 int rc = module->run(argc, argv);
602 int CommandLineModuleManager::runAsMainSingleModule(
603 int argc, char *argv[], CommandLineModuleInterface *module)
605 CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
608 CommandLineModuleManager manager(NULL, &programContext);
609 manager.setSingleModule(module);
610 int rc = manager.run(argc, argv);
611 gmx::finalizeForCommandLine();
614 catch (const std::exception &ex)
616 printFatalErrorMessage(stderr, ex);
617 return processExceptionAtExit(ex);
622 int CommandLineModuleManager::runAsMainCMain(
623 int argc, char *argv[], CMainFunction mainFunction)
625 CMainCommandLineModule module(argv[0], NULL, mainFunction);
626 return runAsMainSingleModule(argc, argv, &module);
629 /********************************************************************
630 * CommandLineModuleGroupData
633 void CommandLineModuleGroupData::addModule(const char *name,
634 const char *description)
636 CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
637 GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
638 "Non-existent module added to a group");
639 if (description == NULL)
641 description = moduleIter->second->shortDescription();
642 GMX_RELEASE_ASSERT(description != NULL,
643 "Module without a description added to a group");
645 std::string tag(formatString("%s-%s", binaryName_, name));
646 modules_.push_back(std::make_pair(tag, description));
649 /********************************************************************
650 * CommandLineModuleGroup
653 void CommandLineModuleGroup::addModule(const char *name)
655 impl_->addModule(name, NULL);
658 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
659 const char *description)
661 impl_->addModule(name, description);