2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements gmx::ProgramInfo.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_utility
42 #include "programinfo.h"
54 #include <boost/scoped_ptr.hpp>
56 #include "gromacs/legacyheaders/thread_mpi/mutex.h"
58 #include "gromacs/utility/exceptions.h"
59 #include "gromacs/utility/file.h"
60 #include "gromacs/utility/gmxassert.h"
61 #include "gromacs/utility/path.h"
62 #include "gromacs/utility/stringutil.h"
70 //! \addtogroup module_utility
73 //! Mutex for updates to the global program info objects.
74 tMPI::mutex g_programInfoMutex;
75 //! Global program info; stores the object initialized with ProgramInfo::init().
76 boost::scoped_ptr<ProgramInfo> g_programInfo;
79 * Quotes a string if it contains spaces.
81 std::string quoteIfNecessary(const char *str)
83 const bool bSpaces = (std::strchr(str, ' ') != NULL);
86 return formatString("'%s'", str);
92 * Default implementation for ExecutableEnvironmentInterface.
94 * Used if ExecutableEnvironmentInterface is not explicitly provided when
95 * constructing ProgramInfo.
97 class DefaultExecutableEnvironment : public ExecutableEnvironmentInterface
100 //! Allocates a default environment.
101 static ExecutableEnvironmentPointer create()
103 return ExecutableEnvironmentPointer(new DefaultExecutableEnvironment());
106 DefaultExecutableEnvironment()
107 : initialWorkingDirectory_(Path::getWorkingDirectory())
111 virtual std::string getWorkingDirectory() const
113 return initialWorkingDirectory_;
115 virtual std::vector<std::string> getExecutablePaths() const
117 return Path::getExecutablePaths();
121 std::string initialWorkingDirectory_;
125 * Finds the absolute path of the binary from \c argv[0].
127 * \param[in] invokedName \c argv[0] the binary was invoked with.
128 * \param[in] env Executable environment.
129 * \returns The full path of the binary.
131 * If a binary with the given name cannot be located, \p invokedName is
134 std::string findFullBinaryPath(const std::string &invokedName,
135 const ExecutableEnvironmentInterface &env)
137 std::string searchName = invokedName;
138 // On Windows & Cygwin we need to add the .exe extension,
139 // or we wont be able to detect that the file exists.
140 #if (defined GMX_NATIVE_WINDOWS || defined GMX_CYGWIN)
141 if (!endsWith(searchName, ".exe"))
143 searchName.append(".exe");
146 if (!Path::containsDirectory(searchName))
148 // No directory in name means it must be in the path - search it!
149 std::vector<std::string> pathEntries = env.getExecutablePaths();
150 std::vector<std::string>::const_iterator i;
151 for (i = pathEntries.begin(); i != pathEntries.end(); ++i)
153 const std::string &dir = i->empty() ? env.getWorkingDirectory() : *i;
154 std::string testPath = Path::join(dir, searchName);
155 if (File::exists(testPath))
161 else if (!Path::isAbsolute(searchName))
163 // Name contains directories, but is not absolute, i.e.,
164 // it is relative to the current directory.
165 std::string cwd = env.getWorkingDirectory();
166 std::string testPath = Path::join(cwd, searchName);
176 /********************************************************************
180 class ProgramInfo::Impl
184 Impl(const char *realBinaryName, int argc, const char *const argv[],
185 ExecutableEnvironmentPointer env);
187 ExecutableEnvironmentPointer executableEnv_;
188 std::string realBinaryName_;
189 std::string invokedName_;
190 std::string programName_;
191 std::string invariantProgramName_;
192 std::string displayName_;
193 std::string commandLine_;
194 mutable std::string fullBinaryPath_;
195 mutable tMPI::mutex displayNameMutex_;
196 mutable tMPI::mutex binaryPathMutex_;
199 ProgramInfo::Impl::Impl()
200 : realBinaryName_("GROMACS"),
201 programName_("GROMACS"), invariantProgramName_("GROMACS")
205 ProgramInfo::Impl::Impl(const char *realBinaryName,
206 int argc, const char *const argv[],
207 ExecutableEnvironmentPointer env)
208 : executableEnv_(move(env)),
209 realBinaryName_(realBinaryName != NULL ? realBinaryName : "")
211 invokedName_ = (argc != 0 ? argv[0] : "");
212 programName_ = Path::splitToPathAndFilename(invokedName_).second;
213 programName_ = stripSuffixIfPresent(programName_, ".exe");
214 invariantProgramName_ = programName_;
215 #ifdef GMX_BINARY_SUFFIX
216 invariantProgramName_ =
217 stripSuffixIfPresent(invariantProgramName_, GMX_BINARY_SUFFIX);
219 if (realBinaryName == NULL)
221 realBinaryName_ = invariantProgramName_;
224 commandLine_ = quoteIfNecessary(programName_.c_str());
225 for (int i = 1; i < argc; ++i)
227 commandLine_.append(" ");
228 commandLine_.append(quoteIfNecessary(argv[i]));
232 /********************************************************************
237 const ProgramInfo &ProgramInfo::getInstance()
239 tMPI::lock_guard<tMPI::mutex> lock(g_programInfoMutex);
240 if (g_programInfo.get() == NULL)
242 static ProgramInfo fallbackInfo;
245 return *g_programInfo;
249 ProgramInfo &ProgramInfo::init(int argc, const char *const argv[])
251 return init(NULL, argc, argv);
255 ProgramInfo &ProgramInfo::init(const char *realBinaryName,
256 int argc, const char *const argv[])
260 tMPI::lock_guard<tMPI::mutex> lock(g_programInfoMutex);
261 if (g_programInfo.get() == NULL)
263 g_programInfo.reset(new ProgramInfo(realBinaryName, argc, argv));
265 return *g_programInfo;
267 catch (const std::exception &ex)
269 printFatalErrorMessage(stderr, ex);
270 std::exit(processExceptionAtExit(ex));
274 ProgramInfo::ProgramInfo()
279 ProgramInfo::ProgramInfo(const char *realBinaryName)
280 : impl_(new Impl(realBinaryName, 1, &realBinaryName,
281 DefaultExecutableEnvironment::create()))
285 ProgramInfo::ProgramInfo(int argc, const char *const argv[])
286 : impl_(new Impl(NULL, argc, argv,
287 DefaultExecutableEnvironment::create()))
291 ProgramInfo::ProgramInfo(const char *realBinaryName,
292 int argc, const char *const argv[])
293 : impl_(new Impl(realBinaryName, argc, argv,
294 DefaultExecutableEnvironment::create()))
298 ProgramInfo::ProgramInfo(const char *realBinaryName,
299 int argc, const char *const argv[],
300 ExecutableEnvironmentPointer env)
301 : impl_(new Impl(realBinaryName, argc, argv, move(env)))
305 ProgramInfo::~ProgramInfo()
309 void ProgramInfo::setDisplayName(const std::string &name)
311 tMPI::lock_guard<tMPI::mutex> lock(impl_->displayNameMutex_);
312 GMX_RELEASE_ASSERT(impl_->displayName_.empty(),
313 "Can only set display name once");
314 impl_->displayName_ = name;
317 const std::string &ProgramInfo::realBinaryName() const
319 return impl_->realBinaryName_;
322 const std::string &ProgramInfo::programName() const
324 return impl_->programName_;
327 const std::string &ProgramInfo::invariantProgramName() const
329 return impl_->invariantProgramName_;
332 const std::string &ProgramInfo::displayName() const
334 tMPI::lock_guard<tMPI::mutex> lock(impl_->displayNameMutex_);
335 return impl_->displayName_.empty()
336 ? impl_->programName_
337 : impl_->displayName_;
340 const std::string &ProgramInfo::commandLine() const
342 return impl_->commandLine_;
345 const std::string &ProgramInfo::fullBinaryPath() const
347 tMPI::lock_guard<tMPI::mutex> lock(impl_->binaryPathMutex_);
348 if (impl_->fullBinaryPath_.empty())
350 impl_->fullBinaryPath_ =
352 Path::resolveSymlinks(
353 findFullBinaryPath(impl_->invokedName_,
354 *impl_->executableEnv_)));
355 // TODO: Investigate/Consider using a dladdr()-based solution.
356 // Potentially less portable, but significantly simpler, and also works
357 // with user binaries even if they are located in some arbitrary location,
358 // as long as shared libraries are used.
360 return impl_->fullBinaryPath_;