Merge remote-tracking branch 'origin/release-4-6' into HEAD
[alexxy/gromacs.git] / src / gromacs / utility / programinfo.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements gmx::ProgramInfo.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_utility
37  */
38 #include "programinfo.h"
39
40 // For GMX_BINARY_SUFFIX
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <cstdlib>
46 #include <cstring>
47
48 #include <algorithm>
49 #include <string>
50
51 #include <boost/scoped_ptr.hpp>
52
53 #include "gromacs/legacyheaders/futil.h"
54 #include "gromacs/legacyheaders/thread_mpi/mutex.h"
55
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/path.h"
58 #include "gromacs/utility/stringutil.h"
59
60 namespace gmx
61 {
62
63 namespace
64 {
65 tMPI::mutex g_programInfoMutex;
66 //! Partially filled program info, needed to support set_program_name().
67 boost::scoped_ptr<ProgramInfo> g_partialProgramInfo;
68 boost::scoped_ptr<ProgramInfo> g_programInfo;
69 } // namespace
70
71 /********************************************************************
72  * ProgramInfo::Impl
73  */
74
75 class ProgramInfo::Impl
76 {
77     public:
78         Impl();
79         Impl(const char *realBinaryName, int argc, const char *const argv[]);
80
81         std::string realBinaryName_;
82         std::string fullInvokedProgram_;
83         std::string programName_;
84         std::string invariantProgramName_;
85         std::string commandLine_;
86 };
87
88 ProgramInfo::Impl::Impl()
89     : realBinaryName_("GROMACS"), fullInvokedProgram_("GROMACS"),
90       programName_("GROMACS"), invariantProgramName_("GROMACS")
91 {
92 }
93
94 ProgramInfo::Impl::Impl(const char *realBinaryName,
95                         int argc, const char *const argv[])
96     : realBinaryName_(realBinaryName != NULL ? realBinaryName : ""),
97       fullInvokedProgram_(argc != 0 ? argv[0] : ""),
98       programName_(Path::splitToPathAndFilename(fullInvokedProgram_).second)
99 {
100     // Temporary hack to make things work on Windows while waiting for #950.
101     // Some places in the existing code expect to have DIR_SEPARATOR in all
102     // input paths, but Windows may also give '/' (and does that, e.g., for
103     // tests invoked through CTest).
104     // When removing this, remove also the #include "futil.h".
105     if (DIR_SEPARATOR == '\\')
106     {
107         std::replace(fullInvokedProgram_.begin(), fullInvokedProgram_.end(),
108                      '/', '\\');
109     }
110     programName_ = stripSuffixIfPresent(programName_, ".exe");
111     invariantProgramName_ = programName_;
112 #ifdef GMX_BINARY_SUFFIX
113     invariantProgramName_ =
114         stripSuffixIfPresent(invariantProgramName_, GMX_BINARY_SUFFIX);
115 #endif
116     if (realBinaryName == NULL)
117     {
118         realBinaryName_ = invariantProgramName_;
119     }
120
121     for (int i = 0; i < argc; ++i)
122     {
123         if (i > 0)
124         {
125             commandLine_.append(" ");
126         }
127         const char *arg = argv[i];
128         bool bSpaces = (std::strchr(arg, ' ') != NULL);
129         if (bSpaces)
130         {
131             commandLine_.append("'");
132         }
133         commandLine_.append(arg);
134         if (bSpaces)
135         {
136             commandLine_.append("'");
137         }
138     }
139 }
140
141 /********************************************************************
142  * ProgramInfo
143  */
144
145 // static
146 const ProgramInfo &ProgramInfo::getInstance()
147 {
148     tMPI::lock_guard<tMPI::mutex> lock(g_programInfoMutex);
149     if (g_programInfo.get() == NULL)
150     {
151         if (g_partialProgramInfo.get() != NULL)
152         {
153             return *g_partialProgramInfo;
154         }
155         static ProgramInfo fallbackInfo;
156         return fallbackInfo;
157     }
158     return *g_programInfo;
159 }
160
161 // static
162 const ProgramInfo &ProgramInfo::init(int argc, const char *const argv[])
163 {
164     return init(NULL, argc, argv);
165 }
166
167 // static
168 const ProgramInfo &ProgramInfo::init(const char *realBinaryName,
169                                      int argc, const char *const argv[])
170 {
171     try
172     {
173         tMPI::lock_guard<tMPI::mutex> lock(g_programInfoMutex);
174         if (g_programInfo.get() == NULL)
175         {
176             // TODO: Remove this hack with negative argc once there is no need for
177             // set_program_name().
178             if (argc < 0)
179             {
180                 if (g_partialProgramInfo.get() == NULL)
181                 {
182                     g_partialProgramInfo.reset(
183                             new ProgramInfo(realBinaryName, -argc, argv));
184                 }
185                 return *g_partialProgramInfo;
186             }
187             g_programInfo.reset(new ProgramInfo(realBinaryName, argc, argv));
188         }
189         return *g_programInfo;
190     }
191     catch (const std::exception &ex)
192     {
193         printFatalErrorMessage(stderr, ex);
194         std::exit(1);
195     }
196 }
197
198 ProgramInfo::ProgramInfo()
199     : impl_(new Impl)
200 {
201 }
202
203 ProgramInfo::ProgramInfo(const char *realBinaryName)
204     : impl_(new Impl(realBinaryName, 1, &realBinaryName))
205 {
206 }
207
208 ProgramInfo::ProgramInfo(int argc, const char *const argv[])
209     : impl_(new Impl(NULL, argc, argv))
210 {
211 }
212
213 ProgramInfo::ProgramInfo(const char *realBinaryName,
214                          int argc, const char *const argv[])
215     : impl_(new Impl(realBinaryName, argc, argv))
216 {
217 }
218
219 ProgramInfo::~ProgramInfo()
220 {
221 }
222
223 const std::string &ProgramInfo::realBinaryName() const
224 {
225     return impl_->realBinaryName_;
226 }
227
228 const std::string &ProgramInfo::programNameWithPath() const
229 {
230     return impl_->fullInvokedProgram_;
231 }
232
233 const std::string &ProgramInfo::programName() const
234 {
235     return impl_->programName_;
236 }
237
238 const std::string &ProgramInfo::invariantProgramName() const
239 {
240     return impl_->invariantProgramName_;
241 }
242
243 const std::string &ProgramInfo::commandLine() const
244 {
245     return impl_->commandLine_;
246 }
247
248 } // namespace gmx