Rename ProgramInfo to CommandLineProgramContext
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinemodulemanager.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements gmx::CommandLineModuleManager.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_commandline
41  */
42 #include "cmdlinemodulemanager.h"
43
44 #include <cstdio>
45
46 #include <string>
47 #include <utility>
48
49 #include "gromacs/legacyheaders/copyrite.h"
50 #include "gromacs/legacyheaders/network.h"
51
52 #include "gromacs/commandline/cmdlinehelpcontext.h"
53 #include "gromacs/commandline/cmdlinehelpmodule.h"
54 #include "gromacs/commandline/cmdlineinit.h"
55 #include "gromacs/commandline/cmdlinemodule.h"
56 #include "gromacs/commandline/cmdlinemodulemanager-impl.h"
57 #include "gromacs/commandline/cmdlineparser.h"
58 #include "gromacs/commandline/cmdlineprogramcontext.h"
59 #include "gromacs/options/basicoptions.h"
60 #include "gromacs/options/options.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/stringutil.h"
64
65 // For GMX_BINARY_SUFFIX
66 #include "config.h"
67
68 namespace gmx
69 {
70
71 namespace
72 {
73
74 //! \addtogroup module_commandline
75 //! \{
76
77 /********************************************************************
78  * CMainCommandLineModule
79  */
80
81 /*! \brief
82  * Implements a CommandLineModuleInterface, given a function with C/C++ main()
83  * signature.
84  */
85 class CMainCommandLineModule : public CommandLineModuleInterface
86 {
87     public:
88         //! \copydoc gmx::CommandLineModuleManager::CMainFunction
89         typedef CommandLineModuleManager::CMainFunction CMainFunction;
90
91         /*! \brief
92          * Creates a wrapper module for the given main function.
93          *
94          * \param[in] name             Name for the module.
95          * \param[in] shortDescription One-line description for the module.
96          * \param[in] mainFunction     Main function to wrap.
97          *
98          * Does not throw.  This is essential for correct implementation of
99          * CommandLineModuleManager::runAsMainCMain().
100          */
101         CMainCommandLineModule(const char *name, const char *shortDescription,
102                                CMainFunction mainFunction)
103             : name_(name), shortDescription_(shortDescription),
104               mainFunction_(mainFunction)
105         {
106         }
107
108         virtual const char *name() const
109         {
110             return name_;
111         }
112         virtual const char *shortDescription() const
113         {
114             return shortDescription_;
115         }
116
117         virtual int run(int argc, char *argv[])
118         {
119             return mainFunction_(argc, argv);
120         }
121         virtual void writeHelp(const CommandLineHelpContext &context) const
122         {
123             char *argv[2];
124             int   argc = 1;
125             // TODO: The constness should not be cast away.
126             argv[0] = const_cast<char *>(name_);
127             argv[1] = NULL;
128             GlobalCommandLineHelpContext global(context);
129             mainFunction_(argc, argv);
130         }
131
132     private:
133         const char             *name_;
134         const char             *shortDescription_;
135         CMainFunction           mainFunction_;
136
137 };
138
139 /********************************************************************
140  * CommonOptionsHolder
141  */
142
143 /*! \brief
144  * Encapsulates some handling of common options to the wrapper binary.
145  */
146 class CommonOptionsHolder
147 {
148     public:
149         CommonOptionsHolder()
150             : options_(NULL, NULL), bHelp_(false), bHidden_(false),
151               bQuiet_(false), bVersion_(false), bCopyright_(true)
152         {
153             binaryInfoSettings_.copyright(true);
154         }
155
156         //! Initializes the common options.
157         void initOptions()
158         {
159             options_.addOption(BooleanOption("h").store(&bHelp_)
160                                    .description("Print help and quit"));
161             options_.addOption(BooleanOption("hidden").store(&bHidden_)
162                                    .hidden()
163                                    .description("Show hidden options in help"));
164             options_.addOption(BooleanOption("quiet").store(&bQuiet_)
165                                    .description("Do not print common startup info or quotes"));
166             options_.addOption(BooleanOption("version").store(&bVersion_)
167                                    .description("Print extended version information and quit"));
168             options_.addOption(BooleanOption("copyright").store(&bCopyright_)
169                                    .description("Print copyright information on startup"));
170         }
171
172         /*! \brief
173          * Finishes option parsing.
174          *
175          * \returns `false` if the wrapper binary should quit without executing
176          *     any module.
177          */
178         bool finishOptions()
179         {
180             options_.finish();
181             binaryInfoSettings_.extendedInfo(bVersion_);
182             // The latter condition suppresses the copyright with
183             // -quiet -version.
184             binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
185             return !bVersion_;
186         }
187
188         //! Returns the internal Options object.
189         Options *options() { return &options_; }
190         //! Returns the settings for printing startup information.
191         const BinaryInformationSettings &binaryInfoSettings() const
192         {
193             return binaryInfoSettings_;
194         }
195
196         /*! \brief
197          * Returns `true` if common options are set such that the wrapper
198          * binary should quit, without running the actual module.
199          */
200         bool shouldIgnoreActualModule() const
201         {
202             return bHelp_ || bVersion_;
203         }
204         //! Returns whether common options specify showing help.
205         bool shouldShowHelp() const { return bHelp_; }
206         //! Returns whether common options specify showing hidden options in help.
207         bool shouldShowHidden() const { return bHidden_; }
208         //! Returns whether common options specify quiet execution.
209         bool shouldBeQuiet() const
210         {
211             return bQuiet_ && !bVersion_;
212         }
213
214         //! Returns the file to which startup information should be printed.
215         FILE *startupInfoFile() const { return (bVersion_ ? stdout : stderr); }
216
217     private:
218         Options                      options_;
219         //! Settings for what to write in the startup header.
220         BinaryInformationSettings    binaryInfoSettings_;
221         bool                         bHelp_;
222         bool                         bHidden_;
223         bool                         bQuiet_;
224         bool                         bVersion_;
225         bool                         bCopyright_;
226 };
227
228 //! \}
229
230 }   // namespace
231
232 /********************************************************************
233  * CommandLineModuleManager::Impl
234  */
235
236 /*! \internal \brief
237  * Private implementation class for CommandLineModuleManager.
238  *
239  * \ingroup module_commandline
240  */
241 class CommandLineModuleManager::Impl
242 {
243     public:
244         /*! \brief
245          * Initializes the implementation class.
246          *
247          * \param[in] binaryName     Name of the running binary
248          *     (without Gromacs binary suffix or .exe on Windows).
249          * \param     programContext Program information for the running binary.
250          */
251         Impl(const char *binaryName, CommandLineProgramContext *programContext);
252
253         /*! \brief
254          * Helper method that adds a given module to the module manager.
255          *
256          * \throws    std::bad_alloc if out of memory.
257          */
258         void addModule(CommandLineModulePointer module);
259         /*! \brief
260          * Creates the help module if it does not yet exist.
261          *
262          * \throws    std::bad_alloc if out of memory.
263          *
264          * This method should be called before accessing \a helpModule_.
265          */
266         void ensureHelpModuleExists();
267
268         /*! \brief
269          * Finds a module that matches a name.
270          *
271          * \param[in] name  Module name to find.
272          * \returns   Iterator to the found module, or
273          *      \c modules_.end() if not found.
274          *
275          * Does not throw.
276          */
277         CommandLineModuleMap::const_iterator
278         findModuleByName(const std::string &name) const;
279         /*! \brief
280          * Finds a module that the name of the binary.
281          *
282          * \param[in] invokedName  Name by which the program was invoked.
283          * \throws    std::bad_alloc if out of memory.
284          * \returns   Iterator to the found module, or
285          *      \c modules_.end() if not found.
286          *
287          * Checks whether the program is invoked through a symlink whose name
288          * is different from \a binaryName_, and if so, checks
289          * if a module name matches the name of the symlink.
290          *
291          * Note that the \p invokedName parameter is currently not necessary
292          * (as the program context object is also available and provides this
293          * value), but it clarifies the control flow.
294          */
295         CommandLineModuleMap::const_iterator
296         findModuleFromBinaryName(const char *invokedName) const;
297
298         /*! \brief
299          * Processes command-line options for the wrapper binary.
300          *
301          * \param[in,out] optionsHolder Common options.
302          * \param[in,out] argc          On input, argc passed to run().
303          *     On output, argc to be passed to the module.
304          * \param[in,out] argv          On input, argv passed to run().
305          *     On output, argv to be passed to the module.
306          * \throws    InvalidInputError if there are invalid options.
307          * \returns   The module that should be run.
308          *
309          * Handles command-line options that affect the wrapper binary
310          * (potentially changing the members of \c this in response to the
311          * options).  Also finds the module that should be run and the
312          * arguments that should be passed to it.
313          */
314         CommandLineModuleInterface *
315         processCommonOptions(CommonOptionsHolder *optionsHolder,
316                              int *argc, char ***argv);
317
318         /*! \brief
319          * Maps module names to module objects.
320          *
321          * Owns the contained modules.
322          */
323         CommandLineModuleMap         modules_;
324         /*! \brief
325          * List of groupings for modules for help output.
326          *
327          * Owns the contained module group data objects.
328          * CommandLineModuleGroup objects point to the data objects contained
329          * here.
330          */
331         CommandLineModuleGroupList   moduleGroups_;
332         //! Information about the currently running program.
333         CommandLineProgramContext   &programContext_;
334         //! Name of the binary.
335         std::string                  binaryName_;
336         /*! \brief
337          * Module that implements help for the binary.
338          *
339          * The pointed module is owned by the \a modules_ container.
340          */
341         CommandLineHelpModule       *helpModule_;
342         //! If non-NULL, run this module in single-module mode.
343         CommandLineModuleInterface  *singleModule_;
344         //! Stores the value set with setQuiet().
345         bool                         bQuiet_;
346
347     private:
348         GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
349 };
350
351 CommandLineModuleManager::Impl::Impl(const char                *binaryName,
352                                      CommandLineProgramContext *programContext)
353     : programContext_(*programContext),
354       binaryName_(binaryName != NULL ? binaryName : ""),
355       helpModule_(NULL), singleModule_(NULL),
356       bQuiet_(false)
357 {
358 }
359
360 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
361 {
362     GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
363                "Attempted to register a duplicate module name");
364     ensureHelpModuleExists();
365     HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
366     modules_.insert(std::make_pair(std::string(module->name()),
367                                    move(module)));
368     helpModule_->addTopic(move(helpTopic));
369 }
370
371 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
372 {
373     if (helpModule_ == NULL)
374     {
375         helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
376                                                 modules_, moduleGroups_);
377         addModule(CommandLineModulePointer(helpModule_));
378     }
379 }
380
381 CommandLineModuleMap::const_iterator
382 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
383 {
384     // TODO: Accept unambiguous prefixes?
385     return modules_.find(name);
386 }
387
388 CommandLineModuleMap::const_iterator
389 CommandLineModuleManager::Impl::findModuleFromBinaryName(
390         const char *invokedName) const
391 {
392     std::string moduleName = invokedName;
393 #ifdef GMX_BINARY_SUFFIX
394     moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
395 #endif
396     if (moduleName == binaryName_)
397     {
398         return modules_.end();
399     }
400     if (startsWith(moduleName, "g_"))
401     {
402         moduleName.erase(0, 2);
403     }
404     if (startsWith(moduleName, "gmx"))
405     {
406         moduleName.erase(0, 3);
407     }
408     return findModuleByName(moduleName);
409 }
410
411 CommandLineModuleInterface *
412 CommandLineModuleManager::Impl::processCommonOptions(
413         CommonOptionsHolder *optionsHolder, int *argc, char ***argv)
414 {
415     // Check if we are directly invoking a certain module.
416     CommandLineModuleInterface *module = singleModule_;
417     if (module == NULL)
418     {
419         // Also check for invokation through named symlinks.
420         CommandLineModuleMap::const_iterator moduleIter
421             = findModuleFromBinaryName(programContext_.programName());
422         if (moduleIter != modules_.end())
423         {
424             module = moduleIter->second.get();
425         }
426     }
427
428     // TODO: It would be nice to propagate at least the -quiet option to
429     // the modules so that they can also be quiet in response to this.
430
431     if (module == NULL)
432     {
433         // If not in single-module mode, process options to the wrapper binary.
434         // TODO: Ideally, this could be done by CommandLineParser.
435         int argcForWrapper = 1;
436         while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
437         {
438             ++argcForWrapper;
439         }
440         if (argcForWrapper > 1)
441         {
442             CommandLineParser(optionsHolder->options())
443                 .parse(&argcForWrapper, *argv);
444         }
445         // If no action requested and there is a module specified, process it.
446         if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
447         {
448             const char *moduleName = (*argv)[argcForWrapper];
449             CommandLineModuleMap::const_iterator moduleIter
450                 = findModuleByName(moduleName);
451             if (moduleIter == modules_.end())
452             {
453                 std::string message =
454                     formatString("'%s' is not a GROMACS command.", moduleName);
455                 GMX_THROW(InvalidInputError(message));
456             }
457             module = moduleIter->second.get();
458             *argc -= argcForWrapper;
459             *argv += argcForWrapper;
460             // After this point, argc and argv are the same independent of
461             // which path is taken: (*argv)[0] is the module name.
462         }
463     }
464     if (module != NULL)
465     {
466         if (singleModule_ == NULL)
467         {
468             programContext_.setDisplayName(binaryName_ + " " + module->name());
469         }
470         // Recognize the common options also after the module name.
471         // TODO: It could be nicer to only recognize -h/-hidden if module is not
472         // null.
473         CommandLineParser(optionsHolder->options())
474             .skipUnknown(true).parse(argc, *argv);
475     }
476     if (!optionsHolder->finishOptions())
477     {
478         return NULL;
479     }
480     // If no module specified and no other action, show the help.
481     // Also explicitly specifying -h for the wrapper binary goes here.
482     if (module == NULL || optionsHolder->shouldShowHelp())
483     {
484         ensureHelpModuleExists();
485         if (module != NULL)
486         {
487             helpModule_->setModuleOverride(*module);
488         }
489         *argc  = 1;
490         module = helpModule_;
491     }
492     if (module == helpModule_)
493     {
494         helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
495         helpModule_->setCommonOptions(optionsHolder->options());
496     }
497     return module;
498 }
499
500 /********************************************************************
501  * CommandLineModuleManager
502  */
503
504 CommandLineModuleManager::CommandLineModuleManager(
505         const char *binaryName, CommandLineProgramContext *programContext)
506     : impl_(new Impl(binaryName, programContext))
507 {
508 }
509
510 CommandLineModuleManager::~CommandLineModuleManager()
511 {
512 }
513
514 void CommandLineModuleManager::setQuiet(bool bQuiet)
515 {
516     impl_->bQuiet_ = bQuiet;
517 }
518
519 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
520 {
521     impl_->singleModule_ = module;
522 }
523
524 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
525 {
526     impl_->addModule(move(module));
527 }
528
529 void CommandLineModuleManager::addModuleCMain(
530         const char *name, const char *shortDescription,
531         CMainFunction mainFunction)
532 {
533     CommandLineModulePointer module(
534             new CMainCommandLineModule(name, shortDescription, mainFunction));
535     addModule(move(module));
536 }
537
538 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
539         const char *title)
540 {
541     const char *const                 binaryName = impl_->binaryName_.c_str();
542     CommandLineModuleGroupDataPointer group(
543             new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
544     impl_->moduleGroups_.push_back(move(group));
545     return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
546 }
547
548 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
549 {
550     impl_->ensureHelpModuleExists();
551     impl_->helpModule_->addTopic(move(topic));
552 }
553
554 int CommandLineModuleManager::run(int argc, char *argv[])
555 {
556     CommandLineModuleInterface *module;
557     const bool                  bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
558     bool                        bQuiet  = impl_->bQuiet_ || !bMaster;
559     CommonOptionsHolder         optionsHolder;
560     try
561     {
562         optionsHolder.initOptions();
563         module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
564     }
565     catch (const std::exception &)
566     {
567         bQuiet |= optionsHolder.shouldBeQuiet();
568         if (!bQuiet)
569         {
570             printBinaryInformation(stderr, impl_->programContext_,
571                                    optionsHolder.binaryInfoSettings());
572         }
573         throw;
574     }
575     bQuiet |= optionsHolder.shouldBeQuiet();
576     if (!bQuiet)
577     {
578         FILE *out = optionsHolder.startupInfoFile();
579         printBinaryInformation(out, impl_->programContext_,
580                                optionsHolder.binaryInfoSettings());
581         fprintf(out, "\n");
582     }
583     if (module == NULL)
584     {
585         return 0;
586     }
587     int rc = module->run(argc, argv);
588     if (!bQuiet)
589     {
590         gmx_thanx(stderr);
591     }
592     return rc;
593 }
594
595 // static
596 int CommandLineModuleManager::runAsMainSingleModule(
597         int argc, char *argv[], CommandLineModuleInterface *module)
598 {
599     CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
600     try
601     {
602         CommandLineModuleManager manager(NULL, &programContext);
603         manager.setSingleModule(module);
604         int rc = manager.run(argc, argv);
605         gmx::finalizeForCommandLine();
606         return rc;
607     }
608     catch (const std::exception &ex)
609     {
610         printFatalErrorMessage(stderr, ex);
611         return processExceptionAtExit(ex);
612     }
613 }
614
615 // static
616 int CommandLineModuleManager::runAsMainCMain(
617         int argc, char *argv[], CMainFunction mainFunction)
618 {
619     CMainCommandLineModule module(argv[0], NULL, mainFunction);
620     return runAsMainSingleModule(argc, argv, &module);
621 }
622
623 /********************************************************************
624  * CommandLineModuleGroupData
625  */
626
627 void CommandLineModuleGroupData::addModule(const char *name,
628                                            const char *description)
629 {
630     CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
631     GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
632                        "Non-existent module added to a group");
633     if (description == NULL)
634     {
635         description = moduleIter->second->shortDescription();
636         GMX_RELEASE_ASSERT(description != NULL,
637                            "Module without a description added to a group");
638     }
639     std::string       tag(formatString("%s-%s", binaryName_, name));
640     modules_.push_back(std::make_pair(tag, description));
641 }
642
643 /********************************************************************
644  * CommandLineModuleGroup
645  */
646
647 void CommandLineModuleGroup::addModule(const char *name)
648 {
649     impl_->addModule(name, NULL);
650 }
651
652 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
653                                                       const char *description)
654 {
655     impl_->addModule(name, description);
656 }
657
658 } // namespace gmx