Allow runAsMainSingleModule to be called more than once
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinemodulemanager.cpp
index 25b78d5d1544f656fab5744006a5861bfb7d99e8..74113cc53126c15e5eeef8b9c595c96674c67766 100644 (file)
  * \author Teemu Murtola <teemu.murtola@gmail.com>
  * \ingroup module_commandline
  */
+#include "gmxpre.h"
+
 #include "cmdlinemodulemanager.h"
 
+#include "config.h"
+
 #include <cstdio>
 
 #include <string>
 #include <utility>
 
-#include "gromacs/legacyheaders/copyrite.h"
-#include "gromacs/legacyheaders/network.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
 #include "gromacs/commandline/cmdlinehelpcontext.h"
-#include "gromacs/commandline/cmdlinehelpmodule.h"
+#include "gromacs/commandline/cmdlineinit.h"
 #include "gromacs/commandline/cmdlinemodule.h"
-#include "gromacs/commandline/cmdlinemodulemanager-impl.h"
 #include "gromacs/commandline/cmdlineparser.h"
+#include "gromacs/commandline/cmdlineprogramcontext.h"
+#include "gromacs/legacyheaders/copyrite.h"
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/options.h"
+#include "gromacs/utility/basenetwork.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/init.h"
-#include "gromacs/utility/programinfo.h"
 #include "gromacs/utility/stringutil.h"
 
-// For GMX_BINARY_SUFFIX
-#include "config.h"
+#include "cmdlinehelpmodule.h"
+#include "cmdlinemodulemanager-impl.h"
 
 namespace gmx
 {
@@ -71,15 +77,16 @@ namespace gmx
 namespace
 {
 
+//! \addtogroup module_commandline
+//! \{
+
 /********************************************************************
  * CMainCommandLineModule
  */
 
-/*! \internal \brief
+/*! \brief
  * Implements a CommandLineModuleInterface, given a function with C/C++ main()
  * signature.
- *
- * \ingroup module_commandline
  */
 class CMainCommandLineModule : public CommandLineModuleInterface
 {
@@ -113,30 +120,84 @@ class CMainCommandLineModule : public CommandLineModuleInterface
             return shortDescription_;
         }
 
+        virtual void init(CommandLineModuleSettings * /*settings*/)
+        {
+        }
         virtual int run(int argc, char *argv[])
         {
             return mainFunction_(argc, argv);
         }
         virtual void writeHelp(const CommandLineHelpContext &context) const
         {
-            char *argv[2];
-            int   argc = 1;
-            // TODO: The constness should not be cast away.
-            argv[0] = const_cast<char *>(name_);
-            argv[1] = NULL;
-            GlobalCommandLineHelpContext global(context);
-            mainFunction_(argc, argv);
+            writeCommandLineHelpCMain(context, name_, mainFunction_);
         }
 
     private:
         const char             *name_;
         const char             *shortDescription_;
         CMainFunction           mainFunction_;
-
 };
 
+//! \}
+
 }   // namespace
 
+/********************************************************************
+ * CommandLineCommonOptionsHolder
+ */
+
+CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder()
+    : options_(NULL, NULL), bHelp_(false), bHidden_(false),
+      bQuiet_(false), bVersion_(false), bCopyright_(true),
+      niceLevel_(19), debugLevel_(0)
+{
+    binaryInfoSettings_.copyright(true);
+}
+
+CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder()
+{
+}
+
+void CommandLineCommonOptionsHolder::initOptions()
+{
+    options_.addOption(BooleanOption("h").store(&bHelp_)
+                           .description("Print help and quit"));
+    options_.addOption(BooleanOption("hidden").store(&bHidden_)
+                           .hidden()
+                           .description("Show hidden options in help"));
+    options_.addOption(BooleanOption("quiet").store(&bQuiet_)
+                           .description("Do not print common startup info or quotes"));
+    options_.addOption(BooleanOption("version").store(&bVersion_)
+                           .description("Print extended version information and quit"));
+    options_.addOption(BooleanOption("copyright").store(&bCopyright_)
+                           .description("Print copyright information on startup"));
+    options_.addOption(IntegerOption("nice").store(&niceLevel_)
+                           .description("Set the nicelevel (default depends on command)"));
+    options_.addOption(IntegerOption("debug").store(&debugLevel_)
+                           .hidden().defaultValueIfSet(1)
+                           .description("Write file with debug information, "
+                                        "1: short (default), 2: also x and f"));
+}
+
+bool CommandLineCommonOptionsHolder::finishOptions()
+{
+    options_.finish();
+    binaryInfoSettings_.extendedInfo(bVersion_);
+    // The latter condition suppresses the copyright with
+    // -quiet -version.
+    binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
+    return !bVersion_;
+}
+
+void CommandLineCommonOptionsHolder::adjustFromSettings(
+        const CommandLineModuleSettings &settings)
+{
+    if (!options_.isSet("nice"))
+    {
+        niceLevel_ = settings.defaultNiceLevel();
+    }
+}
+
 /********************************************************************
  * CommandLineModuleManager::Impl
  */
