CMS 3D CMS Logo

FileInPath.cc
Go to the documentation of this file.
1 // ----------------------------------------------------------------------
2 // ----------------------------------------------------------------------
3 
4 #include <atomic>
5 #include <cstdlib>
6 #include <vector>
7 #include <cassert>
8 #include <filesystem>
9 
14 
15 namespace {
16 
17  std::atomic<bool> s_fileLookupDisabled{false};
18 
22 
23  const std::string PathVariableName("CMSSW_SEARCH_PATH");
24  // Environment variables for local and release areas:
25  const std::string LOCALTOP("CMSSW_BASE");
26  const std::string RELEASETOP("CMSSW_RELEASE_BASE");
27  const std::string DATATOP("CMSSW_DATA_PATH");
28 
29 #if 1
30  // Needed for backward compatibility prior to CMSSW_1_5_0_pre3.
31  // String to serve as placeholder for release top.
32  // Do not change this value.
33  const std::string BASE("BASE");
34 #endif
35  const std::string version("V001");
36 
37  // Remove symlinks from path
38  std::string removeSymLinks(std::string const& envName) {
39  char const* const var = std::getenv(envName.c_str());
40  if (var == nullptr) {
41  return std::string();
42  }
45  return path;
46  }
47 
48  std::string removeSymLinksSrc(std::string const& envName) {
49  char const* const var = std::getenv(envName.c_str());
50  if (var == nullptr) {
51  return std::string();
52  }
53  std::string const src = "/src";
54  std::string path = var + src;
56  size_t actualSize = path.size() - src.size();
57  assert(path.substr(actualSize, src.size()) == src);
58  return path.substr(0, actualSize);
59  }
60 
61  std::string removeSymLinksTokens(std::string const& envName) {
62  char const* const var = std::getenv(envName.c_str());
63  if (var == nullptr) {
64  return std::string();
65  }
66  std::string theSearchPath;
67  typedef std::vector<std::string> stringvec_t;
68  stringvec_t pathElements = edm::tokenize(std::string(var), ":");
69  for (auto& element : pathElements) {
71  if (!theSearchPath.empty())
72  theSearchPath += ":";
73  theSearchPath += element;
74  }
75  return theSearchPath;
76  }
77 
78  // Check for existence of a file for the given relative path and
79  // 'prefix'.
80  // Return true if a file (not directory or symbolic link) is found
81  // Return false is *nothing* is found
82  // Throw an exception if either a directory or symbolic link is found.
83  // If true is returned, then put the
84  bool locateFile(std::filesystem::path p, std::string const& relative) {
85  p /= relative;
86 
87  if (!std::filesystem::exists(p))
88  return false;
89 
90  if (std::filesystem::is_directory(p)) {
91  throw edm::Exception(edm::errors::FileInPathError) << "Path " << p.string() << " is a directory, not a file\n";
92  }
93 
94  if (std::filesystem::is_symlink(std::filesystem::symlink_status(p))) {
96  << "Path " << p.string() << " is a symbolic link, not a file\n";
97  }
98  return true;
99  }
100 } // namespace
101 
102 namespace edm {
103 
104  FileInPath::FileInPath() : relativePath_(), canonicalFilename_(), location_(Unknown) {
105  if (s_fileLookupDisabled) {
106  return;
107  }
108  getEnvironment();
109  }
110 
111  FileInPath::FileInPath(const std::string& r) : relativePath_(r), canonicalFilename_(), location_(Unknown) {
112  if (s_fileLookupDisabled) {
113  return;
114  }
115  getEnvironment();
116  initialize_();
117  }
118 
119  FileInPath::FileInPath(char const* r) : relativePath_(r ? r : ""), canonicalFilename_(), location_(Unknown) {
120  if (s_fileLookupDisabled) {
121  return;
122  }
123  if (r == nullptr) {
124  throw edm::Exception(edm::errors::FileInPathError) << "Relative path must not be null\n";
125  }
126  getEnvironment();
127  initialize_();
128  }
129 
131  relativePath_.swap(other.relativePath_);
132  canonicalFilename_.swap(other.canonicalFilename_);
133  std::swap(location_, other.location_);
134  localTop_.swap(other.localTop_);
135  releaseTop_.swap(other.releaseTop_);
136  dataTop_.swap(other.dataTop_);
137  searchPath_.swap(other.searchPath_);
138  }
139 
141 
143 
145 
146  void FileInPath::write(std::ostream& os) const {
147  if (location_ == Unknown) {
148  if (relativePath_.empty()) {
149  os << version << " @ " << location_;
150  } else {
151  os << version << ' ' << relativePath_ << ' ' << location_;
152  }
153  } else if (location_ == Local) {
154  // Guarantee a site independent value by stripping $LOCALTOP.
155  if (localTop_.empty()) {
156  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n";
157  }
159  if (pos != 0) {
161  << "Path " << canonicalFilename_ << " is not in the local release area " << localTop_ << "\n";
162  }
163  os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(localTop_.size());
164  } else if (location_ == Release) {
165  // Guarantee a site independent value by stripping $RELEASETOP.
166  if (releaseTop_.empty()) {
167  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
168  }
170  if (pos != 0) {
172  << "Path " << canonicalFilename_ << " is not in the base release area " << releaseTop_ << "\n";
173  }
174  os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(releaseTop_.size());
175  } else if (location_ == Data) {
176  // Guarantee a site independent value by stripping $DATATOP.
177  if (dataTop_.empty()) {
178  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
179  }
181  if (pos != 0) {
183  << "Path " << canonicalFilename_ << " is not in the data area " << dataTop_ << "\n";
184  }
185  os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(dataTop_.size());
186  }
187  }
188 
189  void FileInPath::read(std::istream& is) {
190  std::string vsn;
191  std::string relname;
192  std::string canFilename;
193 #if 1
194  // This #if needed for backward compatibility
195  // for files written before CMSSW_1_5_0_pre3.
196  is >> vsn;
197  if (!is)
198  return;
199  bool oldFormat = (version != vsn);
200  if (oldFormat) {
201  relname = vsn;
202  bool local;
203  is >> local;
204  location_ = (local ? Local : Release);
205  is >> canFilename;
206  } else {
207  // Current format
208  int loc;
209  is >> relname >> loc;
210  location_ = static_cast<FileInPath::LocationCode>(loc);
211  if (location_ != Unknown) {
212  is >> canFilename;
213  } else if (relname == "@") {
214  relname = "";
215  }
216  }
217 #else
218  is >> vsn >> relname >> loc >> canFilename;
219 #endif
220  if (!is)
221  return;
222  relativePath_ = relname;
223  if (location_ == Local) {
224  if (localTop_.empty()) {
225  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n"
226  << "Trying to read Local file: " << canFilename << ".\n";
227  }
228 #if 1
229  // This #if needed for backward compatibility
230  // for files written before CMSSW_1_5_0_pre3.
231  if (oldFormat) {
232  canonicalFilename_ = canFilename;
233  } else
234 #endif
235  canonicalFilename_ = localTop_ + canFilename;
236  } else if (location_ == Release) {
237  if (releaseTop_.empty()) {
238  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
239  }
240 #if 1
241  // This #if needed for backward compatibility
242  // for files written before CMSSW_1_5_0_pre3.
243  if (oldFormat) {
244  std::string::size_type pos = canFilename.find(BASE);
245  if (pos == 0) {
246  // Replace the placehoder with the path to the base release (site dependent).
247  canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
248  } else {
249  // Needed for files written before CMSSW_1_2_0_pre2.
250  canonicalFilename_ = canFilename;
251  }
252  } else
253 #endif
254  canonicalFilename_ = releaseTop_ + canFilename;
255  } else if (location_ == Data) {
256  if (dataTop_.empty()) {
257  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
258  }
259  canonicalFilename_ = dataTop_ + canFilename;
260  }
261  }
262 
263  void FileInPath::readFromParameterSetBlob(std::istream& is) {
264  std::string vsn;
265  std::string relname;
266  std::string canFilename;
267  is >> vsn;
268  if (!is)
269  return;
270  bool oldFormat = (version != vsn);
271  if (oldFormat) {
272  relname = vsn;
273  bool local;
274  is >> local;
275  location_ = (local ? Local : Release);
276  is >> canFilename;
277  } else {
278  // Current format
279  int loc;
280  is >> relname >> loc;
281  location_ = static_cast<FileInPath::LocationCode>(loc);
282  if (location_ != Unknown) {
283  is >> canFilename;
284  } else if (relname == "@") {
285  relname = "";
286  }
287  }
288  if (!is)
289  return;
290  relativePath_ = relname;
291  if (location_ == Local) {
292  if (localTop_.empty()) {
293  localTop_ = "@LOCAL";
294  }
295  if (oldFormat) {
296  canonicalFilename_ = canFilename;
297  } else
298  canonicalFilename_ = localTop_ + canFilename;
299  } else if (location_ == Release) {
300  if (releaseTop_.empty()) {
301  releaseTop_ = "@RELEASE";
302  }
303  if (oldFormat) {
304  std::string::size_type pos = canFilename.find(BASE);
305  if (pos == 0) {
306  // Replace the placehoder with the path to the base release (site dependent).
307  canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
308  } else {
309  // Needed for files written before CMSSW_1_2_0_pre2.
310  canonicalFilename_ = canFilename;
311  }
312  } else
313  canonicalFilename_ = releaseTop_ + canFilename;
314  } else if (location_ == Data) {
315  if (dataTop_.empty()) {
316  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
317  }
318  canonicalFilename_ = dataTop_ + canFilename;
319  }
320  }
321 
322  //------------------------------------------------------------
324  static std::string const s_searchPath = removeSymLinksTokens(PathVariableName);
325  return s_searchPath;
326  }
327  //------------------------------------------------------------
328 
331  if (searchPath_.empty()) {
332  throw edm::Exception(edm::errors::FileInPathError) << PathVariableName << " must be defined\n";
333  }
334 
335  static std::string const releaseTop = removeSymLinksSrc(RELEASETOP);
336  releaseTop_ = releaseTop;
337 
338  static std::string const localTop = removeSymLinksSrc(LOCALTOP);
339  localTop_ = localTop;
340 
341  static std::string const dataTop = removeSymLinks(DATATOP);
342  dataTop_ = dataTop;
343 
344  if (releaseTop_.empty()) {
345  // RELEASETOP was not set. This means that the environment is set
346  // for the base release itself. So LOCALTOP actually contains the
347  // location of the base release.
349  localTop_.clear();
350  }
351  if (releaseTop_ == localTop_) {
352  // RELEASETOP is the same as LOCALTOP. This means that the environment is set
353  // for the base release itself. So LOCALTOP actually contains the
354  // location of the base release.
355  localTop_.clear();
356  }
357  }
358 
360  if (relativePath_.empty()) {
361  throw edm::Exception(edm::errors::FileInPathError) << "Relative path must not be empty\n";
362  }
363 
364  // Find the file, based on the value of searchPath.
365  typedef std::vector<std::string> stringvec_t;
366  stringvec_t pathElements = tokenize(searchPath_, ":");
367  for (auto const& element : pathElements) {
368  // Set the path to the current element of CMSSW_SEARCH_PATH:
369  std::filesystem::path pathPrefix(element);
370 
371  // Does the a file exist? locateFile throws is it finds
372  // something goofy.
373  if (locateFile(pathPrefix, relativePath_)) {
374  // Convert relative path to canonical form, and save it.
375  relativePath_ = std::filesystem::path(relativePath_).lexically_normal().string();
376  //std::filesystem::path(relativePath_).normalize().string();
377 
378  // Save the absolute path.
379  canonicalFilename_ = std::filesystem::absolute(pathPrefix / relativePath_).string();
380  if (canonicalFilename_.empty()) {
382  << "fullPath is empty"
383  << "\nrelativePath() is: " << relativePath_ << "\npath prefix is: " << pathPrefix.string() << '\n';
384  }
385 
386  // From the current path element, find the branch path (basically the path minus the
387  // last directory, e.g. /src or /share):
388  for (std::filesystem::path br = pathPrefix.parent_path();
389  !std::filesystem::weakly_canonical(br).string().empty();
390  br = br.parent_path()) {
391  if (!localTop_.empty()) {
392  // Create a path object for our local path LOCALTOP:
394  // If the branch path matches the local path, the file was found locally:
395  if (br == local_) {
396  location_ = Local;
397  return;
398  }
399  }
400 
401  if (!releaseTop_.empty()) {
402  // Create a path object for our release path RELEASETOP:
404  // If the branch path matches the release path, the file was found in the release:
405  if (br == release_) {
406  location_ = Release;
407  return;
408  }
409  }
410 
411  if (!dataTop_.empty()) {
412  // Create a path object for our data path DATATOP:
414  // If the branch path matches the data path, the file was found in the data area:
415  if (br == data_) {
416  location_ = Data;
417  return;
418  }
419  }
420  }
421  }
422  }
423 
424  // If we got here, we ran out of path elements without finding
425  // what we're looking found.
427  << "edm::FileInPath unable to find file " << relativePath_ << " anywhere in the search path."
428  << "\nThe search path is defined by: " << PathVariableName << "\n${" << PathVariableName
429  << "} is: " << std::getenv(PathVariableName.c_str())
430  << "\nCurrent directory is: " << std::filesystem::current_path().string() << "\n";
431  }
432 
433  void FileInPath::disableFileLookup() { s_fileLookupDisabled = true; }
434 
436  // Find the file, based on the value of path variable.
437  auto pathElements = tokenize(searchPath(), ":");
438  for (auto const& element : pathElements) {
439  // Set the boost::fs path to the current element of
440  // CMSSW_SEARCH_PATH:
441  std::filesystem::path pathPrefix(element);
442 
443  // Does the a file exist? locateFile throws is it finds
444  // something goofy.
445  if (locateFile(pathPrefix, iFileName)) {
446  // Convert relative path to canonical form, and save it.
447  return std::filesystem::absolute(pathPrefix / iFileName).string();
448  }
449  }
450  return {};
451  }
452 
453 } // namespace edm
std::string canonicalFilename_
Definition: FileInPath.h:113
void swap(FileInPath &other)
Definition: FileInPath.cc:130
static void disableFileLookup()
Should only be called while the edmWriteConfigs executable runs.
Definition: FileInPath.cc:433
std::string dataTop_
Definition: FileInPath.h:117
void getEnvironment()
Definition: FileInPath.cc:329
std::string searchPath_
Definition: FileInPath.h:118
assert(be >=bs)
uint16_t size_type
void swap(Association< C > &lhs, Association< C > &rhs)
Definition: Association.h:112
std::string releaseTop_
Definition: FileInPath.h:116
LocationCode location_
Definition: FileInPath.h:114
void read(std::istream &is)
Definition: FileInPath.cc:189
static std::string const & searchPath()
Definition: FileInPath.cc:323
LocationCode location() const
Where was the file found?
Definition: FileInPath.cc:142
void initialize_()
Definition: FileInPath.cc:359
const std::string & relativePath() const
Definition: FileInPath.cc:140
std::vector< std::string > tokenize(std::string const &input, std::string const &separator)
breaks the input string into tokens, delimited by the separator
Definition: Parse.cc:52
std::string relativePath_
Definition: FileInPath.h:112
HLT enums.
static std::string findFile(std::string const &)
Definition: FileInPath.cc:435
void readFromParameterSetBlob(std::istream &is)
Definition: FileInPath.cc:263
const std::string & fullPath() const
Definition: FileInPath.cc:144
void resolveSymbolicLinks(std::string &fullPath)
void write(std::ostream &os) const
Definition: FileInPath.cc:146
std::string localTop_
Definition: FileInPath.h:115