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