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