Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2022-05-21 03:38:52

0001 // ----------------------------------------------------------------------
0002 // ----------------------------------------------------------------------
0003 
0004 #include <atomic>
0005 #include <cstdlib>
0006 #include <vector>
0007 #include <cassert>
0008 #include <filesystem>
0009 
0010 #include "FWCore/Utilities/interface/FileInPath.h"
0011 #include "FWCore/Utilities/interface/EDMException.h"
0012 #include "FWCore/Utilities/interface/Parse.h"
0013 #include "FWCore/Utilities/interface/resolveSymbolicLinks.h"
0014 
0015 namespace {
0016 
0017   std::atomic<bool> s_fileLookupDisabled{false};
0018 
0019   /// These are the names of the environment variables which control
0020   /// the behavior  of the FileInPath  class.  They are local to  this
0021   /// class; other code should not even know about them!
0022 
0023   const std::string PathVariableName("CMSSW_SEARCH_PATH");
0024   // Environment variables for local and release areas:
0025   const std::string LOCALTOP("CMSSW_BASE");
0026   const std::string RELEASETOP("CMSSW_RELEASE_BASE");
0027   const std::string DATATOP("CMSSW_DATA_PATH");
0028 
0029 #if 1
0030   // Needed for backward compatibility prior to CMSSW_1_5_0_pre3.
0031   // String to serve as placeholder for release top.
0032   // Do not change this value.
0033   const std::string BASE("BASE");
0034 #endif
0035   const std::string version("V001");
0036 
0037   // Remove symlinks from path
0038   std::string removeSymLinks(std::string const& envName) {
0039     char const* const var = std::getenv(envName.c_str());
0040     if (var == nullptr) {
0041       return std::string();
0042     }
0043     std::string path = var;
0044     edm::resolveSymbolicLinks(path);
0045     return path;
0046   }
0047 
0048   std::string removeSymLinksSrc(std::string const& envName) {
0049     char const* const var = std::getenv(envName.c_str());
0050     if (var == nullptr) {
0051       return std::string();
0052     }
0053     std::string const src = "/src";
0054     std::string path = var + src;
0055     edm::resolveSymbolicLinks(path);
0056     size_t actualSize = path.size() - src.size();
0057     assert(path.substr(actualSize, src.size()) == src);
0058     return path.substr(0, actualSize);
0059   }
0060 
0061   std::string removeSymLinksTokens(std::string const& envName) {
0062     char const* const var = std::getenv(envName.c_str());
0063     if (var == nullptr) {
0064       return std::string();
0065     }
0066     std::string theSearchPath;
0067     typedef std::vector<std::string> stringvec_t;
0068     stringvec_t pathElements = edm::tokenize(std::string(var), ":");
0069     for (auto& element : pathElements) {
0070       edm::resolveSymbolicLinks(element);
0071       if (!theSearchPath.empty())
0072         theSearchPath += ":";
0073       theSearchPath += element;
0074     }
0075     return theSearchPath;
0076   }
0077 
0078   // Check for existence of a file for the given relative path and
0079   // 'prefix'.
0080   // Return true if a file (not directory or symbolic link) is found
0081   // Return false is *nothing* is found
0082   // Throw an exception if either a directory or symbolic link is found.
0083   // If true is returned, then put the
0084   bool locateFile(std::filesystem::path p, std::string const& relative) {
0085     p /= relative;
0086 
0087     if (!std::filesystem::exists(p))
0088       return false;
0089 
0090     if (std::filesystem::is_directory(p)) {
0091       throw edm::Exception(edm::errors::FileInPathError) << "Path " << p.string() << " is a directory, not a file\n";
0092     }
0093 
0094     if (std::filesystem::is_symlink(std::filesystem::symlink_status(p))) {
0095       throw edm::Exception(edm::errors::FileInPathError)
0096           << "Path " << p.string() << " is a symbolic link, not a file\n";
0097     }
0098     return true;
0099   }
0100 }  // namespace
0101 
0102 namespace edm {
0103 
0104   FileInPath::FileInPath() : relativePath_(), canonicalFilename_(), location_(Unknown) {
0105     if (s_fileLookupDisabled) {
0106       return;
0107     }
0108     getEnvironment();
0109   }
0110 
0111   FileInPath::FileInPath(const std::string& r) : relativePath_(r), canonicalFilename_(), location_(Unknown) {
0112     if (s_fileLookupDisabled) {
0113       return;
0114     }
0115     getEnvironment();
0116     initialize_();
0117   }
0118 
0119   FileInPath::FileInPath(char const* r) : relativePath_(r ? r : ""), canonicalFilename_(), location_(Unknown) {
0120     if (s_fileLookupDisabled) {
0121       return;
0122     }
0123     if (r == nullptr) {
0124       throw edm::Exception(edm::errors::FileInPathError) << "Relative path must not be null\n";
0125     }
0126     getEnvironment();
0127     initialize_();
0128   }
0129 
0130   FileInPath::FileInPath(FileInPath const& other)
0131       : relativePath_(other.relativePath_),
0132         canonicalFilename_(other.canonicalFilename_),
0133         location_(other.location_),
0134         localTop_(other.localTop_),
0135         releaseTop_(other.releaseTop_),
0136         dataTop_(other.dataTop_),
0137         searchPath_(other.searchPath_) {}
0138 
0139   FileInPath::~FileInPath() {}
0140 
0141   FileInPath& FileInPath::operator=(FileInPath const& other) {
0142     FileInPath temp(other);
0143     this->swap(temp);
0144     return *this;
0145   }
0146 
0147   void FileInPath::swap(FileInPath& other) {
0148     relativePath_.swap(other.relativePath_);
0149     canonicalFilename_.swap(other.canonicalFilename_);
0150     std::swap(location_, other.location_);
0151     localTop_.swap(other.localTop_);
0152     releaseTop_.swap(other.releaseTop_);
0153     dataTop_.swap(other.dataTop_);
0154     searchPath_.swap(other.searchPath_);
0155   }
0156 
0157   std::string FileInPath::relativePath() const { return relativePath_; }
0158 
0159   FileInPath::LocationCode FileInPath::location() const { return location_; }
0160 
0161   std::string FileInPath::fullPath() const { return canonicalFilename_; }
0162 
0163   void FileInPath::write(std::ostream& os) const {
0164     if (location_ == Unknown) {
0165       os << version << ' ' << relativePath_ << ' ' << location_;
0166     } else if (location_ == Local) {
0167       // Guarantee a site independent value by stripping $LOCALTOP.
0168       if (localTop_.empty()) {
0169         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n";
0170       }
0171       std::string::size_type pos = canonicalFilename_.find(localTop_);
0172       if (pos != 0) {
0173         throw edm::Exception(edm::errors::FileInPathError)
0174             << "Path " << canonicalFilename_ << " is not in the local release area " << localTop_ << "\n";
0175       }
0176       os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(localTop_.size());
0177     } else if (location_ == Release) {
0178       // Guarantee a site independent value by stripping $RELEASETOP.
0179       if (releaseTop_.empty()) {
0180         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
0181       }
0182       std::string::size_type pos = canonicalFilename_.find(releaseTop_);
0183       if (pos != 0) {
0184         throw edm::Exception(edm::errors::FileInPathError)
0185             << "Path " << canonicalFilename_ << " is not in the base release area " << releaseTop_ << "\n";
0186       }
0187       os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(releaseTop_.size());
0188     } else if (location_ == Data) {
0189       // Guarantee a site independent value by stripping $DATATOP.
0190       if (dataTop_.empty()) {
0191         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
0192       }
0193       std::string::size_type pos = canonicalFilename_.find(dataTop_);
0194       if (pos != 0) {
0195         throw edm::Exception(edm::errors::FileInPathError)
0196             << "Path " << canonicalFilename_ << " is not in the data area " << dataTop_ << "\n";
0197       }
0198       os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(dataTop_.size());
0199     }
0200   }
0201 
0202   void FileInPath::read(std::istream& is) {
0203     std::string vsn;
0204     std::string relname;
0205     std::string canFilename;
0206 #if 1
0207     // This #if needed for backward compatibility
0208     // for files written before CMSSW_1_5_0_pre3.
0209     is >> vsn;
0210     if (!is)
0211       return;
0212     bool oldFormat = (version != vsn);
0213     if (oldFormat) {
0214       relname = vsn;
0215       bool local;
0216       is >> local;
0217       location_ = (local ? Local : Release);
0218       is >> canFilename;
0219     } else {
0220       // Current format
0221       int loc;
0222       is >> relname >> loc;
0223       location_ = static_cast<FileInPath::LocationCode>(loc);
0224       if (location_ != Unknown)
0225         is >> canFilename;
0226     }
0227 #else
0228     is >> vsn >> relname >> loc >> canFilename;
0229 #endif
0230     if (!is)
0231       return;
0232     relativePath_ = relname;
0233     if (location_ == Local) {
0234       if (localTop_.empty()) {
0235         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n"
0236                                                            << "Trying to read Local file: " << canFilename << ".\n";
0237       }
0238 #if 1
0239       // This #if needed for backward compatibility
0240       // for files written before CMSSW_1_5_0_pre3.
0241       if (oldFormat) {
0242         canonicalFilename_ = canFilename;
0243       } else
0244 #endif
0245         canonicalFilename_ = localTop_ + canFilename;
0246     } else if (location_ == Release) {
0247       if (releaseTop_.empty()) {
0248         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
0249       }
0250 #if 1
0251       // This #if needed for backward compatibility
0252       // for files written before CMSSW_1_5_0_pre3.
0253       if (oldFormat) {
0254         std::string::size_type pos = canFilename.find(BASE);
0255         if (pos == 0) {
0256           // Replace the placehoder with the path to the base release (site dependent).
0257           canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
0258         } else {
0259           // Needed for files written before CMSSW_1_2_0_pre2.
0260           canonicalFilename_ = canFilename;
0261         }
0262       } else
0263 #endif
0264         canonicalFilename_ = releaseTop_ + canFilename;
0265     } else if (location_ == Data) {
0266       if (dataTop_.empty()) {
0267         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
0268       }
0269       canonicalFilename_ = dataTop_ + canFilename;
0270     }
0271   }
0272 
0273   void FileInPath::readFromParameterSetBlob(std::istream& is) {
0274     std::string vsn;
0275     std::string relname;
0276     std::string canFilename;
0277     is >> vsn;
0278     if (!is)
0279       return;
0280     bool oldFormat = (version != vsn);
0281     if (oldFormat) {
0282       relname = vsn;
0283       bool local;
0284       is >> local;
0285       location_ = (local ? Local : Release);
0286       is >> canFilename;
0287     } else {
0288       // Current format
0289       int loc;
0290       is >> relname >> loc;
0291       location_ = static_cast<FileInPath::LocationCode>(loc);
0292       if (location_ != Unknown)
0293         is >> canFilename;
0294     }
0295     if (!is)
0296       return;
0297     relativePath_ = relname;
0298     if (location_ == Local) {
0299       if (localTop_.empty()) {
0300         localTop_ = "@LOCAL";
0301       }
0302       if (oldFormat) {
0303         canonicalFilename_ = canFilename;
0304       } else
0305         canonicalFilename_ = localTop_ + canFilename;
0306     } else if (location_ == Release) {
0307       if (releaseTop_.empty()) {
0308         releaseTop_ = "@RELEASE";
0309       }
0310       if (oldFormat) {
0311         std::string::size_type pos = canFilename.find(BASE);
0312         if (pos == 0) {
0313           // Replace the placehoder with the path to the base release (site dependent).
0314           canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
0315         } else {
0316           // Needed for files written before CMSSW_1_2_0_pre2.
0317           canonicalFilename_ = canFilename;
0318         }
0319       } else
0320         canonicalFilename_ = releaseTop_ + canFilename;
0321     } else if (location_ == Data) {
0322       if (dataTop_.empty()) {
0323         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
0324       }
0325       canonicalFilename_ = dataTop_ + canFilename;
0326     }
0327   }
0328 
0329   //------------------------------------------------------------
0330   std::string const& FileInPath::searchPath() {
0331     static std::string const s_searchPath = removeSymLinksTokens(PathVariableName);
0332     return s_searchPath;
0333   }
0334   //------------------------------------------------------------
0335 
0336   void FileInPath::getEnvironment() {
0337     searchPath_ = searchPath();
0338     if (searchPath_.empty()) {
0339       throw edm::Exception(edm::errors::FileInPathError) << PathVariableName << " must be defined\n";
0340     }
0341 
0342     static std::string const releaseTop = removeSymLinksSrc(RELEASETOP);
0343     releaseTop_ = releaseTop;
0344 
0345     static std::string const localTop = removeSymLinksSrc(LOCALTOP);
0346     localTop_ = localTop;
0347 
0348     static std::string const dataTop = removeSymLinks(DATATOP);
0349     dataTop_ = dataTop;
0350 
0351     if (releaseTop_.empty()) {
0352       // RELEASETOP was not set.  This means that the environment is set
0353       // for the base release itself.  So LOCALTOP actually contains the
0354       // location of the base release.
0355       releaseTop_ = localTop_;
0356       localTop_.clear();
0357     }
0358     if (releaseTop_ == localTop_) {
0359       // RELEASETOP is the same as LOCALTOP.  This means that the environment is set
0360       // for the base release itself.  So LOCALTOP actually contains the
0361       // location of the base release.
0362       localTop_.clear();
0363     }
0364   }
0365 
0366   void FileInPath::initialize_() {
0367     if (relativePath_.empty()) {
0368       throw edm::Exception(edm::errors::FileInPathError) << "Relative path must not be empty\n";
0369     }
0370 
0371     // Find the file, based on the value of searchPath.
0372     typedef std::vector<std::string> stringvec_t;
0373     stringvec_t pathElements = tokenize(searchPath_, ":");
0374     for (auto const& element : pathElements) {
0375       // Set the path to the current element of CMSSW_SEARCH_PATH:
0376       std::filesystem::path pathPrefix(element);
0377 
0378       // Does the a file exist? locateFile throws is it finds
0379       // something goofy.
0380       if (locateFile(pathPrefix, relativePath_)) {
0381         // Convert relative path to canonical form, and save it.
0382         relativePath_ = std::filesystem::path(relativePath_).lexically_normal().string();
0383         //std::filesystem::path(relativePath_).normalize().string();
0384 
0385         // Save the absolute path.
0386         canonicalFilename_ = std::filesystem::absolute(pathPrefix / relativePath_).string();
0387         if (canonicalFilename_.empty()) {
0388           throw edm::Exception(edm::errors::FileInPathError)
0389               << "fullPath is empty"
0390               << "\nrelativePath() is: " << relativePath_ << "\npath prefix is: " << pathPrefix.string() << '\n';
0391         }
0392 
0393         // From the current path element, find the branch path (basically the path minus the
0394         // last directory, e.g. /src or /share):
0395         for (std::filesystem::path br = pathPrefix.parent_path();
0396              !std::filesystem::weakly_canonical(br).string().empty();
0397              br = br.parent_path()) {
0398           if (!localTop_.empty()) {
0399             // Create a path object for our local path LOCALTOP:
0400             std::filesystem::path local_(localTop_);
0401             // If the branch path matches the local path, the file was found locally:
0402             if (br == local_) {
0403               location_ = Local;
0404               return;
0405             }
0406           }
0407 
0408           if (!releaseTop_.empty()) {
0409             // Create a path object for our release path RELEASETOP:
0410             std::filesystem::path release_(releaseTop_);
0411             // If the branch path matches the release path, the file was found in the release:
0412             if (br == release_) {
0413               location_ = Release;
0414               return;
0415             }
0416           }
0417 
0418           if (!dataTop_.empty()) {
0419             // Create a path object for our data path DATATOP:
0420             std::filesystem::path data_(dataTop_);
0421             // If the branch path matches the data path, the file was found in the data area:
0422             if (br == data_) {
0423               location_ = Data;
0424               return;
0425             }
0426           }
0427         }
0428       }
0429     }
0430 
0431     // If we got here, we ran out of path elements without finding
0432     // what we're looking found.
0433     throw edm::Exception(edm::errors::FileInPathError)
0434         << "edm::FileInPath unable to find file " << relativePath_ << " anywhere in the search path."
0435         << "\nThe search path is defined by: " << PathVariableName << "\n${" << PathVariableName
0436         << "} is: " << std::getenv(PathVariableName.c_str())
0437         << "\nCurrent directory is: " << std::filesystem::current_path().string() << "\n";
0438   }
0439 
0440   void FileInPath::disableFileLookup() { s_fileLookupDisabled = true; }
0441 
0442   std::string FileInPath::findFile(const std::string& iFileName) {
0443     // Find the file, based on the value of path variable.
0444     auto pathElements = tokenize(searchPath(), ":");
0445     for (auto const& element : pathElements) {
0446       // Set the boost::fs path to the current element of
0447       // CMSSW_SEARCH_PATH:
0448       std::filesystem::path pathPrefix(element);
0449 
0450       // Does the a file exist? locateFile throws is it finds
0451       // something goofy.
0452       if (locateFile(pathPrefix, iFileName)) {
0453         // Convert relative path to canonical form, and save it.
0454         return std::filesystem::absolute(pathPrefix / iFileName).string();
0455       }
0456     }
0457     return {};
0458   }
0459 
0460 }  // namespace edm