From 16c1f75544ed83e3eef088b30af69f673aec25aa Mon Sep 17 00:00:00 2001 From: Teemu Murtola Date: Thu, 15 Jan 2015 21:53:16 +0200 Subject: [PATCH] Make data file path search more reusable Instead of returning the path to share/top from ProgramContextInterface, return the installation prefix. Make it the responsibility of the caller to append the relative path to the data files it needs. Clean up config.h defines related to this to make them more generic. This makes it possible to reuse the same logic to find files also in other locations than share/top. DataFileFinder is not yet similarly reusable, but that is a topic for another change. Change-Id: I7c7fc3730417e71ff43d1b6fadf00a0eb465d794 --- CMakeLists.txt | 7 +- src/config.h.cmakein | 12 ++-- .../commandline/cmdlineprogramcontext.cpp | 69 ++++++++++--------- .../commandline/cmdlineprogramcontext.h | 6 +- src/gromacs/gmxlib/copyrite.cpp | 12 ++-- src/gromacs/utility/datafilefinder.cpp | 29 ++++++-- src/gromacs/utility/programcontext.cpp | 7 +- src/gromacs/utility/programcontext.h | 45 +++++++++++- src/gromacs/utility/stringutil.h | 12 +++- src/testutils/testinit.cpp | 11 ++- 10 files changed, 141 insertions(+), 69 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d3e6d03f1..89c7354699 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -740,6 +740,8 @@ mark_as_advanced(GMX_LIB_INSTALL_DIR GMX_DATA_INSTALL_DIR) # customizing the install locations. set(LIB_INSTALL_DIR ${GMX_LIB_INSTALL_DIR}) set(BIN_INSTALL_DIR bin) +# This variable also gets written into config.h for use in finding the data +# directories. set(DATA_INSTALL_DIR share/${GMX_DATA_INSTALL_DIR}) set(MAN_INSTALL_DIR share/man) # If the nesting level wrt. the installation root is changed, @@ -749,11 +751,6 @@ set(CMAKE_INSTALL_DIR share/cmake) set(PKGCONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/pkgconfig) set(INCL_INSTALL_DIR include) -# These variables get written into config.h for use in finding the data -# directories. -set(GMXLIB_SEARCH_DIR share/${GMX_DATA_INSTALL_DIR}/top) -set(GMXLIB_FALLBACK ${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/top) - list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${INCL_INSTALL_DIR}) # Binary and library suffix options diff --git a/src/config.h.cmakein b/src/config.h.cmakein index 89b6d7aa9d..2459207232 100644 --- a/src/config.h.cmakein +++ b/src/config.h.cmakein @@ -47,15 +47,15 @@ /* TODO: For now, disable Doxygen warnings from here */ /*! \cond */ -/* Default location of data files */ -#define GMXLIB_SEARCH_DIR "@GMXLIB_SEARCH_DIR@" - -/* Default location of data files */ -#define GMXLIB_FALLBACK "@GMXLIB_FALLBACK@" - /* Binary suffix for the created binaries */ #define GMX_BINARY_SUFFIX "@GMX_BINARY_SUFFIX@" +/* Installation prefix (default location of data files) */ +#define CMAKE_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@" + +/* Location of data files in the installation directory */ +#define DATA_INSTALL_DIR "@DATA_INSTALL_DIR@" + /* Source directory for the build */ #cmakedefine CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@" diff --git a/src/gromacs/commandline/cmdlineprogramcontext.cpp b/src/gromacs/commandline/cmdlineprogramcontext.cpp index 520ca9fab5..9368e3c925 100644 --- a/src/gromacs/commandline/cmdlineprogramcontext.cpp +++ b/src/gromacs/commandline/cmdlineprogramcontext.cpp @@ -1,7 +1,7 @@ /* * This file is part of the GROMACS molecular simulation package. * - * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by + * Copyright (c) 2012,2013,2014,2015, 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. @@ -178,59 +178,57 @@ bool isAcceptableLibraryPath(const std::string &path) * Returns whether given path prefix contains files from `share/top/`. * * \param[in] path Path prefix to check. - * \param[out] result If return value is `true`, the pointee is set to the - * actual data directory. Otherwise, the pointee is not modified. * \returns `true` if \p path contains the data files. * * Checks whether \p path could be the installation prefix where `share/top/` * files have been installed: appends the relative installation path of the * data files and calls isAcceptableLibraryPath(). */ -bool isAcceptableLibraryPathPrefix(const std::string &path, std::string *result) +bool isAcceptableLibraryPathPrefix(const std::string &path) { - std::string testPath = Path::join(path, GMXLIB_SEARCH_DIR); + std::string testPath = Path::join(path, DATA_INSTALL_DIR, "top"); if (isAcceptableLibraryPath(testPath)) { - *result = testPath; return true; } return false; } /*! \brief - * Returns a fallback data path. + * Returns a fallback installation prefix path. * * Checks a few standard locations for the data files before returning a * configure-time hard-coded path. The hard-coded path is preferred if it * actually contains the data files, though. */ -std::string findFallbackLibraryDataPath() +std::string findFallbackInstallationPrefixPath() { #ifndef GMX_NATIVE_WINDOWS - if (!isAcceptableLibraryPath(GMXLIB_FALLBACK)) + if (!isAcceptableLibraryPathPrefix(CMAKE_INSTALL_PREFIX)) { - std::string foundPath; - if (isAcceptableLibraryPathPrefix("/usr/local", &foundPath)) + if (isAcceptableLibraryPathPrefix("/usr/local")) { - return foundPath; + return "/usr/local"; } - if (isAcceptableLibraryPathPrefix("/usr", &foundPath)) + if (isAcceptableLibraryPathPrefix("/usr")) { - return foundPath; + return "/usr"; } - if (isAcceptableLibraryPathPrefix("/opt", &foundPath)) + if (isAcceptableLibraryPathPrefix("/opt")) { - return foundPath; + return "/opt"; } } #endif - return GMXLIB_FALLBACK; + return CMAKE_INSTALL_PREFIX; } /*! \brief * Finds the library data files based on path of the binary. * - * \param[in] binaryPath Absolute path to the binary. + * \param[in] binaryPath Absolute path to the binary. + * \param[out] bSourceLayout Set to `true` if the binary is run from + * the build tree and the original source directory can be found. * \returns Path to the `share/top/` data files. * * The search based on the path only works if the binary is in the same @@ -241,8 +239,10 @@ std::string findFallbackLibraryDataPath() * Extra logic is present to allow running binaries from the build tree such * that they use up-to-date data files from the source tree. */ -std::string findDefaultLibraryDataPath(const std::string &binaryPath) +std::string findInstallationPrefixPath(const std::string &binaryPath, + bool *bSourceLayout) { + *bSourceLayout = false; // If the input path is not absolute, the binary could not be found. // Don't search anything. if (Path::isAbsolute(binaryPath)) @@ -263,18 +263,19 @@ std::string findDefaultLibraryDataPath(const std::string &binaryPath) std::string testPath = Path::join(CMAKE_SOURCE_DIR, "share/top"); if (isAcceptableLibraryPath(testPath)) { - return testPath; + *bSourceLayout = true; + return CMAKE_SOURCE_DIR; } } #endif // Use the executable path to (try to) find the library dir. + // TODO: Consider only going up exactly the required number of levels. while (!searchPath.empty()) { - std::string testPath = Path::join(searchPath, GMXLIB_SEARCH_DIR); - if (isAcceptableLibraryPath(testPath)) + if (isAcceptableLibraryPathPrefix(searchPath)) { - return testPath; + return searchPath; } searchPath = Path::getParentPath(searchPath); } @@ -282,7 +283,7 @@ std::string findDefaultLibraryDataPath(const std::string &binaryPath) // End of smart searching. If we didn't find it in our parent tree, // or if the program name wasn't set, return a fallback. - return findFallbackLibraryDataPath(); + return findFallbackInstallationPrefixPath(); } //! \} @@ -316,18 +317,19 @@ class CommandLineProgramContext::Impl std::string displayName_; std::string commandLine_; mutable std::string fullBinaryPath_; - mutable std::string defaultLibraryDataPath_; + mutable std::string installationPrefix_; + mutable bool bSourceLayout_; mutable tMPI::mutex binaryPathMutex_; }; CommandLineProgramContext::Impl::Impl() - : programName_("GROMACS") + : programName_("GROMACS"), bSourceLayout_(false) { } CommandLineProgramContext::Impl::Impl(int argc, const char *const argv[], ExecutableEnvironmentPointer env) - : executableEnv_(env) + : executableEnv_(env), bSourceLayout_(false) { invokedName_ = (argc != 0 ? argv[0] : ""); programName_ = Path::getFilename(invokedName_); @@ -415,16 +417,19 @@ const char *CommandLineProgramContext::fullBinaryPath() const return impl_->fullBinaryPath_.c_str(); } -const char *CommandLineProgramContext::defaultLibraryDataPath() const +InstallationPrefixInfo CommandLineProgramContext::installationPrefix() const { tMPI::lock_guard lock(impl_->binaryPathMutex_); - if (impl_->defaultLibraryDataPath_.empty()) + if (impl_->installationPrefix_.empty()) { impl_->findBinaryPath(); - impl_->defaultLibraryDataPath_ = - Path::normalize(findDefaultLibraryDataPath(impl_->fullBinaryPath_)); + impl_->installationPrefix_ = + Path::normalize(findInstallationPrefixPath(impl_->fullBinaryPath_, + &impl_->bSourceLayout_)); } - return impl_->defaultLibraryDataPath_.c_str(); + return InstallationPrefixInfo( + impl_->installationPrefix_.c_str(), + impl_->bSourceLayout_); } } // namespace gmx diff --git a/src/gromacs/commandline/cmdlineprogramcontext.h b/src/gromacs/commandline/cmdlineprogramcontext.h index a112c0cb24..e08f350895 100644 --- a/src/gromacs/commandline/cmdlineprogramcontext.h +++ b/src/gromacs/commandline/cmdlineprogramcontext.h @@ -1,7 +1,7 @@ /* * This file is part of the GROMACS molecular simulation package. * - * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by + * Copyright (c) 2012,2013,2014,2015, 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. @@ -196,7 +196,7 @@ class CommandLineProgramContext : public ProgramContextInterface */ virtual const char *fullBinaryPath() const; /*! \brief - * Returns the default path for \Gromacs data files. + * Returns the installation prefix (for finding \Gromacs data files). * * \throws std::bad_alloc if out of memory. * \throws tMPI::system_error on thread synchronization errors. @@ -204,7 +204,7 @@ class CommandLineProgramContext : public ProgramContextInterface * Returns a hardcoded path set during configuration time if there is * an error in finding the library data files. */ - virtual const char *defaultLibraryDataPath() const; + virtual InstallationPrefixInfo installationPrefix() const; /*! \brief * Returns the full command line used to invoke the binary. * diff --git a/src/gromacs/gmxlib/copyrite.cpp b/src/gromacs/gmxlib/copyrite.cpp index 73e6c45732..ab622947ca 100644 --- a/src/gromacs/gmxlib/copyrite.cpp +++ b/src/gromacs/gmxlib/copyrite.cpp @@ -68,6 +68,7 @@ #include "gromacs/utility/gmxassert.h" #include "gromacs/utility/programcontext.h" #include "gromacs/utility/smalloc.h" +#include "gromacs/utility/stringutil.h" static gmx_bool be_cool(void) { @@ -806,17 +807,18 @@ void printBinaryInformation(FILE *fp, fprintf(fp, "%sGROMACS: %s, %s%s%s\n", prefix, name, gmx_version(), precisionString, suffix); const char *const binaryPath = programContext.fullBinaryPath(); - if (binaryPath != NULL && binaryPath[0] != '\0') + if (!gmx::isNullOrEmpty(binaryPath)) { fprintf(fp, "%sExecutable: %s%s\n", prefix, binaryPath, suffix); } - const char *const libraryPath = programContext.defaultLibraryDataPath(); - if (libraryPath != NULL && libraryPath[0] != '\0') + const gmx::InstallationPrefixInfo installPrefix = programContext.installationPrefix(); + if (!gmx::isNullOrEmpty(installPrefix.path)) { - fprintf(fp, "%sLibrary dir: %s%s\n", prefix, libraryPath, suffix); + fprintf(fp, "%sData prefix: %s%s%s\n", prefix, installPrefix.path, + installPrefix.bSourceLayout ? " (source tree)" : "", suffix); } const char *const commandLine = programContext.commandLine(); - if (commandLine != NULL && commandLine[0] != '\0') + if (!gmx::isNullOrEmpty(commandLine)) { fprintf(fp, "%sCommand line:%s\n%s %s%s\n", prefix, suffix, prefix, commandLine, suffix); diff --git a/src/gromacs/utility/datafilefinder.cpp b/src/gromacs/utility/datafilefinder.cpp index dfad443b4a..bdf673df5d 100644 --- a/src/gromacs/utility/datafilefinder.cpp +++ b/src/gromacs/utility/datafilefinder.cpp @@ -43,6 +43,8 @@ #include "datafilefinder.h" +#include "config.h" + #include #include @@ -65,6 +67,8 @@ namespace gmx class DataFileFinder::Impl { public: + static std::string getDefaultPath(); + Impl() : envName_(NULL), bEnvIsSet_(false) {} const char *envName_; @@ -72,6 +76,19 @@ class DataFileFinder::Impl std::vector searchPath_; }; +std::string DataFileFinder::Impl::getDefaultPath() +{ + const InstallationPrefixInfo installPrefix + = getProgramContext().installationPrefix(); + if (!isNullOrEmpty(installPrefix.path)) + { + const char *const dataPath + = installPrefix.bSourceLayout ? "share" : DATA_INSTALL_DIR; + return Path::join(installPrefix.path, dataPath, "top"); + } + return std::string(); +} + /******************************************************************** * DataFileFinder */ @@ -140,8 +157,8 @@ std::string DataFileFinder::findFile(const DataFileOptions &options) const } } } - const char *const defaultPath = getProgramContext().defaultLibraryDataPath(); - if (defaultPath != NULL && defaultPath[0] != '\0') + const std::string &defaultPath = Impl::getDefaultPath(); + if (!defaultPath.empty()) { std::string testPath = Path::join(defaultPath, options.filename_); if (Path::exists(testPath)) @@ -179,7 +196,7 @@ std::string DataFileFinder::findFile(const DataFileOptions &options) const message.append(*i); } } - if (defaultPath != NULL && defaultPath[0] != '\0') + if (!defaultPath.empty()) { message.append("\n "); message.append(defaultPath); @@ -228,12 +245,12 @@ DataFileFinder::enumerateFiles(const DataFileOptions &options) const } } } - const char *const defaultPath = getProgramContext().defaultLibraryDataPath(); - if (defaultPath != NULL && defaultPath[0] != '\0') + const std::string &defaultPath = Impl::getDefaultPath(); + if (!defaultPath.empty()) { std::vector files = DirectoryEnumerator::enumerateFilesWithExtension( - defaultPath, options.filename_, false); + defaultPath.c_str(), options.filename_, false); for (i = files.begin(); i != files.end(); ++i) { result.push_back(DataFileInfo(defaultPath, *i, true)); diff --git a/src/gromacs/utility/programcontext.cpp b/src/gromacs/utility/programcontext.cpp index ac9bc13dd7..5edf9aa284 100644 --- a/src/gromacs/utility/programcontext.cpp +++ b/src/gromacs/utility/programcontext.cpp @@ -1,7 +1,7 @@ /* * This file is part of the GROMACS molecular simulation package. * - * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Copyright (c) 2013,2014,2015, 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. @@ -70,7 +70,10 @@ class DefaultProgramContext : public ProgramContextInterface virtual const char *programName() const { return "GROMACS"; } virtual const char *displayName() const { return "GROMACS"; } virtual const char *fullBinaryPath() const { return ""; } - virtual const char *defaultLibraryDataPath() const { return ""; } + virtual InstallationPrefixInfo installationPrefix() const + { + return InstallationPrefixInfo("", false); + } virtual const char *commandLine() const { return ""; } }; diff --git a/src/gromacs/utility/programcontext.h b/src/gromacs/utility/programcontext.h index 82d349a2a4..d7aede1988 100644 --- a/src/gromacs/utility/programcontext.h +++ b/src/gromacs/utility/programcontext.h @@ -1,7 +1,7 @@ /* * This file is part of the GROMACS molecular simulation package. * - * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Copyright (c) 2013,2014,2015, 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. @@ -49,6 +49,41 @@ namespace gmx //! \addtogroup module_utility //! \{ +/*! \brief + * Provides information about installation prefix (see + * ProgramContextInterface::installationPrefix()). + * + * \inpublicapi + */ +struct InstallationPrefixInfo +{ + //! Initializes the structure with given values. + InstallationPrefixInfo(const char *path, bool bSource) + : path(path), bSourceLayout(bSource) + { + } + + /*! \brief + * Path to the installation prefix of the current \Gromacs instance. + * + * If this is `NULL` or empty, data files cannot be looked up from the + * install tree and \Gromacs functions that access such files may fail. + * This can also contain a path to the source tree (see \a bSourceLayout). + */ + const char *const path; + /*! \brief + * Whether \a path points to a source tree -like layout. + * + * For testing, it is useful to read data files from the source tree. + * For such cases, the program context can return the source tree root path + * in \a path, and set this to `true` to indicate that the data files + * should be searched using the layout of the source tree instead of the + * installation. + */ + const bool bSourceLayout; +}; + + /*! \brief * Provides context information about the program that is calling the library. * @@ -107,15 +142,19 @@ class ProgramContextInterface */ virtual const char *fullBinaryPath() const = 0; /*! \brief - * Returns the default path for \Gromacs data files. + * Returns the installation prefix for \Gromacs. * * This path is used to locate the data files that are in `share/top/` * in the source directory. * The implementation can provide an empty string if the path is not * available; in such a case, functions that require data files may * fail. + * + * The returned structure also contains a flag to indicate whether the + * prefix actually points to the source tree. This is used for tests + * and to support running binaries directly from the build tree. */ - virtual const char *defaultLibraryDataPath() const = 0; + virtual InstallationPrefixInfo installationPrefix() const = 0; /*! \brief * Returns the full command line used to invoke the binary. * diff --git a/src/gromacs/utility/stringutil.h b/src/gromacs/utility/stringutil.h index 5617fbb899..e74d6c9905 100644 --- a/src/gromacs/utility/stringutil.h +++ b/src/gromacs/utility/stringutil.h @@ -1,7 +1,7 @@ /* * This file is part of the GROMACS molecular simulation package. * - * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by + * Copyright (c) 2011,2012,2013,2014,2015, 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. @@ -54,6 +54,16 @@ namespace gmx //! \addtogroup module_utility //! \{ +/*! \brief + * Tests whether a string is null or empty. + * + * Does not throw. + */ +bool inline isNullOrEmpty(const char *str) +{ + return str == NULL || str[0] == '\0'; +} + /*! \brief * Tests whether a string starts with another string. * diff --git a/src/testutils/testinit.cpp b/src/testutils/testinit.cpp index 8bf66f88c1..3969e4c363 100644 --- a/src/testutils/testinit.cpp +++ b/src/testutils/testinit.cpp @@ -82,7 +82,7 @@ namespace /*! \brief * Custom program context for test binaries. * - * This context overrides the defaultLibraryDataPath() implementation to always + * This context overrides the installationPrefix() implementation to always * load data files from the source directory, as the test binaries are never * installed. It also support overriding the directory through a command-line * option to the test binary. @@ -98,8 +98,7 @@ class TestProgramContext : public ProgramContextInterface * \param[in] context Current \Gromacs program context. */ explicit TestProgramContext(const ProgramContextInterface &context) - : context_(context), - dataPath_(Path::join(CMAKE_SOURCE_DIR, "share/top")) + : context_(context), dataPath_(CMAKE_SOURCE_DIR) { } @@ -108,7 +107,7 @@ class TestProgramContext : public ProgramContextInterface */ void overrideSourceRoot(const std::string &sourceRoot) { - dataPath_ = Path::join(sourceRoot, "share/top"); + dataPath_ = sourceRoot; } virtual const char *programName() const @@ -123,9 +122,9 @@ class TestProgramContext : public ProgramContextInterface { return context_.fullBinaryPath(); } - virtual const char *defaultLibraryDataPath() const + virtual InstallationPrefixInfo installationPrefix() const { - return dataPath_.c_str(); + return InstallationPrefixInfo(dataPath_.c_str(), true); } virtual const char *commandLine() const { -- 2.22.0