/* User doing build */
#cmakedefine BUILD_USER "@BUILD_USER@"
+/* Binary suffix for the created binaries */
+#define GMX_BINARY_SUFFIX "@GMX_BINARY_SUFFIX@"
+
/* Turn off water-water neighborlist optimization only */
#cmakedefine DISABLE_WATERWATER_NLIST
*/
#include "cmdlinemodulemanager.h"
+// For GMX_BINARY_SUFFIX
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include <cstdio>
+#include <cstring>
#include <map>
#include <string>
#include "gromacs/commandline/cmdlinemodule.h"
#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/path.h"
namespace gmx
{
//! Container for mapping module names to module objects.
typedef std::map<std::string, CommandLineModulePointer> ModuleMap;
+ /*! \brief
+ * Initializes the implementation class.
+ *
+ * \param[in] realBinaryName Name of the binary that this manager runs.
+ */
+ explicit Impl(const char *realBinaryName);
+
+ /*! \brief
+ * Finds a module that matches a name.
+ *
+ * \param[in] name Module name to find.
+ * \returns Iterator to the found module, or
+ * \c modules_.end() if not found.
+ *
+ * Does not throw.
+ */
+ ModuleMap::const_iterator findModuleByName(const std::string &name) const;
+ /*! \brief
+ * Finds a module that the name of the binary.
+ *
+ * \param[in] argv0 argv[0] passed to the program.
+ * \throws std::bad_alloc if out of memory.
+ * \returns Iterator to the found module, or
+ * \c modules_.end() if not found.
+ *
+ * Checks whether the program is invoked through a symlink whose name
+ * is different from \a realBinaryName_, and if so, checks if a module
+ * name matches the name of the symlink.
+ */
+ ModuleMap::const_iterator findModuleFromBinaryName(const std::string &argv0) const;
+
//! Prints usage message to stderr.
void printUsage(bool bModuleList) const;
//! Prints the list of modules to stderr.
* Owns the contained modules.
*/
ModuleMap modules_;
+ //! Real name of the binary that is running (without suffixes).
+ std::string realBinaryName_;
};
+CommandLineModuleManager::Impl::Impl(const char *realBinaryName)
+ : realBinaryName_(realBinaryName)
+{
+}
+
+CommandLineModuleManager::Impl::ModuleMap::const_iterator
+CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
+{
+ // TODO: Accept unambiguous prefixes?
+ return modules_.find(name);
+}
+
+CommandLineModuleManager::Impl::ModuleMap::const_iterator
+CommandLineModuleManager::Impl::findModuleFromBinaryName(const std::string &argv0) const
+{
+ // TODO: Move this logic into a common place in utility/ and remove
+ // dependency on config.h from this file.
+ // (most natural place would be in a location that wraps Program() etc.)
+ std::string binaryName = Path::splitToPathAndFilename(argv0).second;
+ if (binaryName.length() >= 4
+ && binaryName.compare(binaryName.length() - 4, 4, ".exe") == 0)
+ {
+ binaryName.erase(binaryName.length() - 4);
+ }
+#ifdef GMX_BINARY_SUFFIX
+ size_t suffixLength = std::strlen(GMX_BINARY_SUFFIX);
+ if (suffixLength > 0 && binaryName.length() >= suffixLength
+ && binaryName.compare(binaryName.length() - suffixLength, suffixLength,
+ GMX_BINARY_SUFFIX) == 0)
+ {
+ binaryName.erase(binaryName.length() - suffixLength);
+ }
+#endif
+ if (binaryName == realBinaryName_)
+ {
+ return modules_.end();
+ }
+ if (binaryName.compare(0, 2, "g_") == 0)
+ {
+ binaryName.erase(0, 2);
+ }
+ return findModuleByName(binaryName);
+}
+
void CommandLineModuleManager::Impl::printUsage(bool bModuleList) const
{
const char *program = ShortProgram();
* CommandLineModuleManager
*/
-CommandLineModuleManager::CommandLineModuleManager()
- : impl_(new Impl)
+CommandLineModuleManager::CommandLineModuleManager(const char *realBinaryName)
+ : impl_(new Impl(realBinaryName))
{
addModule(CommandLineModulePointer(new internal::CommandLineHelpModule(*this)));
}
int CommandLineModuleManager::run(int argc, char *argv[])
{
- if (argc < 2)
+ int argOffset = 0;
+ Impl::ModuleMap::const_iterator module
+ = impl_->findModuleFromBinaryName(argv[0]);
+ if (module == impl_->modules_.end())
{
- fprintf(stderr, "\n");
- impl_->printUsage(false);
- return 2;
+ if (argc < 2)
+ {
+ fprintf(stderr, "\n");
+ impl_->printUsage(false);
+ return 2;
+ }
+ module = impl_->findModuleByName(argv[1]);
+ argOffset = 1;
}
- // TODO: Accept unambiguous prefixes?
- Impl::ModuleMap::const_iterator module = impl_->modules_.find(argv[1]);
if (module == impl_->modules_.end())
{
fprintf(stderr, "\n");
impl_->printUsage(true);
return 2;
}
- return module->second->run(argc - 1, argv + 1);
+ return module->second->run(argc - argOffset, argv + argOffset);
}
} // namespace gmx
CopyRight(stderr, argv[0]);
try
{
- gmx::CommandLineModuleManager manager;
+ gmx::CommandLineModuleManager manager("g_ana");
// <register all necessary modules>
return manager.run(argc, argv);
}
class CommandLineModuleManager
{
public:
- CommandLineModuleManager();
+ /*! \brief
+ * Initializes a command-line module manager.
+ *
+ * \param[in] realBinaryName Name of the binary that this manager runs
+ * (without Gromacs binary suffix or .exe on Windows).
+ * \throws std::bad_alloc if out of memory.
+ *
+ * The binary name is used to detect when the binary is run through a
+ * symlink, and automatically invoke a matching module in such a case.
+ */
+ explicit CommandLineModuleManager(const char *realBinaryName);
~CommandLineModuleManager();
/*! \brief
* \author Teemu Murtola <teemu.murtola@cbr.su.se>
* \ingroup module_commandline
*/
+// For GMX_BINARY_SUFFIX
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include <vector>
#include <gmock/gmock.h>
class CommandLineModuleManagerTest : public ::testing::Test
{
public:
+ CommandLineModuleManagerTest();
+
MockModule &addModule(const char *name, const char *description);
gmx::CommandLineModuleManager manager_;
};
+CommandLineModuleManagerTest::CommandLineModuleManagerTest()
+ : manager_("g_test")
+{
+}
+
MockModule &
CommandLineModuleManagerTest::addModule(const char *name, const char *description)
{
TEST_F(CommandLineModuleManagerTest, RunsModule)
{
const char *const cmdline[] = {
- "test", "module", "-flag", "yes"
+ "g_test", "module", "-flag", "yes"
+ };
+ gmx::test::CommandLine args(cmdline);
+ MockModule &mod1 = addModule("module", "First module");
+ addModule("other", "Second module");
+ using ::testing::_;
+ using ::testing::Args;
+ using ::testing::ElementsAreArray;
+ EXPECT_CALL(mod1, run(_, _))
+ .With(Args<1, 0>(ElementsAreArray(args.argv() + 1, args.argc() - 1)));
+ int rc = 0;
+ ASSERT_NO_THROW(rc = manager_.run(args.argc(), args.argv()));
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CommandLineModuleManagerTest, RunsModuleBasedOnBinaryName)
+{
+ const char *const cmdline[] = {
+ "g_module", "-flag", "yes"
+ };
+ gmx::test::CommandLine args(cmdline);
+ MockModule &mod1 = addModule("module", "First module");
+ addModule("other", "Second module");
+ using ::testing::_;
+ using ::testing::Args;
+ using ::testing::ElementsAreArray;
+ EXPECT_CALL(mod1, run(_, _))
+ .With(Args<1, 0>(ElementsAreArray(args.argv(), args.argc())));
+ int rc = 0;
+ ASSERT_NO_THROW(rc = manager_.run(args.argc(), args.argv()));
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CommandLineModuleManagerTest, RunsModuleBasedOnBinaryNameWithPathAndSuffix)
+{
+ const char *const cmdline[] = {
+ "/usr/local/gromacs/bin/g_module" GMX_BINARY_SUFFIX ".exe", "-flag", "yes"
};
gmx::test::CommandLine args(cmdline);
MockModule &mod1 = addModule("module", "First module");
using ::testing::_;
using ::testing::Args;
using ::testing::ElementsAreArray;
+ EXPECT_CALL(mod1, run(_, _))
+ .With(Args<1, 0>(ElementsAreArray(args.argv(), args.argc())));
+ int rc = 0;
+ ASSERT_NO_THROW(rc = manager_.run(args.argc(), args.argv()));
+ ASSERT_EQ(0, rc);
+}
+
+TEST_F(CommandLineModuleManagerTest, HandlesConflictingBinaryAndModuleNames)
+{
+ const char *const cmdline[] = {
+ "g_test", "test", "-flag", "yes"
+ };
+ gmx::test::CommandLine args(cmdline);
+ MockModule &mod1 = addModule("test", "Test module");
+ addModule("other", "Second module");
+ using ::testing::_;
+ using ::testing::Args;
+ using ::testing::ElementsAreArray;
EXPECT_CALL(mod1, run(_, _))
.With(Args<1, 0>(ElementsAreArray(args.argv() + 1, args.argc() - 1)));
int rc = 0;
#endif
static const char cDirSeparator = '/';
+static const char cDirSeparators[] = "/\\";
namespace gmx
{
return path1 + cDirSeparator + path2 + cDirSeparator + path3;
}
+std::pair<std::string, std::string>
+Path::splitToPathAndFilename(const std::string &path)
+{
+ size_t pos = path.find_last_of(cDirSeparators);
+ if (pos == std::string::npos)
+ {
+ return std::make_pair(std::string(), path);
+ }
+ return std::make_pair(path.substr(0, pos), path.substr(pos+1));
+}
+
int Directory::create(const char *path)
{
#define GMX_UTILITY_PATH_H
#include <string>
+#include <utility>
namespace gmx
{
static std::string join(const std::string &path1,
const std::string &path2,
const std::string &path3);
+ static std::pair<std::string, std::string>
+ splitToPathAndFilename(const std::string &path);
private:
// Disallow instantiation.
CopyRight(stderr, argv[0]);
try
{
- gmx::CommandLineModuleManager manager;
+ gmx::CommandLineModuleManager manager("g_ana");
registerTrajectoryAnalysisModules(&manager);
return manager.run(argc, argv);
}