2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013, 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"
51 #include <boost/scoped_ptr.hpp>
53 #include "gromacs/legacyheaders/copyrite.h"
54 #include "gromacs/legacyheaders/network.h"
55 #include "gromacs/legacyheaders/smalloc.h"
57 #include "gromacs/commandline/cmdlinehelpcontext.h"
58 #include "gromacs/commandline/cmdlinemodule.h"
59 #include "gromacs/commandline/cmdlineparser.h"
60 #include "gromacs/fileio/futil.h"
61 #include "gromacs/onlinehelp/helpformat.h"
62 #include "gromacs/onlinehelp/helpmanager.h"
63 #include "gromacs/onlinehelp/helptopic.h"
64 #include "gromacs/onlinehelp/helpwritercontext.h"
65 #include "gromacs/options/basicoptions.h"
66 #include "gromacs/options/options.h"
67 #include "gromacs/utility/exceptions.h"
68 #include "gromacs/utility/file.h"
69 #include "gromacs/utility/gmxassert.h"
70 #include "gromacs/utility/init.h"
71 #include "gromacs/utility/programinfo.h"
72 #include "gromacs/utility/stringutil.h"
73 #include "gromacs/utility/uniqueptr.h"
78 //! Container type for mapping module names to module objects.
79 typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
80 //! Smart pointer type for managing a CommandLineModuleGroup.
81 typedef gmx_unique_ptr<internal::CommandLineModuleGroupData>::type
82 CommandLineModuleGroupDataPointer;
83 //! Container type for keeping a list of module groups.
84 typedef std::vector<CommandLineModuleGroupDataPointer>
85 CommandLineModuleGroupList;
87 class CommandLineHelpModule;
94 * Internal data for a CommandLineModuleManager module group.
96 * This class contains the state of a module group. CommandLineModuleGroup
97 * provides the public interface to construct/alter the state, and
98 * CommandLineModuleManager and its associated classes use it for help output.
100 * \ingroup module_commandline
102 class CommandLineModuleGroupData
106 * Shorthand for a list of modules contained in the group.
108 * The first element in the contained pair contains the tag
109 * (gmx-something) of the module, and the second element contains the
110 * description. The second element is never NULL.
112 typedef std::vector<std::pair<std::string, const char *> > ModuleList;
115 * Constructs an empty module group.
117 * \param[in] modules List of all modules
118 * (used for checking and default descriptions).
119 * \param[in] title Title of the group.
123 CommandLineModuleGroupData(const CommandLineModuleMap &modules,
125 : allModules_(modules), title_(title)
129 //! Returns the title for the group.
130 const char *title() const { return title_; }
131 //! Returns the list of modules in the group.
132 const ModuleList &modules() const { return modules_; }
135 * Adds a module to the group.
137 * \param[in] name Name of the module.
138 * \param[in] description Description of the module in this group.
139 * \throws std::bad_alloc if out of memory.
141 * If \p description is NULL, the description returned by the module is
144 void addModule(const char *name, const char *description)
146 CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
147 GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
148 "Non-existent module added to a group");
149 if (description == NULL)
151 description = moduleIter->second->shortDescription();
152 GMX_RELEASE_ASSERT(description != NULL,
153 "Module without a description added to a group");
155 const char *const program =
156 ProgramInfo::getInstance().invariantProgramName().c_str();
157 std::string tag(formatString("%s-%s", program, name));
158 modules_.push_back(std::make_pair(tag, description));
162 const CommandLineModuleMap &allModules_;
166 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineModuleGroupData);
169 } // namespace internal
174 /********************************************************************
180 static const char name[];
181 static const char title[];
182 static const char *const text[];
185 // The first two are not used.
186 const char RootHelpText::name[] = "";
187 const char RootHelpText::title[] = "";
188 const char *const RootHelpText::text[] = {
189 "Usage: [PROGRAM] <command> [<args>]",
193 * Help topic that forms the root of the help tree for the help subcommand.
195 * \ingroup module_commandline
197 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
201 * Creates a root help topic.
203 * \param[in] modules List of modules for to use for module listings.
207 explicit RootHelpTopic(const CommandLineModuleMap &modules)
212 virtual void writeHelp(const HelpWriterContext &context) const;
215 void printModuleList(const HelpWriterContext &context) const;
217 const CommandLineModuleMap &modules_;
219 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
222 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
224 if (context.outputFormat() != eHelpOutputFormat_Console)
226 // TODO: Implement once the situation with Redmine issue #969 is more
228 GMX_THROW(NotImplementedError(
229 "Root help is not implemented for this output format"));
231 writeBasicHelpTopic(context, *this, helpText());
232 // TODO: If/when this list becomes long, it may be better to only print
233 // "common" commands here, and have a separate topic (e.g.,
234 // "help commands") that prints the full list.
235 printModuleList(context);
236 context.writeTextBlock(
237 "For additional help on a command, use '[PROGRAM] help <command>'");
238 writeSubTopicList(context,
239 "\nAdditional help is available on the following topics:");
240 context.writeTextBlock(
241 "To access the help, use '[PROGRAM] help <topic>'.");
244 void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
246 if (context.outputFormat() != eHelpOutputFormat_Console)
248 // TODO: Implement once the situation with Redmine issue #969 is more
250 GMX_THROW(NotImplementedError(
251 "Module list is not implemented for this output format"));
253 int maxNameLength = 0;
254 CommandLineModuleMap::const_iterator module;
255 for (module = modules_.begin(); module != modules_.end(); ++module)
257 int nameLength = static_cast<int>(module->first.length());
258 if (module->second->shortDescription() != NULL
259 && nameLength > maxNameLength)
261 maxNameLength = nameLength;
264 File &file = context.outputFile();
265 TextTableFormatter formatter;
266 formatter.addColumn(NULL, maxNameLength + 1, false);
267 formatter.addColumn(NULL, 72 - maxNameLength, true);
268 formatter.setFirstColumnIndent(4);
270 file.writeLine("Available commands:");
271 for (module = modules_.begin(); module != modules_.end(); ++module)
273 const char *name = module->first.c_str();
274 const char *description = module->second->shortDescription();
275 if (description != NULL)
278 formatter.addColumnLine(0, name);
279 formatter.addColumnLine(1, description);
280 file.writeString(formatter.formatRow());
285 /********************************************************************
286 * ModuleHelpTopic declaration
290 * Help topic wrapper for a command-line module.
292 * This class implements HelpTopicInterface such that it wraps a
293 * CommandLineModuleInterface, allowing subcommand "help <command>"
294 * to produce the help for "<command>".
296 * \ingroup module_commandline
298 class ModuleHelpTopic : public HelpTopicInterface
301 //! Constructs a help topic for a specific module.
302 ModuleHelpTopic(const CommandLineModuleInterface &module,
303 const CommandLineHelpModule &helpModule)
304 : module_(module), helpModule_(helpModule)
308 virtual const char *name() const { return module_.name(); }
309 virtual const char *title() const { return NULL; }
310 virtual bool hasSubTopics() const { return false; }
311 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
315 virtual void writeHelp(const HelpWriterContext &context) const;
318 const CommandLineModuleInterface &module_;
319 const CommandLineHelpModule &helpModule_;
321 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
324 /********************************************************************
325 * HelpExportInterface
329 * Callbacks for exporting help information for command-line modules.
331 * \ingroup module_commandline
333 class HelpExportInterface
336 //! Shorthand for a list of modules contained in a group.
337 typedef internal::CommandLineModuleGroupData::ModuleList
340 virtual ~HelpExportInterface() {};
343 * Called once before exporting individual modules.
345 * Can, e.g., open shared output files (e.g., if the output is written
346 * into a single file, or if a separate index is required) and write
349 virtual void startModuleExport() = 0;
351 * Called to export the help for each module.
353 * \param[in] module Module for which the help should be exported.
354 * \param[in] tag Unique tag for the module (gmx-something).
355 * \param[in] displayName Display name for the module (gmx something).
357 virtual void exportModuleHelp(
358 const CommandLineModuleInterface &module,
359 const std::string &tag,
360 const std::string &displayName) = 0;
362 * Called after all modules have been exported.
364 * Can close files opened in startModuleExport(), write footers to them
367 virtual void finishModuleExport() = 0;
370 * Called once before exporting module groups.
372 * Can, e.g., open a single output file for listing all the groups.
374 virtual void startModuleGroupExport() = 0;
376 * Called to export the help for each module group.
378 * \param[in] title Title for the group.
379 * \param[in] modules List of modules in the group.
381 virtual void exportModuleGroup(const char *title,
382 const ModuleGroupContents &modules) = 0;
384 * Called after all module groups have been exported.
386 * Can close files opened in startModuleGroupExport(), write footers to them
389 virtual void finishModuleGroupExport() = 0;
393 * Adds hyperlinks to modules within this binary.
395 * \param[in,out] links Links are added here.
396 * \param[in] modules Modules in the current binary.
397 * \throws std::bad_alloc if out of memory.
399 * Initializes a HelpLinks object with links to modules defined in \p modules.
401 * \ingroup module_commandline
403 void initProgramLinks(HelpLinks *links, const CommandLineModuleMap &modules)
405 // TODO: Use the local ProgramInfo reference from CommandLineModuleManager
406 // (to do this nicely requires reordering the code in the file).
407 const char *const program =
408 ProgramInfo::getInstance().realBinaryName().c_str();
409 CommandLineModuleMap::const_iterator module;
410 for (module = modules.begin(); module != modules.end(); ++module)
412 if (module->second->shortDescription() != NULL)
414 std::string linkName("[gmx-" + module->first + "]");
415 std::string targetName(
416 formatString("%s-%s", program, module->first.c_str()));
417 std::string displayName(
418 formatString("[TT]%s %s[tt]", program, module->first.c_str()));
419 links->addLink(linkName, targetName, displayName);
424 /********************************************************************
429 * Implements export for man pages.
431 * \ingroup module_commandline
433 class HelpExportMan : public HelpExportInterface
436 //! Initializes man page exporter.
437 explicit HelpExportMan(const CommandLineModuleMap &modules)
438 : links_(eHelpOutputFormat_Man)
440 initProgramLinks(&links_, modules);
443 virtual void startModuleExport() {}
444 virtual void exportModuleHelp(
445 const CommandLineModuleInterface &module,
446 const std::string &tag,
447 const std::string &displayName);
448 virtual void finishModuleExport() {}
450 virtual void startModuleGroupExport();
451 virtual void exportModuleGroup(const char *title,
452 const ModuleGroupContents &modules);
453 virtual void finishModuleGroupExport();
457 boost::scoped_ptr<File> man7File_;
458 std::string man7Footer_;
461 void HelpExportMan::exportModuleHelp(
462 const CommandLineModuleInterface &module,
463 const std::string &tag,
464 const std::string &displayName)
466 File file("man1/" + tag + ".1", "w");
468 // TODO: It would be nice to remove the VERSION prefix from the version
469 // string to make it shorter.
470 file.writeLine(formatString(".TH %s 1 \"\" \"%s\" \"GROMACS Manual\"\n",
473 file.writeLine(".SH NAME");
474 file.writeLine(formatString("%s - %s", tag.c_str(),
475 module.shortDescription()));
478 CommandLineHelpContext context(&file, eHelpOutputFormat_Man, &links_);
479 context.setModuleDisplayName(displayName);
480 module.writeHelp(context);
482 file.writeLine(".SH SEE ALSO");
483 file.writeLine(".BR gromacs(7)");
485 file.writeLine("More information about \\fBGROMACS\\fR is available at <\\fIhttp://www.gromacs.org/\\fR>.");
488 void HelpExportMan::startModuleGroupExport()
490 const char *const programListPlaceholder = "@PROGMANPAGES@";
492 const std::string man7Template = gmx::File::readToString("man7/gromacs.7.in");
493 const size_t index = man7Template.find(programListPlaceholder);
494 GMX_RELEASE_ASSERT(index != std::string::npos,
495 "gromacs.7.in must contain a @PROGMANPAGES@ line");
496 std::string header = man7Template.substr(0, index);
497 man7Footer_ = man7Template.substr(index + std::strlen(programListPlaceholder));
498 header = replaceAll(header, "@VERSION@", GromacsVersion());
499 man7File_.reset(new File("man7/gromacs.7", "w"));
500 man7File_->writeLine(header);
503 void HelpExportMan::exportModuleGroup(const char *title,
504 const ModuleGroupContents &modules)
506 man7File_->writeLine(formatString(".Sh \"%s\"", title));
507 man7File_->writeLine(formatString(".IX Subsection \"%s\"", title));
508 man7File_->writeLine(".Vb");
509 man7File_->writeLine(".ta 16n");
511 ModuleGroupContents::const_iterator module;
512 for (module = modules.begin(); module != modules.end(); ++module)
514 const std::string &tag(module->first);
515 man7File_->writeLine(formatString("\\& %s\t%s",
516 tag.c_str(), module->second));
519 man7File_->writeLine(".Ve");
522 void HelpExportMan::finishModuleGroupExport()
524 man7File_->writeLine(man7Footer_);
528 /********************************************************************
533 * Implements export for HTML help.
535 * \ingroup module_commandline
537 class HelpExportHtml : public HelpExportInterface
540 //! Initializes HTML exporter.
541 explicit HelpExportHtml(const CommandLineModuleMap &modules);
543 virtual void startModuleExport();
544 virtual void exportModuleHelp(
545 const CommandLineModuleInterface &module,
546 const std::string &tag,
547 const std::string &displayName);
548 virtual void finishModuleExport();
550 virtual void startModuleGroupExport();
551 virtual void exportModuleGroup(const char *title,
552 const ModuleGroupContents &modules);
553 virtual void finishModuleGroupExport();
556 void setupHeaderAndFooter();
558 void writeHtmlHeader(File *file, const std::string &title) const;
559 void writeHtmlFooter(File *file) const;
562 boost::scoped_ptr<File> indexFile_;
567 HelpExportHtml::HelpExportHtml(const CommandLineModuleMap &modules)
568 : links_(eHelpOutputFormat_Html)
570 initProgramLinks(&links_, modules);
571 char *linksFilename = low_gmxlibfn("links.dat", FALSE, FALSE);
572 if (linksFilename != NULL)
574 scoped_ptr_sfree guard(linksFilename);
575 File linksFile(linksFilename, "r");
577 while (linksFile.readLine(&line))
579 links_.addLink(line, "../online/" + line, line);
582 setupHeaderAndFooter();
585 void HelpExportHtml::setupHeaderAndFooter()
587 header_ = gmx::File::readToString("header.html.in");
588 header_ = replaceAll(header_, "@VERSION@", GromacsVersion());
589 gmx::File::writeFileFromString("header.html", header_);
590 header_ = replaceAll(header_, "@ROOTPATH@", "../");
591 footer_ = gmx::File::readToString("footer.html");
594 void HelpExportHtml::startModuleExport()
596 indexFile_.reset(new File("final/programs/byname.html", "w"));
597 writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Name");
598 indexFile_->writeLine("<H3>GROMACS Programs Alphabetically</H3>");
601 void HelpExportHtml::exportModuleHelp(
602 const CommandLineModuleInterface &module,
603 const std::string &tag,
604 const std::string &displayName)
606 File file("final/programs/" + tag + ".html", "w");
607 writeHtmlHeader(&file, displayName);
609 CommandLineHelpContext context(&file, eHelpOutputFormat_Html, &links_);
610 context.setModuleDisplayName(displayName);
611 module.writeHelp(context);
613 writeHtmlFooter(&file);
616 indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
617 tag.c_str(), displayName.c_str(),
618 module.shortDescription()));
621 void HelpExportHtml::finishModuleExport()
623 writeHtmlFooter(indexFile_.get());
627 void HelpExportHtml::startModuleGroupExport()
629 indexFile_.reset(new File("final/programs/bytopic.html", "w"));
630 writeHtmlHeader(indexFile_.get(), "GROMACS Programs by Topic");
631 indexFile_->writeLine("<H3>GROMACS Programs by Topic</H3>");
634 void HelpExportHtml::exportModuleGroup(const char *title,
635 const ModuleGroupContents &modules)
637 indexFile_->writeLine(formatString("<H4>%s</H4>", title));
639 ModuleGroupContents::const_iterator module;
640 for (module = modules.begin(); module != modules.end(); ++module)
642 const std::string &tag(module->first);
643 std::string displayName(tag);
644 std::replace(displayName.begin(), displayName.end(), '-', ' ');
645 indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
646 tag.c_str(), displayName.c_str(),
651 void HelpExportHtml::finishModuleGroupExport()
653 writeHtmlFooter(indexFile_.get());
657 void HelpExportHtml::writeHtmlHeader(File *file, const std::string &title) const
659 file->writeLine(replaceAll(header_, "@TITLE@", title));
662 void HelpExportHtml::writeHtmlFooter(File *file) const
664 file->writeLine(footer_);
669 /********************************************************************
670 * CommandLineHelpModule
675 * Command-line module for producing help.
677 * This module implements the 'help' subcommand that is automatically added by
678 * CommandLineModuleManager.
680 * \ingroup module_commandline
682 class CommandLineHelpModule : public CommandLineModuleInterface
686 * Creates a command-line help module.
688 * \param[in] programInfo Information about the running binary.
689 * \param[in] modules List of modules for to use for module listings.
690 * \param[in] groups List of module groups.
691 * \throws std::bad_alloc if out of memory.
693 CommandLineHelpModule(const ProgramInfo &programInfo,
694 const CommandLineModuleMap &modules,
695 const CommandLineModuleGroupList &groups);
698 * Adds a top-level help topic.
700 * \param[in] topic Help topic to add.
701 * \throws std::bad_alloc if out of memory.
703 void addTopic(HelpTopicPointer topic);
704 //! Sets whether hidden options will be shown in help.
705 void setShowHidden(bool bHidden) { bHidden_ = bHidden; }
707 * Sets an override to show the help for the given module.
709 * If called, the help module directly prints the help for the given
710 * module when called, skipping any other processing.
712 void setModuleOverride(const CommandLineModuleInterface &module)
714 moduleOverride_ = &module;
717 //! Returns the context object for help output.
718 const CommandLineHelpContext &context() const
722 //! Returns the program info object for the running binary.
723 const ProgramInfo &programInfo() const
728 virtual const char *name() const { return "help"; }
729 virtual const char *shortDescription() const
731 return "Print help information";
734 virtual int run(int argc, char *argv[]);
735 virtual void writeHelp(const CommandLineHelpContext &context) const;
738 void exportHelp(HelpExportInterface *exporter) const;
740 boost::scoped_ptr<RootHelpTopic> rootTopic_;
741 const ProgramInfo &programInfo_;
742 const CommandLineModuleMap &modules_;
743 const CommandLineModuleGroupList &groups_;
745 CommandLineHelpContext *context_;
746 const CommandLineModuleInterface *moduleOverride_;
749 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
752 CommandLineHelpModule::CommandLineHelpModule(
753 const ProgramInfo &programInfo,
754 const CommandLineModuleMap &modules,
755 const CommandLineModuleGroupList &groups)
756 : rootTopic_(new RootHelpTopic(modules)), programInfo_(programInfo),
757 modules_(modules), groups_(groups),
758 context_(NULL), moduleOverride_(NULL), bHidden_(false)
762 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
764 rootTopic_->addSubTopic(move(topic));
767 int CommandLineHelpModule::run(int argc, char *argv[])
769 const char *const exportFormats[] = { "man", "html", "completion" };
770 std::string exportFormat;
771 Options options(NULL, NULL);
772 options.addOption(StringOption("export").store(&exportFormat)
773 .enumValue(exportFormats));
774 CommandLineParser(&options).parse(&argc, argv);
775 if (!exportFormat.empty())
777 boost::scoped_ptr<HelpExportInterface> exporter;
778 if (exportFormat == "man")
780 exporter.reset(new HelpExportMan(modules_));
782 else if (exportFormat == "html")
784 exporter.reset(new HelpExportHtml(modules_));
788 GMX_THROW(NotImplementedError("This help format is not implemented"));
790 exportHelp(exporter.get());
794 HelpLinks links(eHelpOutputFormat_Console);
795 initProgramLinks(&links, modules_);
796 boost::scoped_ptr<CommandLineHelpContext> context(
797 new CommandLineHelpContext(&File::standardOutput(),
798 eHelpOutputFormat_Console, &links));
799 context->setShowHidden(bHidden_);
800 if (moduleOverride_ != NULL)
802 context->setModuleDisplayName(programInfo_.displayName());
803 moduleOverride_->writeHelp(*context);
806 context_ = context.get();
808 HelpManager helpManager(*rootTopic_, context->writerContext());
811 for (int i = 1; i < argc; ++i)
813 helpManager.enterTopic(argv[i]);
816 catch (const InvalidInputError &ex)
818 fprintf(stderr, "%s\n", ex.what());
821 helpManager.writeCurrentTopic();
825 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
827 const HelpWriterContext &writerContext = context.writerContext();
829 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
833 writerContext.writeTextBlock(
834 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
835 // TODO: More information.
838 void CommandLineHelpModule::exportHelp(HelpExportInterface *exporter) const
840 // TODO: Would be nicer to have the file names supplied by the build system
841 // and/or export a list of files from here.
842 const char *const program = programInfo_.realBinaryName().c_str();
844 exporter->startModuleExport();
845 CommandLineModuleMap::const_iterator module;
846 for (module = modules_.begin(); module != modules_.end(); ++module)
848 if (module->second->shortDescription() != NULL)
850 const char *const moduleName = module->first.c_str();
851 std::string tag(formatString("%s-%s", program, moduleName));
852 std::string displayName(tag);
853 std::replace(displayName.begin(), displayName.end(), '-', ' ');
854 exporter->exportModuleHelp(*module->second, tag, displayName);
857 exporter->finishModuleExport();
859 exporter->startModuleGroupExport();
860 CommandLineModuleGroupList::const_iterator group;
861 for (group = groups_.begin(); group != groups_.end(); ++group)
863 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
865 exporter->finishModuleGroupExport();
871 /********************************************************************
872 * ModuleHelpTopic implementation
875 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
877 CommandLineHelpContext context(helpModule_.context());
878 const char *const program =
879 helpModule_.programInfo().realBinaryName().c_str();
880 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
881 module_.writeHelp(context);
884 /********************************************************************
885 * CMainCommandLineModule
889 * Implements a CommandLineModuleInterface, given a function with C/C++ main()
892 * \ingroup module_commandline
894 class CMainCommandLineModule : public CommandLineModuleInterface
897 //! \copydoc gmx::CommandLineModuleManager::CMainFunction
898 typedef CommandLineModuleManager::CMainFunction CMainFunction;
901 * Creates a wrapper module for the given main function.
903 * \param[in] name Name for the module.
904 * \param[in] shortDescription One-line description for the module.
905 * \param[in] mainFunction Main function to wrap.
907 * Does not throw. This is essential for correct implementation of
908 * CommandLineModuleManager::runAsMainCMain().
910 CMainCommandLineModule(const char *name, const char *shortDescription,
911 CMainFunction mainFunction)
912 : name_(name), shortDescription_(shortDescription),
913 mainFunction_(mainFunction)
917 virtual const char *name() const
921 virtual const char *shortDescription() const
923 return shortDescription_;
926 virtual int run(int argc, char *argv[])
928 return mainFunction_(argc, argv);
930 virtual void writeHelp(const CommandLineHelpContext &context) const
934 // TODO: The constness should not be cast away.
935 argv[0] = const_cast<char *>(name_);
937 GlobalCommandLineHelpContext global(context);
938 mainFunction_(argc, argv);
943 const char *shortDescription_;
944 CMainFunction mainFunction_;
950 /********************************************************************
951 * CommandLineModuleManager::Impl
955 * Private implementation class for CommandLineModuleManager.
957 * \ingroup module_commandline
959 class CommandLineModuleManager::Impl
963 * Initializes the implementation class.
965 * \param programInfo Program information for the running binary.
967 explicit Impl(ProgramInfo *programInfo);
970 * Helper method that adds a given module to the module manager.
972 * \throws std::bad_alloc if out of memory.
974 void addModule(CommandLineModulePointer module);
976 * Creates the help module if it does not yet exist.
978 * \throws std::bad_alloc if out of memory.
980 * This method should be called before accessing \a helpModule_.
982 void ensureHelpModuleExists();
985 * Finds a module that matches a name.
987 * \param[in] name Module name to find.
988 * \returns Iterator to the found module, or
989 * \c modules_.end() if not found.
993 CommandLineModuleMap::const_iterator
994 findModuleByName(const std::string &name) const;
996 * Finds a module that the name of the binary.
998 * \param[in] programInfo Program information object to use.
999 * \throws std::bad_alloc if out of memory.
1000 * \returns Iterator to the found module, or
1001 * \c modules_.end() if not found.
1003 * Checks whether the program is invoked through a symlink whose name
1004 * is different from ProgramInfo::realBinaryName(), and if so, checks
1005 * if a module name matches the name of the symlink.
1007 * Note that the \p programInfo parameter is currently not necessary
1008 * (as the program info object is also contained as a member), but it
1009 * clarifies the control flow.
1011 CommandLineModuleMap::const_iterator
1012 findModuleFromBinaryName(const ProgramInfo &programInfo) const;
1015 * Processes command-line options for the wrapper binary.
1017 * \param[in,out] argc On input, argc passed to run().
1018 * On output, argc to be passed to the module.
1019 * \param[in,out] argv On input, argv passed to run().
1020 * On output, argv to be passed to the module.
1021 * \throws InvalidInputError if there are invalid options.
1022 * \returns The module that should be run.
1024 * Handles command-line options that affect the wrapper binary
1025 * (potentially changing the members of \c this in response to the
1026 * options). Also finds the module that should be run and the
1027 * arguments that should be passed to it.
1029 CommandLineModuleInterface *
1030 processCommonOptions(int *argc, char ***argv);
1033 * Maps module names to module objects.
1035 * Owns the contained modules.
1037 CommandLineModuleMap modules_;
1039 * List of groupings for modules for help output.
1041 * Owns the contained module group data objects.
1042 * CommandLineModuleGroup objects point to the data objects contained
1045 CommandLineModuleGroupList moduleGroups_;
1046 //! Information about the currently running program.
1047 ProgramInfo &programInfo_;
1049 * Module that implements help for the binary.
1051 * The pointed module is owned by the \a modules_ container.
1053 CommandLineHelpModule *helpModule_;
1054 //! Settings for what to write in the startup header.
1055 BinaryInformationSettings binaryInfoSettings_;
1056 //! If non-NULL, run this module in single-module mode.
1057 CommandLineModuleInterface *singleModule_;
1058 //! Whether all stderr output should be suppressed.
1060 //! Whether to write the startup information to stdout iso stderr.
1064 GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
1067 CommandLineModuleManager::Impl::Impl(ProgramInfo *programInfo)
1068 : programInfo_(*programInfo), helpModule_(NULL), singleModule_(NULL),
1069 bQuiet_(false), bStdOutInfo_(false)
1071 binaryInfoSettings_.copyright(true);
1074 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
1076 GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
1077 "Attempted to register a duplicate module name");
1078 ensureHelpModuleExists();
1079 HelpTopicPointer helpTopic(new ModuleHelpTopic(*module, *helpModule_));
1080 modules_.insert(std::make_pair(std::string(module->name()),
1082 helpModule_->addTopic(move(helpTopic));
1085 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
1087 if (helpModule_ == NULL)
1089 helpModule_ = new CommandLineHelpModule(programInfo_, modules_,
1091 addModule(CommandLineModulePointer(helpModule_));
1095 CommandLineModuleMap::const_iterator
1096 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
1098 // TODO: Accept unambiguous prefixes?
1099 return modules_.find(name);
1102 CommandLineModuleMap::const_iterator
1103 CommandLineModuleManager::Impl::findModuleFromBinaryName(
1104 const ProgramInfo &programInfo) const
1106 std::string binaryName = programInfo.invariantProgramName();
1107 if (binaryName == programInfo.realBinaryName())
1109 return modules_.end();
1111 if (binaryName.compare(0, 2, "g_") == 0)
1113 binaryName.erase(0, 2);
1115 if (binaryName.compare(0, 3, "gmx") == 0)
1117 binaryName.erase(0, 3);
1119 return findModuleByName(binaryName);
1122 CommandLineModuleInterface *
1123 CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
1125 // Check if we are directly invoking a certain module.
1126 CommandLineModuleInterface *module = singleModule_;
1129 // Also check for invokation through named symlinks.
1130 CommandLineModuleMap::const_iterator moduleIter
1131 = findModuleFromBinaryName(programInfo_);
1132 if (moduleIter != modules_.end())
1134 module = moduleIter->second.get();
1139 bool bHidden = false;
1140 bool bVersion = false;
1141 bool bCopyright = true;
1142 // TODO: Print the common options into the help.
1143 // TODO: It would be nice to propagate at least the -quiet option to
1144 // the modules so that they can also be quiet in response to this.
1145 Options options(NULL, NULL);
1146 options.addOption(BooleanOption("h").store(&bHelp));
1147 options.addOption(BooleanOption("hidden").store(&bHidden));
1148 options.addOption(BooleanOption("quiet").store(&bQuiet_));
1149 options.addOption(BooleanOption("version").store(&bVersion));
1150 options.addOption(BooleanOption("copyright").store(&bCopyright));
1154 // If not in single-module mode, process options to the wrapper binary.
1155 // TODO: Ideally, this could be done by CommandLineParser.
1156 int argcForWrapper = 1;
1157 while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
1161 if (argcForWrapper > 1)
1163 CommandLineParser(&options).parse(&argcForWrapper, *argv);
1165 // If no action requested and there is a module specified, process it.
1166 if (argcForWrapper < *argc && !bHelp && !bVersion)
1168 const char *moduleName = (*argv)[argcForWrapper];
1169 CommandLineModuleMap::const_iterator moduleIter
1170 = findModuleByName(moduleName);
1171 if (moduleIter == modules_.end())
1173 std::string message =
1174 formatString("'%s' is not a GROMACS command.", moduleName);
1175 GMX_THROW(InvalidInputError(message));
1177 module = moduleIter->second.get();
1178 *argc -= argcForWrapper;
1179 *argv += argcForWrapper;
1180 // After this point, argc and argv are the same independent of
1181 // which path is taken: (*argv)[0] is the module name.
1186 if (singleModule_ == NULL)
1188 programInfo_.setDisplayName(
1189 programInfo_.realBinaryName() + " " + module->name());
1191 // Recognize the common options also after the module name.
1192 // TODO: It could be nicer to only recognize -h/-hidden if module is not
1194 CommandLineParser(&options).skipUnknown(true).parse(argc, *argv);
1197 binaryInfoSettings_.extendedInfo(bVersion);
1198 binaryInfoSettings_.copyright(bCopyright);
1202 bStdOutInfo_ = true;
1205 // If no module specified and no other action, show the help.
1206 // Also explicitly specifying -h for the wrapper binary goes here.
1207 if (module == NULL || bHelp)
1209 ensureHelpModuleExists();
1212 helpModule_->setModuleOverride(*module);
1215 module = helpModule_;
1217 if (module == helpModule_)
1219 helpModule_->setShowHidden(bHidden);
1224 /********************************************************************
1225 * CommandLineModuleManager
1228 CommandLineModuleManager::CommandLineModuleManager(ProgramInfo *programInfo)
1229 : impl_(new Impl(programInfo))
1233 CommandLineModuleManager::~CommandLineModuleManager()
1237 void CommandLineModuleManager::setQuiet(bool bQuiet)
1239 impl_->bQuiet_ = bQuiet;
1242 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
1244 impl_->singleModule_ = module;
1247 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
1249 impl_->addModule(move(module));
1252 void CommandLineModuleManager::addModuleCMain(
1253 const char *name, const char *shortDescription,
1254 CMainFunction mainFunction)
1256 CommandLineModulePointer module(
1257 new CMainCommandLineModule(name, shortDescription, mainFunction));
1258 addModule(move(module));
1261 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
1264 CommandLineModuleGroupDataPointer group(
1265 new internal::CommandLineModuleGroupData(impl_->modules_, title));
1266 impl_->moduleGroups_.push_back(move(group));
1267 return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
1270 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
1272 impl_->ensureHelpModuleExists();
1273 impl_->helpModule_->addTopic(move(topic));
1276 int CommandLineModuleManager::run(int argc, char *argv[])
1278 CommandLineModuleInterface *module;
1279 const bool bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
1282 module = impl_->processCommonOptions(&argc, &argv);
1284 catch (const std::exception &)
1286 if (bMaster && !impl_->bQuiet_)
1288 printBinaryInformation(stderr, impl_->programInfo_,
1289 impl_->binaryInfoSettings_);
1295 impl_->bQuiet_ = true;
1297 if (!impl_->bQuiet_)
1299 FILE *out = (impl_->bStdOutInfo_ ? stdout : stderr);
1300 printBinaryInformation(out, impl_->programInfo_,
1301 impl_->binaryInfoSettings_);
1308 int rc = module->run(argc, argv);
1309 if (!impl_->bQuiet_)
1317 int CommandLineModuleManager::runAsMainSingleModule(
1318 int argc, char *argv[], CommandLineModuleInterface *module)
1320 ProgramInfo &programInfo = gmx::init(&argc, &argv);
1323 CommandLineModuleManager manager(&programInfo);
1324 manager.setSingleModule(module);
1325 int rc = manager.run(argc, argv);
1329 catch (const std::exception &ex)
1331 printFatalErrorMessage(stderr, ex);
1332 return processExceptionAtExit(ex);
1337 int CommandLineModuleManager::runAsMainCMain(
1338 int argc, char *argv[], CMainFunction mainFunction)
1340 CMainCommandLineModule module(argv[0], NULL, mainFunction);
1341 return runAsMainSingleModule(argc, argv, &module);
1344 /********************************************************************
1345 * CommandLineModuleGroup
1348 void CommandLineModuleGroup::addModule(const char *name)
1350 impl_->addModule(name, NULL);
1353 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
1354 const char *description)
1356 impl_->addModule(name, description);