ATLAS Offline Software
PathResolver.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
7 
8 #include <cstdlib>
9 #include <format>
10 #include <fstream>
11 #include <ranges>
12 #include <stdexcept>
13 #include <string_view>
14 #include <memory>
15 
16 #include "TFile.h"
17 #include "TSystem.h"
18 
19 
20 namespace fs = std::filesystem;
21 
22 namespace {
23  const char path_separator = ':'; // Linux and MacOS
24  const char* const pathResolverEnvVar = "PATHRESOLVER_DEVAREARESPONSE";
25 
28  bool download_with_curl(const std::string& url, const std::string& output_path) {
29  // -L follows redirects
30  // -s silent mode (no progress bar), remove if you want curl's output
31  std::string cmd = "curl -L -s -o " + output_path + " " + url;
32  int ret = std::system(cmd.c_str());
33  return ret == 0;
34  }
35 
37  void checkForDev(asg::AsgMessaging& asgmsg,
38  const std::string& logical_file_name) {
39 
40  asgmsg.msg(MSG::DEBUG) << "Trying to locate " << logical_file_name << endmsg;
41 
42  if (logical_file_name.starts_with("dev/")) {
43  const char* env = std::getenv(pathResolverEnvVar);
44  const std::string dev_area_response = env ? env : "DEFAULT";
45 
46  MSG::Level level{};
47  if (dev_area_response == "SILENT") {
48  return;
49  }
50  else if (dev_area_response == "THROW") {
51  throw std::runtime_error(
52  "Loading dev area file " + logical_file_name + " is not allowed! "
53  "To override this error set the environment variable " +
54  pathResolverEnvVar + " to SILENT, DEFAULT, INFO, WARNING or ERROR");
55  }
56  else if (dev_area_response == "DEFAULT") {
57  #ifdef XAOD_ANALYSIS
58  level = MSG::WARNING;
59  #else
60  level = MSG::ERROR;
61  #endif
62  }
63  else if (dev_area_response == "INFO") level = MSG::INFO;
64  else if (dev_area_response == "WARNING") level = MSG::WARNING;
65  else if (dev_area_response == "ERROR") level = MSG::ERROR;
66  else {
67  throw std::runtime_error(std::format("{} set to '{}', not sure what to do. "
68  "Options are DEFAULT, THROW, INFO, WARNING, ERROR or SILENT",
69  pathResolverEnvVar, dev_area_response));
70  }
71 
72  // Print message at appropriate level
73  asgmsg.msg(level) << "Locating dev file " << logical_file_name << ". Do not let this propagate to a release!" << endmsg;
74  }
75  }
76 }
77 
78 
80 #ifdef XAOD_STANDALONE
81  static thread_local asg::AsgMessaging asgMsg("PathResolver");
82 #else
83  static asg::AsgMessaging asgMsg ATLAS_THREAD_SAFE ("PathResolver");
84 #endif
85 #ifndef XAOD_ANALYSIS
88 #else
89  asgMsg.msg().setLevel(m_level);
90 #endif
91  return asgMsg;
92 }
93 
94 
98 bool PathResolver::PR_find( const std::string& logical_file_name, const std::string& search_list,
99  fs::file_type file_type, std::string& result ) {
100 
101  // expand filename before finding
102  TString tmpString(logical_file_name);
103  gSystem->ExpandPathName(tmpString);
104 
105  fs::path file(tmpString.Data());
106  fs::path locationToDownloadTo = "."; // will replace with first search location
107 
108  // First always search for filename as given in local directory
109  const std::string searchPath = std::format("./{}{}", path_separator, search_list);
110 
111  // iterate through search list
112  for (const auto r : searchPath | std::views::split(path_separator)) {
113  std::string_view path(r.begin(), r.end());
114  const bool is_http = path.starts_with("http//");
115  if( (is_http || path.starts_with("https//")) &&
116  file_type==fs::file_type::regular && std::getenv("PATHRESOLVER_ALLOWHTTPDOWNLOAD") ) { // only http download files, not directories
117 
118  // Try to do an http download to the local location.
119  // Need to restore the proper http protocol (cannot use ":" in search paths)
120  const std::string fileToDownload = std::format("{}://{}/{}", is_http ? "http" : "https",
121  path.substr(6), file.string());
122 
123  const fs::path targetPath = locationToDownloadTo / file;
124  fs::path targetDir = targetPath;
125  targetDir.remove_filename();
126  msg(MSG::DEBUG) << "Attempting http download of " << fileToDownload << " to " << targetDir << endmsg;
127 
128  if (!is_directory(targetDir)) {
129  msg(MSG::DEBUG) << "Creating directory " << targetDir << endmsg;
130  if(!fs::create_directories(targetDir)) {
131  msg(MSG::ERROR) << "Unable to create directories to write file to " << targetDir << endmsg;
132  return false;
133  }
134  }
135 
136  if (!TFile::Cp(fileToDownload.c_str(), targetPath.c_str(), false)) {
137  msg(MSG::INFO) << "Unable to download file "
138  << fileToDownload
139  << " with ROOT, falling back to command line tools"
140  << endmsg;
141  if (download_with_curl(fileToDownload, targetPath)) {
142  msg(MSG::INFO) << "Successfully curled " << fileToDownload << endmsg;
143  result = targetPath;
144  return true;
145  } else {
146  msg(MSG::WARNING) << "Unable to download file "
147  << fileToDownload
148  << endmsg;
149  }
150  } else {
151  msg(MSG::DEBUG) << "Successfully downloaded " << fileToDownload << endmsg;
152  result = targetPath;
153  return true;
154  }
155 
156  } else if (locationToDownloadTo==".") {
157  // Prefer first non-pwd location (usually local build area) for downloading to.
158  fs::path dummyFile = fs::path(path) / "._pathresolver_dummy";
159  std::ofstream ofs(dummyFile); // check if writable
160  if (ofs.is_open()) {
161  locationToDownloadTo = path;
162  ofs.close();
163  fs::remove(dummyFile);
164  }
165  }
166 
167  fs::path fp = path / file;
168  try {
169  if (fs::status(fp).type() == file_type) {
170  result = fs::absolute(fp).string();
171  return true;
172  }
173  } catch (const fs::filesystem_error&) {
174  // file not accessible or does not exist
175  }
176 
177  }
178 
179  return false; // not found
180 }
181 
182 
183 std::string PathResolver::find_file(const std::string& logical_file_name,
184  const std::string& search_path) {
185 
186 #ifndef XAOD_ANALYSIS
187  if (logical_file_name.starts_with('/')) {
188  msg(MSG::ERROR) << "Use of an absolute file name: " << logical_file_name << endmsg;
189  }
190 #endif
191 
192  const char* path_list = std::getenv(search_path.c_str());
193  if (path_list == nullptr) {
194  msg(MSG::ERROR) << search_path << " environment variable not defined!" << endmsg;
195  return {};
196  }
197 
198  return find_file_from_list(logical_file_name, path_list);
199 }
200 
201 
202 std::string PathResolver::find_file_from_list (const std::string& logical_file_name,
203  const std::string& search_list)
204 {
205  std::string result;
206  PR_find (logical_file_name, search_list, fs::file_type::regular, result);
207 
208  return result;
209 }
210 
211 
212 std::string PathResolver::find_directory (const std::string& logical_file_name,
213  const std::string& search_path)
214 {
215  const char* path_list = std::getenv(search_path.c_str());
216  if(path_list == nullptr) {
217  msg(MSG::ERROR) << search_path << " environment variable not defined!" << endmsg;
218  return {};
219  }
220 
221  return find_directory_from_list(logical_file_name, path_list);
222 }
223 
224 
225 std::string PathResolver::find_directory_from_list (const std::string& logical_file_name,
226  const std::string& search_list)
227 {
228  std::string result;
229  PR_find(logical_file_name, search_list, fs::file_type::directory, result);
230 
231  return result;
232 }
233 
234 
235 std::string PathResolver::find_calib_file (const std::string& logical_file_name)
236 {
237  checkForDev(asgMsg(), logical_file_name);
238 
239  if (logical_file_name.starts_with("root://")) {
240  //xrootd access .. try to open file ...
241  std::unique_ptr<TFile> fTmp{TFile::Open(logical_file_name.c_str())};
242  if (!fTmp || fTmp->IsZombie()) {
243  msg(MSG::WARNING) << "Could not open " << logical_file_name << endmsg;
244  return {};
245  }
246  return logical_file_name;
247  }
248 
249  std::string out = PathResolver::find_file (logical_file_name, "CALIBPATH");
250  if (out.empty()) {
251  msg(MSG::WARNING) << "Could not locate " << logical_file_name << endmsg;
252  }
253  return out;
254 }
255 
256 
257 std::string PathResolver::find_calib_directory (const std::string& logical_file_name)
258 {
259  checkForDev(asgMsg(), logical_file_name);
260 
261  std::string out = PathResolver::find_directory (logical_file_name, "CALIBPATH");
262  if (out.empty()) {
263  msg(MSG::WARNING) << "Could not locate " << logical_file_name << endmsg;
264  }
265  return out;
266 }
267 
268 
270  m_level = level;
271 }
272 
273 std::string PathResolverFindXMLFile (const std::string& logical_file_name)
274 {
275  return PathResolver::find_file (logical_file_name, "XMLPATH");
276 }
277 
278 std::string PathResolverFindDataFile (const std::string& logical_file_name)
279 {
280  return PathResolver::find_file (logical_file_name, "DATAPATH");
281 }
282 
283 std::string PathResolverFindCalibFile (const std::string& logical_file_name) {
284  return PathResolver::find_calib_file(logical_file_name);
285 }
286 
287 
288 std::string PathResolverFindCalibDirectory (const std::string& logical_file_name) {
289  return PathResolver::find_calib_directory(logical_file_name);
290 }
291 
292 
295 }
PathResolver::find_calib_file
static std::string find_calib_file(const std::string &logical_file_name)
Definition: PathResolver.cxx:235
beamspotman.r
def r
Definition: beamspotman.py:674
PathResolver::asgMsg
static asg::AsgMessaging & asgMsg()
Definition: PathResolver.cxx:79
get_generator_info.result
result
Definition: get_generator_info.py:21
athena.path
path
python interpreter configuration --------------------------------------—
Definition: athena.py:128
vtune_athena.format
format
Definition: vtune_athena.py:14
PathResolver::find_directory
static std::string find_directory(const std::string &logical_file_name, const std::string &search_path)
Definition: PathResolver.cxx:212
pool_uuid.regular
bool regular
Definition: pool_uuid.py:103
rerun_display.cmd
string cmd
Definition: rerun_display.py:67
PathResolver::find_calib_directory
static std::string find_calib_directory(const std::string &logical_file_name)
Definition: PathResolver.cxx:257
plotting.yearwise_luminosity.absolute
absolute
Definition: yearwise_luminosity.py:29
python.AthDsoLogger.out
out
Definition: AthDsoLogger.py:70
PathResolver::find_file_from_list
static std::string find_file_from_list(const std::string &logical_file_name, const std::string &search_list)
Definition: PathResolver.cxx:202
physics_parameters.url
string url
Definition: physics_parameters.py:27
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
python.iconfTool.models.loaders.level
level
Definition: loaders.py:20
PixelModuleFeMask_create_db.remove
string remove
Definition: PixelModuleFeMask_create_db.py:83
AthMessaging::setLevel
void setLevel(MSG::Level lvl)
Change the current logging level.
Definition: AthMessaging.cxx:28
TrigConf::MSGTC::Level
Level
Definition: Trigger/TrigConfiguration/TrigConfBase/TrigConfBase/MsgStream.h:21
PathResolver::msg
static MsgStream & msg()
Definition: PathResolver.h:73
DeMoScan.directory
string directory
Definition: DeMoScan.py:78
PathResolver::PR_find
static bool PR_find(const std::string &logical_file_name, const std::string &search_list, std::filesystem::file_type file_type, std::string &result)
Main private search method used by all public methods.
Definition: PathResolver.cxx:98
trigmenu_modify_prescale_json.fp
fp
Definition: trigmenu_modify_prescale_json.py:53
endmsg
#define endmsg
Definition: AnalysisConfig_Ntuple.cxx:63
PathResolver::find_directory_from_list
static std::string find_directory_from_list(const std::string &logical_file_name, const std::string &search_list)
Definition: PathResolver.cxx:225
PathResolverFindXMLFile
std::string PathResolverFindXMLFile(const std::string &logical_file_name)
Definition: PathResolver.cxx:273
asg::AsgMessaging::msg
MsgStream & msg() const
The standard message stream.
Definition: AsgMessaging.cxx:49
file
TFile * file
Definition: tile_monitor.h:29
PathResolverFindDataFile
std::string PathResolverFindDataFile(const std::string &logical_file_name)
Definition: PathResolver.cxx:278
PathResolver::m_level
static std::atomic< MSG::Level > m_level
Definition: PathResolver.h:71
PathResolver.h
asg::AsgMessaging
Class mimicking the AthMessaging class from the offline software.
Definition: AsgMessaging.h:40
PathResolverSetOutputLevel
void PathResolverSetOutputLevel(int lvl)
Definition: PathResolver.cxx:293
PathResolverFindCalibDirectory
std::string PathResolverFindCalibDirectory(const std::string &logical_file_name)
Definition: PathResolver.cxx:288
SCT_ConditionsAlgorithms::CoveritySafe::getenv
std::string getenv(const std::string &variableName)
get an environment variable
Definition: SCT_ConditionsUtilities.cxx:17
python.Constants.INFO
int INFO
Definition: Control/AthenaCommon/python/Constants.py:15
athena.path_list
list path_list
Definition: athena.py:93
PathResolver::find_file
static std::string find_file(const std::string &logical_file_name, const std::string &search_path)
Definition: PathResolver.cxx:183
DEBUG
#define DEBUG
Definition: page_access.h:11
Herwig7_QED_EvtGen_ll.fs
dictionary fs
Definition: Herwig7_QED_EvtGen_ll.py:17
PathResolverFindCalibFile
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
Definition: PathResolver.cxx:283
PathResolver::setOutputLevel
static void setOutputLevel(MSG::Level level)
Definition: PathResolver.cxx:269
merge.status
status
Definition: merge.py:16
ATLAS_THREAD_SAFE
#define ATLAS_THREAD_SAFE
Definition: checker_macros.h:211
python.DataFormatRates.env
env
Definition: DataFormatRates.py:32
checker_macros.h
Define macros for attributes used to control the static checker.
Trk::split
@ split
Definition: LayerMaterialProperties.h:38