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