File indexing completed on 2025-01-18 09:15:56
0001
0002
0003
0004 #pragma once
0005
0006 #include <DD4hep/DetFactoryHelper.h>
0007 #include <DD4hep/Factories.h>
0008 #include <DD4hep/Primitives.h>
0009 #include <DD4hep/Printout.h>
0010
0011 #include <fmt/core.h>
0012
0013 #include <cstdlib>
0014 #include <filesystem>
0015 #include <iostream>
0016 #include <regex>
0017 #include <string>
0018
0019 namespace fs = std::filesystem;
0020
0021 using dd4hep::ERROR, dd4hep::WARNING, dd4hep::VERBOSE, dd4hep::INFO;
0022 using dd4hep::printout;
0023
0024 namespace FileLoaderHelper {
0025 static constexpr const char* const kCommand = "curl --retry 5 --location --fail {0} --output {1}";
0026 }
0027
0028
0029 inline void EnsureFileFromURLExists(std::string url, std::string file, std::string cache_str = "") {
0030
0031 auto pos = std::string::npos;
0032 while ((pos = cache_str.find('$')) != std::string::npos) {
0033 auto after = cache_str.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
0034 "abcdefghijklmnopqrstuvwxyz"
0035 "0123456789"
0036 "_",
0037 pos + 1);
0038 if (after == std::string::npos)
0039 after = cache_str.size();
0040 const std::string env_name(cache_str.substr(pos + 1, after - pos - 1));
0041 auto env_ptr = std::getenv(env_name.c_str());
0042 const std::string env_value(env_ptr != nullptr ? env_ptr : "");
0043 cache_str.erase(pos, after - pos);
0044 cache_str.insert(pos, env_value);
0045 printout(INFO, "FileLoader", "$" + env_name + " -> " + env_value);
0046 }
0047
0048
0049 std::regex cache_sep(":");
0050 std::sregex_token_iterator cache_iter(cache_str.begin(), cache_str.end(), cache_sep, -1);
0051 std::sregex_token_iterator cache_end;
0052 std::vector<std::string> cache_vec(cache_iter, cache_end);
0053
0054
0055 fs::path file_path(file);
0056
0057
0058 std::string hash =
0059 fmt::format("{:016x}", dd4hep::detail::hash64(url));
0060
0061
0062 fs::path parent_path = file_path.parent_path();
0063 if (!fs::exists(parent_path)) {
0064 if (fs::create_directories(parent_path) == false) {
0065 printout(ERROR, "FileLoader", "parent path " + parent_path.string() + " cannot be created");
0066 printout(ERROR, "FileLoader", "hint: try running 'mkdir -p " + parent_path.string() + "'");
0067 std::_Exit(EXIT_FAILURE);
0068 }
0069 }
0070
0071
0072 fs::path hash_path(parent_path / hash);
0073 if (fs::exists(file_path) && fs::exists(hash_path) && fs::equivalent(file_path, hash_path)) {
0074 printout(INFO, "FileLoader", "link " + file + " -> hash " + hash + " already exists");
0075 return;
0076 }
0077
0078 if (fs::exists(fs::symlink_status(hash_path))) {
0079 printout(INFO, "FileLoader", "removing symlink \"" + hash_path.string() + "\"");
0080 remove(hash_path);
0081 }
0082
0083
0084 if (!fs::exists(hash_path)) {
0085
0086 bool success = false;
0087 for (auto cache : cache_vec) {
0088 fs::path cache_path(cache);
0089 printout(INFO, "FileLoader", "cache " + cache_path.string());
0090 if (fs::exists(cache_path)) {
0091 auto check_path = [&](const fs::path& cache_dir_path) {
0092 printout(VERBOSE, "FileLoader", "checking " + cache_dir_path.string());
0093 fs::path cache_hash_path = cache_dir_path / hash;
0094 if (fs::exists(cache_hash_path)) {
0095
0096 printout(VERBOSE, "FileLoader",
0097 "file " + file + " with hash " + hash + " found in " +
0098 cache_hash_path.string());
0099 fs::path link_target;
0100 if (cache_hash_path.is_absolute()) {
0101 link_target = cache_hash_path;
0102 } else {
0103 link_target = fs::proximate(cache_hash_path, parent_path);
0104 }
0105 try {
0106 fs::create_symlink(link_target, hash_path);
0107 success = true;
0108 } catch (const fs::filesystem_error&) {
0109 printout(ERROR, "FileLoader",
0110 "unable to link from " + hash_path.string() + " to " + link_target.string());
0111 std::_Exit(EXIT_FAILURE);
0112 }
0113 return true;
0114 }
0115 return false;
0116 };
0117 if (!check_path(cache_path)) {
0118 for (auto const& dir_entry : fs::recursive_directory_iterator(cache_path)) {
0119 if (!dir_entry.is_directory())
0120 continue;
0121 if (check_path(dir_entry.path())) {
0122 break;
0123 };
0124 }
0125 }
0126 }
0127 if (success)
0128 break;
0129 }
0130 }
0131
0132
0133 if (!fs::exists(hash_path)) {
0134 std::string cmd =
0135 fmt::format(FileLoaderHelper::kCommand, url, hash_path.c_str());
0136 printout(INFO, "FileLoader", "downloading " + file + " as hash " + hash + " with " + cmd);
0137
0138 auto ret = std::system(cmd.c_str());
0139 if (!fs::exists(hash_path)) {
0140 printout(ERROR, "FileLoader", "unable to run the download command " + cmd);
0141 printout(ERROR, "FileLoader", "the return value was ", ret);
0142 printout(ERROR, "FileLoader", "hint: check the command and try running manually");
0143 printout(ERROR, "FileLoader",
0144 "hint: allow insecure connections on some systems with the flag -k");
0145 std::_Exit(EXIT_FAILURE);
0146 }
0147 }
0148
0149
0150 if (fs::exists(file_path)) {
0151
0152 if (fs::is_symlink(file_path)) {
0153
0154 fs::path symlink_target = fs::read_symlink(file_path);
0155 if (fs::exists(symlink_target) && fs::equivalent(hash_path, symlink_target)) {
0156
0157 return;
0158 } else {
0159
0160 if (fs::remove(file_path) == false) {
0161 printout(ERROR, "FileLoader", "unable to remove symlink " + file_path.string());
0162 printout(ERROR, "FileLoader",
0163 "we tried to create a symlink " + file_path.string() +
0164 " to the actual resource, " +
0165 "but a symlink already exists there and points to an incorrect location");
0166 printout(ERROR, "FileLoader",
0167 "hint: this may be resolved by removing directory " + parent_path.string());
0168 printout(ERROR, "FileLoader",
0169 "hint: or in that directory removing the file or link " + file_path.string());
0170 std::_Exit(EXIT_FAILURE);
0171 }
0172 }
0173 } else {
0174
0175 printout(ERROR, "FileLoader",
0176 "file " + file_path.string() + " already exists but is not a symlink");
0177 printout(ERROR, "FileLoader",
0178 "we tried to create a symlink " + file_path.string() + " to the actual resource, " +
0179 "but a file already exists there and we will not remove it automatically");
0180 printout(ERROR, "FileLoader", "hint: backup the file, remove it manually, and retry");
0181 std::_Exit(EXIT_FAILURE);
0182 }
0183 }
0184
0185
0186
0187 try {
0188
0189 fs::create_symlink(fs::path(hash), file_path);
0190 } catch (const fs::filesystem_error&) {
0191 printout(ERROR, "FileLoader",
0192 "unable to link from " + file_path.string() + " to " + hash_path.string());
0193 printout(ERROR, "FileLoader", "check permissions and retry");
0194 std::_Exit(EXIT_FAILURE);
0195 }
0196
0197
0198 if (fs::file_size(file_path) == 0) {
0199 printout(ERROR, "FileLoader",
0200 "zero file size of symlink from " + file_path.string() + " to (ultimately) " +
0201 fs::canonical(file_path).string());
0202 printout(ERROR, "FileLoader",
0203 "hint: check whether the file " + fs::canonical(file_path).string() +
0204 " has any content");
0205 printout(ERROR, "FileLoader", "hint: check whether the URL " + url + " has any content");
0206 std::_Exit(EXIT_FAILURE);
0207 }
0208 }