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