@@ -152,11 +213,11 @@ class CommandLineModuleManager::Impl
         /*! \brief
          * Initializes the implementation class.
          *
-         * \param[in] binaryName   Name of the running binary
+         * \param[in] binaryName     Name of the running binary
          *     (without Gromacs binary suffix or .exe on Windows).
-         * \param     programInfo  Program information for the running binary.
+         * \param     programContext Program information for the running binary.
          */
-        Impl(const char *binaryName, ProgramInfo *programInfo);
+        Impl(const char *binaryName, CommandLineProgramContext *programContext);
 
         /*! \brief
          * Helper method that adds a given module to the module manager.
@@ -187,7 +248,7 @@ class CommandLineModuleManager::Impl
         /*! \brief
          * Finds a module that the name of the binary.
          *
-         * \param[in] programInfo  Program information object to use.
+         * \param[in] invokedName  Name by which the program was invoked.
          * \throws    std::bad_alloc if out of memory.
          * \returns   Iterator to the found module, or
          *      \c modules_.end() if not found.
@@ -196,19 +257,20 @@ class CommandLineModuleManager::Impl
          * is different from \a binaryName_, and if so, checks
          * if a module name matches the name of the symlink.
          *
-         * Note that the \p programInfo parameter is currently not necessary
-         * (as the program info object is also contained as a member), but it
-         * clarifies the control flow.
+         * Note that the \p invokedName parameter is currently not necessary
+         * (as the program context object is also available and provides this
+         * value), but it clarifies the control flow.
          */
         CommandLineModuleMap::const_iterator
-        findModuleFromBinaryName(const ProgramInfo &programInfo) const;
+        findModuleFromBinaryName(const char *invokedName) const;
 
         /*! \brief
          * Processes command-line options for the wrapper binary.
          *
-         * \param[in,out] argc On input, argc passed to run().
+         * \param[in,out] optionsHolder Common options.
+         * \param[in,out] argc          On input, argc passed to run().
          *     On output, argc to be passed to the module.
-         * \param[in,out] argv On input, argv passed to run().
+         * \param[in,out] argv          On input, argv passed to run().
          *     On output, argv to be passed to the module.
          * \throws    InvalidInputError if there are invalid options.
          * \returns   The module that should be run.
@@ -219,7 +281,8 @@ class CommandLineModuleManager::Impl
          * arguments that should be passed to it.
          */
         CommandLineModuleInterface *
-        processCommonOptions(int *argc, char ***argv);
+        processCommonOptions(CommandLineCommonOptionsHolder *optionsHolder,
+                             int *argc, char ***argv);
 
         /*! \brief
          * Maps module names to module objects.
@@ -236,7 +299,7 @@ class CommandLineModuleManager::Impl
          */
         CommandLineModuleGroupList   moduleGroups_;
         //! Information about the currently running program.
-        ProgramInfo                 &programInfo_;
+        CommandLineProgramContext   &programContext_;
         //! Name of the binary.
         std::string                  binaryName_;
         /*! \brief
@@ -245,27 +308,24 @@ class CommandLineModuleManager::Impl
          * The pointed module is owned by the \a modules_ container.
          */
         CommandLineHelpModule       *helpModule_;
-        //! Settings for what to write in the startup header.
-        BinaryInformationSettings    binaryInfoSettings_;
         //! If non-NULL, run this module in single-module mode.
         CommandLineModuleInterface  *singleModule_;
-        //! Whether all stderr output should be suppressed.
+        //! Stores the value set with setQuiet().
         bool                         bQuiet_;
-        //! Whether to write the startup information to stdout iso stderr.
-        bool                         bStdOutInfo_;
 
     private:
         GMX_DISALLOW_COPY_AND_ASSIGN(Impl);
 };
 
-CommandLineModuleManager::Impl::Impl(const char  *binaryName,
-                                     ProgramInfo *programInfo)
-    : programInfo_(*programInfo),
+CommandLineModuleManager::Impl::Impl(const char                *binaryName,
+                                     CommandLineProgramContext *programContext)
+    : programContext_(*programContext),
       binaryName_(binaryName != NULL ? binaryName : ""),
       helpModule_(NULL), singleModule_(NULL),
-      bQuiet_(false), bStdOutInfo_(false)
+      bQuiet_(false)
 {
-    binaryInfoSettings_.copyright(true);
+    GMX_RELEASE_ASSERT(binaryName_.find('-') == std::string::npos,
+                       "Help export does not currently work with binary names with dashes");
 }
 
 void CommandLineModuleManager::Impl::addModule(CommandLineModulePointer module)
