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