Allow runAsMainSingleModule to be called more than once
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinemodulemanager.cpp
index 7b510b4a01e998cb8b109f0b060b9fc7bae1f49a..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/stringutil.h"
 
-// For GMX_BINARY_SUFFIX
-#include "config.h"
+#include "cmdlinehelpmodule.h"
+#include "cmdlinemodulemanager-impl.h"
 
 namespace gmx
 {
@@ -114,120 +120,83 @@ 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
+
 /********************************************************************
- * CommonOptionsHolder
+ * CommandLineCommonOptionsHolder
  */
 
-/*! \brief
- * Encapsulates some handling of common options to the wrapper binary.
- */
-class CommonOptionsHolder
+CommandLineCommonOptionsHolder::CommandLineCommonOptionsHolder()
+    : options_(NULL, NULL), bHelp_(false), bHidden_(false),
+      bQuiet_(false), bVersion_(false), bCopyright_(true),
+      niceLevel_(19), debugLevel_(0)
 {
-    public:
-        CommonOptionsHolder()
-            : options_(NULL, NULL), bHelp_(false), bHidden_(false),
-              bQuiet_(false), bVersion_(false), bCopyright_(true)
-        {
-            binaryInfoSettings_.copyright(true);
-        }
-
-        //! Initializes the common options.
-        void 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"));
-        }
-
-        /*! \brief
-         * Finishes option parsing.
-         *
-         * \returns `false` if the wrapper binary should quit without executing
-         *     any module.
-         */
-        bool finishOptions()
-        {
-            options_.finish();
-            binaryInfoSettings_.extendedInfo(bVersion_);
-            // The latter condition suppresses the copyright with
-            // -quiet -version.
-            binaryInfoSettings_.copyright(bCopyright_ && !bQuiet_);
-            return !bVersion_;
-        }
-
-        //! Returns the internal Options object.
-        Options *options() { return &options_; }
-        //! Returns the settings for printing startup information.
-        const BinaryInformationSettings &binaryInfoSettings() const
-        {
-            return binaryInfoSettings_;
-        }
-
-        /*! \brief
-         * Returns `true` if common options are set such that the wrapper
-         * binary should quit, without running the actual module.
-         */
-        bool shouldIgnoreActualModule() const
-        {
-            return bHelp_ || bVersion_;
-        }
-        //! Returns whether common options specify showing help.
-        bool shouldShowHelp() const { return bHelp_; }
-        //! Returns whether common options specify showing hidden options in help.
-        bool shouldShowHidden() const { return bHidden_; }
-        //! Returns whether common options specify quiet execution.
-        bool shouldBeQuiet() const
-        {
-            return bQuiet_ && !bVersion_;
-        }
+    binaryInfoSettings_.copyright(true);
+}
 
-        //! Returns the file to which startup information should be printed.
-        FILE *startupInfoFile() const { return (bVersion_ ? stdout : stderr); }
+CommandLineCommonOptionsHolder::~CommandLineCommonOptionsHolder()
+{
+}
 
-    private:
-        Options                      options_;
-        //! Settings for what to write in the startup header.
-        BinaryInformationSettings    binaryInfoSettings_;
-        bool                         bHelp_;
-        bool                         bHidden_;
-        bool                         bQuiet_;
-        bool                         bVersion_;
-        bool                         bCopyright_;
-};
+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_;
+}
 
-}   // namespace
+void CommandLineCommonOptionsHolder::adjustFromSettings(
+        const CommandLineModuleSettings &settings)
+{
+    if (!options_.isSet("nice"))
+    {
+        niceLevel_ = settings.defaultNiceLevel();
+    }
+}
 
 /********************************************************************
  * CommandLineModuleManager::Impl
@@ -312,7 +281,7 @@ class CommandLineModuleManager::Impl
          * arguments that should be passed to it.
          */
         CommandLineModuleInterface *
-        processCommonOptions(CommonOptionsHolder *optionsHolder,
+        processCommonOptions(CommandLineCommonOptionsHolder *optionsHolder,
                              int *argc, char ***argv);
 
         /*! \brief
@@ -355,6 +324,8 @@ CommandLineModuleManager::Impl::Impl(const char                *binaryName,
       helpModule_(NULL), singleModule_(NULL),
       bQuiet_(false)
 {
+    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)
@@ -410,7 +381,7 @@ CommandLineModuleManager::Impl::findModuleFromBinaryName(
 
 CommandLineModuleInterface *
 CommandLineModuleManager::Impl::processCommonOptions(
-        CommonOptionsHolder *optionsHolder, int *argc, char ***argv)
+        CommandLineCommonOptionsHolder *optionsHolder, int *argc, char ***argv)
 {
     // Check if we are directly invoking a certain module.
     CommandLineModuleInterface *module = singleModule_;
@@ -492,7 +463,6 @@ CommandLineModuleManager::Impl::processCommonOptions(
     if (module == helpModule_)
     {
         helpModule_->setShowHidden(optionsHolder->shouldShowHidden());
-        helpModule_->setCommonOptions(optionsHolder->options());
     }
     return module;
 }
@@ -516,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;
@@ -553,10 +529,10 @@ void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
 
 int CommandLineModuleManager::run(int argc, char *argv[])
 {
-    CommandLineModuleInterface *module;
-    const bool                  bMaster = (!gmx_mpi_initialized() || gmx_node_rank() == 0);
-    bool                        bQuiet  = impl_->bQuiet_ || !bMaster;
-    CommonOptionsHolder         optionsHolder;
+    CommandLineModuleInterface    *module;
+    const bool                     bMaster = (gmx_node_rank() == 0);
+    bool                           bQuiet  = impl_->bQuiet_ || !bMaster;
+    CommandLineCommonOptionsHolder optionsHolder;
     try
     {
         optionsHolder.initOptions();
@@ -584,7 +560,45 @@ int CommandLineModuleManager::run(int argc, char *argv[])
     {
         return 0;
     }
-    int rc = module->run(argc, argv);
+
+    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);
@@ -608,7 +622,7 @@ int CommandLineModuleManager::runAsMainSingleModule(
     catch (const std::exception &ex)
     {
         printFatalErrorMessage(stderr, ex);
-        return processExceptionAtExit(ex);
+        return processExceptionAtExitForCommandLine(ex);
     }
 }