@@ -283,7 +343,7 @@ void CommandLineModuleManager::Impl::ensureHelpModuleExists()
 {
     if (helpModule_ == NULL)
     {
-        helpModule_ = new CommandLineHelpModule(programInfo_, binaryName_,
+        helpModule_ = new CommandLineHelpModule(programContext_, binaryName_,
                                                 modules_, moduleGroups_);
         addModule(CommandLineModulePointer(helpModule_));
     }
@@ -298,9 +358,9 @@ CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
 
 CommandLineModuleMap::const_iterator
 CommandLineModuleManager::Impl::findModuleFromBinaryName(
-        const ProgramInfo &programInfo) const
+        const char *invokedName) const
 {
-    std::string moduleName = programInfo.programName();
+    std::string moduleName = invokedName;
 #ifdef GMX_BINARY_SUFFIX
     moduleName = stripSuffixIfPresent(moduleName, GMX_BINARY_SUFFIX);
 #endif
@@ -320,7 +380,8 @@ CommandLineModuleManager::Impl::findModuleFromBinaryName(
 }
 
 CommandLineModuleInterface *
-CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
+CommandLineModuleManager::Impl::processCommonOptions(
+        CommandLineCommonOptionsHolder *optionsHolder, int *argc, char ***argv)
 {
     // Check if we are directly invoking a certain module.
     CommandLineModuleInterface *module = singleModule_;
@@ -328,26 +389,15 @@ CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
     {
         // Also check for invokation through named symlinks.
         CommandLineModuleMap::const_iterator moduleIter
-            = findModuleFromBinaryName(programInfo_);
+            = findModuleFromBinaryName(programContext_.programName());
         if (moduleIter != modules_.end())
         {
             module = moduleIter->second.get();
         }
     }
 
-    bool bHelp      = false;
-    bool bHidden    = false;
-    bool bVersion   = false;
-    bool bCopyright = true;
-    // TODO: Print the common options into the help.
     // TODO: It would be nice to propagate at least the -quiet option to
     // the modules so that they can also be quiet in response to this.
-    Options options(NULL, NULL);
-    options.addOption(BooleanOption("h").store(&bHelp));
-    options.addOption(BooleanOption("hidden").store(&bHidden));
-    options.addOption(BooleanOption("quiet").store(&bQuiet_));
-    options.addOption(BooleanOption("version").store(&bVersion));
-    options.addOption(BooleanOption("copyright").store(&bCopyright));
 
     if (module == NULL)
     {
@@ -360,10 +410,11 @@ CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
         }
         if (argcForWrapper > 1)
         {
-            CommandLineParser(&options).parse(&argcForWrapper, *argv);
+            CommandLineParser(optionsHolder->options())
+                .parse(&argcForWrapper, *argv);
         }
         // If no action requested and there is a module specified, process it.
-        if (argcForWrapper < *argc && !bHelp && !bVersion)
+        if (argcForWrapper < *argc && !optionsHolder->shouldIgnoreActualModule())
         {
             const char *moduleName = (*argv)[argcForWrapper];
             CommandLineModuleMap::const_iterator moduleIter
@@ -385,25 +436,21 @@ CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
     {
         if (singleModule_ == NULL)
         {
-            programInfo_.setDisplayName(binaryName_ + " " + module->name());
+            programContext_.setDisplayName(binaryName_ + " " + module->name());
         }
         // Recognize the common options also after the module name.
         // TODO: It could be nicer to only recognize -h/-hidden if module is not
         // null.
-        CommandLineParser(&options).skipUnknown(true).parse(argc, *argv);
+        CommandLineParser(optionsHolder->options())
+            .skipUnknown(true).parse(argc, *argv);
     }
-    options.finish();
-    binaryInfoSettings_.extendedInfo(bVersion);
-    binaryInfoSettings_.copyright(bCopyright);
-    if (bVersion)
+    if (!optionsHolder->finishOptions())
     {
-        bQuiet_      = false;
-        bStdOutInfo_ = true;
         return NULL;
     }
     // If no module specified and no other action, show the help.
     // Also explicitly specifying -h for the wrapper binary goes here.
-    if (module == NULL || bHelp)
+    if (module == NULL || optionsHolder->shouldShowHelp())
     {
         ensureHelpModuleExists();
         if (module != NULL)
@@ -415,7 +462,7 @@ CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
     }
     if (module == helpModule_)
     {
-        helpModule_->setShowHidden(bHidden);
+        helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
     }
     return module;
 }
@@ -424,9 +471,9 @@ CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
  * CommandLineModuleManager
  */
 
-CommandLineModuleManager::CommandLineModuleManager(const char  *binaryName,
-                                                   ProgramInfo *programInfo)
-    : impl_(new Impl(binaryName, programInfo))
+CommandLineModuleManager::CommandLineModuleManager(
+        const char *binaryName, CommandLineProgramContext *programContext)
+    : impl_(new Impl(binaryName, programContext))
 {
 }
 
@@ -439,6 +486,12 @@ void CommandLineModuleManager::setQuiet(bool bQuiet)
     impl_->bQuiet_ = bQuiet;
 }
 
+void CommandLineModuleManager::setOutputRedirect(File *output)
+{
+    impl_->ensureHelpModuleExists();
+    impl_->helpModule_->setOutputRedirect(output);
+}
+
 void CommandLineModuleManager::setSingleModule(CommandLineModuleInterface *module)
 {
     impl_->singleModule_ = module;
@@ -476,38 +529,77 @@ void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
 
 int CommandLineModuleManager::run(int argc, char *argv[])
 {
-    CommandLineModuleInterface *module;
-    const bool                  bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
+    CommandLineModuleInterface    *module;
+    const bool                     bMaster = (gmx_node_rank() == 0);
+    bool                           bQuiet  = impl_->bQuiet_ || !bMaster;
+    CommandLineCommonOptionsHolder optionsHolder;
     try
     {
-        module = impl_->processCommonOptions(&argc, &argv);
+        optionsHolder.initOptions();
+        module = impl_->processCommonOptions(&optionsHolder, &argc, &argv);
     }
     catch (const std::exception &)
     {
-        if (bMaster && !impl_->bQuiet_)
+        bQuiet |= optionsHolder.shouldBeQuiet();
+        if (!bQuiet)
         {
-            printBinaryInformation(stderr, impl_->programInfo_,
-                                   impl_->binaryInfoSettings_);
+            printBinaryInformation(stderr, impl_->programContext_,
+                                   optionsHolder.binaryInfoSettings());
         }
         throw;
     }
-    if (!bMaster)
+    bQuiet |= optionsHolder.shouldBeQuiet();
+    if (!bQuiet)
     {
-        impl_->bQuiet_ = true;
-    }
-    if (!impl_->bQuiet_)
-    {
-        FILE *out = (impl_->bStdOutInfo_ ? stdout : stderr);
-        printBinaryInformation(out, impl_->programInfo_,
-                               impl_->binaryInfoSettings_);
+        FILE *out = optionsHolder.startupInfoFile();
+        printBinaryInformation(out, impl_->programContext_,
+                               optionsHolder.binaryInfoSettings());
         fprintf(out, "\n");
     }
     if (module == NULL)
     {
         return 0;
     }
-    int rc = module->run(argc, argv);
-    if (!impl_->bQuiet_)
+
+    CommandLineModuleSettings settings;
+    module->init(&settings);
+    optionsHolder.adjustFromSettings(settings);
+
+    // Open the debug file.
+    if (optionsHolder.debugLevel() > 0)
+    {
+        std::string filename(impl_->programContext_.programName());
+        if (gmx_node_num() > 1)
+        {
+            filename.append(formatString("%d", gmx_node_rank()));
+        }
+        filename.append(".debug");
+
+        fprintf(stderr, "Will write debug log file: %s\n", filename.c_str());
+        gmx_init_debug(optionsHolder.debugLevel(), filename.c_str());
+    }
+#if defined(HAVE_UNISTD_H) && !defined(GMX_NO_NICE) && !defined(__MINGW32__)
+    // Set the nice level unless disabled in the configuration.
+    if (optionsHolder.niceLevel() != 0)
+    {
+        static bool bNiceSet = false; // Only set it once.
+        if (!bNiceSet)
+        {
+            if (nice(optionsHolder.niceLevel()) == -1)
+            {
+                // Do nothing, but use the return value to avoid warnings.
+            }
+            bNiceSet = true;
+        }
+    }
+#endif
+
+    int rc = 0;
+    if (!(module == impl_->helpModule_ && !bMaster))
+    {
+        rc = module->run(argc, argv);
+    }
+    if (!bQuiet)
     {
         gmx_thanx(stderr);
     }
@@ -518,19 +610,19 @@ int CommandLineModuleManager::run(int argc, char *argv[])
 int CommandLineModuleManager::runAsMainSingleModule(
         int argc, char *argv[], CommandLineModuleInterface *module)
 {
-    ProgramInfo &programInfo = gmx::init(&argc, &argv);
+    CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
     try
     {
-        CommandLineModuleManager manager(NULL, &programInfo);
+        CommandLineModuleManager manager(NULL, &programContext);
         manager.setSingleModule(module);
         int rc = manager.run(argc, argv);
-        gmx::finalize();
+        gmx::finalizeForCommandLine();
         return rc;
     }
     catch (const std::exception &ex)
     {
         printFatalErrorMessage(stderr, ex);
-        return processExceptionAtExit(ex);
+        return processExceptionAtExitForCommandLine(ex);
     }
 }