* the research papers on the package. Check out http://www.gromacs.org.
*/
#ifdef HAVE_CONFIG_H
-#include <config.h>
+#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <io.h>
#endif
-#include "sysstuff.h"
-#include "string2.h"
-#include "futil.h"
-#include "network.h"
-#include "gmx_fatal.h"
-#include "smalloc.h"
-#include "statutil.h"
+/* Windows file stuff, only necessary for visual studio */
+#ifdef _MSC_VER
+#include <windows.h>
+#endif
+#include "gromacs/legacyheaders/gmx_fatal.h"
+#include "gromacs/legacyheaders/network.h"
+#include "gromacs/legacyheaders/smalloc.h"
+#include "gromacs/legacyheaders/string2.h"
-#ifdef GMX_THREAD_MPI
-#include "thread_mpi.h"
-#endif
+#include "gromacs/fileio/futil.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/path.h"
+#include "gromacs/utility/programinfo.h"
+#include "gromacs/utility/stringutil.h"
-/* Windows file stuff, only necessary for visual studio */
-#ifdef _MSC_VER
-#include "windows.h"
+#ifdef GMX_THREAD_MPI
+#include "thread_mpi/threads.h"
#endif
/* we keep a linked list of all files opened through pipes (i.e.
}
}
-static gmx_bool gmx_is_file(const char *fname)
-{
- FILE *test;
-
- if (fname == NULL)
- {
- return FALSE;
- }
- test = fopen(fname, "r");
- if (test == NULL)
- {
- return FALSE;
- }
- else
- {
- fclose(test);
- /*Windows doesn't allow fopen of directory - so we don't need to check this seperately */
- #if (!((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__))
- {
- int status;
- struct stat st_buf;
- status = stat (fname, &st_buf);
- if (status != 0 || !S_ISREG(st_buf.st_mode))
- {
- return FALSE;
- }
- }
- #endif
- return TRUE;
- }
-}
-
gmx_bool gmx_fexist_master(const char *fname, t_commrec *cr)
{
}
-/* Check if the program name begins with "/" on unix/cygwin, or
- * with "\" or "X:\" on windows. If not, the program name
- * is relative to the current directory.
- */
-static gmx_bool filename_is_absolute(char *name)
-{
-#ifdef GMX_NATIVE_WINDOWS
- return ((name[0] == DIR_SEPARATOR) || ((strlen(name) > 3) && strncmp(name+1, ":\\", 2)) == 0);
-#else
- return (name[0] == DIR_SEPARATOR);
-#endif
-}
-
void get_libdir(char *libdir)
{
-#define GMX_BINNAME_MAX 512
- char bin_name[GMX_BINNAME_MAX];
- char buf[GMX_BINNAME_MAX];
- char full_path[GMX_PATH_MAX+GMX_BINNAME_MAX];
- char system_path[GMX_PATH_MAX];
- char *dir, *ptr, *s;
- gmx_bool found = FALSE;
- int i;
-
- if (Program() != NULL)
+ // TODO: There is a potential buffer overrun in the way libdir is passed in.
+ try
{
+ std::string fullPath = gmx::ProgramInfo::getInstance().fullBinaryPath();
- /* First - detect binary name */
- if (strlen(Program()) >= GMX_BINNAME_MAX)
- {
- gmx_fatal(FARGS, "The name of the binary is longer than the allowed buffer size (%d):\n'%s'", GMX_BINNAME_MAX, Program());
- }
- strncpy(bin_name, Program(), GMX_BINNAME_MAX-1);
-
- /* On windows & cygwin we need to add the .exe extension
- * too, or we wont be able to detect that the file exists
- */
-#if (defined GMX_NATIVE_WINDOWS || defined GMX_CYGWIN)
- if (strlen(bin_name) < 3 || gmx_strncasecmp(bin_name+strlen(bin_name)-4, ".exe", 4))
- {
- strcat(bin_name, ".exe");
- }
-#endif
-
- /* Only do the smart search part if we got a real name */
- if (NULL != bin_name && strncmp(bin_name, "GROMACS", GMX_BINNAME_MAX))
- {
-
- if (!strchr(bin_name, DIR_SEPARATOR))
- {
- /* No slash or backslash in name means it must be in the path - search it! */
- /* Add the local dir since it is not in the path on windows */
- gmx_getcwd(system_path, sizeof(system_path));
- sprintf(full_path, "%s%c%s", system_path, DIR_SEPARATOR, bin_name);
- found = gmx_is_file(full_path);
- if (!found && (s = getenv("PATH")) != NULL)
- {
- char *dupped;
-
- dupped = gmx_strdup(s);
- s = dupped;
- while (!found && (dir = gmx_strsep(&s, PATH_SEPARATOR)) != NULL)
- {
- sprintf(full_path, "%s%c%s", dir, DIR_SEPARATOR, bin_name);
- found = gmx_is_file(full_path);
- }
- sfree(dupped);
- }
- if (!found)
- {
- strcpy(libdir, GMXLIB_FALLBACK);
- return;
- }
- }
- else if (!filename_is_absolute(bin_name))
- {
- /* name contains directory separators, but
- * it does not start at the root, i.e.
- * name is relative to the current dir
- */
- gmx_getcwd(buf, sizeof(buf));
- sprintf(full_path, "%s%c%s", buf, DIR_SEPARATOR, bin_name);
- }
- else
- {
- strncpy(full_path, bin_name, GMX_PATH_MAX);
- }
-
- /* Now we should have a full path and name in full_path,
- * but on unix it might be a link, or a link to a link to a link..
- */
-#ifndef GMX_NATIVE_WINDOWS
- while ( (i = readlink(full_path, buf, sizeof(buf)-1)) > 0)
- {
- buf[i] = '\0';
- /* If it doesn't start with "/" it is relative */
- if (buf[0] != DIR_SEPARATOR)
- {
- strncpy(strrchr(full_path, DIR_SEPARATOR)+1, buf, GMX_PATH_MAX);
- }
- else
- {
- strncpy(full_path, buf, GMX_PATH_MAX);
- }
- }
-#endif
-
- /* If running directly from the build tree, try to use the source
- * directory.
- */
+ // If running directly from the build tree, try to use the source
+ // directory.
#if (defined CMAKE_SOURCE_DIR && defined CMAKE_BINARY_DIR)
- if (strncmp(full_path, CMAKE_BINARY_DIR, strlen(CMAKE_BINARY_DIR)) == 0)
+ // TODO: Consider adding Path::startsWith(), as this may not work as
+ // expected.
+ if (gmx::startsWith(fullPath, CMAKE_BINARY_DIR))
+ {
+ if (search_subdirs(CMAKE_SOURCE_DIR, libdir))
{
- if (search_subdirs(CMAKE_SOURCE_DIR, libdir))
- {
- return;
- }
+ return;
}
+ }
#endif
- /* Remove the executable name - it always contains at least one slash */
- *(strrchr(full_path, DIR_SEPARATOR)+1) = '\0';
- /* Now we have the full path to the gromacs executable.
- * Use it to find the library dir.
- */
- found = FALSE;
- while (!found && ( (ptr = strrchr(full_path, DIR_SEPARATOR)) != NULL ) )
+ // Remove the executable name
+ fullPath = gmx::Path::splitToPathAndFilename(fullPath).first;
+ // Now we have the full path to the gromacs executable.
+ // Use it to find the library dir.
+ while (!fullPath.empty())
+ {
+ if (search_subdirs(fullPath.c_str(), libdir))
{
- *ptr = '\0';
- found = search_subdirs(full_path, libdir);
+ return;
}
+ fullPath = gmx::Path::splitToPathAndFilename(fullPath).first;
}
}
+ GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
+
/* End of smart searching. If we didn't find it in our parent tree,
* or if the program name wasn't set, at least try some standard
* locations before giving up, in case we are running from e.g.
* a users home directory. This only works on unix or cygwin...
*/
+ bool found = false;
#ifndef GMX_NATIVE_WINDOWS
if (!found)
{
fprintf(fp, "%sGROMACS: %s, %s%s%s\n", prefix, name.c_str(),
GromacsVersion(), precisionString, suffix);
fprintf(fp, "%sExecutable: %s%s\n", prefix,
- programInfo.programNameWithPath().c_str(), suffix);
+ programInfo.fullBinaryPath().c_str(), suffix);
fprintf(fp, "%sCommand line:%s\n%s %s%s\n",
prefix, suffix, prefix, programInfo.commandLine().c_str(), suffix);
if (settings.bExtendedInfo_)
{
try
{
- return oenv->programInfo.programNameWithPath().c_str();
+ return oenv->programInfo.fullBinaryPath().c_str();
}
GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
}
{
try
{
- return gmx::ProgramInfo::getInstance().programNameWithPath().c_str();
+ return gmx::ProgramInfo::getInstance().fullBinaryPath().c_str();
}
GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
}
gmx_header_config_gen.h
gitversion.c
+tests/test-bin
#include <string>
#include <vector>
-#include "gromacs/fileio/futil.h"
+#include <sys/stat.h>
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/stringutil.h"
+#include "gmx_header_config.h"
+
namespace gmx
{
// static
bool File::exists(const char *filename)
{
- return gmx_fexist(filename);
+ if (filename == NULL)
+ {
+ return false;
+ }
+ FILE *test = fopen(filename, "r");
+ if (test == NULL)
+ {
+ return false;
+ }
+ else
+ {
+ fclose(test);
+ // Windows doesn't allow fopen of directory, so we don't need to check
+ // this separately.
+#ifndef GMX_NATIVE_WINDOWS
+ struct stat st_buf;
+ int status = stat(filename, &st_buf);
+ if (status != 0 || !S_ISREG(st_buf.st_mode))
+ {
+ return false;
+ }
+#endif
+ return true;
+ }
}
// static
void writeLine();
/*! \brief
- * Checks whether a file exists.
+ * Checks whether a file exists and is a regular file.
*
* \param[in] filename Path to the file to check.
* \returns true if \p filename exists and is accessible.
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2011,2012, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013, by the GROMACS development team, led by
* David van der Spoel, Berk Hess, Erik Lindahl, and including many
* others, as listed in the AUTHORS file in the top-level source
* directory and at http://www.gromacs.org.
* \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_utility
*/
-#include "path.h"
+#include "gromacs/utility/path.h"
-#include "gmx_header_config.h"
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
-#include <errno.h>
-#include <sys/stat.h>
+#include <algorithm>
+
+#include "config.h"
+#include <sys/stat.h>
#ifdef GMX_NATIVE_WINDOWS
#include <direct.h>
+#else
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
#endif
+#include "gromacs/fileio/futil.h"
+
namespace
{
//! Directory separators to use when parsing paths.
const char cDirSeparators[] = "/\\";
+//! Check whether a given character is a directory separator.
+bool isDirSeparator(char chr)
+{
+ return std::strchr(cDirSeparators, chr);
+}
+
} // namespace
namespace gmx
{
+/********************************************************************
+ * Path
+ */
+
+bool Path::containsDirectory(const std::string &path)
+{
+ return path.find_first_of(cDirSeparators) != std::string::npos;
+}
+
+/* Check if the program name begins with "/" on unix/cygwin, or
+ * with "\" or "X:\" on windows. If not, the program name
+ * is relative to the current directory.
+ */
+bool Path::isAbsolute(const char *path)
+{
+ if (isDirSeparator(path[0]))
+ {
+ return true;
+ }
+#ifdef GMX_NATIVE_WINDOWS
+ return path[0] != '\0' && path[1] == ':' && isDirSeparator(path[2]);
+#else
+ return false;
+#endif
+}
+
+bool Path::isAbsolute(const std::string &path)
+{
+ return isAbsolute(path.c_str());
+}
+
std::string Path::join(const std::string &path1,
const std::string &path2)
{
return std::make_pair(path.substr(0, pos), path.substr(pos+1));
}
+std::string Path::normalize(const std::string &path)
+{
+ std::string result(path);
+ if (DIR_SEPARATOR != '/')
+ {
+ std::replace(result.begin(), result.end(), '/', DIR_SEPARATOR);
+ }
+ return result;
+}
+
+bool Path::exists(const char *path)
+{
+ return gmx_fexist(path);
+}
+
+bool Path::exists(const std::string &path)
+{
+ return exists(path.c_str());
+}
+
+std::string Path::getWorkingDirectory()
+{
+ // TODO: Use exceptions instead of gmx_fatal().
+ char cwd[GMX_PATH_MAX];
+ gmx_getcwd(cwd, sizeof(cwd));
+ return cwd;
+}
+
+void Path::splitPathEnvironment(const std::string &pathEnv,
+ std::vector<std::string> *result)
+{
+ size_t prevPos = 0;
+ size_t separator;
+ do
+ {
+ separator = pathEnv.find_first_of(PATH_SEPARATOR, prevPos);
+ result->push_back(pathEnv.substr(prevPos, separator - prevPos));
+ prevPos = separator + 1;
+ }
+ while (separator != std::string::npos);
+}
+
+std::vector<std::string> Path::getExecutablePaths()
+{
+ std::vector<std::string> result;
+#ifdef GMX_NATIVE_WINDOWS
+ // Add the local dir since it is not in the path on Windows.
+ result.push_back("");
+#endif
+ const char *path = std::getenv("PATH");
+ if (path != NULL)
+ {
+ splitPathEnvironment(path, &result);
+ }
+ return result;
+}
+
+std::string Path::resolveSymlinks(const std::string &path)
+{
+ std::string result(path);
+#ifndef GMX_NATIVE_WINDOWS
+ char buf[GMX_PATH_MAX];
+ int length;
+ while ((length = readlink(result.c_str(), buf, sizeof(buf)-1)) > 0)
+ {
+ buf[length] = '\0';
+ if (isAbsolute(buf))
+ {
+ result = buf;
+ }
+ else
+ {
+ result = join(splitToPathAndFilename(result).first, buf);
+ }
+ }
+#endif
+ return result;
+}
+
+
+/********************************************************************
+ * Directory
+ */
int Directory::create(const char *path)
{
#include <string>
#include <utility>
+#include <vector>
namespace gmx
{
class Path
{
public:
+ static bool containsDirectory(const std::string &path);
+ static bool isAbsolute(const char *path);
+ static bool isAbsolute(const std::string &path);
+
static std::string join(const std::string &path1,
const std::string &path2);
static std::string join(const std::string &path1,
const std::string &path3);
static std::pair<std::string, std::string>
splitToPathAndFilename(const std::string &path);
+ static std::string normalize(const std::string &path);
+
+ static bool exists(const char *path);
+ static bool exists(const std::string &path);
+ static std::string getWorkingDirectory();
+
+ static void splitPathEnvironment(const std::string &pathEnv,
+ std::vector<std::string> *result);
+ static std::vector<std::string> getExecutablePaths();
+
+ static std::string resolveSymlinks(const std::string &path);
private:
// Disallow instantiation.
*/
#include "programinfo.h"
-// For GMX_BINARY_SUFFIX
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <cstdlib>
#include <cstring>
-#include <algorithm>
#include <string>
+#include <vector>
#include <boost/scoped_ptr.hpp>
-#include "gromacs/fileio/futil.h"
#include "gromacs/legacyheaders/thread_mpi/mutex.h"
#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/file.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/path.h"
#include "gromacs/utility/stringutil.h"
return str;
}
+/*! \brief
+ * Default implementation for ExecutableEnvironmentInterface.
+ *
+ * Used if ExecutableEnvironmentInterface is not explicitly provided when
+ * constructing ProgramInfo.
+ */
+class DefaultExecutableEnvironment : public ExecutableEnvironmentInterface
+{
+ public:
+ //! Allocates a default environment.
+ static ExecutableEnvironmentPointer create()
+ {
+ return ExecutableEnvironmentPointer(new DefaultExecutableEnvironment());
+ }
+
+ DefaultExecutableEnvironment()
+ : initialWorkingDirectory_(Path::getWorkingDirectory())
+ {
+ }
+
+ virtual std::string getWorkingDirectory() const
+ {
+ return initialWorkingDirectory_;
+ }
+ virtual std::vector<std::string> getExecutablePaths() const
+ {
+ return Path::getExecutablePaths();
+ }
+
+ private:
+ std::string initialWorkingDirectory_;
+};
+
+/*! \brief
+ * Finds the absolute path of the binary from \c argv[0].
+ *
+ * \param[in] invokedName \c argv[0] the binary was invoked with.
+ * \param[in] env Executable environment.
+ * \returns The full path of the binary.
+ *
+ * If a binary with the given name cannot be located, \p invokedName is
+ * returned.
+ */
+std::string findFullBinaryPath(const std::string &invokedName,
+ const ExecutableEnvironmentInterface &env)
+{
+ std::string searchName = invokedName;
+ // On Windows & Cygwin we need to add the .exe extension,
+ // or we wont be able to detect that the file exists.
+#if (defined GMX_NATIVE_WINDOWS || defined GMX_CYGWIN)
+ if (!endsWith(searchName, ".exe"))
+ {
+ searchName.append(".exe");
+ }
+#endif
+ if (!Path::containsDirectory(searchName))
+ {
+ // No directory in name means it must be in the path - search it!
+ std::vector<std::string> pathEntries = env.getExecutablePaths();
+ std::vector<std::string>::const_iterator i;
+ for (i = pathEntries.begin(); i != pathEntries.end(); ++i)
+ {
+ const std::string &dir = i->empty() ? env.getWorkingDirectory() : *i;
+ std::string testPath = Path::join(dir, searchName);
+ if (File::exists(testPath))
+ {
+ return testPath;
+ }
+ }
+ }
+ else if (!Path::isAbsolute(searchName))
+ {
+ // Name contains directories, but is not absolute, i.e.,
+ // it is relative to the current directory.
+ std::string cwd = env.getWorkingDirectory();
+ std::string testPath = Path::join(cwd, searchName);
+ return testPath;
+ }
+ return searchName;
+}
+
//! \}
} // namespace
{
public:
Impl();
- Impl(const char *realBinaryName, int argc, const char *const argv[]);
-
- std::string realBinaryName_;
- std::string fullInvokedProgram_;
- std::string programName_;
- std::string invariantProgramName_;
- std::string commandLine_;
- std::string displayName_;
- mutable tMPI::mutex displayNameMutex_;
+ Impl(const char *realBinaryName, int argc, const char *const argv[],
+ ExecutableEnvironmentPointer env);
+
+ ExecutableEnvironmentPointer executableEnv_;
+ std::string realBinaryName_;
+ std::string invokedName_;
+ std::string programName_;
+ std::string invariantProgramName_;
+ std::string displayName_;
+ std::string commandLine_;
+ mutable std::string fullBinaryPath_;
+ mutable tMPI::mutex displayNameMutex_;
+ mutable tMPI::mutex binaryPathMutex_;
};
ProgramInfo::Impl::Impl()
- : realBinaryName_("GROMACS"), fullInvokedProgram_("GROMACS"),
+ : realBinaryName_("GROMACS"),
programName_("GROMACS"), invariantProgramName_("GROMACS")
{
}
ProgramInfo::Impl::Impl(const char *realBinaryName,
- int argc, const char *const argv[])
- : realBinaryName_(realBinaryName != NULL ? realBinaryName : ""),
- fullInvokedProgram_(argc != 0 ? argv[0] : ""),
- programName_(Path::splitToPathAndFilename(fullInvokedProgram_).second)
+ int argc, const char *const argv[],
+ ExecutableEnvironmentPointer env)
+ : executableEnv_(move(env)),
+ realBinaryName_(realBinaryName != NULL ? realBinaryName : "")
{
- // Temporary hack to make things work on Windows while waiting for #950.
- // Some places in the existing code expect to have DIR_SEPARATOR in all
- // input paths, but Windows may also give '/' (and does that, e.g., for
- // tests invoked through CTest).
- // When removing this, remove also the #include "gromacs/fileio/futil.h".
- if (DIR_SEPARATOR == '\\')
- {
- std::replace(fullInvokedProgram_.begin(), fullInvokedProgram_.end(),
- '/', '\\');
- }
+ invokedName_ = (argc != 0 ? argv[0] : "");
+ programName_ = Path::splitToPathAndFilename(invokedName_).second;
programName_ = stripSuffixIfPresent(programName_, ".exe");
invariantProgramName_ = programName_;
#ifdef GMX_BINARY_SUFFIX
}
ProgramInfo::ProgramInfo(const char *realBinaryName)
- : impl_(new Impl(realBinaryName, 1, &realBinaryName))
+ : impl_(new Impl(realBinaryName, 1, &realBinaryName,
+ DefaultExecutableEnvironment::create()))
{
}
ProgramInfo::ProgramInfo(int argc, const char *const argv[])
- : impl_(new Impl(NULL, argc, argv))
+ : impl_(new Impl(NULL, argc, argv,
+ DefaultExecutableEnvironment::create()))
{
}
ProgramInfo::ProgramInfo(const char *realBinaryName,
int argc, const char *const argv[])
- : impl_(new Impl(realBinaryName, argc, argv))
+ : impl_(new Impl(realBinaryName, argc, argv,
+ DefaultExecutableEnvironment::create()))
+{
+}
+
+ProgramInfo::ProgramInfo(const char *realBinaryName,
+ int argc, const char *const argv[],
+ ExecutableEnvironmentPointer env)
+ : impl_(new Impl(realBinaryName, argc, argv, move(env)))
{
}
return impl_->realBinaryName_;
}
-const std::string &ProgramInfo::programNameWithPath() const
-{
- return impl_->fullInvokedProgram_;
-}
-
const std::string &ProgramInfo::programName() const
{
return impl_->programName_;
return impl_->commandLine_;
}
+const std::string &ProgramInfo::fullBinaryPath() const
+{
+ tMPI::lock_guard<tMPI::mutex> lock(impl_->binaryPathMutex_);
+ if (impl_->fullBinaryPath_.empty())
+ {
+ impl_->fullBinaryPath_ =
+ Path::normalize(
+ Path::resolveSymlinks(
+ findFullBinaryPath(impl_->invokedName_,
+ *impl_->executableEnv_)));
+ // TODO: Investigate/Consider using a dladdr()-based solution.
+ // Potentially less portable, but significantly simpler, and also works
+ // with user binaries even if they are located in some arbitrary location,
+ // as long as shared libraries are used.
+ }
+ return impl_->fullBinaryPath_;
+}
+
} // namespace gmx
#define GMX_UTILITY_PROGRAMINFO_H
#include <string>
+#include <vector>
#include "common.h"
+#include "uniqueptr.h"
namespace gmx
{
+//! \addtogroup module_utility
+//! \{
+
+/*! \libinternal \brief
+ * Allows customization of the way various directories are found by
+ * ProgramInfo.
+ *
+ * For the ProgramInfo constructors that do not take this interface as a
+ * parameter, a default implementation is used that forwards the calls to the
+ * corresponding methods in gmx::Path.
+ *
+ * \inlibraryapi
+ */
+class ExecutableEnvironmentInterface
+{
+ public:
+ virtual ~ExecutableEnvironmentInterface() {}
+
+ /*! \brief
+ * Returns the working directory when the program was launched.
+ */
+ virtual std::string getWorkingDirectory() const = 0;
+ /*! \brief
+ * Returns list of paths where executables are searched for.
+ *
+ * The returned list should be in priority order. An empty string in
+ * the returned list corresponds to getWorkindDirectory().
+ */
+ virtual std::vector<std::string> getExecutablePaths() const = 0;
+};
+
+//! Shorthand for a smart pointer to ExecutableEnvironmentInterface.
+typedef gmx_unique_ptr<ExecutableEnvironmentInterface>::type
+ ExecutableEnvironmentPointer;
+
/*! \libinternal \brief
* Helper class for managing information about the running binary.
*
* exceptions.
*
* \inlibraryapi
- * \ingroup module_utility
*/
class ProgramInfo
{
*/
ProgramInfo(const char *realBinaryName,
int argc, const char *const argv[]);
+ /*! \brief
+ * Initializes a program information object based on binary name and
+ * command line.
+ *
+ * \param[in] realBinaryName Name of the binary
+ * (without Gromacs binary suffix or .exe on Windows).
+ * \param[in] argc argc value passed to main().
+ * \param[in] argv argv array passed to main().
+ * \param[in] env Customizes the way the binary name is handled.
+ *
+ * This overload allows one to customize the way the binary is located
+ * by providing a custom ExecutableEnvironmentInterface implementation.
+ * This is mainly useful for testing purposes to make it possible to
+ * test different paths without setting environment variables, changing
+ * the working directory or doing other process-wide operations.
+ * It may also be useful for making Gromacs behave better when linked
+ * into a non-Gromacs executable (with possible extensions in
+ * ExecutableEnvironmentInterface).
+ */
+ ProgramInfo(const char *realBinaryName,
+ int argc, const char *const argv[],
+ ExecutableEnvironmentPointer env);
~ProgramInfo();
/*! \brief
* Does not throw.
*/
const std::string &realBinaryName() const;
- /*! \brief
- * Returns the path and name of the binary as it was invoked.
- *
- * Does not throw.
- */
- const std::string &programNameWithPath() const;
/*! \brief
* Returns the name of the binary as it was invoked without any path.
*
*/
const std::string &commandLine() const;
+ /*! \brief
+ * Returns the full path of the invoked binary.
+ *
+ * Returns argv[0] if there was an error in finding the absolute path.
+ *
+ * Does not throw.
+ */
+ const std::string &fullBinaryPath() const;
+
private:
class Impl;
#
# This file is part of the GROMACS molecular simulation package.
#
-# Copyright (c) 2012, by the GROMACS development team, led by
+# Copyright (c) 2012,2013, by the GROMACS development team, led by
# David van der Spoel, Berk Hess, Erik Lindahl, and including many
# others, as listed in the AUTHORS file in the top-level source
# directory and 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.
+set(EXECUTABLE_EXTENSION "")
+if (GMX_NATIVE_WINDOWS OR GMX_CYGWIN)
+ set(EXECUTABLE_EXTENSION ".exe")
+endif ()
+set(PATH_SEARCH_TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test-bin)
+file(MAKE_DIRECTORY ${PATH_SEARCH_TEST_DIR}/bin)
+file(WRITE ${PATH_SEARCH_TEST_DIR}/bin/test-exe${EXECUTABLE_EXTENSION}
+ "Test executable for path searching")
+if (UNIX)
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -E create_symlink
+ test-exe
+ ${PATH_SEARCH_TEST_DIR}/bin/test-rel-link)
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -E create_symlink
+ ${PATH_SEARCH_TEST_DIR}/bin/test-exe
+ ${PATH_SEARCH_TEST_DIR}/bin/test-abs-link)
+endif ()
+
gmx_add_unit_test(UtilityUnitTests utility-test
+ programinfo.cpp
stringutil.cpp)
--- /dev/null
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, by the GROMACS development team, led by
+ * David van der Spoel, Berk Hess, 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
+ * Tests for gmx::ProgramInfo.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/utility/common.h"
+#include "gromacs/utility/path.h"
+#include "gromacs/utility/programinfo.h"
+#include "gromacs/utility/uniqueptr.h"
+
+#include "testutils/cmdlinetest.h"
+
+#include "config.h"
+
+using gmx::test::CommandLine;
+using gmx::Path;
+
+#if (defined GMX_NATIVE_WINDOWS || defined GMX_CYGWIN)
+//! Extension for executable files on the platform.
+#define EXECUTABLE_EXTENSION ".exe"
+#else
+//! Extension for executable files on the platform.
+#define EXECUTABLE_EXTENSION ""
+//! Defined if the platform supports symlinks and those can be tested.
+#define TEST_SYMLINKS
+#endif
+
+namespace
+{
+
+class TestExecutableEnvironment : public gmx::ExecutableEnvironmentInterface
+{
+ public:
+ TestExecutableEnvironment()
+ : workingDirectory_(CMAKE_BINARY_DIR "/src/gromacs/utility/tests/test-bin")
+ {
+ }
+
+ virtual std::string getWorkingDirectory() const
+ {
+ return workingDirectory_;
+ }
+ virtual std::vector<std::string> getExecutablePaths() const
+ {
+ return path_;
+ }
+
+ std::string workingDirectory_;
+ std::vector<std::string> path_;
+
+ GMX_DISALLOW_COPY_AND_ASSIGN(TestExecutableEnvironment);
+};
+
+//! Shorthand for a smart pointer to TestExecutableEnvironment.
+typedef gmx::gmx_unique_ptr<TestExecutableEnvironment>::type
+ TestExecutableEnvironmentPointer;
+
+class ProgramInfoTest : public ::testing::Test
+{
+ public:
+ ProgramInfoTest()
+ : env_(new TestExecutableEnvironment())
+ {
+ expectedExecutable_ =
+ Path::normalize(
+ Path::join(env_->getWorkingDirectory(),
+ "bin/test-exe" EXECUTABLE_EXTENSION));
+ }
+
+ void testBinaryPathSearch(const char *argv0)
+ {
+ ASSERT_TRUE(env_.get() != NULL);
+ gmx::ProgramInfo info(NULL, 1, &argv0, move(env_));
+ EXPECT_EQ(expectedExecutable_, info.fullBinaryPath());
+ }
+ void testBinaryPathSearch(const std::string &argv0)
+ {
+ testBinaryPathSearch(argv0.c_str());
+ }
+
+ std::string expectedExecutable_;
+ TestExecutableEnvironmentPointer env_;
+};
+
+TEST_F(ProgramInfoTest, FindsBinaryWithAbsolutePath)
+{
+ testBinaryPathSearch(Path::join(env_->getWorkingDirectory(), "bin/test-exe"));
+}
+
+TEST_F(ProgramInfoTest, FindsBinaryWithRelativePath)
+{
+ testBinaryPathSearch("bin/test-exe");
+}
+
+TEST_F(ProgramInfoTest, FindsBinaryFromPath)
+{
+ env_->path_.push_back(Path::join(env_->getWorkingDirectory(), "bin"));
+ testBinaryPathSearch("test-exe");
+}
+
+TEST_F(ProgramInfoTest, FindsBinaryFromCurrentDirectory)
+{
+ env_->workingDirectory_ = Path::join(env_->getWorkingDirectory(), "bin");
+ env_->path_.push_back("");
+ testBinaryPathSearch("test-exe");
+}
+
+#ifdef TEST_SYMLINKS
+TEST_F(ProgramInfoTest, FindsBinaryFromAbsoluteSymLink)
+{
+ testBinaryPathSearch("bin/test-abs-link");
+}
+
+TEST_F(ProgramInfoTest, FindsBinaryFromRelativeSymLink)
+{
+ testBinaryPathSearch("bin/test-rel-link");
+}
+#endif
+
+} // namespace