Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:13:13

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