2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * 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"
50 #include <boost/scoped_ptr.hpp>
52 #include "gromacs/legacyheaders/copyrite.h"
53 #include "gromacs/legacyheaders/network.h"
55 #include "gromacs/commandline/cmdlinehelpcontext.h"
56 #include "gromacs/commandline/cmdlinemodule.h"
57 #include "gromacs/commandline/cmdlineparser.h"
58 #include "gromacs/onlinehelp/helpformat.h"
59 #include "gromacs/onlinehelp/helpmanager.h"
60 #include "gromacs/onlinehelp/helptopic.h"
61 #include "gromacs/onlinehelp/helpwritercontext.h"
62 #include "gromacs/options/basicoptions.h"
63 #include "gromacs/options/options.h"
64 #include "gromacs/utility/exceptions.h"
65 #include "gromacs/utility/file.h"
66 #include "gromacs/utility/gmxassert.h"
67 #include "gromacs/utility/init.h"
68 #include "gromacs/utility/programinfo.h"
69 #include "gromacs/utility/stringutil.h"
74 //! Container type for mapping module names to module objects.
75 typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
77 class CommandLineHelpModule;
82 /********************************************************************
88 static const char name[];
89 static const char title[];
90 static const char *const text[];
93 // The first two are not used.
94 const char RootHelpText::name[] = "";
95 const char RootHelpText::title[] = "";
96 const char *const RootHelpText::text[] = {
97 "Usage: [PROGRAM] <command> [<args>]",
101 * Help topic that forms the root of the help tree for the help subcommand.
103 * \ingroup module_commandline
105 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
109 * Creates a root help topic.
111 * \param[in] modules List of modules for to use for module listings.
115 explicit RootHelpTopic(const CommandLineModuleMap &modules)
120 virtual void writeHelp(const HelpWriterContext &context) const;
123 void printModuleList(const HelpWriterContext &context) const;
125 const CommandLineModuleMap &modules_;
127 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
130 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
132 if (context.outputFormat() != eHelpOutputFormat_Console)
134 // TODO: Implement once the situation with Redmine issue #969 is more
136 GMX_THROW(NotImplementedError(
137 "Root help is not implemented for this output format"));
139 writeBasicHelpTopic(context, *this, helpText());
140 // TODO: If/when this list becomes long, it may be better to only print
141 // "common" commands here, and have a separate topic (e.g.,
142 // "help commands") that prints the full list.
143 printModuleList(context);
144 context.writeTextBlock(
145 "For additional help on a command, use '[PROGRAM] help <command>'");
146 writeSubTopicList(context,
147 "\nAdditional help is available on the following topics:");
148 context.writeTextBlock(
149 "To access the help, use '[PROGRAM] help <topic>'.");
152 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
154 if (context.outputFormat() != eHelpOutputFormat_Console)
156 // TODO: Implement once the situation with Redmine issue #969 is more
158 GMX_THROW(NotImplementedError(
159 "Module list is not implemented for this output format"));
161 int maxNameLength = 0;
162 CommandLineModuleMap::const_iterator module;
163 for (module = modules_.begin(); module != modules_.end(); ++module)
165 int nameLength = static_cast<int>(module->first.length());
166 if (module->second->shortDescription() != NULL
167 && nameLength > maxNameLength)
169 maxNameLength = nameLength;
172 File &file = context.outputFile();
173 TextTableFormatter formatter;
174 formatter.addColumn(NULL, maxNameLength + 1, false);
175 formatter.addColumn(NULL, 72 - maxNameLength, true);
176 formatter.setFirstColumnIndent(4);
178 file.writeLine("Available commands:");
179 for (module = modules_.begin(); module != modules_.end(); ++module)
181 const char *name = module->first.c_str();
182 const char *description = module->second->shortDescription();
183 if (description != NULL)
186 formatter.addColumnLine(0, name);
187 formatter.addColumnLine(1, description);
188 file.writeString(formatter.formatRow());
193 /********************************************************************
194 * ModuleHelpTopic declaration
198 * Help topic wrapper for a command-line module.
200 * This class implements HelpTopicInterface such that it wraps a
201 * CommandLineModuleInterface, allowing subcommand "help <command>"
202 * to produce the help for "<command>".
204 * \ingroup module_commandline
206 class ModuleHelpTopic : public HelpTopicInterface
209 //! Constructs a help topic for a specific module.
210 ModuleHelpTopic(const CommandLineModuleInterface &module,
211 const CommandLineHelpModule &helpModule)
212 : module_(module), helpModule_(helpModule)
216 virtual const char *name() const { return module_.name(); }
217 virtual const char *title() const { return NULL; }
218 virtual bool hasSubTopics() const { return false; }
219 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
223 virtual void writeHelp(const HelpWriterContext &context) const;
226 const CommandLineModuleInterface &module_;
227 const CommandLineHelpModule &helpModule_;
229 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
232 /********************************************************************
233 * HelpExportInterface
237 * Callbacks for exporting help information for command-line modules.
239 * \ingroup module_commandline
241 class HelpExportInterface
244 virtual ~HelpExportInterface() {};
247 * Called to export the help for each module.
249 * \param[in] tag Unique tag for the module (gmx-something).
250 * \param[in] module Module for which the help should be exported.
252 virtual void exportModuleHelp(const std::string &tag,
253 const CommandLineModuleInterface &module) = 0;
258 /********************************************************************
259 * CommandLineHelpModule
264 * Command-line module for producing help.
266 * This module implements the 'help' subcommand that is automatically added by
267 * CommandLineModuleManager.
269 * \ingroup module_commandline
271 class CommandLineHelpModule : public CommandLineModuleInterface
275 * Creates a command-line help module.
277 * \param[in] modules List of modules for to use for module listings.
278 * \throws std::bad_alloc if out of memory.
280 explicit CommandLineHelpModule(const CommandLineModuleMap &modules);
283 * Adds a top-level help topic.
285 * \param[in] topic Help topic to add.
286 * \throws std::bad_alloc if out of memory.
288 void addTopic(HelpTopicPointer topic);
289 //! Sets whether hidden options will be shown in help.
290 void setShowHidden(bool bHidden) { bHidden_ = bHidden; }
292 * Sets an override to show the help for the given module.
294 * If called, the help module directly prints the help for the given
295 * module when called, skipping any other processing.
297 void setModuleOverride(const CommandLineModuleInterface &module)
299 moduleOverride_ = &module;
302 //! Returns the context object for help output.
303 const CommandLineHelpContext &context() const
308 virtual const char *name() const { return "help"; }
309 virtual const char *shortDescription() const
311 return "Print help information";
314 virtual int run(int argc, char *argv[]);
315 virtual void writeHelp(const CommandLineHelpContext &context) const;
318 void exportHelp(HelpExportInterface *exporter) const;
320 boost::scoped_ptr<RootHelpTopic> rootTopic_;
321 const CommandLineModuleMap &modules_;
323 CommandLineHelpContext *context_;
324 const CommandLineModuleInterface *moduleOverride_;
327 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
330 CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
331 : rootTopic_(new RootHelpTopic(modules)), modules_(modules),
332 context_(NULL), moduleOverride_(NULL), bHidden_(false)
336 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
338 rootTopic_->addSubTopic(move(topic));
341 int CommandLineHelpModule::run(int argc, char *argv[])
343 const char *const exportFormats[] = { "man", "html", "completion" };
344 std::string exportFormat;
345 Options options(NULL, NULL);
346 options.addOption(StringOption("export").store(&exportFormat)
347 .enumValue(exportFormats));
348 CommandLineParser(&options).parse(&argc, argv);
349 if (!exportFormat.empty())
351 boost::scoped_ptr<HelpExportInterface> exporter;
353 GMX_THROW(NotImplementedError("This help format is not implemented"));
355 exportHelp(exporter.get());
359 boost::scoped_ptr<CommandLineHelpContext> context(
360 new CommandLineHelpContext(&File::standardOutput(),
361 eHelpOutputFormat_Console));
362 context->setShowHidden(bHidden_);
363 context_ = context.get();
364 if (moduleOverride_ != NULL)
366 ModuleHelpTopic(*moduleOverride_, *this).writeHelp(context->writerContext());
370 HelpManager helpManager(*rootTopic_, context->writerContext());
373 for (int i = 1; i < argc; ++i)
375 helpManager.enterTopic(argv[i]);
378 catch (const InvalidInputError &ex)
380 fprintf(stderr, "%s\n", ex.what());
383 helpManager.writeCurrentTopic();
387 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
389 const HelpWriterContext &writerContext = context.writerContext();
391 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
395 writerContext.writeTextBlock(
396 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
397 // TODO: More information.
400 void CommandLineHelpModule::exportHelp(HelpExportInterface *exporter) const
402 // TODO: Would be nicer to have the file names supplied by the build system
403 // and/or export a list of files from here.
404 const char *const program =
405 ProgramInfo::getInstance().invariantProgramName().c_str();
407 CommandLineModuleMap::const_iterator module;
408 for (module = modules_.begin(); module != modules_.end(); ++module)
410 if (module->second->shortDescription() != NULL)
412 const char *const moduleName = module->first.c_str();
413 std::string tag(formatString("%s-%s", program, moduleName));
414 exporter->exportModuleHelp(tag, *module->second);
422 /********************************************************************
423 * ModuleHelpTopic implementation
426 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
428 module_.writeHelp(helpModule_.context());
431 /********************************************************************
432 * CMainCommandLineModule
436 * Implements a CommandLineModuleInterface, given a function with C/C++ main()
439 * \ingroup module_commandline
441 class CMainCommandLineModule : public CommandLineModuleInterface
444 //! \copydoc gmx::CommandLineModuleManager::CMainFunction
445 typedef CommandLineModuleManager::CMainFunction CMainFunction;
448 * Creates a wrapper module for the given main function.
450 * \param[in] name Name for the module.
451 * \param[in] shortDescription One-line description for the module.
452 * \param[in] mainFunction Main function to wrap.
454 * Does not throw. This is essential for correct implementation of
455 * CommandLineModuleManager::runAsMainCMain().
457 CMainCommandLineModule(const char *name, const char *shortDescription,
458 CMainFunction mainFunction)
459 : name_(name), shortDescription_(shortDescription),
460 mainFunction_(mainFunction)
464 virtual const char *name() const
468 virtual const char *shortDescription() const
470 return shortDescription_;
473 virtual int run(int argc, char *argv[])
475 return mainFunction_(argc, argv);
477 virtual void writeHelp(const CommandLineHelpContext &context) const
479 const HelpOutputFormat format = context.writerContext().outputFormat();
483 case eHelpOutputFormat_Console:
487 GMX_THROW(NotImplementedError(
488 "Command-line help is not implemented for this output format"));
492 // TODO: The constness should not be cast away.
493 argv[0] = const_cast<char *>(name_);
494 argv[1] = const_cast<char *>("-man");
495 argv[2] = const_cast<char *>(type);
497 GlobalCommandLineHelpContext global(context);
498 mainFunction_(argc, argv);
503 const char *shortDescription_;
504 CMainFunction mainFunction_;
510 /********************************************************************
511 * CommandLineModuleManager::Impl
515 * Private implementation class for CommandLineModuleManager.
517 * \ingroup module_commandline
519 class CommandLineModuleManager::Impl
523 * Initializes the implementation class.
525 * \param programInfo Program information for the running binary.
527 explicit Impl(ProgramInfo *programInfo);
530 * Helper method that adds a given module to the module manager.
532 * \throws std::bad_alloc if out of memory.
534 void addModule(CommandLineModulePointer module);
536 * Creates the help module if it does not yet exist.
538 * \throws std::bad_alloc if out of memory.
540 * This method should be called before accessing \a helpModule_.
542 void ensureHelpModuleExists();
545 * Finds a module that matches a name.
547 * \param[in] name Module name to find.
548 * \returns Iterator to the found module, or
549 * \c modules_.end() if not found.
553 CommandLineModuleMap::const_iterator
554 findModuleByName(const std::string &name) const;
556 * Finds a module that the name of the binary.
558 * \param[in] programInfo Program information object to use.
559 * \throws std::bad_alloc if out of memory.
560 * \returns Iterator to the found module, or
561 * \c modules_.end() if not found.
563 * Checks whether the program is invoked through a symlink whose name
564 * is different from ProgramInfo::realBinaryName(), and if so, checks
565 * if a module name matches the name of the symlink.
567 * Note that the \p programInfo parameter is currently not necessary
568 * (as the program info object is also contained as a member), but it
569 * clarifies the control flow.
571 CommandLineModuleMap::const_iterator
572 findModuleFromBinaryName(const ProgramInfo &programInfo) const;
575 * Processes command-line options for the wrapper binary.
577 * \param[in,out] argc On input, argc passed to run().
578 * On output, argc to be passed to the module.
579 * \param[in,out] argv On input, argv passed to run().
580 * On output, argv to be passed to the module.
581 * \throws InvalidInputError if there are invalid options.
582 * \returns The module that should be run.
584 * Handles command-line options that affect the wrapper binary
585 * (potentially changing the members of \c this in response to the
586 * options). Also finds the module that should be run and the
587 * arguments that should be passed to it.
589 CommandLineModuleInterface *
590 processCommonOptions(int *argc, char ***argv);
593 * Maps module names to module objects.
595 * Owns the contained modules.
597 CommandLineModuleMap modules_;
598 //! Information about the currently running program.
599 ProgramInfo &programInfo_;
601 * Module that implements help for the binary.
603 * The pointed module is owned by the \a modules_ container.
605 CommandLineHelpModule *helpModule_;
606 //! Settings for what to write in the startup header.
607 BinaryInformationSettings binaryInfoSettings_;
608 //! If non-NULL, run this module in single-module mode.
609 CommandLineModuleInterface *singleModule_;
610 //! Whether all stderr output should be suppressed.
612 //! Whether to write the startup information to stdout iso stderr.
616 GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
619 CommandLineModuleManager::Impl::Impl(ProgramInfo *programInfo)
620 : programInfo_(*programInfo), helpModule_(NULL), singleModule_(NULL),
621 bQuiet_(false), bStdOutInfo_(false)
623 binaryInfoSettings_.copyright(true);
626 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
628 GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
629 "Attempted to register a duplicate module name");
630 ensureHelpModuleExists();
631 HelpTopicPointer helpTopic(new ModuleHelpTopic(*module, *helpModule_));
632 modules_.insert(std::make_pair(std::string(module->name()),
634 helpModule_->addTopic(move(helpTopic));
637 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
639 if (helpModule_ == NULL)
641 helpModule_ = new CommandLineHelpModule(modules_);
642 addModule(CommandLineModulePointer(helpModule_));
646 CommandLineModuleMap::const_iterator
647 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
649 // TODO: Accept unambiguous prefixes?
650 return modules_.find(name);
653 CommandLineModuleMap::const_iterator
654 CommandLineModuleManager::Impl::findModuleFromBinaryName(
655 const ProgramInfo &programInfo) const
657 std::string binaryName = programInfo.invariantProgramName();
658 if (binaryName == programInfo.realBinaryName())
660 return modules_.end();
662 if (binaryName.compare(0, 2, "g_") == 0)
664 binaryName.erase(0, 2);
666 if (binaryName.compare(0, 3, "gmx") == 0)
668 binaryName.erase(0, 3);
670 return findModuleByName(binaryName);
673 CommandLineModuleInterface *
674 CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
676 // Check if we are directly invoking a certain module.
677 CommandLineModuleInterface *module = singleModule_;
680 // Also check for invokation through named symlinks.
681 CommandLineModuleMap::const_iterator moduleIter
682 = findModuleFromBinaryName(programInfo_);
683 if (moduleIter != modules_.end())
685 module = moduleIter->second.get();
690 bool bHidden = false;
691 bool bVersion = false;
692 bool bCopyright = true;
693 // TODO: Print the common options into the help.
694 // TODO: It would be nice to propagate at least the -quiet option to
695 // the modules so that they can also be quiet in response to this.
696 Options options(NULL, NULL);
697 options.addOption(BooleanOption("h").store(&bHelp));
698 options.addOption(BooleanOption("hidden").store(&bHidden));
699 options.addOption(BooleanOption("quiet").store(&bQuiet_));
700 options.addOption(BooleanOption("version").store(&bVersion));
701 options.addOption(BooleanOption("copyright").store(&bCopyright));
705 // If not in single-module mode, process options to the wrapper binary.
706 // TODO: Ideally, this could be done by CommandLineParser.
707 int argcForWrapper = 1;
708 while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
712 if (argcForWrapper > 1)
714 CommandLineParser(&options).parse(&argcForWrapper, *argv);
716 // If no action requested and there is a module specified, process it.
717 if (argcForWrapper < *argc && !bHelp && !bVersion)
719 const char *moduleName = (*argv)[argcForWrapper];
720 CommandLineModuleMap::const_iterator moduleIter
721 = findModuleByName(moduleName);
722 if (moduleIter == modules_.end())
724 std::string message =
725 formatString("'%s' is not a GROMACS command.", moduleName);
726 GMX_THROW(InvalidInputError(message));
728 module = moduleIter->second.get();
729 programInfo_.setDisplayName(
730 programInfo_.realBinaryName() + "-" + moduleIter->first);
731 *argc -= argcForWrapper;
732 *argv += argcForWrapper;
733 // After this point, argc and argv are the same independent of
734 // which path is taken: (*argv)[0] is the module name.
739 // Recognize the common options also after the module name.
740 // TODO: It could be nicer to only recognize -h/-hidden if module is not
742 CommandLineParser(&options).skipUnknown(true).parse(argc, *argv);
745 binaryInfoSettings_.extendedInfo(bVersion);
746 binaryInfoSettings_.copyright(bCopyright);
753 // If no module specified and no other action, show the help.
754 // Also explicitly specifying -h for the wrapper binary goes here.
755 if (module == NULL || bHelp)
757 ensureHelpModuleExists();
760 helpModule_->setModuleOverride(*module);
763 module = helpModule_;
765 if (module == helpModule_)
767 helpModule_->setShowHidden(bHidden);
772 /********************************************************************
773 * CommandLineModuleManager
776 CommandLineModuleManager::CommandLineModuleManager(ProgramInfo *programInfo)
777 : impl_(new Impl(programInfo))
781 CommandLineModuleManager::~CommandLineModuleManager()
785 void CommandLineModuleManager::setQuiet(bool bQuiet)
787 impl_->bQuiet_ = bQuiet;
790 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
792 impl_->addModule(move(module));
795 void CommandLineModuleManager::addModuleCMain(
796 const char *name, const char *shortDescription,
797 CMainFunction mainFunction)
799 CommandLineModulePointer module(
800 new CMainCommandLineModule(name, shortDescription, mainFunction));
801 addModule(move(module));
804 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
806 impl_->ensureHelpModuleExists();
807 impl_->helpModule_->addTopic(move(topic));
810 int CommandLineModuleManager::run(int argc, char *argv[])
812 CommandLineModuleInterface *module;
813 const bool bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
816 module = impl_->processCommonOptions(&argc, &argv);
818 catch (const std::exception &)
820 if (bMaster && !impl_->bQuiet_)
822 printBinaryInformation(stderr, impl_->programInfo_,
823 impl_->binaryInfoSettings_);
829 impl_->bQuiet_ = true;
833 FILE *out = (impl_->bStdOutInfo_ ? stdout : stderr);
834 printBinaryInformation(out, impl_->programInfo_,
835 impl_->binaryInfoSettings_);
842 int rc = module->run(argc, argv);
851 int CommandLineModuleManager::runAsMainSingleModule(
852 int argc, char *argv[], CommandLineModuleInterface *module)
854 ProgramInfo &programInfo = gmx::init(&argc, &argv);
857 CommandLineModuleManager manager(&programInfo);
858 manager.impl_->singleModule_ = module;
859 int rc = manager.run(argc, argv);
863 catch (const std::exception &ex)
865 printFatalErrorMessage(stderr, ex);
866 return processExceptionAtExit(ex);
871 int CommandLineModuleManager::runAsMainCMain(
872 int argc, char *argv[], CMainFunction mainFunction)
874 CMainCommandLineModule module(argv[0], NULL, mainFunction);
875 return runAsMainSingleModule(argc, argv, &module);