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/cmdlinemodule.h"
55 #include "gromacs/commandline/cmdlinemodulemanager-impl.h"
56 #include "gromacs/commandline/cmdlineparser.h"
57 #include "gromacs/options/basicoptions.h"
58 #include "gromacs/options/options.h"
59 #include "gromacs/utility/exceptions.h"
60 #include "gromacs/utility/gmxassert.h"
61 #include "gromacs/utility/init.h"
62 #include "gromacs/utility/programinfo.h"
63 #include "gromacs/utility/stringutil.h"
65 // For GMX_BINARY_SUFFIX
74 /********************************************************************
75 * CMainCommandLineModule
79 * Implements a CommandLineModuleInterface, given a function with C/C++ main()
82 * \ingroup module_commandline
84 class CMainCommandLineModule : public CommandLineModuleInterface
87 //! \copydoc gmx::CommandLineModuleManager::CMainFunction
88 typedef CommandLineModuleManager::CMainFunction CMainFunction;
91 * Creates a wrapper module for the given main function.
93 * \param[in] name Name for the module.
94 * \param[in] shortDescription One-line description for the module.
95 * \param[in] mainFunction Main function to wrap.
97 * Does not throw. This is essential for correct implementation of
98 * CommandLineModuleManager::runAsMainCMain().
100 CMainCommandLineModule(const char *name, const char *shortDescription,
101 CMainFunction mainFunction)
102 : name_(name), shortDescription_(shortDescription),
103 mainFunction_(mainFunction)
107 virtual const char *name() const
111 virtual const char *shortDescription() const
113 return shortDescription_;
116 virtual int run(int argc, char *argv[])
118 return mainFunction_(argc, argv);
120 virtual void writeHelp(const CommandLineHelpContext &context) const
124 // TODO: The constness should not be cast away.
125 argv[0] = const_cast<char *>(name_);
127 GlobalCommandLineHelpContext global(context);
128 mainFunction_(argc, argv);
133 const char *shortDescription_;
134 CMainFunction mainFunction_;
140 /********************************************************************
141 * CommandLineModuleManager::Impl
145 * Private implementation class for CommandLineModuleManager.
147 * \ingroup module_commandline
149 class CommandLineModuleManager::Impl
153 * Initializes the implementation class.
155 * \param[in] binaryName Name of the running binary
156 * (without Gromacs binary suffix or .exe on Windows).
157 * \param programInfo Program information for the running binary.
159 Impl(const char *binaryName, ProgramInfo *programInfo);
162 * Helper method that adds a given module to the module manager.
164 * \throws std::bad_alloc if out of memory.
166 void addModule(CommandLineModulePointer module);
168 * Creates the help module if it does not yet exist.
170 * \throws std::bad_alloc if out of memory.
172 * This method should be called before accessing \a helpModule_.
174 void ensureHelpModuleExists();
177 * Finds a module that matches a name.
179 * \param[in] name Module name to find.
180 * \returns Iterator to the found module, or
181 * \c modules_.end() if not found.
185 CommandLineModuleMap::const_iterator
186 findModuleByName(const std::string &name) const;
188 * Finds a module that the name of the binary.
190 * \param[in] programInfo Program information object to use.
191 * \throws std::bad_alloc if out of memory.
192 * \returns Iterator to the found module, or
193 * \c modules_.end() if not found.
195 * Checks whether the program is invoked through a symlink whose name
196 * is different from \a binaryName_, and if so, checks
197 * if a module name matches the name of the symlink.
199 * Note that the \p programInfo parameter is currently not necessary
200 * (as the program info object is also contained as a member), but it
201 * clarifies the control flow.
203 CommandLineModuleMap::const_iterator
204 findModuleFromBinaryName(const ProgramInfo &programInfo) const;
207 * Processes command-line options for the wrapper binary.
209 * \param[in,out] argc On input, argc passed to run().
210 * On output, argc to be passed to the module.
211 * \param[in,out] argv On input, argv passed to run().
212 * On output, argv to be passed to the module.
213 * \throws InvalidInputError if there are invalid options.
214 * \returns The module that should be run.
216 * Handles command-line options that affect the wrapper binary
217 * (potentially changing the members of \c this in response to the
218 * options). Also finds the module that should be run and the
219 * arguments that should be passed to it.
221 CommandLineModuleInterface *
222 processCommonOptions(int *argc, char ***argv);
225 * Maps module names to module objects.
227 * Owns the contained modules.
229 CommandLineModuleMap modules_;
231 * List of groupings for modules for help output.
233 * Owns the contained module group data objects.
234 * CommandLineModuleGroup objects point to the data objects contained
237 CommandLineModuleGroupList moduleGroups_;
238 //! Information about the currently running program.
239 ProgramInfo &programInfo_;
240 //! Name of the binary.
241 std::string binaryName_;
243 * Module that implements help for the binary.
245 * The pointed module is owned by the \a modules_ container.
247 CommandLineHelpModule *helpModule_;
248 //! Settings for what to write in the startup header.
249 BinaryInformationSettings binaryInfoSettings_;
250 //! If non-NULL, run this module in single-module mode.
251 CommandLineModuleInterface *singleModule_;
252 //! Whether all stderr output should be suppressed.
254 //! Whether to write the startup information to stdout iso stderr.
258 GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
261 CommandLineModuleManager::Impl::Impl(const char *binaryName,
262 ProgramInfo *programInfo)
263 : programInfo_(*programInfo),
264 binaryName_(binaryName != NULL ? binaryName : ""),
265 helpModule_(NULL), singleModule_(NULL),
266 bQuiet_(false), bStdOutInfo_(false)
268 binaryInfoSettings_.copyright(true);
271 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
273 GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
274 "Attempted to register a duplicate module name");
275 ensureHelpModuleExists();
276 HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
277 modules_.insert(std::make_pair(std::string(module->name()),
279 helpModule_->addTopic(move(helpTopic));
282 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
284 if (helpModule_ == NULL)
286 helpModule_ = new CommandLineHelpModule(programInfo_, binaryName_,
287 modules_, moduleGroups_);
288 addModule(CommandLineModulePointer(helpModule_));
292 CommandLineModuleMap::const_iterator
293 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
295 // TODO: Accept unambiguous prefixes?
296 return modules_.find(name);
299 CommandLineModuleMap::const_iterator
300 CommandLineModuleManager::Impl::findModuleFromBinaryName(
301 const ProgramInfo &programInfo) const
303 std::string moduleName = programInfo.programName();
304 #ifdef GMX_BINARY_SUFFIX
305 moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
307 if (moduleName == binaryName_)
309 return modules_.end();
311 if (startsWith(moduleName, "g_"))
313 moduleName.erase(0, 2);
315 if (startsWith(moduleName, "gmx"))
317 moduleName.erase(0, 3);
319 return findModuleByName(moduleName);
322 CommandLineModuleInterface *
323 CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
325 // Check if we are directly invoking a certain module.
326 CommandLineModuleInterface *module = singleModule_;
329 // Also check for invokation through named symlinks.
330 CommandLineModuleMap::const_iterator moduleIter
331 = findModuleFromBinaryName(programInfo_);
332 if (moduleIter != modules_.end())
334 module = moduleIter->second.get();
339 bool bHidden = false;
340 bool bVersion = false;
341 bool bCopyright = true;
342 // TODO: Print the common options into the help.
343 // TODO: It would be nice to propagate at least the -quiet option to
344 // the modules so that they can also be quiet in response to this.
345 Options options(NULL, NULL);
346 options.addOption(BooleanOption("h").store(&bHelp));
347 options.addOption(BooleanOption("hidden").store(&bHidden));
348 options.addOption(BooleanOption("quiet").store(&bQuiet_));
349 options.addOption(BooleanOption("version").store(&bVersion));
350 options.addOption(BooleanOption("copyright").store(&bCopyright));
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(&options).parse(&argcForWrapper, *argv);
365 // If no action requested and there is a module specified, process it.
366 if (argcForWrapper < *argc && !bHelp && !bVersion)
368 const char *moduleName = (*argv)[argcForWrapper];
369 CommandLineModuleMap::const_iterator moduleIter
370 = findModuleByName(moduleName);
371 if (moduleIter == modules_.end())
373 std::string message =
374 formatString("'%s' is not a GROMACS command.", moduleName);
375 GMX_THROW(InvalidInputError(message));
377 module = moduleIter->second.get();
378 *argc -= argcForWrapper;
379 *argv += argcForWrapper;
380 // After this point, argc and argv are the same independent of
381 // which path is taken: (*argv)[0] is the module name.
386 if (singleModule_ == NULL)
388 programInfo_.setDisplayName(binaryName_ + " " + module->name());
390 // Recognize the common options also after the module name.
391 // TODO: It could be nicer to only recognize -h/-hidden if module is not
393 CommandLineParser(&options).skipUnknown(true).parse(argc, *argv);
396 binaryInfoSettings_.extendedInfo(bVersion);
397 binaryInfoSettings_.copyright(bCopyright);
404 // If no module specified and no other action, show the help.
405 // Also explicitly specifying -h for the wrapper binary goes here.
406 if (module == NULL || bHelp)
408 ensureHelpModuleExists();
411 helpModule_->setModuleOverride(*module);
414 module = helpModule_;
416 if (module == helpModule_)
418 helpModule_->setShowHidden(bHidden);
423 /********************************************************************
424 * CommandLineModuleManager
427 CommandLineModuleManager::CommandLineModuleManager(const char *binaryName,
428 ProgramInfo *programInfo)
429 : impl_(new Impl(binaryName, programInfo))
433 CommandLineModuleManager::~CommandLineModuleManager()
437 void CommandLineModuleManager::setQuiet(bool bQuiet)
439 impl_->bQuiet_ = bQuiet;
442 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
444 impl_->singleModule_ = module;
447 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
449 impl_->addModule(move(module));
452 void CommandLineModuleManager::addModuleCMain(
453 const char *name, const char *shortDescription,
454 CMainFunction mainFunction)
456 CommandLineModulePointer module(
457 new CMainCommandLineModule(name, shortDescription, mainFunction));
458 addModule(move(module));
461 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
464 const char *const binaryName = impl_->binaryName_.c_str();
465 CommandLineModuleGroupDataPointer group(
466 new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
467 impl_->moduleGroups_.push_back(move(group));
468 return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
471 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
473 impl_->ensureHelpModuleExists();
474 impl_->helpModule_->addTopic(move(topic));
477 int CommandLineModuleManager::run(int argc, char *argv[])
479 CommandLineModuleInterface *module;
480 const bool bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
483 module = impl_->processCommonOptions(&argc, &argv);
485 catch (const std::exception &)
487 if (bMaster && !impl_->bQuiet_)
489 printBinaryInformation(stderr, impl_->programInfo_,
490 impl_->binaryInfoSettings_);
496 impl_->bQuiet_ = true;
500 FILE *out = (impl_->bStdOutInfo_ ? stdout : stderr);
501 printBinaryInformation(out, impl_->programInfo_,
502 impl_->binaryInfoSettings_);
509 int rc = module->run(argc, argv);
518 int CommandLineModuleManager::runAsMainSingleModule(
519 int argc, char *argv[], CommandLineModuleInterface *module)
521 ProgramInfo &programInfo = gmx::init(&argc, &argv);
524 CommandLineModuleManager manager(NULL, &programInfo);
525 manager.setSingleModule(module);
526 int rc = manager.run(argc, argv);
530 catch (const std::exception &ex)
532 printFatalErrorMessage(stderr, ex);
533 return processExceptionAtExit(ex);
538 int CommandLineModuleManager::runAsMainCMain(
539 int argc, char *argv[], CMainFunction mainFunction)
541 CMainCommandLineModule module(argv[0], NULL, mainFunction);
542 return runAsMainSingleModule(argc, argv, &module);
545 /********************************************************************
546 * CommandLineModuleGroupData
549 void CommandLineModuleGroupData::addModule(const char *name,
550 const char *description)
552 CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
553 GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
554 "Non-existent module added to a group");
555 if (description == NULL)
557 description = moduleIter->second->shortDescription();
558 GMX_RELEASE_ASSERT(description != NULL,
559 "Module without a description added to a group");
561 std::string tag(formatString("%s-%s", binaryName_, name));
562 modules_.push_back(std::make_pair(tag, description));
565 /********************************************************************
566 * CommandLineModuleGroup
569 void CommandLineModuleGroup::addModule(const char *name)
571 impl_->addModule(name, NULL);
574 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
575 const char *description)
577 impl_->addModule(name, description);