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
44 #include "cmdlinemodulemanager.h"
57 #include "gromacs/commandline/cmdlinehelpcontext.h"
58 #include "gromacs/commandline/cmdlineinit.h"
59 #include "gromacs/commandline/cmdlinemodule.h"
60 #include "gromacs/commandline/cmdlineparser.h"
61 #include "gromacs/commandline/cmdlineprogramcontext.h"
62 #include "gromacs/legacyheaders/copyrite.h"
63 #include "gromacs/options/basicoptions.h"
64 #include "gromacs/options/options.h"
65 #include "gromacs/utility/basenetwork.h"
66 #include "gromacs/utility/exceptions.h"
67 #include "gromacs/utility/fatalerror.h"
68 #include "gromacs/utility/gmxassert.h"
69 #include "gromacs/utility/stringutil.h"
71 #include "cmdlinehelpmodule.h"
72 #include "cmdlinemodulemanager-impl.h"
80 //! \addtogroup module_commandline
83 /********************************************************************
84 * CMainCommandLineModule
88 * Implements a CommandLineModuleInterface, given a function with C/C++ main()
91 class CMainCommandLineModule : public CommandLineModuleInterface
94 //! \copydoc gmx::CommandLineModuleManager::CMainFunction
95 typedef CommandLineModuleManager::CMainFunction CMainFunction;
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.
104 * Does not throw. This is essential for correct implementation of
105 * CommandLineModuleManager::runAsMainCMain().
107 CMainCommandLineModule(const char *name, const char *shortDescription,
108 CMainFunction mainFunction)
109 : name_(name), shortDescription_(shortDescription),
110 mainFunction_(mainFunction)
114 virtual const char *name() const
118 virtual const char *shortDescription() const
120 return shortDescription_;
123 virtual void init(CommandLineModuleSettings * /*settings*/)
126 virtual int run(int argc, char *argv[])
128 return mainFunction_(argc, argv);
130 virtual void writeHelp(const CommandLineHelpContext &context) const
132 writeCommandLineHelpCMain(context, name_, mainFunction_);
137 const char *shortDescription_;
138 CMainFunction mainFunction_;
145 /********************************************************************
146 * CommandLineCommonOptionsHolder
149 CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder()
150 : options_(NULL, NULL), bHelp_(false), bHidden_(false),
151 bQuiet_(false), bVersion_(false), bCopyright_(true),
152 niceLevel_(19), debugLevel_(0)
154 binaryInfoSettings_.copyright(true);
157 CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder()
161 void CommandLineCommonOptionsHolder::initOptions()
163 options_.addOption(BooleanOption("h").store(&bHelp_)
164 .description("Print help and quit"));
165 options_.addOption(BooleanOption("hidden").store(&bHidden_)
167 .description("Show hidden options in help"));
168 options_.addOption(BooleanOption("quiet").store(&bQuiet_)
169 .description("Do not print common startup info or quotes"));
170 options_.addOption(BooleanOption("version").store(&bVersion_)
171 .description("Print extended version information and quit"));
172 options_.addOption(BooleanOption("copyright").store(&bCopyright_)
173 .description("Print copyright information on startup"));
174 options_.addOption(IntegerOption("nice").store(&niceLevel_)
175 .description("Set the nicelevel (default depends on command)"));
176 options_.addOption(IntegerOption("debug").store(&debugLevel_)
177 .hidden().defaultValueIfSet(1)
178 .description("Write file with debug information, "
179 "1: short (default), 2: also x and f"));
182 bool CommandLineCommonOptionsHolder::finishOptions()
185 binaryInfoSettings_.extendedInfo(bVersion_);
186 // The latter condition suppresses the copyright with
188 binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
192 void CommandLineCommonOptionsHolder::adjustFromSettings(
193 const CommandLineModuleSettings &settings)
195 if (!options_.isSet("nice"))
197 niceLevel_ = settings.defaultNiceLevel();
201 /********************************************************************
202 * CommandLineModuleManager::Impl
206 * Private implementation class for CommandLineModuleManager.
208 * \ingroup module_commandline
210 class CommandLineModuleManager::Impl
214 * Initializes the implementation class.
216 * \param[in] binaryName Name of the running binary
217 * (without Gromacs binary suffix or .exe on Windows).
218 * \param programContext Program information for the running binary.
220 Impl(const char *binaryName, CommandLineProgramContext *programContext);
223 * Helper method that adds a given module to the module manager.
225 * \throws std::bad_alloc if out of memory.
227 void addModule(CommandLineModulePointer module);
229 * Creates the help module if it does not yet exist.
231 * \throws std::bad_alloc if out of memory.
233 * This method should be called before accessing \a helpModule_.
235 void ensureHelpModuleExists();
238 * Finds a module that matches a name.
240 * \param[in] name Module name to find.
241 * \returns Iterator to the found module, or
242 * \c modules_.end() if not found.
246 CommandLineModuleMap::const_iterator
247 findModuleByName(const std::string &name) const;
249 * Finds a module that the name of the binary.
251 * \param[in] invokedName Name by which the program was invoked.
252 * \throws std::bad_alloc if out of memory.
253 * \returns Iterator to the found module, or
254 * \c modules_.end() if not found.
256 * Checks whether the program is invoked through a symlink whose name
257 * is different from \a binaryName_, and if so, checks
258 * if a module name matches the name of the symlink.
260 * Note that the \p invokedName parameter is currently not necessary
261 * (as the program context object is also available and provides this
262 * value), but it clarifies the control flow.
264 CommandLineModuleMap::const_iterator
265 findModuleFromBinaryName(const char *invokedName) const;
268 * Processes command-line options for the wrapper binary.
270 * \param[in,out] optionsHolder Common options.
271 * \param[in,out] argc On input, argc passed to run().
272 * On output, argc to be passed to the module.
273 * \param[in,out] argv On input, argv passed to run().
274 * On output, argv to be passed to the module.
275 * \throws InvalidInputError if there are invalid options.
276 * \returns The module that should be run.
278 * Handles command-line options that affect the wrapper binary
279 * (potentially changing the members of \c this in response to the
280 * options). Also finds the module that should be run and the
281 * arguments that should be passed to it.
283 CommandLineModuleInterface *
284 processCommonOptions(CommandLineCommonOptionsHolder *optionsHolder,
285 int *argc, char ***argv);
288 * Maps module names to module objects.
290 * Owns the contained modules.
292 CommandLineModuleMap modules_;
294 * List of groupings for modules for help output.
296 * Owns the contained module group data objects.
297 * CommandLineModuleGroup objects point to the data objects contained
300 CommandLineModuleGroupList moduleGroups_;
301 //! Information about the currently running program.
302 CommandLineProgramContext &programContext_;
303 //! Name of the binary.
304 std::string binaryName_;
306 * Module that implements help for the binary.
308 * The pointed module is owned by the \a modules_ container.
310 CommandLineHelpModule *helpModule_;
311 //! If non-NULL, run this module in single-module mode.
312 CommandLineModuleInterface *singleModule_;
313 //! Stores the value set with setQuiet().
317 GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
320 CommandLineModuleManager::Impl::Impl(const char *binaryName,
321 CommandLineProgramContext *programContext)
322 : programContext_(*programContext),
323 binaryName_(binaryName != NULL ? binaryName : ""),
324 helpModule_(NULL), singleModule_(NULL),
327 GMX_RELEASE_ASSERT(binaryName_.find('-') == std::string::npos,
328 "Help export does not currently work with binary names with dashes");
331 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
333 GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
334 "Attempted to register a duplicate module name");
335 ensureHelpModuleExists();
336 HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
337 modules_.insert(std::make_pair(std::string(module->name()),
339 helpModule_->addTopic(move(helpTopic));
342 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
344 if (helpModule_ == NULL)
346 helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
347 modules_, moduleGroups_);
348 addModule(CommandLineModulePointer(helpModule_));
352 CommandLineModuleMap::const_iterator
353 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
355 // TODO: Accept unambiguous prefixes?
356 return modules_.find(name);
359 CommandLineModuleMap::const_iterator
360 CommandLineModuleManager::Impl::findModuleFromBinaryName(
361 const char *invokedName) const
363 std::string moduleName = invokedName;
364 #ifdef GMX_BINARY_SUFFIX
365 moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
367 if (moduleName == binaryName_)
369 return modules_.end();
371 if (startsWith(moduleName, "g_"))
373 moduleName.erase(0, 2);
375 if (startsWith(moduleName, "gmx"))
377 moduleName.erase(0, 3);
379 return findModuleByName(moduleName);
382 CommandLineModuleInterface *
383 CommandLineModuleManager::Impl::processCommonOptions(
384 CommandLineCommonOptionsHolder *optionsHolder, int *argc, char ***argv)
386 // Check if we are directly invoking a certain module.
387 CommandLineModuleInterface *module = singleModule_;
390 // Also check for invokation through named symlinks.
391 CommandLineModuleMap::const_iterator moduleIter
392 = findModuleFromBinaryName(programContext_.programName());
393 if (moduleIter != modules_.end())
395 module = moduleIter->second.get();
399 // TODO: It would be nice to propagate at least the -quiet option to
400 // the modules so that they can also be quiet in response to this.
404 // If not in single-module mode, process options to the wrapper binary.
405 // TODO: Ideally, this could be done by CommandLineParser.
406 int argcForWrapper = 1;
407 while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
411 if (argcForWrapper > 1)
413 CommandLineParser(optionsHolder->options())
414 .parse(&argcForWrapper, *argv);
416 // If no action requested and there is a module specified, process it.
417 if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
419 const char *moduleName = (*argv)[argcForWrapper];
420 CommandLineModuleMap::const_iterator moduleIter
421 = findModuleByName(moduleName);
422 if (moduleIter == modules_.end())
424 std::string message =
425 formatString("'%s' is not a GROMACS command.", moduleName);
426 GMX_THROW(InvalidInputError(message));
428 module = moduleIter->second.get();
429 *argc -= argcForWrapper;
430 *argv += argcForWrapper;
431 // After this point, argc and argv are the same independent of
432 // which path is taken: (*argv)[0] is the module name.
437 if (singleModule_ == NULL)
439 programContext_.setDisplayName(binaryName_ + " " + module->name());
441 // Recognize the common options also after the module name.
442 // TODO: It could be nicer to only recognize -h/-hidden if module is not
444 CommandLineParser(optionsHolder->options())
445 .skipUnknown(true).parse(argc, *argv);
447 if (!optionsHolder->finishOptions())
451 // If no module specified and no other action, show the help.
452 // Also explicitly specifying -h for the wrapper binary goes here.
453 if (module == NULL || optionsHolder->shouldShowHelp())
455 ensureHelpModuleExists();
458 helpModule_->setModuleOverride(*module);
461 module = helpModule_;
463 if (module == helpModule_)
465 helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
470 /********************************************************************
471 * CommandLineModuleManager
474 CommandLineModuleManager::CommandLineModuleManager(
475 const char *binaryName, CommandLineProgramContext *programContext)
476 : impl_(new Impl(binaryName, programContext))
480 CommandLineModuleManager::~CommandLineModuleManager()
484 void CommandLineModuleManager::setQuiet(bool bQuiet)
486 impl_->bQuiet_ = bQuiet;
489 void CommandLineModuleManager::setOutputRedirect(File *output)
491 impl_->ensureHelpModuleExists();
492 impl_->helpModule_->setOutputRedirect(output);
495 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
497 impl_->singleModule_ = module;
500 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
502 impl_->addModule(move(module));
505 void CommandLineModuleManager::addModuleCMain(
506 const char *name, const char *shortDescription,
507 CMainFunction mainFunction)
509 CommandLineModulePointer module(
510 new CMainCommandLineModule(name, shortDescription, mainFunction));
511 addModule(move(module));
514 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
517 const char *const binaryName = impl_->binaryName_.c_str();
518 CommandLineModuleGroupDataPointer group(
519 new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
520 impl_->moduleGroups_.push_back(move(group));
521 return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
524 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
526 impl_->ensureHelpModuleExists();
527 impl_->helpModule_->addTopic(move(topic));
530 int CommandLineModuleManager::run(int argc, char *argv[])
532 CommandLineModuleInterface *module;
533 const bool bMaster = (gmx_node_rank() == 0);
534 bool bQuiet = impl_->bQuiet_ || !bMaster;
535 CommandLineCommonOptionsHolder optionsHolder;
538 optionsHolder.initOptions();
539 module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
541 catch (const std::exception &)
543 bQuiet |= optionsHolder.shouldBeQuiet();
546 printBinaryInformation(stderr, impl_->programContext_,
547 optionsHolder.binaryInfoSettings());
551 bQuiet |= optionsHolder.shouldBeQuiet();
554 FILE *out = optionsHolder.startupInfoFile();
555 printBinaryInformation(out, impl_->programContext_,
556 optionsHolder.binaryInfoSettings());
564 CommandLineModuleSettings settings;
565 module->init(&settings);
566 optionsHolder.adjustFromSettings(settings);
568 // Open the debug file.
569 if (optionsHolder.debugLevel() > 0)
571 std::string filename(impl_->programContext_.programName());
572 if (gmx_node_num() > 1)
574 filename.append(formatString("%d", gmx_node_rank()));
576 filename.append(".debug");
578 fprintf(stderr, "Will write debug log file: %s\n", filename.c_str());
579 gmx_init_debug(optionsHolder.debugLevel(), filename.c_str());
581 #if defined(HAVE_UNISTD_H) && !defined(GMX_NO_NICE) && !defined(__MINGW32__)
582 // Set the nice level unless disabled in the configuration.
583 if (optionsHolder.niceLevel() != 0)
585 static bool bNiceSet = false; // Only set it once.
588 if (nice(optionsHolder.niceLevel()) == -1)
590 // Do nothing, but use the return value to avoid warnings.
598 if (!(module == impl_->helpModule_ && !bMaster))
600 rc = module->run(argc, argv);
610 int CommandLineModuleManager::runAsMainSingleModule(
611 int argc, char *argv[], CommandLineModuleInterface *module)
613 CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
616 CommandLineModuleManager manager(NULL, &programContext);
617 manager.setSingleModule(module);
618 int rc = manager.run(argc, argv);
619 gmx::finalizeForCommandLine();
622 catch (const std::exception &ex)
624 printFatalErrorMessage(stderr, ex);
625 return processExceptionAtExit(ex);
630 int CommandLineModuleManager::runAsMainCMain(
631 int argc, char *argv[], CMainFunction mainFunction)
633 CMainCommandLineModule module(argv[0], NULL, mainFunction);
634 return runAsMainSingleModule(argc, argv, &module);
637 /********************************************************************
638 * CommandLineModuleGroupData
641 void CommandLineModuleGroupData::addModule(const char *name,
642 const char *description)
644 CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
645 GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
646 "Non-existent module added to a group");
647 if (description == NULL)
649 description = moduleIter->second->shortDescription();
650 GMX_RELEASE_ASSERT(description != NULL,
651 "Module without a description added to a group");
653 std::string tag(formatString("%s-%s", binaryName_, name));
654 modules_.push_back(std::make_pair(tag, description));
657 /********************************************************************
658 * CommandLineModuleGroup
661 void CommandLineModuleGroup::addModule(const char *name)
663 impl_->addModule(name, NULL);
666 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
667 const char *description)
669 impl_->addModule(name, description);