2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015, 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::CommandLineHelpModule.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_commandline
44 #include "cmdlinehelpmodule.h"
49 #include <boost/scoped_ptr.hpp>
51 #include "gromacs/commandline/cmdlinehelpcontext.h"
52 #include "gromacs/commandline/cmdlinehelpwriter.h"
53 #include "gromacs/commandline/cmdlineparser.h"
54 #include "gromacs/onlinehelp/helpformat.h"
55 #include "gromacs/onlinehelp/helpmanager.h"
56 #include "gromacs/onlinehelp/helptopic.h"
57 #include "gromacs/onlinehelp/helpwritercontext.h"
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/options.h"
60 #include "gromacs/utility/baseversion.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/file.h"
63 #include "gromacs/utility/gmxassert.h"
64 #include "gromacs/utility/programcontext.h"
65 #include "gromacs/utility/stringutil.h"
67 #include "shellcompletions.h"
74 class HelpExportInterface;
78 /********************************************************************
79 * CommandLineHelpModuleImpl declaration
82 class CommandLineHelpModuleImpl
85 CommandLineHelpModuleImpl(const ProgramContextInterface &programContext,
86 const std::string &binaryName,
87 const CommandLineModuleMap &modules,
88 const CommandLineModuleGroupList &groups);
90 void exportHelp(HelpExportInterface *exporter) const;
92 boost::scoped_ptr<RootHelpTopic> rootTopic_;
93 const ProgramContextInterface &programContext_;
94 std::string binaryName_;
95 const CommandLineModuleMap &modules_;
96 const CommandLineModuleGroupList &groups_;
98 CommandLineHelpContext *context_;
99 const CommandLineModuleInterface *moduleOverride_;
102 File *outputOverride_;
104 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
110 /********************************************************************
116 static const char name[];
117 static const char title[];
118 static const char *const text[];
121 // The first two are not used.
122 const char RootHelpText::name[] = "";
123 const char RootHelpText::title[] = "";
124 const char *const RootHelpText::text[] = { "" };
127 * Help topic that forms the root of the help tree for the help subcommand.
129 * \ingroup module_commandline
131 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
135 * Creates a root help topic.
139 explicit RootHelpTopic(const CommandLineHelpModuleImpl &helpModule)
140 : helpModule_(helpModule)
144 virtual void writeHelp(const HelpWriterContext &context) const;
147 const CommandLineHelpModuleImpl &helpModule_;
149 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
152 void RootHelpTopic::writeHelp(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 "Root help is not implemented for this output format"));
162 CommandLineCommonOptionsHolder optionsHolder;
163 CommandLineHelpContext cmdlineContext(*helpModule_.context_);
164 cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
165 optionsHolder.initOptions();
166 // TODO: Add <command> [<args>] into the synopsis.
167 CommandLineHelpWriter(*optionsHolder.options())
168 .writeHelp(cmdlineContext);
170 // TODO: Consider printing a list of "core" commands. Would require someone
171 // to determine such a set...
172 writeSubTopicList(context,
173 "Additional help is available on the following topics:");
174 context.writeTextBlock("To access the help, use '[PROGRAM] help <topic>'.");
175 context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
178 /********************************************************************
183 * Help topic for listing the commands.
185 * \ingroup module_commandline
187 class CommandsHelpTopic : public HelpTopicInterface
191 * Creates a command list help topic.
193 * \param[in] helpModule Help module to get module information from.
197 explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
198 : helpModule_(helpModule)
202 virtual const char *name() const { return "commands"; }
203 virtual const char *title() const { return "List of available commands"; }
204 virtual bool hasSubTopics() const { return false; }
205 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
210 virtual void writeHelp(const HelpWriterContext &context) const;
213 const CommandLineHelpModuleImpl &helpModule_;
215 GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
218 void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
220 if (context.outputFormat() != eHelpOutputFormat_Console)
222 GMX_THROW(NotImplementedError(
223 "Module list is not implemented for this output format"));
225 int maxNameLength = 0;
226 const CommandLineModuleMap &modules = helpModule_.modules_;
227 CommandLineModuleMap::const_iterator module;
228 for (module = modules.begin(); module != modules.end(); ++module)
230 int nameLength = static_cast<int>(module->first.length());
231 if (module->second->shortDescription() != NULL
232 && nameLength > maxNameLength)
234 maxNameLength = nameLength;
237 context.writeTextBlock(
238 "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
239 "Available commands:");
240 File &file = context.outputFile();
241 TextTableFormatter formatter;
242 formatter.addColumn(NULL, maxNameLength + 1, false);
243 formatter.addColumn(NULL, 72 - maxNameLength, true);
244 formatter.setFirstColumnIndent(4);
245 for (module = modules.begin(); module != modules.end(); ++module)
247 const char *name = module->first.c_str();
248 const char *description = module->second->shortDescription();
249 if (description != NULL)
252 formatter.addColumnLine(0, name);
253 formatter.addColumnLine(1, description);
254 file.writeString(formatter.formatRow());
257 context.writeTextBlock(
258 "For help on a command, use '[PROGRAM] help <command>'.");
261 /********************************************************************
266 * Help topic wrapper for a command-line module.
268 * This class implements HelpTopicInterface such that it wraps a
269 * CommandLineModuleInterface, allowing subcommand "help <command>"
270 * to produce the help for "<command>".
272 * \ingroup module_commandline
274 class ModuleHelpTopic : public HelpTopicInterface
277 //! Constructs a help topic for a specific module.
278 ModuleHelpTopic(const CommandLineModuleInterface &module,
279 const CommandLineHelpModuleImpl &helpModule)
280 : module_(module), helpModule_(helpModule)
284 virtual const char *name() const { return module_.name(); }
285 virtual const char *title() const { return NULL; }
286 virtual bool hasSubTopics() const { return false; }
287 virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
291 virtual void writeHelp(const HelpWriterContext &context) const;
294 const CommandLineModuleInterface &module_;
295 const CommandLineHelpModuleImpl &helpModule_;
297 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
300 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
302 CommandLineHelpContext context(*helpModule_.context_);
303 const char *const program = helpModule_.binaryName_.c_str();
304 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
305 module_.writeHelp(context);
308 /********************************************************************
309 * HelpExportInterface
313 * Callbacks for exporting help information for command-line modules.
315 * \ingroup module_commandline
317 class HelpExportInterface
320 //! Shorthand for a list of modules contained in a group.
321 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
323 virtual ~HelpExportInterface() {};
326 * Called once before exporting individual modules.
328 * Can, e.g., open shared output files (e.g., if the output is written
329 * into a single file, or if a separate index is required) and write
332 virtual void startModuleExport() = 0;
334 * Called to export the help for each module.
336 * \param[in] module Module for which the help should be exported.
337 * \param[in] tag Unique tag for the module (gmx-something).
338 * \param[in] displayName Display name for the module (gmx something).
340 virtual void exportModuleHelp(
341 const CommandLineModuleInterface &module,
342 const std::string &tag,
343 const std::string &displayName) = 0;
345 * Called after all modules have been exported.
347 * Can close files opened in startModuleExport(), write footers to them
350 virtual void finishModuleExport() = 0;
353 * Called once before exporting module groups.
355 * Can, e.g., open a single output file for listing all the groups.
357 virtual void startModuleGroupExport() = 0;
359 * Called to export the help for each module group.
361 * \param[in] title Title for the group.
362 * \param[in] modules List of modules in the group.
364 virtual void exportModuleGroup(const char *title,
365 const ModuleGroupContents &modules) = 0;
367 * Called after all module groups have been exported.
369 * Can close files opened in startModuleGroupExport(), write footers to them
372 virtual void finishModuleGroupExport() = 0;
376 * Adds hyperlinks to modules within this binary.
378 * \param[in,out] links Links are added here.
379 * \param[in] helpModule Help module to get module information from.
380 * \throws std::bad_alloc if out of memory.
382 * Initializes a HelpLinks object with links to modules defined in
385 * \ingroup module_commandline
387 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
389 const char *const program = helpModule.binaryName_.c_str();
390 CommandLineModuleMap::const_iterator module;
391 for (module = helpModule.modules_.begin();
392 module != helpModule.modules_.end();
395 if (module->second->shortDescription() != NULL)
397 std::string linkName("[gmx-" + module->first + "]");
398 const char *name = module->first.c_str();
399 std::string reference(
400 formatString(":doc:`%s %s <%s-%s>`", program, name, program, name));
401 std::string displayName(
402 formatString("[TT]%s %s[tt]", program, name));
403 links->addLink(linkName, reference, displayName);
408 /********************************************************************
409 * HelpExportReStructuredText
413 * Implements export for web pages as reStructuredText.
415 * \ingroup module_commandline
417 class HelpExportReStructuredText : public HelpExportInterface
420 //! Initializes reST exporter.
421 explicit HelpExportReStructuredText(
422 const CommandLineHelpModuleImpl &helpModule);
424 virtual void startModuleExport();
425 virtual void exportModuleHelp(
426 const CommandLineModuleInterface &module,
427 const std::string &tag,
428 const std::string &displayName);
429 virtual void finishModuleExport();
431 virtual void startModuleGroupExport();
432 virtual void exportModuleGroup(const char *title,
433 const ModuleGroupContents &modules);
434 virtual void finishModuleGroupExport();
438 boost::scoped_ptr<File> indexFile_;
439 boost::scoped_ptr<File> manPagesFile_;
442 HelpExportReStructuredText::HelpExportReStructuredText(
443 const CommandLineHelpModuleImpl &helpModule)
444 : links_(eHelpOutputFormat_Rst)
446 File linksFile("links.dat", "r");
448 while (linksFile.readLine(&line))
450 links_.addLink("[REF]." + line + "[ref]",
451 formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
453 links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
456 initProgramLinks(&links_, helpModule);
459 void HelpExportReStructuredText::startModuleExport()
461 indexFile_.reset(new File("programs/byname.rst", "w"));
462 indexFile_->writeLine("Tools by Name");
463 indexFile_->writeLine("=============");
464 manPagesFile_.reset(new File("conf-man.py", "w"));
465 manPagesFile_->writeLine("man_pages = [");
468 void HelpExportReStructuredText::exportModuleHelp(
469 const CommandLineModuleInterface &module,
470 const std::string &tag,
471 const std::string &displayName)
473 // TODO: Ideally, the file would only be touched if it really changes.
474 // This would make Sphinx reruns much faster.
475 File file("programs/" + tag + ".rst", "w");
476 file.writeLine(formatString(".. _%s:", displayName.c_str()));
477 if (0 == displayName.compare("gmx mdrun"))
479 // Make an extra link target for the convenience of
480 // MPI-specific documentation
481 file.writeLine(".. _mdrun_mpi:");
484 file.writeLine(displayName);
485 file.writeLine(std::string(displayName.length(), '='));
486 CommandLineHelpContext context(&file, eHelpOutputFormat_Rst, &links_);
487 context.setModuleDisplayName(displayName);
488 module.writeHelp(context);
491 file.writeLine(".. only:: man");
493 file.writeLine(" See also");
494 file.writeLine(" --------");
496 file.writeLine(" :manpage:`gromacs(7)`");
498 file.writeLine(" More information about |Gromacs| is available at <http://www.gromacs.org/>.");
501 indexFile_->writeLine(formatString("* :doc:`%s <%s>` - %s",
502 displayName.c_str(), tag.c_str(),
503 module.shortDescription()));
504 manPagesFile_->writeLine(
505 formatString(" ('programs/%s', '%s', \"%s\", '', 1),",
506 tag.c_str(), tag.c_str(), module.shortDescription()));
509 void HelpExportReStructuredText::finishModuleExport()
513 manPagesFile_->writeLine(" ('man/gromacs.7', 'gromacs', 'molecular dynamics simulation suite', '', 7)");
514 manPagesFile_->writeLine("]");
515 manPagesFile_->close();
516 manPagesFile_.reset();
519 void HelpExportReStructuredText::startModuleGroupExport()
521 indexFile_.reset(new File("programs/bytopic.rst", "w"));
522 indexFile_->writeLine("Tools by Topic");
523 indexFile_->writeLine("==============");
524 manPagesFile_.reset(new File("man/bytopic.rst", "w"));
527 void HelpExportReStructuredText::exportModuleGroup(
529 const ModuleGroupContents &modules)
531 indexFile_->writeLine(title);
532 indexFile_->writeLine(std::string(std::strlen(title), '-'));
533 manPagesFile_->writeLine(title);
534 manPagesFile_->writeLine(std::string(std::strlen(title), '+'));
536 ModuleGroupContents::const_iterator module;
537 for (module = modules.begin(); module != modules.end(); ++module)
539 const std::string &tag(module->first);
540 std::string displayName(tag);
541 // TODO: This does not work if the binary name would contain a dash,
542 // but that is not currently the case.
543 const size_t dashPos = displayName.find('-');
544 GMX_RELEASE_ASSERT(dashPos != std::string::npos,
545 "There should always be at least one dash in the tag");
546 displayName[dashPos] = ' ';
547 indexFile_->writeLine(formatString("| :doc:`%s <%s>` - %s",
548 displayName.c_str(), tag.c_str(),
550 manPagesFile_->writeLine(formatString("| ``%s`` - %s",
554 indexFile_->writeLine();
555 manPagesFile_->writeLine();
558 void HelpExportReStructuredText::finishModuleGroupExport()
562 manPagesFile_->close();
563 manPagesFile_.reset();
566 /********************************************************************
567 * HelpExportCompletion
571 * Implements export for command-line completion.
573 * \ingroup module_commandline
575 class HelpExportCompletion : public HelpExportInterface
578 //! Initializes completion exporter.
579 explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
581 virtual void startModuleExport();
582 virtual void exportModuleHelp(
583 const CommandLineModuleInterface &module,
584 const std::string &tag,
585 const std::string &displayName);
586 virtual void finishModuleExport();
588 virtual void startModuleGroupExport() {}
589 virtual void exportModuleGroup(const char * /*title*/,
590 const ModuleGroupContents & /*modules*/) {}
591 virtual void finishModuleGroupExport() {}
594 ShellCompletionWriter bashWriter_;
595 std::vector<std::string> modules_;
598 HelpExportCompletion::HelpExportCompletion(
599 const CommandLineHelpModuleImpl &helpModule)
600 : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
604 void HelpExportCompletion::startModuleExport()
606 bashWriter_.startCompletions();
609 void HelpExportCompletion::exportModuleHelp(
610 const CommandLineModuleInterface &module,
611 const std::string & /*tag*/,
612 const std::string & /*displayName*/)
614 modules_.push_back(module.name());
616 CommandLineHelpContext context(&bashWriter_);
617 // We use the display name to pass the name of the module to the
618 // completion writer.
619 context.setModuleDisplayName(module.name());
620 module.writeHelp(context);
624 void HelpExportCompletion::finishModuleExport()
626 CommandLineCommonOptionsHolder optionsHolder;
627 optionsHolder.initOptions();
628 bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
629 bashWriter_.finishCompletions();
634 /********************************************************************
635 * CommandLineHelpModuleImpl implementation
637 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
638 const ProgramContextInterface &programContext,
639 const std::string &binaryName,
640 const CommandLineModuleMap &modules,
641 const CommandLineModuleGroupList &groups)
642 : rootTopic_(new RootHelpTopic(*this)), programContext_(programContext),
643 binaryName_(binaryName), modules_(modules), groups_(groups),
644 context_(NULL), moduleOverride_(NULL), bHidden_(false),
645 outputOverride_(NULL)
649 void CommandLineHelpModuleImpl::exportHelp(HelpExportInterface *exporter) const
651 // TODO: Would be nicer to have the file names supplied by the build system
652 // and/or export a list of files from here.
653 const char *const program = binaryName_.c_str();
655 exporter->startModuleExport();
656 CommandLineModuleMap::const_iterator module;
657 for (module = modules_.begin(); module != modules_.end(); ++module)
659 if (module->second->shortDescription() != NULL)
661 const char *const moduleName = module->first.c_str();
662 std::string tag(formatString("%s-%s", program, moduleName));
663 std::string displayName(formatString("%s %s", program, moduleName));
664 exporter->exportModuleHelp(*module->second, tag, displayName);
667 exporter->finishModuleExport();
669 exporter->startModuleGroupExport();
670 CommandLineModuleGroupList::const_iterator group;
671 for (group = groups_.begin(); group != groups_.end(); ++group)
673 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
675 exporter->finishModuleGroupExport();
678 /********************************************************************
679 * CommandLineHelpModule
682 CommandLineHelpModule::CommandLineHelpModule(
683 const ProgramContextInterface &programContext,
684 const std::string &binaryName,
685 const CommandLineModuleMap &modules,
686 const CommandLineModuleGroupList &groups)
687 : impl_(new Impl(programContext, binaryName, modules, groups))
691 CommandLineHelpModule::~CommandLineHelpModule()
695 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
696 const CommandLineModuleInterface &module) const
698 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
701 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
703 impl_->rootTopic_->addSubTopic(move(topic));
706 void CommandLineHelpModule::setShowHidden(bool bHidden)
708 impl_->bHidden_ = bHidden;
711 void CommandLineHelpModule::setModuleOverride(
712 const CommandLineModuleInterface &module)
714 impl_->moduleOverride_ = &module;
717 void CommandLineHelpModule::setOutputRedirect(File *output)
719 impl_->outputOverride_ = output;
722 int CommandLineHelpModule::run(int argc, char *argv[])
724 // Add internal topics lazily here.
725 addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)));
727 const char *const exportFormats[] = { "rst", "completion" };
728 std::string exportFormat;
729 Options options(NULL, NULL);
730 options.addOption(StringOption("export").store(&exportFormat)
731 .enumValue(exportFormats));
732 CommandLineParser(&options).parse(&argc, argv);
733 if (!exportFormat.empty())
735 boost::scoped_ptr<HelpExportInterface> exporter;
736 if (exportFormat == "rst")
738 exporter.reset(new HelpExportReStructuredText(*impl_));
740 else if (exportFormat == "completion")
742 exporter.reset(new HelpExportCompletion(*impl_));
746 GMX_THROW(NotImplementedError("This help format is not implemented"));
748 impl_->exportHelp(exporter.get());
752 File *outputFile = &File::standardOutput();
753 if (impl_->outputOverride_ != NULL)
755 outputFile = impl_->outputOverride_;
757 HelpLinks links(eHelpOutputFormat_Console);
758 initProgramLinks(&links, *impl_);
759 boost::scoped_ptr<CommandLineHelpContext> context(
760 new CommandLineHelpContext(outputFile,
761 eHelpOutputFormat_Console, &links));
762 context->setShowHidden(impl_->bHidden_);
763 if (impl_->moduleOverride_ != NULL)
765 context->setModuleDisplayName(impl_->programContext_.displayName());
766 impl_->moduleOverride_->writeHelp(*context);
769 impl_->context_ = context.get();
771 HelpManager helpManager(*impl_->rootTopic_, context->writerContext());
774 for (int i = 1; i < argc; ++i)
776 helpManager.enterTopic(argv[i]);
779 catch (const InvalidInputError &ex)
781 fprintf(stderr, "%s\n", ex.what());
784 helpManager.writeCurrentTopic();
788 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
790 const HelpWriterContext &writerContext = context.writerContext();
792 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
796 writerContext.writeTextBlock(
797 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
798 // TODO: More information.