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