Merge release-4-6 into release-5-0
[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
141 }   // namespace
142
143 /********************************************************************
144  * CommandLineCommonOptionsHolder
145  */
146
147 CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder()
148     : options_(NULL, NULL), bHelp_(false), bHidden_(false),
149       bQuiet_(false), bVersion_(false), bCopyright_(true)
150 {
151     binaryInfoSettings_.copyright(true);
152 }
153
154 CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder()
155 {
156 }
157
158 void CommandLineCommonOptionsHolder::initOptions()
159 {
160     options_.addOption(BooleanOption("h").store(&bHelp_)
161                            .description("Print help and quit"));
162     options_.addOption(BooleanOption("hidden").store(&bHidden_)
163                            .hidden()
164                            .description("Show hidden options in help"));
165     options_.addOption(BooleanOption("quiet").store(&bQuiet_)
166                            .description("Do not print common startup info or quotes"));
167     options_.addOption(BooleanOption("version").store(&bVersion_)
168                            .description("Print extended version information and quit"));
169     options_.addOption(BooleanOption("copyright").store(&bCopyright_)
170                            .description("Print copyright information on startup"));
171 }
172
173 bool CommandLineCommonOptionsHolder::finishOptions()
174 {
175     options_.finish();
176     binaryInfoSettings_.extendedInfo(bVersion_);
177     // The latter condition suppresses the copyright with
178     // -quiet -version.
179     binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
180     return !bVersion_;
181 }
182
183 /********************************************************************
184  * CommandLineModuleManager::Impl
185  */
186
187 /*! \internal \brief
188  * Private implementation class for CommandLineModuleManager.
189  *
190  * \ingroup module_commandline
191  */
192 class CommandLineModuleManager::Impl
193 {
194     public:
195         /*! \brief
196          * Initializes the implementation class.
197          *
198          * \param[in] binaryName     Name of the running binary
199          *     (without Gromacs binary suffix or .exe on Windows).
200          * \param     programContext Program information for the running binary.
201          */
202         Impl(const char *binaryName, CommandLineProgramContext *programContext);
203
204         /*! \brief
205          * Helper method that adds a given module to the module manager.
206          *
207          * \throws    std::bad_alloc if out of memory.
208          */
209         void addModule(CommandLineModulePointer module);
210         /*! \brief
211          * Creates the help module if it does not yet exist.
212          *
213          * \throws    std::bad_alloc if out of memory.
214          *
215          * This method should be called before accessing \a helpModule_.
216          */
217         void ensureHelpModuleExists();
218
219         /*! \brief
220          * Finds a module that matches a name.
221          *
222          * \param[in] name  Module name to find.
223          * \returns   Iterator to the found module, or
224          *      \c modules_.end() if not found.
225          *
226          * Does not throw.
227          */
228         CommandLineModuleMap::const_iterator
229         findModuleByName(const std::string &name) const;
230         /*! \brief
231          * Finds a module that the name of the binary.
232          *
233          * \param[in] invokedName  Name by which the program was invoked.
234          * \throws    std::bad_alloc if out of memory.
235          * \returns   Iterator to the found module, or
236          *      \c modules_.end() if not found.
237          *
238          * Checks whether the program is invoked through a symlink whose name
239          * is different from \a binaryName_, and if so, checks
240          * if a module name matches the name of the symlink.
241          *
242          * Note that the \p invokedName parameter is currently not necessary
243          * (as the program context object is also available and provides this
244          * value), but it clarifies the control flow.
245          */
246         CommandLineModuleMap::const_iterator
247         findModuleFromBinaryName(const char *invokedName) const;
248
249         /*! \brief
250          * Processes command-line options for the wrapper binary.
251          *
252          * \param[in,out] optionsHolder Common options.
253          * \param[in,out] argc          On input, argc passed to run().
254          *     On output, argc to be passed to the module.
255          * \param[in,out] argv          On input, argv passed to run().
256          *     On output, argv to be passed to the module.
257          * \throws    InvalidInputError if there are invalid options.
258          * \returns   The module that should be run.
259          *
260          * Handles command-line options that affect the wrapper binary
261          * (potentially changing the members of \c this in response to the
262          * options).  Also finds the module that should be run and the
263          * arguments that should be passed to it.
264          */
265         CommandLineModuleInterface *
266         processCommonOptions(CommandLineCommonOptionsHolder *optionsHolder,
267                              int *argc, char ***argv);
268
269         /*! \brief
270          * Maps module names to module objects.
271          *
272          * Owns the contained modules.
273          */
274         CommandLineModuleMap         modules_;
275         /*! \brief
276          * List of groupings for modules for help output.
277          *
278          * Owns the contained module group data objects.
279          * CommandLineModuleGroup objects point to the data objects contained
280          * here.
281          */
282         CommandLineModuleGroupList   moduleGroups_;
283         //! Information about the currently running program.
284         CommandLineProgramContext   &programContext_;
285         //! Name of the binary.
286         std::string                  binaryName_;
287         /*! \brief
288          * Module that implements help for the binary.
289          *
290          * The pointed module is owned by the \a modules_ container.
291          */
292         CommandLineHelpModule       *helpModule_;
293         //! If non-NULL, run this module in single-module mode.
294         CommandLineModuleInterface  *singleModule_;
295         //! Stores the value set with setQuiet().
296         bool                         bQuiet_;
297
298     private:
299         GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
300 };
301
302 CommandLineModuleManager::Impl::Impl(const char                *binaryName,
303                                      CommandLineProgramContext *programContext)
304     : programContext_(*programContext),
305       binaryName_(binaryName != NULL ? binaryName : ""),
306       helpModule_(NULL), singleModule_(NULL),
307       bQuiet_(false)
308 {
309 }
310
311 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
312 {
313     GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
314                "Attempted to register a duplicate module name");
315     ensureHelpModuleExists();
316     HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
317     modules_.insert(std::make_pair(std::string(module->name()),
318                                    move(module)));
319     helpModule_->addTopic(move(helpTopic));
320 }
321
322 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
323 {
324     if (helpModule_ == NULL)
325     {
326         helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
327                                                 modules_, moduleGroups_);
328         addModule(CommandLineModulePointer(helpModule_));
329     }
330 }
331
332 CommandLineModuleMap::const_iterator
333 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
334 {
335     // TODO: Accept unambiguous prefixes?
336     return modules_.find(name);
337 }
338
339 CommandLineModuleMap::const_iterator
340 CommandLineModuleManager::Impl::findModuleFromBinaryName(
341         const char *invokedName) const
342 {
343     std::string moduleName = invokedName;
344 #ifdef GMX_BINARY_SUFFIX
345     moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
346 #endif
347     if (moduleName == binaryName_)
348     {
349         return modules_.end();
350     }
351     if (startsWith(moduleName, "g_"))
352     {
353         moduleName.erase(0, 2);
354     }
355     if (startsWith(moduleName, "gmx"))
356     {
357         moduleName.erase(0, 3);
358     }
359     return findModuleByName(moduleName);
360 }
361
362 CommandLineModuleInterface *
363 CommandLineModuleManager::Impl::processCommonOptions(
364         CommandLineCommonOptionsHolder *optionsHolder, int *argc, char ***argv)
365 {
366     // Check if we are directly invoking a certain module.
367     CommandLineModuleInterface *module = singleModule_;
368     if (module == NULL)
369     {
370         // Also check for invokation through named symlinks.
371         CommandLineModuleMap::const_iterator moduleIter
372             = findModuleFromBinaryName(programContext_.programName());
373         if (moduleIter != modules_.end())
374         {
375             module = moduleIter->second.get();
376         }
377     }
378
379     // TODO: It would be nice to propagate at least the -quiet option to
380     // the modules so that they can also be quiet in response to this.
381
382     if (module == NULL)
383     {
384         // If not in single-module mode, process options to the wrapper binary.
385         // TODO: Ideally, this could be done by CommandLineParser.
386         int argcForWrapper = 1;
387         while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
388         {
389             ++argcForWrapper;
390         }
391         if (argcForWrapper > 1)
392         {
393             CommandLineParser(optionsHolder->options())
394                 .parse(&argcForWrapper, *argv);
395         }
396         // If no action requested and there is a module specified, process it.
397         if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
398         {
399             const char *moduleName = (*argv)[argcForWrapper];
400             CommandLineModuleMap::const_iterator moduleIter
401                 = findModuleByName(moduleName);
402             if (moduleIter == modules_.end())
403             {
404                 std::string message =
405                     formatString("'%s' is not a GROMACS command.", moduleName);
406                 GMX_THROW(InvalidInputError(message));
407             }
408             module = moduleIter->second.get();
409             *argc -= argcForWrapper;
410             *argv += argcForWrapper;
411             // After this point, argc and argv are the same independent of
412             // which path is taken: (*argv)[0] is the module name.
413         }
414     }
415     if (module != NULL)
416     {
417         if (singleModule_ == NULL)
418         {
419             programContext_.setDisplayName(binaryName_ + " " + module->name());
420         }
421         // Recognize the common options also after the module name.
422         // TODO: It could be nicer to only recognize -h/-hidden if module is not
423         // null.
424         CommandLineParser(optionsHolder->options())
425             .skipUnknown(true).parse(argc, *argv);
426     }
427     if (!optionsHolder->finishOptions())
428     {
429         return NULL;
430     }
431     // If no module specified and no other action, show the help.
432     // Also explicitly specifying -h for the wrapper binary goes here.
433     if (module == NULL || optionsHolder->shouldShowHelp())
434     {
435         ensureHelpModuleExists();
436         if (module != NULL)
437         {
438             helpModule_->setModuleOverride(*module);
439         }
440         *argc  = 1;
441         module = helpModule_;
442     }
443     if (module == helpModule_)
444     {
445         helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
446     }
447     return module;
448 }
449
450 /********************************************************************
451  * CommandLineModuleManager
452  */
453
454 CommandLineModuleManager::CommandLineModuleManager(
455         const char *binaryName, CommandLineProgramContext *programContext)
456     : impl_(new Impl(binaryName, programContext))
457 {
458 }
459
460 CommandLineModuleManager::~CommandLineModuleManager()
461 {
462 }
463
464 void CommandLineModuleManager::setQuiet(bool bQuiet)
465 {
466     impl_->bQuiet_ = bQuiet;
467 }
468
469 void CommandLineModuleManager::setOutputRedirect(File *output)
470 {
471     impl_->ensureHelpModuleExists();
472     impl_->helpModule_->setOutputRedirect(output);
473 }
474
475 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
476 {
477     impl_->singleModule_ = module;
478 }
479
480 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
481 {
482     impl_->addModule(move(module));
483 }
484
485 void CommandLineModuleManager::addModuleCMain(
486         const char *name, const char *shortDescription,
487         CMainFunction mainFunction)
488 {
489     CommandLineModulePointer module(
490             new CMainCommandLineModule(name, shortDescription, mainFunction));
491     addModule(move(module));
492 }
493
494 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
495         const char *title)
496 {
497     const char *const                 binaryName = impl_->binaryName_.c_str();
498     CommandLineModuleGroupDataPointer group(
499             new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
500     impl_->moduleGroups_.push_back(move(group));
501     return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
502 }
503
504 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
505 {
506     impl_->ensureHelpModuleExists();
507     impl_->helpModule_->addTopic(move(topic));
508 }
509
510 int CommandLineModuleManager::run(int argc, char *argv[])
511 {
512     CommandLineModuleInterface    *module;
513     const bool                     bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
514     bool                           bQuiet  = impl_->bQuiet_ || !bMaster;
515     CommandLineCommonOptionsHolder optionsHolder;
516     try
517     {
518         optionsHolder.initOptions();
519         module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
520     }
521     catch (const std::exception &)
522     {
523         bQuiet |= optionsHolder.shouldBeQuiet();
524         if (!bQuiet)
525         {
526             printBinaryInformation(stderr, impl_->programContext_,
527                                    optionsHolder.binaryInfoSettings());
528         }
529         throw;
530     }
531     bQuiet |= optionsHolder.shouldBeQuiet();
532     if (!bQuiet)
533     {
534         FILE *out = optionsHolder.startupInfoFile();
535         printBinaryInformation(out, impl_->programContext_,
536                                optionsHolder.binaryInfoSettings());
537         fprintf(out, "\n");
538     }
539     if (module == NULL)
540     {
541         return 0;
542     }
543     int rc = module->run(argc, argv);
544     if (!bQuiet)
545     {
546         gmx_thanx(stderr);
547     }
548     return rc;
549 }
550
551 // static
552 int CommandLineModuleManager::runAsMainSingleModule(
553         int argc, char *argv[], CommandLineModuleInterface *module)
554 {
555     CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
556     try
557     {
558         CommandLineModuleManager manager(NULL, &programContext);
559         manager.setSingleModule(module);
560         int rc = manager.run(argc, argv);
561         gmx::finalizeForCommandLine();
562         return rc;
563     }
564     catch (const std::exception &ex)
565     {
566         printFatalErrorMessage(stderr, ex);
567         return processExceptionAtExit(ex);
568     }
569 }
570
571 // static
572 int CommandLineModuleManager::runAsMainCMain(
573         int argc, char *argv[], CMainFunction mainFunction)
574 {
575     CMainCommandLineModule module(argv[0], NULL, mainFunction);
576     return runAsMainSingleModule(argc, argv, &module);
577 }
578
579 /********************************************************************
580  * CommandLineModuleGroupData
581  */
582
583 void CommandLineModuleGroupData::addModule(const char *name,
584                                            const char *description)
585 {
586     CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
587     GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
588                        "Non-existent module added to a group");
589     if (description == NULL)
590     {
591         description = moduleIter->second->shortDescription();
592         GMX_RELEASE_ASSERT(description != NULL,
593                            "Module without a description added to a group");
594     }
595     std::string       tag(formatString("%s-%s", binaryName_, name));
596     modules_.push_back(std::make_pair(tag, description));
597 }
598
599 /********************************************************************
600  * CommandLineModuleGroup
601  */
602
603 void CommandLineModuleGroup::addModule(const char *name)
604 {
605     impl_->addModule(name, NULL);
606 }
607
608 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
609                                                       const char *description)
610 {
611     impl_->addModule(name, description);
612 }
613
614 } // namespace gmx