SYCL: Avoid using no_init read accessor in rocFFT
[alexxy/gromacs.git] / src / gromacs / utility / datafilefinder.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
5  * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Implements gmx::DataFileFinder.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_utility
42  */
43 #include "gmxpre.h"
44
45 #include "datafilefinder.h"
46
47 #include <cstdlib>
48
49 #include <set>
50 #include <string>
51 #include <vector>
52
53 #include "buildinfo.h"
54 #include "gromacs/utility/directoryenumerator.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/filestream.h"
57 #include "gromacs/utility/path.h"
58 #include "gromacs/utility/programcontext.h"
59 #include "gromacs/utility/stringutil.h"
60
61 namespace gmx
62 {
63
64 /********************************************************************
65  * DataFileFinder::Impl
66  */
67
68 class DataFileFinder::Impl
69 {
70 public:
71     static std::string getDefaultPath();
72
73     Impl() : envName_(nullptr), bEnvIsSet_(false) {}
74
75     const char*              envName_;
76     bool                     bEnvIsSet_;
77     std::vector<std::string> searchPath_;
78 };
79
80 std::string DataFileFinder::Impl::getDefaultPath()
81 {
82     const InstallationPrefixInfo installPrefix = getProgramContext().installationPrefix();
83     if (!isNullOrEmpty(installPrefix.path))
84     {
85         const char* const dataPath = installPrefix.bSourceLayout ? "share" : GMX_INSTALL_GMXDATADIR;
86         return Path::join(installPrefix.path, dataPath, "top");
87     }
88     return std::string();
89 }
90
91 /********************************************************************
92  * DataFileFinder
93  */
94
95 DataFileFinder::DataFileFinder() : impl_(nullptr) {}
96
97 DataFileFinder::~DataFileFinder() {}
98
99 void DataFileFinder::setSearchPathFromEnv(const char* envVarName)
100 {
101     if (!impl_)
102     {
103         impl_ = std::make_unique<Impl>();
104     }
105     impl_->envName_       = envVarName;
106     const char* const lib = getenv(envVarName);
107     if (!isNullOrEmpty(lib))
108     {
109         std::vector<std::string>& path        = impl_->searchPath_; // convenience
110         const std::string         defaultPath = impl_->getDefaultPath();
111         std::vector<std::string>  tmpPath;
112         Path::splitPathEnvironment(lib, &tmpPath);
113         std::set<std::string> pathsSeen;
114         pathsSeen.insert(defaultPath);
115         for (auto& d : tmpPath)
116         {
117             if (!pathsSeen.count(d))
118             {
119                 path.push_back(d);
120                 pathsSeen.insert(d);
121             }
122         }
123         impl_->bEnvIsSet_ = true;
124     }
125 }
126
127 FilePtr DataFileFinder::openFile(const DataFileOptions& options) const
128 {
129     // TODO: There is a small race here, since there is some time between
130     // the exists() calls and actually opening the file.  It would be better
131     // to leave the file open after a successful exists() if the desire is to
132     // actually open the file.
133     std::string filename = findFile(options);
134     if (filename.empty())
135     {
136         return nullptr;
137     }
138 #if 0
139     if (debug)
140     {
141         fprintf(debug, "Opening library file %s\n", fn);
142     }
143 #endif
144     return TextInputFile::openRawHandle(filename);
145 }
146
147 std::string DataFileFinder::findFile(const DataFileOptions& options) const
148 {
149     if (options.bCurrentDir_ && Path::exists(options.filename_))
150     {
151         return options.filename_;
152     }
153     if (impl_ != nullptr)
154     {
155         std::vector<std::string>::const_iterator i;
156         for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
157         {
158             // TODO: Deal with an empty search path entry more reasonably.
159             std::string testPath = Path::join(*i, options.filename_);
160             // TODO: Consider skipping directories.
161             if (Path::exists(testPath))
162             {
163                 return testPath;
164             }
165         }
166     }
167     const std::string& defaultPath = Impl::getDefaultPath();
168     if (!defaultPath.empty())
169     {
170         std::string testPath = Path::join(defaultPath, options.filename_);
171         if (Path::exists(testPath))
172         {
173             return testPath;
174         }
175     }
176     if (options.bThrow_)
177     {
178         const char* const envName   = (impl_ != nullptr ? impl_->envName_ : nullptr);
179         const bool        bEnvIsSet = (impl_ != nullptr ? impl_->bEnvIsSet_ : false);
180         std::string       message(formatString("Library file '%s' not found", options.filename_));
181         if (options.bCurrentDir_)
182         {
183             message.append(" in current dir nor");
184         }
185         if (bEnvIsSet)
186         {
187             message.append(formatString(" in your %s path nor", envName));
188         }
189         message.append(" in the default directories.\nThe following paths were searched:");
190         if (options.bCurrentDir_)
191         {
192             message.append("\n  ");
193             message.append(Path::getWorkingDirectory());
194             message.append(" (current dir)");
195         }
196         if (impl_ != nullptr)
197         {
198             std::vector<std::string>::const_iterator i;
199             for (i = impl_->searchPath_.begin(); i != impl_->searchPath_.end(); ++i)
200             {
201                 message.append("\n  ");
202                 message.append(*i);
203             }
204         }
205         if (!defaultPath.empty())
206         {
207             message.append("\n  ");
208             message.append(defaultPath);
209             message.append(" (default)");
210         }
211         if (!bEnvIsSet && envName != nullptr)
212         {
213             message.append(
214                     formatString("\nYou can set additional directories to search "
215                                  "with the %s path variable.",
216                                  envName));
217         }
218         GMX_THROW(FileIOError(message));
219     }
220     return std::string();
221 }
222
223 std::vector<DataFileInfo> DataFileFinder::enumerateFiles(const DataFileOptions& options) const
224 {
225     // TODO: Consider if not being able to list one of the directories should
226     // really be a fatal error. Or alternatively, check somewhere else that
227     // paths in GMXLIB are valid.
228     std::vector<DataFileInfo>                result;
229     std::vector<std::string>::const_iterator i;
230     if (options.bCurrentDir_)
231     {
232         std::vector<std::string> files =
233                 DirectoryEnumerator::enumerateFilesWithExtension(".", options.filename_, false);
234         for (i = files.begin(); i != files.end(); ++i)
235         {
236             result.emplace_back(".", *i, false);
237         }
238     }
239     if (impl_ != nullptr)
240     {
241         std::vector<std::string>::const_iterator j;
242         for (j = impl_->searchPath_.begin(); j != impl_->searchPath_.end(); ++j)
243         {
244             std::vector<std::string> files = DirectoryEnumerator::enumerateFilesWithExtension(
245                     j->c_str(), options.filename_, false);
246             for (i = files.begin(); i != files.end(); ++i)
247             {
248                 result.emplace_back(*j, *i, false);
249             }
250         }
251     }
252     const std::string& defaultPath = Impl::getDefaultPath();
253     if (!defaultPath.empty())
254     {
255         std::vector<std::string> files = DirectoryEnumerator::enumerateFilesWithExtension(
256                 defaultPath.c_str(), options.filename_, false);
257         for (i = files.begin(); i != files.end(); ++i)
258         {
259             result.emplace_back(defaultPath, *i, true);
260         }
261     }
262     if (result.empty() && options.bThrow_)
263     {
264         // TODO: Print the search path as is done in findFile().
265         std::string message(
266                 formatString("Could not find any files ending on '%s' in the "
267                              "current directory or the GROMACS library search path",
268                              options.filename_));
269         GMX_THROW(FileIOError(message));
270     }
271     return result;
272 }
273
274 } // namespace gmx