Options for controlling startup output.
authorTeemu Murtola <teemu.murtola@gmail.com>
Thu, 4 Jul 2013 19:27:17 +0000 (22:27 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Sat, 20 Jul 2013 17:53:49 +0000 (19:53 +0200)
Add a -quiet option to the wrapper binary.  This option suppresses all
startup headers and the gmx_thanx() call in the end.  Restructure
CommandLineModuleManager::run() to avoid multiple paths that all need to
call gmx_thanx() and potentially also print the startup header.

Make -quiet suppress the startup header also in parse_common_args().
Reordering required by this causes invalid command-line option error
messages to be printed before the header gets printed, but this only
affects those few binaries that don't go through the wrapper binary.

Make gmx_print_version_info() callable through printBinaryInformation(),
and do this in the wrapper binary and in parse_common_args() in
response to -version.

Print the copyright in response -copyright in the wrapper binary.  Easy
to add to parse_common_args() as well, but even better would be to merge
the remaining few programs into the wrapper binary.

Part of #1209.

Change-Id: I0c7dddc91065b12f347da12acd82047e2d94b44c

cmake/BuildManPages.cmake
src/gromacs/commandline/cmdlinemodulemanager.cpp
src/gromacs/gmxlib/copyrite.cpp
src/gromacs/gmxlib/statutil.cpp
src/gromacs/legacyheaders/copyrite.h

index a1fa0e5268bd019e203b9ccd2488a5b7c932e0f9..88fc9d62f7cca19b2a896161fe19e0f9fc6ad98a 100644 (file)
@@ -52,8 +52,6 @@ function (gmx_add_man_page EXENAME)
             message(WARNING "Missing description for ${EXENAME}")
         endif()
         add_custom_command(TARGET ${EXENAME} POST_BUILD 
-            #The redirect is a hack to avoid showing copyright. 
-            #Ideally there would be a -quiet that would skip all output.
             COMMAND ${CMAKE_COMMAND} -DINFILE=${EXENAME}${GMX_BINARY_SUFFIX}.nroff 
                 -DOUTFILE=${MAN1_PATH}/${EXENAME}.1 -DDESC=" - ${DESC}"
                 -DEXENAME="${CMAKE_BINARY_DIR}/bin/${EXENAME}${GMX_BINARY_SUFFIX}"
index 642e3588cd2700e9aaece0b0156b5130657d5a6f..0f4276de45ec9b959a8b121b551dc5a55cd8018e 100644 (file)
 #include "gromacs/legacyheaders/copyrite.h"
 
 #include "gromacs/commandline/cmdlinemodule.h"
+#include "gromacs/commandline/cmdlineparser.h"
 #include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/onlinehelp/helpmanager.h"
 #include "gromacs/onlinehelp/helptopic.h"
 #include "gromacs/onlinehelp/helpwritercontext.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/options.h"
 #include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/file.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/programinfo.h"
 #include "gromacs/utility/stringutil.h"
@@ -264,9 +267,6 @@ class CommandLineHelpModule : public CommandLineModuleInterface
         virtual int run(int argc, char *argv[]);
         virtual void writeHelp(const HelpWriterContext &context) const;
 
-        //! Prints usage message to stderr.
-        void printUsage() const;
-
     private:
         CompositeHelpTopicPointer   rootTopic_;
 
@@ -311,13 +311,6 @@ void CommandLineHelpModule::writeHelp(const HelpWriterContext &context) const
     // TODO: More information.
 }
 
-void CommandLineHelpModule::printUsage() const
-{
-    HelpWriterContext context(&File::standardError(),
-                              eHelpOutputFormat_Console);
-    rootTopic_->writeHelp(context);
-}
-
 /********************************************************************
  * CommandLineModuleManager::Impl
  */
@@ -368,6 +361,24 @@ class CommandLineModuleManager::Impl
         CommandLineModuleMap::const_iterator
         findModuleFromBinaryName(const ProgramInfo &programInfo) const;
 
+        /*! \brief
+         * Processes command-line options for the wrapper binary.
+         *
+         * \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().
+         *     On output, argv to be passed to the module.
+         * \throws    InvalidInputError if there are invalid options.
+         * \returns   The module that should be run.
+         *
+         * Handles command-line options that affect the wrapper binary
+         * (potentially changing the members of \c this in response to the
+         * options).  Also finds the module that should be run and the
+         * arguments that should be passed to it.
+         */
+        CommandLineModuleInterface *
+        processCommonOptions(int *argc, char ***argv);
+
         /*! \brief
          * Maps module names to module objects.
          *
@@ -382,12 +393,17 @@ 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_;
         //! Whether all stderr output should be suppressed.
         bool                    bQuiet_;
+        //! Whether to write the startup information to stdout iso stderr.
+        bool                    bStdOutInfo_;
 };
 
 CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
-    : programInfo_(programInfo), helpModule_(NULL), bQuiet_(false)
+    : programInfo_(programInfo), helpModule_(NULL), bQuiet_(false),
+      bStdOutInfo_(false)
 {
 }
 
@@ -414,6 +430,68 @@ CommandLineModuleManager::Impl::findModuleFromBinaryName(
     return findModuleByName(binaryName);
 }
 
+CommandLineModuleInterface *
+CommandLineModuleManager::Impl::processCommonOptions(int *argc, char ***argv)
+{
+    // Check if the module is called through a symlink.
+    CommandLineModuleMap::const_iterator module
+        = findModuleFromBinaryName(programInfo_);
+    if (module != modules_.end())
+    {
+        // TODO: Process common options also in this case.
+        return module->second.get();
+    }
+    // If not, process options to the wrapper binary.
+    // TODO: This should be done by CommandLineParser
+    // (together with the above TODO).
+    int moduleArgOffset = 1;
+    while (moduleArgOffset < *argc && (*argv)[moduleArgOffset][0] == '-')
+    {
+        ++moduleArgOffset;
+    }
+    bool bHelp      = false;
+    bool bVersion   = false;
+    bool bCopyright = false;
+    if (moduleArgOffset > 1)
+    {
+        // TODO: Print these 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("quiet").store(&bQuiet_));
+        options.addOption(BooleanOption("version").store(&bVersion));
+        options.addOption(BooleanOption("copyright").store(&bCopyright));
+        CommandLineParser(&options).parse(&moduleArgOffset, *argv);
+        options.finish();
+        binaryInfoSettings_.extendedInfo(bVersion);
+        binaryInfoSettings_.copyright(bCopyright);
+    }
+    if (bVersion || bCopyright)
+    {
+        bQuiet_      = false;
+        bStdOutInfo_ = true;
+        return NULL;
+    }
+    // If no module or help requested, show the help.
+    if (moduleArgOffset == *argc || bHelp)
+    {
+        *argc = 1;
+        return helpModule_;
+    }
+    // Find the module to run and arguments to it.
+    const char *moduleName = (*argv)[moduleArgOffset];
+    module = findModuleByName(moduleName);
+    if (module == modules_.end())
+    {
+        std::string message = formatString("'%s' is not a GROMACS command.", moduleName);
+        GMX_THROW(InvalidInputError(message));
+    }
+    *argc -= moduleArgOffset;
+    *argv += moduleArgOffset;
+    return module->second.get();
+}
+
 /********************************************************************
  * CommandLineModuleManager
  */
@@ -451,38 +529,29 @@ void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
 
 int CommandLineModuleManager::run(int argc, char *argv[])
 {
-    int argOffset = 0;
-    CommandLineModuleMap::const_iterator module
-        = impl_->findModuleFromBinaryName(impl_->programInfo_);
-    if (!impl_->bQuiet_)
+    CommandLineModuleInterface *module;
+    try
     {
-        printBinaryInformation(stderr, impl_->programInfo_);
+        module = impl_->processCommonOptions(&argc, &argv);
     }
-    if (module == impl_->modules_.end())
+    catch (const std::exception &)
     {
-        if (argc < 2)
+        if (!impl_->bQuiet_)
         {
-            impl_->helpModule_->printUsage();
-            if (!impl_->bQuiet_)
-            {
-                gmx_thanx(stderr);
-            }
-            return 2;
+            printBinaryInformation(stderr, impl_->programInfo_);
         }
-        module    = impl_->findModuleByName(argv[1]);
-        argOffset = 1;
+        throw;
     }
-    if (module == impl_->modules_.end())
+    if (!impl_->bQuiet_)
     {
-        fprintf(stderr, "Unknown command: '%s'\n\n", argv[1]);
-        impl_->helpModule_->printUsage();
-        if (!impl_->bQuiet_)
-        {
-            gmx_thanx(stderr);
-        }
-        return 2;
+        printBinaryInformation(impl_->bStdOutInfo_ ? stdout : stderr,
+                               impl_->programInfo_, impl_->binaryInfoSettings_);
+    }
+    if (module == NULL)
+    {
+        return 0;
     }
-    int rc = module->second->run(argc - argOffset, argv + argOffset);
+    int rc = module->run(argc, argv);
     if (!impl_->bQuiet_)
     {
         gmx_thanx(stderr);
index fdb7de8da9cd95ad801a718a062a5f3b8d5eedbc..96137542ba09ca4647200709b13dcab30206ccb9 100644 (file)
@@ -178,7 +178,7 @@ void cool_quote(char *retstring, int retsize, int *cqnum)
     sfree(tmpstr);
 }
 
-void CopyRight(FILE *out)
+static void CopyRight(FILE *out)
 {
     static const char * const CopyrightText[] = {
         "Written by Emile Apol, Rossen Apostolov, Herman J.C. Berendsen,",
@@ -211,6 +211,8 @@ void CopyRight(FILE *out)
     char tmpstr[1024];
     int  i;
 
+    // TODO: Consider making the output more compact to fit better with
+    // other information written by printBinaryInformation().
     ster_print(out, "G  R  O  M  A  C  S");
     fprintf(out, "\n");
 
@@ -683,7 +685,18 @@ void gmx_print_version_info(FILE *fp)
 namespace gmx
 {
 
+BinaryInformationSettings::BinaryInformationSettings()
+    : bExtendedInfo_(false), bCopyright_(false)
+{
+}
+
 void printBinaryInformation(FILE *fp, const ProgramInfo &programInfo)
+{
+    printBinaryInformation(fp, programInfo, BinaryInformationSettings());
+}
+
+void printBinaryInformation(FILE *fp, const ProgramInfo &programInfo,
+                            const BinaryInformationSettings &settings)
 {
     const char *precisionString = "";
 #ifdef GMX_DOUBLE
@@ -694,6 +707,16 @@ void printBinaryInformation(FILE *fp, const ProgramInfo &programInfo)
             GromacsVersion(), precisionString);
     fprintf(fp, "Executable: %s\n", programInfo.programNameWithPath().c_str());
     fprintf(fp, "Command line:\n  %s\n", programInfo.commandLine().c_str());
+    if (settings.bCopyright_)
+    {
+        fprintf(fp, "\n");
+        CopyRight(fp);
+    }
+    if (settings.bExtendedInfo_)
+    {
+        fprintf(fp, "\n");
+        gmx_print_version_info(fp);
+    }
     fprintf(fp, "\n");
 }
 
index 9ca20407dbc6a049d83137ebb97321004931fd79..28d5590a40f45df7eb97f47003a69132538847e5 100644 (file)
@@ -584,10 +584,6 @@ void parse_common_args(int *argc, char *argv[], unsigned long Flags,
     // Ensure that the program info is initialized; if already done, returns
     // the already initialized object.
     const gmx::ProgramInfo &programInfo = gmx::ProgramInfo::init(*argc, argv);
-    if (FF(PCA_STANDALONE))
-    {
-        gmx::printBinaryInformation(stderr, programInfo);
-    }
     /* Check for double arguments */
     for (i = 1; (i < *argc); i++)
     {
@@ -705,11 +701,12 @@ void parse_common_args(int *argc, char *argv[], unsigned long Flags,
     output_env_init(oenv, *argc, argv, (time_unit_t)nenum(time_units), bView,
                     (xvg_format_t)nenum(xvg_format), 0, debug_level);
 
-    if (bVersion)
+    if (!FF(PCA_QUIET) && FF(PCA_STANDALONE) && (!bQuiet || bVersion))
     {
-        printf("Program: %s\n", output_env_get_program_name(*oenv));
-        gmx_print_version_info(stdout);
-        exit(0);
+        gmx::BinaryInformationSettings settings;
+        settings.extendedInfo(bVersion);
+        gmx::printBinaryInformation(bVersion ? stdout : stderr,
+                                    programInfo, settings);
     }
 
     if (FF(PCA_CAN_SET_DEFFNM) && (deffnm != NULL))
@@ -752,7 +749,7 @@ void parse_common_args(int *argc, char *argv[], unsigned long Flags,
         all_pa[i].desc = mk_desc(&(all_pa[i]), output_env_get_time_unit(*oenv));
     }
 
-    bExit = bHelp || (strcmp(manstr[0], "no") != 0);
+    bExit = bHelp || bVersion || (strcmp(manstr[0], "no") != 0);
 
 #if (defined __sgi && USE_SGI_FPE)
     doexceptions();
index e3830bb8af877acd4160558136cc85936438d561..a7075520c80023c0c3520b419cde5c150b75cc54 100644 (file)
@@ -76,6 +76,38 @@ namespace gmx
 
 class ProgramInfo;
 
+/*! \brief
+ * Settings for printBinaryInformation().
+ *
+ * This class is used to specify what printBinaryInformation() prints.
+ */
+class BinaryInformationSettings
+{
+    public:
+        BinaryInformationSettings();
+
+        //! Print information about build settings.
+        BinaryInformationSettings &extendedInfo(bool bEnabled)
+        {
+            bExtendedInfo_ = bEnabled;
+            return *this;
+        }
+        //! Print copyright and license information.
+        BinaryInformationSettings &copyright(bool bEnabled)
+        {
+            bCopyright_ = bEnabled;
+            return *this;
+        }
+
+    private:
+        bool        bExtendedInfo_;
+        bool        bCopyright_;
+
+        //! Needed to read the members without otherwise unnecessary accessors.
+        friend void printBinaryInformation(FILE *fp, const ProgramInfo &programInfo,
+                                           const BinaryInformationSettings &settings);
+};
+
 /*! \brief
  * Print basic information about the executable.
  *
@@ -83,6 +115,17 @@ class ProgramInfo;
  * \param[in] programInfo  Program information object to use.
  */
 void printBinaryInformation(FILE *fp, const ProgramInfo &programInfo);
+/*! \brief
+ * Print basic information about the executable with custom settings.
+ *
+ * \param     fp           Where to print the information to.
+ * \param[in] programInfo  Program information object to use.
+ * \param[in] settings     Specifies what to print.
+ *
+ * \see BinaryInformationSettings
+ */
+void printBinaryInformation(FILE *fp, const ProgramInfo &programInfo,
+                            const BinaryInformationSettings &settings);
 
 } // namespace gmx;