Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-10-12 04:20:44

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   void FileInPath::swap(FileInPath& other) {
0131     relativePath_.swap(other.relativePath_);
0132     canonicalFilename_.swap(other.canonicalFilename_);
0133     std::swap(location_, other.location_);
0134     localTop_.swap(other.localTop_);
0135     releaseTop_.swap(other.releaseTop_);
0136     dataTop_.swap(other.dataTop_);
0137     searchPath_.swap(other.searchPath_);
0138   }
0139 
0140   const std::string& FileInPath::relativePath() const { return relativePath_; }
0141 
0142   FileInPath::LocationCode FileInPath::location() const { return location_; }
0143 
0144   const std::string& FileInPath::fullPath() const { return canonicalFilename_; }
0145 
0146   void FileInPath::write(std::ostream& os) const {
0147     if (location_ == Unknown) {
0148       if (relativePath_.empty()) {
0149         os << version << " @ " << location_;
0150       } else {
0151         os << version << ' ' << relativePath_ << ' ' << location_;
0152       }
0153     } else if (location_ == Local) {
0154       // Guarantee a site independent value by stripping $LOCALTOP.
0155       if (localTop_.empty()) {
0156         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n";
0157       }
0158       std::string::size_type pos = canonicalFilename_.find(localTop_);
0159       if (pos != 0) {
0160         throw edm::Exception(edm::errors::FileInPathError)
0161             << "Path " << canonicalFilename_ << " is not in the local release area " << localTop_ << "\n";
0162       }
0163       os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(localTop_.size());
0164     } else if (location_ == Release) {
0165       // Guarantee a site independent value by stripping $RELEASETOP.
0166       if (releaseTop_.empty()) {
0167         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
0168       }
0169       std::string::size_type pos = canonicalFilename_.find(releaseTop_);
0170       if (pos != 0) {
0171         throw edm::Exception(edm::errors::FileInPathError)
0172             << "Path " << canonicalFilename_ << " is not in the base release area " << releaseTop_ << "\n";
0173       }
0174       os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(releaseTop_.size());
0175     } else if (location_ == Data) {
0176       // Guarantee a site independent value by stripping $DATATOP.
0177       if (dataTop_.empty()) {
0178         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
0179       }
0180       std::string::size_type pos = canonicalFilename_.find(dataTop_);
0181       if (pos != 0) {
0182         throw edm::Exception(edm::errors::FileInPathError)
0183             << "Path " << canonicalFilename_ << " is not in the data area " << dataTop_ << "\n";
0184       }
0185       os << version << ' ' << relativePath_ << ' ' << location_ << ' ' << canonicalFilename_.substr(dataTop_.size());
0186     }
0187   }
0188 
0189   void FileInPath::read(std::istream& is) {
0190     std::string vsn;
0191     std::string relname;
0192     std::string canFilename;
0193 #if 1
0194     // This #if needed for backward compatibility
0195     // for files written before CMSSW_1_5_0_pre3.
0196     is >> vsn;
0197     if (!is)
0198       return;
0199     bool oldFormat = (version != vsn);
0200     if (oldFormat) {
0201       relname = vsn;
0202       bool local;
0203       is >> local;
0204       location_ = (local ? Local : Release);
0205       is >> canFilename;
0206     } else {
0207       // Current format
0208       int loc;
0209       is >> relname >> loc;
0210       location_ = static_cast<FileInPath::LocationCode>(loc);
0211       if (location_ != Unknown) {
0212         is >> canFilename;
0213       } else if (relname == "@") {
0214         relname = "";
0215       }
0216     }
0217 #else
0218     is >> vsn >> relname >> loc >> canFilename;
0219 #endif
0220     if (!is)
0221       return;
0222     relativePath_ = relname;
0223     if (location_ == Local) {
0224       if (localTop_.empty()) {
0225         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << LOCALTOP << " is not set.\n"
0226                                                            << "Trying to read Local file: " << canFilename << ".\n";
0227       }
0228 #if 1
0229       // This #if needed for backward compatibility
0230       // for files written before CMSSW_1_5_0_pre3.
0231       if (oldFormat) {
0232         canonicalFilename_ = canFilename;
0233       } else
0234 #endif
0235         canonicalFilename_ = localTop_ + canFilename;
0236     } else if (location_ == Release) {
0237       if (releaseTop_.empty()) {
0238         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << RELEASETOP << " is not set.\n";
0239       }
0240 #if 1
0241       // This #if needed for backward compatibility
0242       // for files written before CMSSW_1_5_0_pre3.
0243       if (oldFormat) {
0244         std::string::size_type pos = canFilename.find(BASE);
0245         if (pos == 0) {
0246           // Replace the placehoder with the path to the base release (site dependent).
0247           canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
0248         } else {
0249           // Needed for files written before CMSSW_1_2_0_pre2.
0250           canonicalFilename_ = canFilename;
0251         }
0252       } else
0253 #endif
0254         canonicalFilename_ = releaseTop_ + canFilename;
0255     } else if (location_ == Data) {
0256       if (dataTop_.empty()) {
0257         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
0258       }
0259       canonicalFilename_ = dataTop_ + canFilename;
0260     }
0261   }
0262 
0263   void FileInPath::readFromParameterSetBlob(std::istream& is) {
0264     std::string vsn;
0265     std::string relname;
0266     std::string canFilename;
0267     is >> vsn;
0268     if (!is)
0269       return;
0270     bool oldFormat = (version != vsn);
0271     if (oldFormat) {
0272       relname = vsn;
0273       bool local;
0274       is >> local;
0275       location_ = (local ? Local : Release);
0276       is >> canFilename;
0277     } else {
0278       // Current format
0279       int loc;
0280       is >> relname >> loc;
0281       location_ = static_cast<FileInPath::LocationCode>(loc);
0282       if (location_ != Unknown) {
0283         is >> canFilename;
0284       } else if (relname == "@") {
0285         relname = "";
0286       }
0287     }
0288     if (!is)
0289       return;
0290     relativePath_ = relname;
0291     if (location_ == Local) {
0292       if (localTop_.empty()) {
0293         localTop_ = "@LOCAL";
0294       }
0295       if (oldFormat) {
0296         canonicalFilename_ = canFilename;
0297       } else
0298         canonicalFilename_ = localTop_ + canFilename;
0299     } else if (location_ == Release) {
0300       if (releaseTop_.empty()) {
0301         releaseTop_ = "@RELEASE";
0302       }
0303       if (oldFormat) {
0304         std::string::size_type pos = canFilename.find(BASE);
0305         if (pos == 0) {
0306           // Replace the placehoder with the path to the base release (site dependent).
0307           canonicalFilename_ = releaseTop_ + canFilename.substr(BASE.size());
0308         } else {
0309           // Needed for files written before CMSSW_1_2_0_pre2.
0310           canonicalFilename_ = canFilename;
0311         }
0312       } else
0313         canonicalFilename_ = releaseTop_ + canFilename;
0314     } else if (location_ == Data) {
0315       if (dataTop_.empty()) {
0316         throw edm::Exception(edm::errors::FileInPathError) << "Environment Variable " << DATATOP << " is not set.\n";
0317       }
0318       canonicalFilename_ = dataTop_ + canFilename;
0319     }
0320   }
0321 
0322   //------------------------------------------------------------
0323   std::string const& FileInPath::searchPath() {
0324     static std::string const s_searchPath = removeSymLinksTokens(PathVariableName);
0325     return s_searchPath;
0326   }
0327   //------------------------------------------------------------
0328 
0329   void FileInPath::getEnvironment() {
0330     searchPath_ = searchPath();
0331     if (searchPath_.empty()) {
0332       throw edm::Exception(edm::errors::FileInPathError) << PathVariableName << " must be defined\n";
0333     }
0334 
0335     static std::string const releaseTop = removeSymLinksSrc(RELEASETOP);
0336     releaseTop_ = releaseTop;
0337 
0338     static std::string const localTop = removeSymLinksSrc(LOCALTOP);
0339     localTop_ = localTop;
0340 
0341     static std::string const dataTop = removeSymLinks(DATATOP);
0342     dataTop_ = dataTop;
0343 
0344     if (releaseTop_.empty()) {
0345       // RELEASETOP was not set.  This means that the environment is set
0346       // for the base release itself.  So LOCALTOP actually contains the
0347       // location of the base release.
0348       releaseTop_ = localTop_;
0349       localTop_.clear();
0350     }
0351     if (releaseTop_ == localTop_) {
0352       // RELEASETOP is the same as LOCALTOP.  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       localTop_.clear();
0356     }
0357   }
0358 
0359   void FileInPath::initialize_() {
0360     if (relativePath_.empty()) {
0361       throw edm::Exception(edm::errors::FileInPathError) << "Relative path must not be empty\n";
0362     }
0363 
0364     // Find the file, based on the value of searchPath.
0365     typedef std::vector<std::string> stringvec_t;
0366     stringvec_t pathElements = tokenize(searchPath_, ":");
0367     for (auto const& element : pathElements) {
0368       // Set the path to the current element of CMSSW_SEARCH_PATH:
0369       std::filesystem::path pathPrefix(element);
0370 
0371       // Does the a file exist? locateFile throws is it finds
0372       // something goofy.
0373       if (locateFile(pathPrefix, relativePath_)) {
0374         // Convert relative path to canonical form, and save it.
0375         relativePath_ = std::filesystem::path(relativePath_).lexically_normal().string();
0376         //std::filesystem::path(relativePath_).normalize().string();
0377 
0378         // Save the absolute path.
0379         canonicalFilename_ = std::filesystem::absolute(pathPrefix / relativePath_).string();
0380         if (canonicalFilename_.empty()) {
0381           throw edm::Exception(edm::errors::FileInPathError)
0382               << "fullPath is empty"
0383               << "\nrelativePath() is: " << relativePath_ << "\npath prefix is: " << pathPrefix.string() << '\n';
0384         }
0385 
0386         // From the current path element, find the branch path (basically the path minus the
0387         // last directory, e.g. /src or /share):
0388         for (std::filesystem::path br = pathPrefix.parent_path();
0389              !std::filesystem::weakly_canonical(br).string().empty();
0390              br = br.parent_path()) {
0391           if (!localTop_.empty()) {
0392             // Create a path object for our local path LOCALTOP:
0393             std::filesystem::path local_(localTop_);
0394             // If the branch path matches the local path, the file was found locally:
0395             if (br == local_) {
0396               location_ = Local;
0397               return;
0398             }
0399           }
0400 
0401           if (!releaseTop_.empty()) {
0402             // Create a path object for our release path RELEASETOP:
0403             std::filesystem::path release_(releaseTop_);
0404             // If the branch path matches the release path, the file was found in the release:
0405             if (br == release_) {
0406               location_ = Release;
0407               return;
0408             }
0409           }
0410 
0411           if (!dataTop_.empty()) {
0412             // Create a path object for our data path DATATOP:
0413             std::filesystem::path data_(dataTop_);
0414             // If the branch path matches the data path, the file was found in the data area:
0415             if (br == data_) {
0416               location_ = Data;
0417               return;
0418             }
0419           }
0420         }
0421       }
0422     }
0423 
0424     // If we got here, we ran out of path elements without finding
0425     // what we're looking found.
0426     throw edm::Exception(edm::errors::FileInPathError)
0427         << "edm::FileInPath unable to find file " << relativePath_ << " anywhere in the search path."
0428         << "\nThe search path is defined by: " << PathVariableName << "\n${" << PathVariableName
0429         << "} is: " << std::getenv(PathVariableName.c_str())
0430         << "\nCurrent directory is: " << std::filesystem::current_path().string() << "\n";
0431   }
0432 
0433   void FileInPath::disableFileLookup() { s_fileLookupDisabled = true; }
0434 
0435   std::string FileInPath::findFile(const std::string& iFileName) {
0436     // Find the file, based on the value of path variable.
0437     auto pathElements = tokenize(searchPath(), ":");
0438     for (auto const& element : pathElements) {
0439       // Set the boost::fs path to the current element of
0440       // CMSSW_SEARCH_PATH:
0441       std::filesystem::path pathPrefix(element);
0442 
0443       // Does the a file exist? locateFile throws is it finds
0444       // something goofy.
0445       if (locateFile(pathPrefix, iFileName)) {
0446         // Convert relative path to canonical form, and save it.
0447         return std::filesystem::absolute(pathPrefix / iFileName).string();
0448       }
0449     }
0450     return {};
0451   }
0452 
0453 }  // namespace edm