|
13 | 13 | #include <unistd.h> // getuid
|
14 | 14 | #endif
|
15 | 15 |
|
| 16 | +#ifdef _WIN32 |
| 17 | +#include <windows.h> |
| 18 | +#endif |
16 | 19 | namespace node {
|
17 | 20 |
|
| 21 | +#ifdef _WIN32 |
| 22 | +using fs::ConvertWideToUTF8; |
| 23 | +#endif |
18 | 24 | using v8::Function;
|
19 | 25 | using v8::Local;
|
20 | 26 | using v8::Module;
|
@@ -223,13 +229,102 @@ void CompileCacheHandler::ReadCacheFile(CompileCacheEntry* entry) {
|
223 | 229 | Debug(" success, size=%d\n", total_read);
|
224 | 230 | }
|
225 | 231 |
|
| 232 | +#ifdef _WIN32 |
| 233 | +constexpr bool IsWindowsDeviceRoot(const char c) noexcept { |
| 234 | + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
| 235 | +} |
| 236 | +#endif |
| 237 | + |
| 238 | +static std::string NormalisePath(std::string_view path) { |
| 239 | + std::string normalised_string(path); |
| 240 | + constexpr std::string_view file_scheme = "file://"; |
| 241 | + if (normalised_string.rfind(file_scheme, 0) == 0) { |
| 242 | + normalised_string.erase(0, file_scheme.size()); |
| 243 | + } |
| 244 | + |
| 245 | +#ifdef _WIN32 |
| 246 | + if (normalised_string.size() > 2 && |
| 247 | + IsWindowsDeviceRoot(normalised_string[0]) && |
| 248 | + normalised_string[1] == ':' && |
| 249 | + (normalised_string[2] == '/' || normalised_string[2] == '\\')) { |
| 250 | + normalised_string[0] = ToLower(normalised_string[0]); |
| 251 | + } |
| 252 | +#endif |
| 253 | + for (char& c : normalised_string) { |
| 254 | + if (c == '\\') { |
| 255 | + c = '/'; |
| 256 | + } |
| 257 | + } |
| 258 | + |
| 259 | + normalised_string = NormalizeString(normalised_string, false, "/"); |
| 260 | + return normalised_string; |
| 261 | +} |
| 262 | + |
| 263 | +// Check if a path looks like an absolute path or file URL. |
| 264 | +static bool IsAbsoluteFilePath(std::string_view path) { |
| 265 | + if (path.rfind("file://", 0) == 0) { |
| 266 | + return true; |
| 267 | + } |
| 268 | +#ifdef _WIN32 |
| 269 | + if (path.size() > 2 && IsWindowsDeviceRoot(path[0]) && |
| 270 | + (path[1] == ':' && (path[2] == '/' || path[2] == '\\'))) |
| 271 | + return true; |
| 272 | + if (path.size() > 1 && path[0] == '\\' && path[1] == '\\') return true; |
| 273 | +#else |
| 274 | + if (path.size() > 0 && path[0] == '/') return true; |
| 275 | +#endif |
| 276 | + return false; |
| 277 | +} |
| 278 | + |
| 279 | +static std::string GetRelativePath(std::string_view path, |
| 280 | + std::string_view base) { |
| 281 | +// On Windows, the native encoding is UTF-16, so we need to convert |
| 282 | +// the paths to wide strings before using std::filesystem::path. |
| 283 | +// On other platforms, std::filesystem::path can handle UTF-8 directly. |
| 284 | +#ifdef _WIN32 |
| 285 | + std::wstring wpath = ConvertToWideString(std::string(path), CP_UTF8); |
| 286 | + std::wstring wbase = ConvertToWideString(std::string(base), CP_UTF8); |
| 287 | + std::filesystem::path relative = |
| 288 | + std::filesystem::path(wpath).lexically_relative( |
| 289 | + std::filesystem::path(wbase)); |
| 290 | + if (relative.empty()) { |
| 291 | + return std::string(); |
| 292 | + } |
| 293 | + std::string relative_path = ConvertWideToUTF8(relative.wstring()); |
| 294 | + return relative_path; |
| 295 | +#else |
| 296 | + std::filesystem::path relative = |
| 297 | + std::filesystem::path(path).lexically_relative( |
| 298 | + std::filesystem::path(base)); |
| 299 | + if (relative.empty()) { |
| 300 | + return std::string(); |
| 301 | + } |
| 302 | + return relative.generic_string(); |
| 303 | +#endif |
| 304 | +} |
| 305 | + |
226 | 306 | CompileCacheEntry* CompileCacheHandler::GetOrInsert(Local<String> code,
|
227 | 307 | Local<String> filename,
|
228 | 308 | CachedCodeType type) {
|
229 | 309 | DCHECK(!compile_cache_dir_.empty());
|
230 | 310 |
|
231 | 311 | Utf8Value filename_utf8(isolate_, filename);
|
232 |
| - uint32_t key = GetCacheKey(filename_utf8.ToStringView(), type); |
| 312 | + std::string file_path = filename_utf8.ToString(); |
| 313 | + // If the relative path is enabled, we try to use a relative path |
| 314 | + // from the compile cache directory to the file path |
| 315 | + if (portable_ && IsAbsoluteFilePath(file_path)) { |
| 316 | + // Normalise the path to ensure it is consistent. |
| 317 | + std::string normalised_file_path = NormalisePath(file_path); |
| 318 | + std::string relative_path = |
| 319 | + GetRelativePath(normalised_file_path, normalised_compile_cache_dir_); |
| 320 | + if (!relative_path.empty()) { |
| 321 | + file_path = relative_path; |
| 322 | + Debug("[compile cache] using relative path %s from %s\n", |
| 323 | + file_path.c_str(), |
| 324 | + absolute_compile_cache_dir_.c_str()); |
| 325 | + } |
| 326 | + } |
| 327 | + uint32_t key = GetCacheKey(file_path, type); |
233 | 328 |
|
234 | 329 | // TODO(joyeecheung): don't encode this again into UTF8. If we read the
|
235 | 330 | // UTF8 content on disk as raw buffer (from the JS layer, while watching out
|
@@ -500,11 +595,15 @@ CompileCacheHandler::CompileCacheHandler(Environment* env)
|
500 | 595 | // - $NODE_VERSION-$ARCH-$CACHE_DATA_VERSION_TAG-$UID
|
501 | 596 | // - $FILENAME_AND_MODULE_TYPE_HASH.cache: a hash of filename + module type
|
502 | 597 | CompileCacheEnableResult CompileCacheHandler::Enable(Environment* env,
|
503 |
| - const std::string& dir) { |
| 598 | + const std::string& dir, |
| 599 | + bool portable) { |
504 | 600 | std::string cache_tag = GetCacheVersionTag();
|
505 |
| - std::string absolute_cache_dir_base = PathResolve(env, {dir}); |
506 |
| - std::string cache_dir_with_tag = |
507 |
| - absolute_cache_dir_base + kPathSeparator + cache_tag; |
| 601 | + std::string base_dir = dir; |
| 602 | + if (!portable) { |
| 603 | + base_dir = PathResolve(env, {dir}); |
| 604 | + } |
| 605 | + |
| 606 | + std::string cache_dir_with_tag = base_dir + kPathSeparator + cache_tag; |
508 | 607 | CompileCacheEnableResult result;
|
509 | 608 | Debug("[compile cache] resolved path %s + %s -> %s\n",
|
510 | 609 | dir,
|
@@ -546,8 +645,11 @@ CompileCacheEnableResult CompileCacheHandler::Enable(Environment* env,
|
546 | 645 | return result;
|
547 | 646 | }
|
548 | 647 |
|
549 |
| - result.cache_directory = absolute_cache_dir_base; |
| 648 | + result.cache_directory = base_dir; |
550 | 649 | compile_cache_dir_ = cache_dir_with_tag;
|
| 650 | + absolute_compile_cache_dir_ = PathResolve(env, {compile_cache_dir_}); |
| 651 | + portable_ = portable; |
| 652 | + normalised_compile_cache_dir_ = NormalisePath(absolute_compile_cache_dir_); |
551 | 653 | result.status = CompileCacheEnableStatus::ENABLED;
|
552 | 654 | return result;
|
553 | 655 | }
|
|
0 commit comments