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     GMX_RELEASE_ASSERT(binaryName_.find('-') == std::string::npos,
310                        "Help export does not currently work with binary names with dashes");
311 }
312
313 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
314 {
315     GMX_ASSERT(modules_.find(module->name()) == modules_.end(),
316                "Attempted to register a duplicate module name");
317     ensureHelpModuleExists();
318     HelpTopicPointer helpTopic(helpModule_->createModuleHelpTopic(*module));
319     modules_.insert(std::make_pair(std::string(module->name()),
320                                    move(module)));
321     helpModule_->addTopic(move(helpTopic));
322 }
323
324 void CommandLineModuleManager::Impl::ensureHelpModuleExists()
325 {
326     if (helpModule_ == NULL)
327     {
328         helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
329                                                 modules_, moduleGroups_);
330         addModule(CommandLineModulePointer(helpModule_));
331     }
332 }
333
334 CommandLineModuleMap::const_iterator
335 CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
336 {
337     // TODO: Accept unambiguous prefixes?
338     return modules_.find(name);
339 }
340
341 CommandLineModuleMap::const_iterator
342 CommandLineModuleManager::Impl::findModuleFromBinaryName(
343         const char *invokedName) const
344 {
345     std::string moduleName = invokedName;
346 #ifdef GMX_BINARY_SUFFIX
347     moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
348 #endif
349     if (moduleName == binaryName_)
350     {
351         return modules_.end();
352     }
353     if (startsWith(moduleName, "g_"))
354     {
355         moduleName.erase(0, 2);
356     }
357     if (startsWith(moduleName, "gmx"))
358     {
359         moduleName.erase(0, 3);
360     }
361     return findModuleByName(moduleName);
362 }
363
364 CommandLineModuleInterface *
365 CommandLineModuleManager::Impl::processCommonOptions(
366         CommandLineCommonOptionsHolder *optionsHolder, int *argc, char ***argv)
367 {
368     // Check if we are directly invoking a certain module.
369     CommandLineModuleInterface *module = singleModule_;
370     if (module == NULL)
371     {
372         // Also check for invokation through named symlinks.
373         CommandLineModuleMap::const_iterator moduleIter
374             = findModuleFromBinaryName(programContext_.programName());
375         if (moduleIter != modules_.end())
376         {
377             module = moduleIter->second.get();
378         }
379     }
380
381     // TODO: It would be nice to propagate at least the -quiet option to
382     // the modules so that they can also be quiet in response to this.
383
384     if (module == NULL)
385     {
386         // If not in single-module mode, process options to the wrapper binary.
387         // TODO: Ideally, this could be done by CommandLineParser.
388         int argcForWrapper = 1;
389         while (argcForWrapper < *argc && (*argv)[argcForWrapper][0] == '-')
390         {
391             ++argcForWrapper;
392         }
393         if (argcForWrapper > 1)
394         {
395             CommandLineParser(optionsHolder->options())
396                 .parse(&argcForWrapper, *argv);
397         }
398         // If no action requested and there is a module specified, process it.
399         if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
400         {
401             const char *moduleName = (*argv)[argcForWrapper];
402             CommandLineModuleMap::const_iterator moduleIter
403                 = findModuleByName(moduleName);
404             if (moduleIter == modules_.end())
405             {
406                 std::string message =
407                     formatString("'%s' is not a GROMACS command.", moduleName);
408                 GMX_THROW(InvalidInputError(message));
409             }
410             module = moduleIter->second.get();
411             *argc -= argcForWrapper;
412             *argv += argcForWrapper;
413             // After this point, argc and argv are the same independent of
414             // which path is taken: (*argv)[0] is the module name.
415         }
416     }
417     if (module != NULL)
418     {
419         if (singleModule_ == NULL)
420         {
421             programContext_.setDisplayName(binaryName_ + " " + module->name());
422         }
423         // Recognize the common options also after the module name.
424         // TODO: It could be nicer to only recognize -h/-hidden if module is not
425         // null.
426         CommandLineParser(optionsHolder->options())
427             .skipUnknown(true).parse(argc, *argv);
428     }
429     if (!optionsHolder->finishOptions())
430     {
431         return NULL;
432     }
433     // If no module specified and no other action, show the help.
434     // Also explicitly specifying -h for the wrapper binary goes here.
435     if (module == NULL || optionsHolder->shouldShowHelp())
436     {
437         ensureHelpModuleExists();
438         if (module != NULL)
439         {
440             helpModule_->setModuleOverride(*module);
441         }
442         *argc  = 1;
443         module = helpModule_;
444     }
445     if (module == helpModule_)
446     {
447         helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
448     }
449     return module;
450 }
451
452 /********************************************************************
453  * CommandLineModuleManager
454  */
455
456 CommandLineModuleManager::CommandLineModuleManager(
457         const char *binaryName, CommandLineProgramContext *programContext)
458     : impl_(new Impl(binaryName, programContext))
459 {
460 }
461
462 CommandLineModuleManager::~CommandLineModuleManager()
463 {
464 }
465
466 void CommandLineModuleManager::setQuiet(bool bQuiet)
467 {
468     impl_->bQuiet_ = bQuiet;
469 }
470
471 void CommandLineModuleManager::setOutputRedirect(File *output)
472 {
473     impl_->ensureHelpModuleExists();
474     impl_->helpModule_->setOutputRedirect(output);
475 }
476
477 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
478 {
479     impl_->singleModule_ = module;
480 }
481
482 void CommandLineModuleManager::addModule(CommandLineModulePointer module)
483 {
484     impl_->addModule(move(module));
485 }
486
487 void CommandLineModuleManager::addModuleCMain(
488         const char *name, const char *shortDescription,
489         CMainFunction mainFunction)
490 {
491     CommandLineModulePointer module(
492             new CMainCommandLineModule(name, shortDescription, mainFunction));
493     addModule(move(module));
494 }
495
496 CommandLineModuleGroup CommandLineModuleManager::addModuleGroup(
497         const char *title)
498 {
499     const char *const                 binaryName = impl_->binaryName_.c_str();
500     CommandLineModuleGroupDataPointer group(
501             new CommandLineModuleGroupData(impl_->modules_, binaryName, title));
502     impl_->moduleGroups_.push_back(move(group));
503     return CommandLineModuleGroup(impl_->moduleGroups_.back().get());
504 }
505
506 void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
507 {
508     impl_->ensureHelpModuleExists();
509     impl_->helpModule_->addTopic(move(topic));
510 }
511
512 int CommandLineModuleManager::run(int argc, char *argv[])
513 {
514     CommandLineModuleInterface    *module;
515     const bool                     bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
516     bool                           bQuiet  = impl_->bQuiet_ || !bMaster;
517     CommandLineCommonOptionsHolder optionsHolder;
518     try
519     {
520         optionsHolder.initOptions();
521         module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
522     }
523     catch (const std::exception &)
524     {
525         bQuiet |= optionsHolder.shouldBeQuiet();
526         if (!bQuiet)
527         {
528             printBinaryInformation(stderr, impl_->programContext_,
529                                    optionsHolder.binaryInfoSettings());
530         }
531         throw;
532     }
533     bQuiet |= optionsHolder.shouldBeQuiet();
534     if (!bQuiet)
535     {
536         FILE *out = optionsHolder.startupInfoFile();
537         printBinaryInformation(out, impl_->programContext_,
538                                optionsHolder.binaryInfoSettings());
539         fprintf(out, "\n");
540     }
541     if (module == NULL)
542     {
543         return 0;
544     }
545     int rc = module->run(argc, argv);
546     if (!bQuiet)
547     {
548         gmx_thanx(stderr);
549     }
550     return rc;
551 }
552
553 // static
554 int CommandLineModuleManager::runAsMainSingleModule(
555         int argc, char *argv[], CommandLineModuleInterface *module)
556 {
557     CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
558     try
559     {
560         CommandLineModuleManager manager(NULL, &programContext);
561         manager.setSingleModule(module);
562         int rc = manager.run(argc, argv);
563         gmx::finalizeForCommandLine();
564         return rc;
565     }
566     catch (const std::exception &ex)
567     {
568         printFatalErrorMessage(stderr, ex);
569         return processExceptionAtExit(ex);
570     }
571 }
572
573 // static
574 int CommandLineModuleManager::runAsMainCMain(
575         int argc, char *argv[], CMainFunction mainFunction)
576 {
577     CMainCommandLineModule module(argv[0], NULL, mainFunction);
578     return runAsMainSingleModule(argc, argv, &module);
579 }
580
581 /********************************************************************
582  * CommandLineModuleGroupData
583  */
584
585 void CommandLineModuleGroupData::addModule(const char *name,
586                                            const char *description)
587 {
588     CommandLineModuleMap::const_iterator moduleIter = allModules_.find(name);
589     GMX_RELEASE_ASSERT(moduleIter != allModules_.end(),
590                        "Non-existent module added to a group");
591     if (description == NULL)
592     {
593         description = moduleIter->second->shortDescription();
594         GMX_RELEASE_ASSERT(description != NULL,
595                            "Module without a description added to a group");
596     }
597     std::string       tag(formatString("%s-%s", binaryName_, name));
598     modules_.push_back(std::make_pair(tag, description));
599 }
600
601 /********************************************************************
602  * CommandLineModuleGroup
603  */
604
605 void CommandLineModuleGroup::addModule(const char *name)
606 {
607     impl_->addModule(name, NULL);
608 }
609
610 void CommandLineModuleGroup::addModuleWithDescription(const char *name,
611                                                       const char *description)
612 {
613     impl_->addModule(name, description);
614 }
615
616 } // namespace gmx