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_(other.relativePath_),
132  canonicalFilename_(other.canonicalFilename_),
133  location_(other.location_),
134  localTop_(other.localTop_),
135  releaseTop_(other.releaseTop_),
136  dataTop_(other.dataTop_),
137  searchPath_(other.searchPath_) {}
138 
140 
143  this->swap(temp);
144  return *this;
145  }
146 
148  relativePath_.swap(other.relativePath_);
149  canonicalFilename_.swap(other.canonicalFilename_);
150  std::swap(location_, other.location_);
151  localTop_.swap(other.localTop_);
152  releaseTop_.swap(other.releaseTop_);
153  dataTop_.swap(other.dataTop_);
154  searchPath_.swap(other.searchPath_);
155  }
156 
158 
160 
162 
163  void FileInPath::write(std::ostream& os) const {
164  if (location_ == Unknown) {
165  os << version << ' ' << relativePath_ << ' ' << location_;
166  } else if (location_ == Local) {
167  // Guarantee a site independent value by stripping $LOCALTOP.
168  if (localTop_.empty()) {
169  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n";
170  }
172  if (pos != 0) {
174  << "Path " << canonicalFilename_ << " is not in the local release area " << localTop_ << "\n";
175  }
176  os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(localTop_.size());
177  } else if (location_ == Release) {
178  // Guarantee a site independent value by stripping $RELEASETOP.
179  if (releaseTop_.empty()) {
180  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
181  }
183  if (pos != 0) {
185  << "Path " << canonicalFilename_ << " is not in the base release area " << releaseTop_ << "\n";
186  }
187  os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(releaseTop_.size());
188  } else if (location_ == Data) {
189  // Guarantee a site independent value by stripping $DATATOP.
190  if (dataTop_.empty()) {
191  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
192  }
194  if (pos != 0) {
196  << "Path " << canonicalFilename_ << " is not in the data area " << dataTop_ << "\n";
197  }
198  os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(dataTop_.size());
199  }
200  }
201 
202  void FileInPath::read(std::istream& is) {
203  std::string vsn;
204  std::string relname;
205  std::string canFilename;
206 #if 1
207  // This #if needed for backward compatibility
208  // for files written before CMSSW_1_5_0_pre3.
209  is >> vsn;
210  if (!is)
211  return;
212  bool oldFormat = (version != vsn);
213  if (oldFormat) {
214  relname = vsn;
215  bool local;
216  is >> local;
217  location_ = (local ? Local : Release);
218  is >> canFilename;
219  } else {
220  // Current format
221  int loc;
222  is >> relname >> loc;
223  location_ = static_cast<FileInPath::LocationCode>(loc);
224  if (location_ != Unknown)
225  is >> canFilename;
226  }
227 #else
228  is >> vsn >> relname >> loc >> canFilename;
229 #endif
230  if (!is)
231  return;
232  relativePath_ = relname;
233  if (location_ == Local) {
234  if (localTop_.empty()) {
235  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n"
236  << "Trying to read Local file: " << canFilename << ".\n";
237  }
238 #if 1
239  // This #if needed for backward compatibility
240  // for files written before CMSSW_1_5_0_pre3.
241  if (oldFormat) {
242  canonicalFilename_ = canFilename;
243  } else
244 #endif
245  canonicalFilename_ = localTop_ + canFilename;
246  } else if (location_ == Release) {
247  if (releaseTop_.empty()) {
248  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
249  }
250 #if 1
251  // This #if needed for backward compatibility
252  // for files written before CMSSW_1_5_0_pre3.
253  if (oldFormat) {
254  std::string::size_type pos = canFilename.find(BASE);
255  if (pos == 0) {
256  // Replace the placehoder with the path to the base release (site dependent).
257  canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
258  } else {
259  // Needed for files written before CMSSW_1_2_0_pre2.
260  canonicalFilename_ = canFilename;
261  }
262  } else
263 #endif
264  canonicalFilename_ = releaseTop_ + canFilename;
265  } else if (location_ == Data) {
266  if (dataTop_.empty()) {
267  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
268  }
269  canonicalFilename_ = dataTop_ + canFilename;
270  }
271  }
272 
273  void FileInPath::readFromParameterSetBlob(std::istream& is) {
274  std::string vsn;
275  std::string relname;
276  std::string canFilename;
277  is >> vsn;
278  if (!is)
279  return;
280  bool oldFormat = (version != vsn);
281  if (oldFormat) {
282  relname = vsn;
283  bool local;
284  is >> local;
285  location_ = (local ? Local : Release);
286  is >> canFilename;
287  } else {
288  // Current format
289  int loc;
290  is >> relname >> loc;
291  location_ = static_cast<FileInPath::LocationCode>(loc);
292  if (location_ != Unknown)
293  is >> canFilename;
294  }
295  if (!is)
296  return;
297  relativePath_ = relname;
298  if (location_ == Local) {
299  if (localTop_.empty()) {
300  localTop_ = "@LOCAL";
301  }
302  if (oldFormat) {
303  canonicalFilename_ = canFilename;
304  } else
305  canonicalFilename_ = localTop_ + canFilename;
306  } else if (location_ == Release) {
307  if (releaseTop_.empty()) {
308  releaseTop_ = "@RELEASE";
309  }
310  if (oldFormat) {
311  std::string::size_type pos = canFilename.find(BASE);
312  if (pos == 0) {
313  // Replace the placehoder with the path to the base release (site dependent).
314  canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
315  } else {
316  // Needed for files written before CMSSW_1_2_0_pre2.
317  canonicalFilename_ = canFilename;
318  }
319  } else
320  canonicalFilename_ = releaseTop_ + canFilename;
321  } else if (location_ == Data) {
322  if (dataTop_.empty()) {
323  throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
324  }
325  canonicalFilename_ = dataTop_ + canFilename;
326  }
327  }
328 
329  //------------------------------------------------------------
331  static std::string const s_searchPath = removeSymLinksTokens(PathVariableName);
332  return s_searchPath;
333  }
334  //------------------------------------------------------------
335 
338  if (searchPath_.empty()) {
339  throw edm::Exception(edm::errors::FileInPathError) << PathVariableName << " must be defined\n";
340  }
341 
342  static std::string const releaseTop = removeSymLinksSrc(RELEASETOP);
343  releaseTop_ = releaseTop;
344 
345  static std::string const localTop = removeSymLinksSrc(LOCALTOP);
346  localTop_ = localTop;
347 
348  static std::string const dataTop = removeSymLinks(DATATOP);
349  dataTop_ = dataTop;
350 
351  if (releaseTop_.empty()) {
352  // RELEASETOP was not set. This means that the environment is set
353  // for the base release itself. So LOCALTOP actually contains the
354  // location of the base release.
356  localTop_.clear();
357  }
358  if (releaseTop_ == localTop_) {
359  // RELEASETOP is the same as LOCALTOP. This means that the environment is set
360  // for the base release itself. So LOCALTOP actually contains the
361  // location of the base release.
362  localTop_.clear();
363  }
364  }
365 
367  if (relativePath_.empty()) {
368  throw edm::Exception(edm::errors::FileInPathError) << "Relative path must not be empty\n";
369  }
370 
371  // Find the file, based on the value of searchPath.
372  typedef std::vector<std::string> stringvec_t;
373  stringvec_t pathElements = tokenize(searchPath_, ":");
374  for (auto const& element : pathElements) {
375  // Set the path to the current element of CMSSW_SEARCH_PATH:
376  std::filesystem::path pathPrefix(element);
377 
378  // Does the a file exist? locateFile throws is it finds
379  // something goofy.
380  if (locateFile(pathPrefix, relativePath_)) {
381  // Convert relative path to canonical form, and save it.
382  relativePath_ = std::filesystem::path(relativePath_).lexically_normal().string();
383  //std::filesystem::path(relativePath_).normalize().string();
384 
385  // Save the absolute path.
386  canonicalFilename_ = std::filesystem::absolute(pathPrefix / relativePath_).string();
387  if (canonicalFilename_.empty()) {
389  << "fullPath is empty"
390  << "\nrelativePath() is: " << relativePath_ << "\npath prefix is: " << pathPrefix.string() << '\n';
391  }
392 
393  // From the current path element, find the branch path (basically the path minus the
394  // last directory, e.g. /src or /share):
395  for (std::filesystem::path br = pathPrefix.parent_path();
396  !std::filesystem::weakly_canonical(br).string().empty();
397  br = br.parent_path()) {
398  if (!localTop_.empty()) {
399  // Create a path object for our local path LOCALTOP:
401  // If the branch path matches the local path, the file was found locally:
402  if (br == local_) {
403  location_ = Local;
404  return;
405  }
406  }
407 
408  if (!releaseTop_.empty()) {
409  // Create a path object for our release path RELEASETOP:
411  // If the branch path matches the release path, the file was found in the release:
412  if (br == release_) {
413  location_ = Release;
414  return;
415  }
416  }
417 
418  if (!dataTop_.empty()) {
419  // Create a path object for our data path DATATOP:
421  // If the branch path matches the data path, the file was found in the data area:
422  if (br == data_) {
423  location_ = Data;
424  return;
425  }
426  }
427  }
428  }
429  }
430 
431  // If we got here, we ran out of path elements without finding
432  // what we're looking found.
434  << "edm::FileInPath unable to find file " << relativePath_ << " anywhere in the search path."
435  << "\nThe search path is defined by: " << PathVariableName << "\n${" << PathVariableName
436  << "} is: " << std::getenv(PathVariableName.c_str())
437  << "\nCurrent directory is: " << std::filesystem::current_path().string() << "\n";
438  }
439 
440  void FileInPath::disableFileLookup() { s_fileLookupDisabled = true; }
441 
443  // Find the file, based on the value of path variable.
444  auto pathElements = tokenize(searchPath(), ":");
445  for (auto const& element : pathElements) {
446  // Set the boost::fs path to the current element of
447  // CMSSW_SEARCH_PATH:
448  std::filesystem::path pathPrefix(element);
449 
450  // Does the a file exist? locateFile throws is it finds
451  // something goofy.
452  if (locateFile(pathPrefix, iFileName)) {
453  // Convert relative path to canonical form, and save it.
454  return std::filesystem::absolute(pathPrefix / iFileName).string();
455  }
456  }
457  return {};
458  }
459 
460 } // namespace edm
std::string canonicalFilename_
Definition: FileInPath.h:116
void swap(FileInPath &other)
Definition: FileInPath.cc:147
std::string fullPath() const
Definition: FileInPath.cc:161
static void disableFileLookup()
Should only be called while the edmWriteConfigs executable runs.
Definition: FileInPath.cc:440
std::string dataTop_
Definition: FileInPath.h:120
void getEnvironment()
Definition: FileInPath.cc:336
std::string searchPath_
Definition: FileInPath.h:121
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:119
LocationCode location_
Definition: FileInPath.h:117
void read(std::istream &is)
Definition: FileInPath.cc:202
static std::string const & searchPath()
Definition: FileInPath.cc:330
LocationCode location() const
Where was the file found?
Definition: FileInPath.cc:159
void initialize_()
Definition: FileInPath.cc:366
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:115
HLT enums.
static std::string findFile(std::string const &)
Definition: FileInPath.cc:442
FileInPath & operator=(FileInPath const &other)
Definition: FileInPath.cc:141
void readFromParameterSetBlob(std::istream &is)
Definition: FileInPath.cc:273
std::string relativePath() const
Definition: FileInPath.cc:157
void resolveSymbolicLinks(std::string &fullPath)
void write(std::ostream &os) const
Definition: FileInPath.cc:163
std::string localTop_
Definition: FileInPath.h:118