The %main() method also catches all exceptions, and if one is caught, prints an
error message and terminates the program cleanly.
-Command line manager
+Command line modules
+====================
+
+All modules within the wrapper binary are implemented as classes that implement
+the gmx::CommandLineModuleInterface interface. There is generally some helper
+class in between:
+ * General C++ modules typically use gmx::Options for their command-line
+ handling. Instead of each module implementing parsing and help separately
+ with identical code, they implement gmx::CommandLineOptionsModuleInterface
+ instead. The framework then provides a bridge class that contains the
+ common code and wraps gmx::CommandLineOptionsModuleInterface into a
+ gmx::CommandLineModuleInterface.
+ * For C++ trajectory analysis modules, there is a general implementation for
+ running the gmx::TrajectoryAnalysisModule subclasses in cmdlinerunner.cpp.
+ * For old C-style %main() functions, see \ref section_wrapperbinary_cmain.
+
+Command line manager {#section_wrapperbinary_manager}
====================
The core of the wrapper binary is the gmx::CommandLineModuleManager::run()
these partial HTML files.
The final HTML help is produced in `share/html/final/`.
-Handling C %main() functions
+Handling C %main() functions {#section_wrapperbinary_cmain}
----------------------------
Many pre-5.0 modules are still implemented as a function with a C %main()
the facilities provided by \ref module_commandline. There are a few
different alternatives, depending on how much control you want to give
\Gromacs:
- - For C++ code, you can implement gmx::CommandLineModuleInterface, and
- use gmx::runCommandLineModule() to execute it. This requires using some
- additional \Gromacs classes (in particular, for implementing
- gmx::CommandLineModuleInterface::writeHelp(), if you want to support the
- `-h` option).
+ - For C++ code, you can implement gmx::CommandLineOptionsModuleInterface and
+ use gmx::runCommandLineModule() to execute it. This interface assumes
+ the use of the gmx::Options mechanism for declaring command-line options
+ (see \ref module_options).
+ For a lower-level interface, gmx::CommandLineModuleInterface can be used,
+ but this requires you to implement `-h` output and command-line parsing
+ yourself (possibly using classes that \Gromacs provides).
- For C code, you can use gmx_run_cmain() to wrap an existing C main
method. The only constraint on the provided main method is that it
should use parse_common_args() for argument processing.
* - Helper classes/functions for implementing the %main() function.
* See \ref page_usinglibrary for an overview of those available for user
* programs. These are declared in cmdlineinit.h
- * (gmx::CommandLineModuleInterface is declared in cmdlinemodule.h).
+ * (gmx::CommandLineModuleInterface is declared in cmdlinemodule.h and
+ * gmx::CommandLineOptionsInterface in cmdlineoptionsmodule.h).
* \if libapi
*
* Additionally, for internal \Gromacs use, gmx::CommandLineModuleManager
#include "gromacs/commandline/cmdlinehelpwriter.h"
#include "gromacs/commandline/cmdlineinit.h"
#include "gromacs/commandline/cmdlinemodule.h"
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
#include "gromacs/commandline/cmdlineparser.h"
#include "gromacs/commandline/pargs.h"
cmdlinehelpwriter.h
cmdlineinit.h
cmdlinemodule.h
+ cmdlineoptionsmodule.h
cmdlineparser.h
cmdlineprogramcontext.h
pargs.h
#include <boost/scoped_ptr.hpp>
#include "gromacs/commandline/cmdlinemodulemanager.h"
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
#include "gromacs/commandline/cmdlineprogramcontext.h"
#include "gromacs/legacyheaders/network.h"
#include "gromacs/legacyheaders/types/commrec.h"
int runCommandLineModule(int argc, char *argv[],
CommandLineModuleInterface *module)
{
- return gmx::CommandLineModuleManager::runAsMainSingleModule(argc, argv, module);
+ return CommandLineModuleManager::runAsMainSingleModule(argc, argv, module);
+}
+
+int runCommandLineModule(int argc, char *argv[],
+ const char *name, const char *description,
+ CommandLineOptionsModuleInterface *(*factory)())
+{
+ return CommandLineOptionsModuleInterface::runAsMain(
+ argc, argv, name, description, factory);
}
} // namespace gmx
{
class CommandLineModuleInterface;
+class CommandLineOptionsModuleInterface;
/*! \brief
* Initializes the \Gromacs library for command-line use.
*/
int runCommandLineModule(int argc, char *argv[],
CommandLineModuleInterface *module);
+/*! \brief
+ * Implements a main() method that runs a single module.
+ *
+ * \param argc \c argc passed to main().
+ * \param argv \c argv passed to main().
+ * \param[in] name Name for the module.
+ * \param[in] description Short description for the module.
+ * \param factory Factory method that creates the module to run.
+ *
+ * This method allows for uniform behavior for binaries that only
+ * contain a single module without duplicating any of the
+ * implementation from CommandLineModuleManager (startup headers,
+ * common options etc.).
+ *
+ * Usage:
+ * \code
+ class CustomCommandLineOptionsModule : public CommandLineOptionsModuleInterface
+ {
+ // <...>
+ };
+
+ static CommandLineOptionsModuleInterface *create()
+ {
+ return new CustomCommandLineOptionsModule();
+ }
+
+ int main(int argc, char *argv[])
+ {
+ return gmx::runCommandLineModule(
+ argc, argv, "mymodule", "short description", &create);
+ }
+ \endcode
+ *
+ * Does not throw. All exceptions are caught and handled internally.
+ */
+int runCommandLineModule(int argc, char *argv[],
+ const char *name, const char *description,
+ CommandLineOptionsModuleInterface *(*factory)());
} // namespace gmx
* Module that can be run from command line using CommandLineModuleManager.
*
* \see CommandLineModuleManager
+ * \see CommandLineOptionsModule
*
* \inpublicapi
* \ingroup module_commandline
*
* This will be called before run(), and can be used to adjust
* initialization that the runner does.
+ *
+ * This method is currently not called when writing the help.
*/
virtual void init(CommandLineModuleSettings *settings) = 0;
/*! \brief
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Implements supporting routines for gmx::CommandLineOptionsModuleInterface.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_commandline
+ */
+#include "gmxpre.h"
+
+#include "cmdlineoptionsmodule.h"
+
+#include <boost/scoped_ptr.hpp>
+
+#include "gromacs/commandline/cmdlinehelpwriter.h"
+#include "gromacs/commandline/cmdlinemodulemanager.h"
+#include "gromacs/commandline/cmdlineparser.h"
+#include "gromacs/options/filenameoptionmanager.h"
+#include "gromacs/options/options.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+namespace
+{
+
+/********************************************************************
+ * CommandLineOptionsModule
+ */
+
+class CommandLineOptionsModule : public CommandLineModuleInterface
+{
+ public:
+ //! Shorthand for the factory function pointer type.
+ typedef CommandLineOptionsModuleInterface::FactoryMethod FactoryMethod;
+
+ CommandLineOptionsModule(const char *name, const char *description,
+ FactoryMethod factory)
+ : name_(name), description_(description), factory_(factory)
+ {
+ }
+ virtual const char *name() const { return name_; }
+ virtual const char *shortDescription() const { return description_; }
+
+ virtual void init(CommandLineModuleSettings *settings);
+ virtual int run(int argc, char *argv[]);
+ virtual void writeHelp(const CommandLineHelpContext &context) const;
+
+ private:
+ void parseOptions(int argc, char *argv[]);
+
+ const char *name_;
+ const char *description_;
+ FactoryMethod factory_;
+ boost::scoped_ptr<CommandLineOptionsModuleInterface> module_;
+};
+
+void CommandLineOptionsModule::init(CommandLineModuleSettings *settings)
+{
+ module_.reset(factory_());
+ module_->init(settings);
+}
+
+int CommandLineOptionsModule::run(int argc, char *argv[])
+{
+ GMX_RELEASE_ASSERT(module_, "init() has not been called");
+ parseOptions(argc, argv);
+ return module_->run();
+}
+
+void CommandLineOptionsModule::writeHelp(const CommandLineHelpContext &context) const
+{
+ boost::scoped_ptr<CommandLineOptionsModuleInterface> module(factory_());
+ Options options(name(), shortDescription());
+ module->initOptions(&options);
+ CommandLineHelpWriter(options)
+ .setShowDescriptions(true)
+ .writeHelp(context);
+}
+
+void CommandLineOptionsModule::parseOptions(int argc, char *argv[])
+{
+ FileNameOptionManager fileoptManager;
+ Options options(name_, description_);
+
+ options.addManager(&fileoptManager);
+
+ module_->initOptions(&options);
+ {
+ CommandLineParser parser(&options);
+ parser.parse(&argc, argv);
+ options.finish();
+ }
+ module_->optionsFinished(&options);
+}
+
+} // namespace
+
+/********************************************************************
+ * CommandLineOptionsModuleInterface
+ */
+
+CommandLineOptionsModuleInterface::~CommandLineOptionsModuleInterface()
+{
+}
+
+// static
+CommandLineModuleInterface *
+CommandLineOptionsModuleInterface::createModule(
+ const char *name, const char *description, FactoryMethod factory)
+{
+ return new CommandLineOptionsModule(name, description, factory);
+}
+
+// static
+int CommandLineOptionsModuleInterface::runAsMain(
+ int argc, char *argv[], const char *name, const char *description,
+ FactoryMethod factory)
+{
+ CommandLineOptionsModule module(name, description, factory);
+ return CommandLineModuleManager::runAsMainSingleModule(argc, argv, &module);
+}
+
+// static
+void CommandLineOptionsModuleInterface::registerModule(
+ CommandLineModuleManager *manager, const char *name,
+ const char *description, FactoryMethod factory)
+{
+ CommandLineModulePointer module(createModule(name, description, factory));
+ manager->addModule(move(module));
+}
+
+} // namespace gmx
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::CommandLineOptionsModuleInterface and supporting routines.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEOPTIONSMODULE_H
+#define GMX_COMMANDLINE_CMDLINEOPTIONSMODULE_H
+
+#include "gromacs/commandline/cmdlinemodule.h"
+
+namespace gmx
+{
+
+class CommandLineModuleInterface;
+class CommandLineModuleManager;
+class Options;
+
+/*! \brief
+ * Module that can be run from a command line and uses gmx::Options for
+ * argument processing.
+ *
+ * This class provides a higher-level interface on top of
+ * gmx::CommandLineModuleInterface for cases where gmx::Options will be used
+ * for declaring the command-line arguments. The module only needs to declare
+ * the options it uses, and the framework takes care of command-line parsing
+ * and help output. The module typically consists of the following parts:
+ * - init() allows for some interaction between the module and the framework
+ * when running the module; see CommandLineModuleInterface::init(). If no
+ * such customization is necessary, an empty implementation is sufficient.
+ * - initOptions() is called both for running the module and for printing help
+ * for the module, and it should add the options that the module
+ * understands. Values provided for the options are typically stored in
+ * member variables.
+ * - optionsFinished() can be implemented in case additional processing is
+ * needed (e.g., checking whether an option was set by the user).
+ * - run() is called when running the module, after command-line options have
+ * been parsed and their values stored in the corresponding member
+ * variables.
+ *
+ * registerModule(), runAsMain(), or createModule() can be used to use modules
+ * of this type in all contexts where a gmx::CommandLineModuleInterface is
+ * expected. These methods create a gmx::CommandLineModuleInterface
+ * implementation that contains the common code needed to parse command-line
+ * options and write help, based on the information provided from the methods
+ * in this class.
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+class CommandLineOptionsModuleInterface
+{
+ public:
+ /*! \brief
+ * Function pointer to a factory method that returns an interface of
+ * this type.
+ *
+ * \returns Module to run (should be allocated with `new`).
+ * \throws std::bad_alloc if out of memory.
+ *
+ * The caller takes responsibility to `delete` the returned pointer.
+ */
+ typedef CommandLineOptionsModuleInterface *(*FactoryMethod)();
+
+ /*! \brief
+ * Creates a CommandLineModuleInterface to run the specified module.
+ *
+ * \param[in] name Name for the module.
+ * \param[in] description Short description for the module.
+ * \param[in] factory Factory that returns the module to run.
+ * \returns CommandLineModuleInterface object that runs the module
+ * returned by \p factory. Caller must `delete` the object.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * This is mainly used by unit tests that want to bypass
+ * CommandLineModuleManager.
+ */
+ static CommandLineModuleInterface *
+ createModule(const char *name, const char *description,
+ FactoryMethod factory);
+ /*! \brief
+ * Implements a main() method that runs a single module.
+ *
+ * \param argc \c argc passed to main().
+ * \param argv \c argv passed to main().
+ * \param[in] name Name for the module.
+ * \param[in] description Short description for the module.
+ * \param[in] factory Factory that returns the module to run.
+ *
+ * This method allows for uniform behavior for binaries that only
+ * contain a single module without duplicating any of the
+ * implementation from CommandLineModuleManager (startup headers,
+ * common options etc.).
+ *
+ * \see runCommandLineModule()
+ */
+ static int
+ runAsMain(int argc, char *argv[], const char *name,
+ const char *description, FactoryMethod factory);
+ /*! \brief
+ * Registers a module of a certain type to this manager.
+ *
+ * \param manager Manager to register to.
+ * \param[in] name Name for the module.
+ * \param[in] description Short description for the module.
+ * \param[in] factory Factory that returns the module to register.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * This method internally creates a CommandLineModuleInterface module
+ * with the given \p name and \p description, and adds that to
+ * \p manager. When run or asked to write the help, the module calls
+ * \p factory to get the actual module, and forwards the necessary
+ * calls.
+ */
+ static void
+ registerModule(CommandLineModuleManager *manager,
+ const char *name, const char *description,
+ FactoryMethod factory);
+
+ virtual ~CommandLineOptionsModuleInterface();
+
+ //! \copydoc gmx::CommandLineModuleInterface::init()
+ virtual void init(CommandLineModuleSettings *settings) = 0;
+ /*! \brief
+ * Initializes command-line arguments understood by the module.
+ *
+ * \param[in,out] options Options object to add the options to.
+ *
+ * When running the module, this method is called after init().
+ * When printing help, there is no call to init(), and this is the only
+ * method called.
+ * In both cases, the implementation should add options understood by
+ * the module to \p options. Output values from options should be
+ * stored in member variables.
+ */
+ virtual void initOptions(Options *options) = 0;
+ /*! \brief
+ * Called after all option values have been set.
+ *
+ * \param[in,out] options Options object in which options are stored.
+ *
+ * When running the module, this method is called after all
+ * command-line arguments have been parsed, but while the Options
+ * object still exists.
+ *
+ * If the module needs to call, e.g., Options::isSet(), this is the
+ * place to do that.
+ */
+ virtual void optionsFinished(Options *options) = 0;
+
+ /*! \brief
+ * Runs the module.
+ *
+ * \throws unspecified May throw exceptions to indicate errors.
+ * \returns Exit code for the program.
+ * \retval 0 on successful termination.
+ *
+ * This method is called after optionsFinished() when running the
+ * module, and should do all the processing for the module.
+ */
+ virtual int run() = 0;
+};
+
+} // namespace gmx
+
+#endif
#include <sstream>
#include <vector>
+#include <boost/scoped_ptr.hpp>
+
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
#include "gromacs/commandline/cmdlineprogramcontext.h"
#include "gromacs/utility/arrayref.h"
#include "gromacs/utility/file.h"
* CommandLineTestHelper
*/
+// static
+int CommandLineTestHelper::runModule(
+ CommandLineModuleInterface *module, CommandLine *commandLine)
+{
+ CommandLineModuleSettings settings;
+ module->init(&settings);
+ return module->run(commandLine->argc(), commandLine->argv());
+}
+
+// static
+int CommandLineTestHelper::runModule(
+ CommandLineOptionsModuleInterface::FactoryMethod factory,
+ CommandLine *commandLine)
+{
+ // The name and description are not used in the tests, so they can be NULL.
+ boost::scoped_ptr<CommandLineModuleInterface> module(
+ CommandLineOptionsModuleInterface::createModule(NULL, NULL, factory));
+ return runModule(module.get(), commandLine);
+}
+
CommandLineTestHelper::CommandLineTestHelper(TestFileManager *fileManager)
: impl_(new Impl(fileManager))
{
namespace gmx
{
+
+class CommandLineModuleInterface;
+class CommandLineOptionsModuleInterface;
+
namespace test
{
* setOutputFileNoTest()).
* 3. Checking the contents of some of the output files using
* TestReferenceData (setOutputFile() and checkOutputFiles()).
+ * 4. Static methods for easily executing command-line modules
+ * (various overloads of runModule()).
*
* All files created during the test are cleaned up at the end of the test.
*
class CommandLineTestHelper
{
public:
+ /*! \brief
+ * Runs a command-line program that implements CommandLineModuleInterface.
+ *
+ * \param[in,out] module Module to run.
+ * The function does not take ownership.
+ * \param[in,out] commandLine Command line parameters to pass.
+ * This is only modified if \p module modifies it.
+ * \returns The return value of the module.
+ * \throws unspecified Any exception thrown by the module.
+ */
+ static int
+ runModule(CommandLineModuleInterface *module, CommandLine *commandLine);
+ /*! \brief
+ * Runs a command-line program that implements
+ * CommandLineOptionsModuleInterface.
+ *
+ * \param[in] factory Factory method for the module to run.
+ * \param[in,out] commandLine Command line parameters to pass.
+ * This is only modified if the module modifies it.
+ * \returns The return value of the module.
+ * \throws unspecified Any exception thrown by the factory or the
+ * module.
+ */
+ static int
+ runModule(CommandLineOptionsModuleInterface *(*factory)(),
+ CommandLine *commandLine);
+
/*! \brief
* Initializes an instance.
*