]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
refactor: fs::path-ify some functions
authorJoel Rosdahl <joel@rosdahl.net>
Sun, 14 Apr 2024 08:03:24 +0000 (10:03 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Sat, 27 Apr 2024 13:04:28 +0000 (15:04 +0200)
Fixes #1417.

42 files changed:
src/ccache/ArgsInfo.hpp
src/ccache/CMakeLists.txt
src/ccache/Config.cpp
src/ccache/Config.hpp
src/ccache/Context.cpp
src/ccache/Context.hpp
src/ccache/Depfile.cpp
src/ccache/InodeCache.cpp
src/ccache/Util.cpp [deleted file]
src/ccache/Util.hpp [deleted file]
src/ccache/argprocessing.cpp
src/ccache/ccache.cpp
src/ccache/core/FileRecompressor.cpp
src/ccache/core/ResultExtractor.cpp
src/ccache/core/Statistics.cpp
src/ccache/core/common.cpp
src/ccache/core/common.hpp
src/ccache/core/mainoptions.cpp
src/ccache/execute.cpp
src/ccache/storage/Storage.cpp
src/ccache/storage/local/util.cpp
src/ccache/storage/remote/FileStorage.cpp
src/ccache/util/LockFile.cpp
src/ccache/util/LongLivedLockFileManager.cpp
src/ccache/util/assertions.cpp
src/ccache/util/path.cpp
src/ccache/util/path.hpp
test/run
test/suites/depend.bash
unittest/CMakeLists.txt
unittest/main.cpp
unittest/test_Config.cpp
unittest/test_Depfile.cpp
unittest/test_InodeCache.cpp
unittest/test_Util.cpp [deleted file]
unittest/test_argprocessing.cpp
unittest/test_ccache.cpp
unittest/test_core_StatsLog.cpp
unittest/test_storage_local_StatsFile.cpp
unittest/test_util_LockFile.cpp
unittest/test_util_Tokenizer.cpp
unittest/test_util_path.cpp

index 2a46a4fb02b29991b54af4a89c0ab67cfc68196a..fe7c0f4171be864512aa04c748a3dc8db36e5ff2 100644 (file)
@@ -37,9 +37,6 @@ struct ArgsInfo
   // Prefix to the input file when adding it to a command line.
   std::string input_file_prefix;
 
-  // The source file path run through Util::normalize_concrete_absolute_path.
-  std::string normalized_input_file;
-
   // In normal compiler operation an output file is created if there is no
   // compiler error. However certain flags like -fsyntax-only change this
   // behavior.
index 876156ceef698135d9e08fb0a982863f9420f6d1..a80f41ebf1619be5daad2f8f01d88966c8f67c11 100644 (file)
@@ -6,7 +6,6 @@ set(
   Depfile.cpp
   Hash.cpp
   ProgressBar.cpp
-  Util.cpp
   argprocessing.cpp
   ccache.cpp
   compopt.cpp
index bb2c4ac2ddf977fb5b278beef0e40634542eba6e..dbf89d073dc93f92c2dfe3d760dd17b2dfbb891b 100644 (file)
 
 #include "Config.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/core/AtomicFile.hpp>
 #include <ccache/core/common.hpp>
 #include <ccache/core/exceptions.hpp>
 #include <ccache/core/types.hpp>
 #include <ccache/util/DirEntry.hpp>
+#include <ccache/util/PathString.hpp>
 #include <ccache/util/Tokenizer.hpp>
 #include <ccache/util/UmaskScope.hpp>
 #include <ccache/util/assertions.hpp>
@@ -32,6 +32,7 @@
 #include <ccache/util/file.hpp>
 #include <ccache/util/filesystem.hpp>
 #include <ccache/util/format.hpp>
+#include <ccache/util/path.hpp>
 #include <ccache/util/string.hpp>
 #include <ccache/util/wincompat.hpp>
 
@@ -60,7 +61,9 @@ const char k_sysconfdir[4096 + 1] = SYSCONFDIR;
 
 namespace fs = util::filesystem;
 
+using pstr = util::PathString;
 using util::DirEntry;
+using util::make_path;
 
 namespace {
 
@@ -383,9 +386,9 @@ format_umask(std::optional<mode_t> umask)
 }
 
 void
-verify_absolute_path(const std::string& value)
+verify_absolute_path(const fs::path& value)
 {
-  if (!fs::path(value).is_absolute()) {
+  if (!value.is_absolute()) {
     throw core::Error(FMT("not an absolute path: \"{}\"", value));
   }
 }
@@ -548,8 +551,8 @@ Config::read(const std::vector<std::string>& cmdline_config_settings)
   auto cmdline_settings_map =
     create_cmdline_settings_map(cmdline_config_settings);
 
-  const std::string home_dir = home_directory();
-  const std::string legacy_ccache_dir = Util::make_path(home_dir, ".ccache");
+  const fs::path home_dir = home_directory();
+  const fs::path legacy_ccache_dir = home_dir / ".ccache";
   const bool legacy_ccache_dir_exists =
     DirEntry(legacy_ccache_dir).is_directory();
 #ifdef _WIN32
@@ -567,22 +570,21 @@ Config::read(const std::vector<std::string>& cmdline_config_settings)
     // Only used for ccache tests:
     const char* const env_ccache_configpath2 = getenv("CCACHE_CONFIGPATH2");
 
-    std::string sysconfdir = Util::make_path(k_sysconfdir);
+    fs::path sysconfdir(k_sysconfdir);
 #ifdef _WIN32
     if (const char* program_data = getenv("ALLUSERSPROFILE"))
-      sysconfdir = Util::make_path(program_data, "ccache");
+      sysconfdir = fs::path(program_data) / "ccache";
 #endif
 
-    set_system_config_path(env_ccache_configpath2
-                             ? env_ccache_configpath2
-                             : Util::make_path(sysconfdir, "ccache.conf"));
+    set_system_config_path(env_ccache_configpath2 ? env_ccache_configpath2
+                                                  : sysconfdir / "ccache.conf");
     // A missing config file in SYSCONFDIR is OK so don't check return value.
     update_from_file(system_config_path());
 
     const char* const env_ccache_dir = getenv("CCACHE_DIR");
     auto cmdline_cache_dir = cmdline_settings_map.find("cache_dir");
 
-    std::string config_dir;
+    fs::path config_dir;
     if (cmdline_cache_dir != cmdline_settings_map.end()) {
       config_dir = cmdline_cache_dir->second;
     } else if (env_ccache_dir && *env_ccache_dir) {
@@ -594,14 +596,13 @@ Config::read(const std::vector<std::string>& cmdline_config_settings)
 #ifdef _WIN32
     } else if (env_local_appdata
                && DirEntry(
-                 Util::make_path(env_local_appdata, "ccache", "ccache.conf"))) {
-      config_dir = Util::make_path(env_local_appdata, "ccache");
+                 make_path(env_local_appdata, "ccache", "ccache.conf"))) {
+      config_dir = make_path(env_local_appdata, "ccache");
     } else if (env_appdata
-               && DirEntry(
-                 Util::make_path(env_appdata, "ccache", "ccache.conf"))) {
-      config_dir = Util::make_path(env_appdata, "ccache");
+               && DirEntry(make_path(env_appdata, "ccache", "ccache.conf"))) {
+      config_dir = make_path(env_appdata, "ccache");
     } else if (env_local_appdata) {
-      config_dir = Util::make_path(env_local_appdata, "ccache");
+      config_dir = make_path(env_local_appdata, "ccache");
     } else {
       throw core::Fatal(
         "could not find configuration file and the LOCALAPPDATA environment"
@@ -609,12 +610,12 @@ Config::read(const std::vector<std::string>& cmdline_config_settings)
     }
 #else
     } else if (env_xdg_config_home) {
-      config_dir = Util::make_path(env_xdg_config_home, "ccache");
+      config_dir = make_path(env_xdg_config_home, "ccache");
     } else {
       config_dir = default_config_dir(home_dir);
     }
 #endif
-    set_config_path(Util::make_path(config_dir, "ccache.conf"));
+    set_config_path(config_dir / "ccache.conf");
   }
 
   const std::string& cache_dir_before_config_file_was_read = cache_dir();
@@ -631,10 +632,10 @@ Config::read(const std::vector<std::string>& cmdline_config_settings)
 
   if (cache_dir().empty()) {
     if (legacy_ccache_dir_exists) {
-      set_cache_dir(legacy_ccache_dir);
+      set_cache_dir(pstr(legacy_ccache_dir));
 #ifdef _WIN32
     } else if (env_local_appdata) {
-      set_cache_dir(Util::make_path(env_local_appdata, "ccache"));
+      set_cache_dir(pstr(fs::path(env_local_appdata) / "ccache"));
     } else {
       throw core::Fatal(
         "could not find cache directory and the LOCALAPPDATA environment"
@@ -642,7 +643,7 @@ Config::read(const std::vector<std::string>& cmdline_config_settings)
     }
 #else
     } else if (env_xdg_cache_home) {
-      set_cache_dir(Util::make_path(env_xdg_cache_home, "ccache"));
+      set_cache_dir(make_path(env_xdg_cache_home, "ccache"));
     } else {
       set_cache_dir(default_cache_dir(home_dir));
     }
@@ -655,37 +656,37 @@ Config::read(const std::vector<std::string>& cmdline_config_settings)
   // system config).
 }
 
-const std::string&
+const fs::path&
 Config::config_path() const
 {
   return m_config_path;
 }
 
-const std::string&
+const fs::path&
 Config::system_config_path() const
 {
   return m_system_config_path;
 }
 
 void
-Config::set_config_path(std::string path)
+Config::set_config_path(const fs::path& path)
 {
-  m_config_path = std::move(path);
+  m_config_path = path;
 }
 
 void
-Config::set_system_config_path(std::string path)
+Config::set_system_config_path(const fs::path& path)
 {
-  m_system_config_path = std::move(path);
+  m_system_config_path = path;
 }
 
 bool
-Config::update_from_file(const std::string& path)
+Config::update_from_file(const fs::path& path)
 {
   return parse_config_file(
-    path, [&](const auto& /*line*/, const auto& key, const auto& value) {
+    pstr(path), [&](const auto& /*line*/, const auto& key, const auto& value) {
       if (!key.empty()) {
-        set_item(key, value, std::nullopt, false, path);
+        set_item(key, value, std::nullopt, false, pstr(path));
       }
     });
 }
@@ -753,7 +754,7 @@ Config::get_string_value(const std::string& key) const
     return format_bool(m_absolute_paths_in_stderr);
 
   case ConfigItem::base_dir:
-    return m_base_dir;
+    return pstr(m_base_dir).str();
 
   case ConfigItem::cache_dir:
     return m_cache_dir;
@@ -982,7 +983,7 @@ Config::set_item(const std::string& key,
     m_base_dir = value;
     if (!m_base_dir.empty()) { // The empty string means "disable"
       verify_absolute_path(m_base_dir);
-      m_base_dir = Util::normalize_abstract_absolute_path(m_base_dir);
+      m_base_dir = m_base_dir.lexically_normal();
     }
     break;
 
index 2ac11185410bff582a481a36ca1fe1460852a332..60ed5254d656a18327c07269e5c9db34bc5f435c 100644 (file)
@@ -53,7 +53,7 @@ public:
   void read(const std::vector<std::string>& cmdline_config_settings = {});
 
   bool absolute_paths_in_stderr() const;
-  const std::string& base_dir() const;
+  const std::filesystem::path& base_dir() const;
   const std::string& cache_dir() const;
   const std::string& compiler() const;
   const std::string& compiler_check() const;
@@ -106,7 +106,7 @@ public:
   util::SizeUnitPrefixType size_unit_prefix_type() const;
   std::string default_temporary_dir() const;
 
-  void set_base_dir(const std::string& value);
+  void set_base_dir(const std::filesystem::path& value);
   void set_cache_dir(const std::string& value);
   void set_compiler(const std::string& value);
   void set_compiler_type(CompilerType value);
@@ -124,12 +124,12 @@ public:
   void set_temporary_dir(const std::string& value);
 
   // Where to write configuration changes.
-  const std::string& config_path() const;
+  const std::filesystem::path& config_path() const;
   // System (read-only) configuration file (if any).
-  const std::string& system_config_path() const;
+  const std::filesystem::path& system_config_path() const;
 
-  void set_config_path(std::string path);
-  void set_system_config_path(std::string path);
+  void set_config_path(const std::filesystem::path& path);
+  void set_system_config_path(const std::filesystem::path& path);
 
   using ItemVisitor = std::function<void(const std::string& key,
                                          const std::string& value,
@@ -139,7 +139,7 @@ public:
   //
   // Returns false if the file can't be opened, otherwise true. Throws Error on
   // invalid configuration values.
-  bool update_from_file(const std::string& path);
+  bool update_from_file(const std::filesystem::path& path);
 
   // Set config values from a map with key-value pairs.
   //
@@ -164,11 +164,11 @@ public:
   static void check_key_tables_consistency();
 
 private:
-  std::string m_config_path;
-  std::string m_system_config_path;
+  std::filesystem::path m_config_path;
+  std::filesystem::path m_system_config_path;
 
   bool m_absolute_paths_in_stderr = false;
-  std::string m_base_dir;
+  std::filesystem::path m_base_dir;
   std::string m_cache_dir;
   std::string m_compiler;
   std::string m_compiler_check = "mtime";
@@ -236,7 +236,7 @@ Config::absolute_paths_in_stderr() const
   return m_absolute_paths_in_stderr;
 }
 
-inline const std::string&
+inline const std::filesystem::path&
 Config::base_dir() const
 {
   return m_base_dir;
@@ -516,7 +516,7 @@ Config::size_unit_prefix_type() const
 }
 
 inline void
-Config::set_base_dir(const std::string& value)
+Config::set_base_dir(const std::filesystem::path& value)
 {
   m_base_dir = value;
 }
index 31e03745625faf2d12dd5d634e05e450b70c269f..1e4a88a4be9e2f8a0f9715bb234685bc8b9bab3a 100644 (file)
 #include "Context.hpp"
 
 #include <ccache/SignalHandler.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/hashutil.hpp>
 #include <ccache/util/TimePoint.hpp>
 #include <ccache/util/file.hpp>
+#include <ccache/util/filesystem.hpp>
 #include <ccache/util/logging.hpp>
 #include <ccache/util/path.hpp>
 #include <ccache/util/process.hpp>
 #include <string>
 #include <vector>
 
+namespace fs = util::filesystem;
+
 Context::Context()
-  : actual_cwd(util::actual_cwd()),
+  : actual_cwd(fs::current_path().value_or("")),
     apparent_cwd(util::apparent_cwd(actual_cwd)),
     storage(config),
 #ifdef INODE_CACHE_SUPPORTED
index 7226162617cc955b9f816634981822a3b0173ba6..144d135b3ad084d0fef62182f95b3a6f88295983 100644 (file)
@@ -59,10 +59,10 @@ public:
   Config config;
 
   // Current working directory as returned by getcwd(3).
-  std::string actual_cwd;
+  std::filesystem::path actual_cwd;
 
   // Current working directory according to $PWD (falling back to getcwd(3)).
-  std::string apparent_cwd;
+  std::filesystem::path apparent_cwd;
 
   // The original argument list.
   Args orig_args;
index c6a6feea5a670eeb18126e95c182df8df91e3de9..36fb32b76802c7646a601d9771f2ef99fdc657fd 100644 (file)
@@ -20,8 +20,9 @@
 
 #include <ccache/Context.hpp>
 #include <ccache/Hash.hpp>
-#include <ccache/Util.hpp>
+#include <ccache/core/common.hpp>
 #include <ccache/core/exceptions.hpp>
+#include <ccache/util/PathString.hpp>
 #include <ccache/util/Tokenizer.hpp>
 #include <ccache/util/assertions.hpp>
 #include <ccache/util/file.hpp>
@@ -34,6 +35,8 @@
 
 namespace fs = util::filesystem;
 
+using pstr = util::PathString;
+
 namespace Depfile {
 
 std::string
@@ -76,10 +79,10 @@ rewrite_source_paths(const Context& ctx, std::string_view content)
     if (token.empty() || token == ":") {
       continue;
     }
-    auto rel_path = Util::make_relative_path(ctx, token);
+    auto rel_path = core::make_relative_path(ctx, token);
     if (rel_path != token) {
       rewritten = true;
-      token = std::move(rel_path);
+      token = pstr(rel_path).str();
     }
   }
 
index 774633b52ca5ef781b15abf5d0a74c23ee6eed72..d4ce38381245f5ecf88b66b6cb467f3d3ef51ca9 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <ccache/Config.hpp>
 #include <ccache/Hash.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/util/DirEntry.hpp>
 #include <ccache/util/Fd.hpp>
 #include <ccache/util/Finalizer.hpp>
diff --git a/src/ccache/Util.cpp b/src/ccache/Util.cpp
deleted file mode 100644 (file)
index 3d35aa6..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright (C) 2019-2024 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "Util.hpp"
-
-#include <ccache/Config.hpp>
-#include <ccache/Context.hpp>
-#include <ccache/core/exceptions.hpp>
-#include <ccache/util/DirEntry.hpp>
-#include <ccache/util/expected.hpp>
-#include <ccache/util/file.hpp>
-#include <ccache/util/filesystem.hpp>
-#include <ccache/util/format.hpp>
-#include <ccache/util/logging.hpp>
-#include <ccache/util/path.hpp>
-#include <ccache/util/string.hpp>
-#include <ccache/util/wincompat.hpp>
-
-#ifdef HAVE_UNISTD_H
-#  include <unistd.h>
-#endif
-
-#include <fcntl.h>
-
-namespace fs = util::filesystem;
-
-using util::DirEntry;
-
-namespace Util {
-
-size_t
-common_dir_prefix_length(std::string_view dir, std::string_view path)
-{
-  if (dir.empty() || path.empty() || dir == "/" || path == "/") {
-    return 0;
-  }
-
-  ASSERT(dir[0] == '/');
-  ASSERT(path[0] == '/');
-
-  const size_t limit = std::min(dir.length(), path.length());
-  size_t i = 0;
-
-  while (i < limit && dir[i] == path[i]) {
-    ++i;
-  }
-
-  if ((i == dir.length() && i == path.length())
-      || (i == dir.length() && path[i] == '/')
-      || (i == path.length() && dir[i] == '/')) {
-    return i;
-  }
-
-  do {
-    --i;
-  } while (i > 0 && dir[i] != '/' && path[i] != '/');
-
-  return i;
-}
-
-std::string
-get_relative_path(std::string_view dir, std::string_view path)
-{
-  ASSERT(fs::path(dir).is_absolute());
-  ASSERT(fs::path(path).is_absolute());
-
-#ifdef _WIN32
-  // Paths can be escaped by a slash for use with e.g. -isystem.
-  if (dir.length() >= 3 && dir[0] == '/' && dir[2] == ':') {
-    dir = dir.substr(1);
-  }
-  if (path.length() >= 3 && path[0] == '/' && path[2] == ':') {
-    path = path.substr(1);
-  }
-  if (dir[0] != path[0]) {
-    // Drive letters differ.
-    return std::string(path);
-  }
-  dir = dir.substr(2);
-  path = path.substr(2);
-#endif
-
-  std::string result;
-  size_t common_prefix_len = Util::common_dir_prefix_length(dir, path);
-  if (common_prefix_len > 0 || dir != "/") {
-    for (size_t i = common_prefix_len; i < dir.length(); ++i) {
-      if (dir[i] == '/') {
-        if (!result.empty()) {
-          result += '/';
-        }
-        result += "..";
-      }
-    }
-  }
-  if (path.length() > common_prefix_len) {
-    if (!result.empty()) {
-      result += '/';
-    }
-    result += std::string(path.substr(common_prefix_len + 1));
-  }
-  result.erase(result.find_last_not_of('/') + 1);
-  return result.empty() ? "." : result;
-}
-
-std::string
-make_relative_path(const std::string& base_dir,
-                   const std::string& actual_cwd,
-                   const std::string& apparent_cwd,
-                   std::string_view path)
-{
-  if (base_dir.empty() || !util::path_starts_with(path, base_dir)) {
-    return std::string(path);
-  }
-
-#ifdef _WIN32
-  std::string winpath;
-  if (path.length() >= 3 && path[0] == '/') {
-    if (isalpha(path[1]) && path[2] == '/') {
-      // Transform /c/path... to c:/path...
-      winpath = FMT("{}:/{}", path[1], path.substr(3));
-      path = winpath;
-    } else if (path[2] == ':') {
-      // Transform /c:/path to c:/path
-      winpath = std::string(path.substr(1));
-      path = winpath;
-    }
-  }
-#endif
-
-  // The algorithm for computing relative paths below only works for existing
-  // paths. If the path doesn't exist, find the first ancestor directory that
-  // does exist and assemble the path again afterwards.
-
-  std::vector<std::string> relpath_candidates;
-  const auto original_path = path;
-  fs::path path_path = path;
-  while (!fs::exists(path_path)) {
-    path_path = path_path.parent_path();
-  }
-  std::string path_str = path_path.string();
-  path = path_str;
-  const auto path_suffix = std::string(original_path.substr(path.length()));
-  const fs::path real_path = fs::canonical(path).value_or(fs::path(path));
-
-  const auto add_relpath_candidates = [&](auto p) {
-    const std::string normalized_path =
-      Util::normalize_abstract_absolute_path(p);
-    relpath_candidates.push_back(
-      Util::get_relative_path(actual_cwd, normalized_path));
-    if (apparent_cwd != actual_cwd) {
-      relpath_candidates.emplace_back(
-        Util::get_relative_path(apparent_cwd, normalized_path));
-    }
-  };
-  add_relpath_candidates(path);
-  if (real_path != path) {
-    add_relpath_candidates(real_path.string());
-  }
-
-  // Find best (i.e. shortest existing) match:
-  std::sort(relpath_candidates.begin(),
-            relpath_candidates.end(),
-            [](const auto& path1, const auto& path2) {
-              return path1.length() < path2.length();
-            });
-  for (const auto& relpath : relpath_candidates) {
-    if (fs::equivalent(relpath, path_path)) {
-      return relpath + path_suffix;
-    }
-  }
-
-  // No match so nothing else to do than to return the unmodified path.
-  return std::string(original_path);
-}
-
-std::string
-make_relative_path(const Context& ctx, std::string_view path)
-{
-  return make_relative_path(
-    ctx.config.base_dir(), ctx.actual_cwd, ctx.apparent_cwd, path);
-}
-
-static std::string
-do_normalize_abstract_absolute_path(std::string_view path)
-{
-  if (!fs::path(path).is_absolute()) {
-    return std::string(path);
-  }
-
-#ifdef _WIN32
-  std::string drive(path.substr(0, 2));
-  path = path.substr(2);
-#endif
-
-  std::string result = "/";
-  const size_t npos = std::string_view::npos;
-  size_t left = 1;
-
-  while (true) {
-    if (left >= path.length()) {
-      break;
-    }
-    const auto right = path.find('/', left);
-    std::string_view part =
-      path.substr(left, right == npos ? npos : right - left);
-    if (part == "..") {
-      if (result.length() > 1) {
-        // "/x/../part" -> "/part"
-        result.erase(result.rfind('/', result.length() - 2) + 1);
-      } else {
-        // "/../part" -> "/part"
-      }
-    } else if (part == ".") {
-      // "/x/." -> "/x"
-    } else {
-      result.append(part.begin(), part.end());
-      if (result[result.length() - 1] != '/') {
-        result += '/';
-      }
-    }
-    if (right == npos) {
-      break;
-    }
-    left = right + 1;
-  }
-  if (result.length() > 1) {
-    result.erase(result.find_last_not_of('/') + 1);
-  }
-
-#ifdef _WIN32
-  return drive + result;
-#else
-  return result;
-#endif
-}
-
-std::string
-normalize_abstract_absolute_path(std::string_view path)
-{
-#ifdef _WIN32
-  std::string new_path(path);
-  std::replace(new_path.begin(), new_path.end(), '\\', '/');
-  return do_normalize_abstract_absolute_path(new_path);
-#else
-  return do_normalize_abstract_absolute_path(path);
-#endif
-}
-
-std::string
-normalize_concrete_absolute_path(const std::string& path)
-{
-  const auto normalized_path = normalize_abstract_absolute_path(path);
-  return (normalized_path == path
-          || DirEntry(normalized_path).same_inode_as(DirEntry(path)))
-           ? normalized_path
-           : path;
-}
-
-} // namespace Util
diff --git a/src/ccache/Util.hpp b/src/ccache/Util.hpp
deleted file mode 100644 (file)
index 77c4653..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2019-2024 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#pragma once
-
-#include <filesystem>
-#include <string>
-#include <string_view>
-
-class Context;
-
-namespace Util {
-
-// Compute the length of the longest directory path that is common to paths
-// `dir` (a directory) and `path` (any path).
-size_t common_dir_prefix_length(std::string_view dir, std::string_view path);
-
-// Compute a relative path from `dir` (an absolute path to a directory) to
-// `path` (an absolute path). Assumes that both `dir` and `path` are normalized.
-// The algorithm does *not* follow symlinks, so the result may not actually
-// resolve to the same file as `path`.
-std::string get_relative_path(std::string_view dir, std::string_view path);
-
-// Construct a normalized native path.
-//
-// Example:
-//
-//   std::string path = Util::make_path("usr", "local", "bin");
-template<typename... T>
-std::string
-make_path(const T&... args)
-{
-  return (std::filesystem::path{} / ... / args).lexically_normal().string();
-}
-
-// Make a relative path from current working directory (either `actual_cwd` or
-// `apparent_cwd`) to `path` if `path` is under `base_dir`.
-std::string make_relative_path(const std::string& base_dir,
-                               const std::string& actual_cwd,
-                               const std::string& apparent_cwd,
-                               std::string_view path);
-
-// Like above but with base directory and apparent/actual CWD taken from `ctx`.
-std::string make_relative_path(const Context& ctx, std::string_view path);
-
-// Normalize absolute path `path`, not taking symlinks into account.
-//
-// Normalization here means syntactically removing redundant slashes and
-// resolving "." and ".." parts. The algorithm does however *not* follow
-// symlinks, so the result may not actually resolve to the same filesystem entry
-// as `path` (nor to any existing file system entry for that matter).
-//
-// On Windows: Backslashes are replaced with forward slashes.
-std::string normalize_abstract_absolute_path(std::string_view path);
-
-// Like normalize_abstract_absolute_path, but returns `path` unchanged if the
-// normalized result doesn't resolve to the same file system entry as `path`.
-std::string normalize_concrete_absolute_path(const std::string& path);
-
-} // namespace Util
index ee9eb2d1ea267624e34c9a01092732a56c7065fe..f8f454eb1863185b6c57d1159f322b760c16510a 100644 (file)
@@ -20,8 +20,8 @@
 
 #include <ccache/Context.hpp>
 #include <ccache/Depfile.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/compopt.hpp>
+#include <ccache/core/common.hpp>
 #include <ccache/language.hpp>
 #include <ccache/util/PathString.hpp>
 #include <ccache/util/assertions.hpp>
@@ -252,7 +252,7 @@ process_profiling_option(const Context& ctx,
       new_profile_path = ".";
     } else {
       // GCC uses $PWD/$(basename $obj).
-      new_profile_path = ctx.apparent_cwd;
+      new_profile_path = pstr(ctx.apparent_cwd).str();
     }
   } else if (util::starts_with(arg, "-fprofile-generate=")
              || util::starts_with(arg, "-fprofile-instr-generate=")) {
@@ -737,7 +737,8 @@ process_option_arg(const Context& ctx,
 
     if (state.output_dep_origin <= OutputDepOrigin::mf) {
       state.output_dep_origin = OutputDepOrigin::mf;
-      args_info.output_dep = Util::make_relative_path(ctx, dep_file);
+      args_info.output_dep =
+        pstr(core::make_relative_path(ctx, dep_file)).str();
     }
     // Keep the format of the args the same.
     if (separate_argument) {
@@ -869,8 +870,8 @@ process_option_arg(const Context& ctx,
 
   if (util::starts_with(arg, "--sysroot=")) {
     auto path = std::string_view(arg).substr(10);
-    auto relpath = Util::make_relative_path(ctx, path);
-    state.common_args.push_back("--sysroot=" + relpath);
+    auto relpath = core::make_relative_path(ctx, path);
+    state.common_args.push_back(FMT("--sysroot={}", relpath));
     return Statistic::none;
   }
 
@@ -881,8 +882,8 @@ process_option_arg(const Context& ctx,
       return Statistic::bad_compiler_arguments;
     }
     state.common_args.push_back(args[i]);
-    auto relpath = Util::make_relative_path(ctx, args[i + 1]);
-    state.common_args.push_back(relpath);
+    auto relpath = core::make_relative_path(ctx, args[i + 1]);
+    state.common_args.push_back(pstr(relpath).str());
     i++;
     return Statistic::none;
   }
@@ -974,7 +975,8 @@ process_option_arg(const Context& ctx,
       return Statistic::bad_compiler_arguments;
     }
     args_info.generating_diagnostics = true;
-    args_info.output_dia = Util::make_relative_path(ctx, args[i + 1]);
+    args_info.output_dia =
+      pstr(core::make_relative_path(ctx, args[i + 1])).str();
     i++;
     return Statistic::none;
   }
@@ -1080,14 +1082,14 @@ process_option_arg(const Context& ctx,
     // Potentially rewrite path argument to relative path to get better hit
     // rate. A secondary effect is that paths in the standard error output
     // produced by the compiler will be normalized.
-    std::string relpath = Util::make_relative_path(ctx, args[i + next]);
+    fs::path relpath = core::make_relative_path(ctx, args[i + next]);
     auto& dest_args =
       compopt_affects_cpp_output(arg) ? state.cpp_args : state.common_args;
     dest_args.push_back(args[i]);
     if (next == 2) {
       dest_args.push_back(args[i + 1]);
     }
-    dest_args.push_back(relpath);
+    dest_args.push_back(pstr(relpath).str());
 
     i += next;
     return Statistic::none;
@@ -1116,7 +1118,7 @@ process_option_arg(const Context& ctx,
     const auto [option, path] = util::split_option_with_concat_path(arg);
     if (path) {
       if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) {
-        const auto relpath = Util::make_relative_path(ctx, *path);
+        const auto relpath = core::make_relative_path(ctx, *path);
         std::string new_option = FMT("{}{}", option, relpath);
         if (compopt_affects_cpp_output(option)) {
           state.cpp_args.push_back(std::move(new_option));
@@ -1268,9 +1270,7 @@ process_args(Context& ctx)
   args_info.orig_input_file = pstr(state.input_files.front()).str();
   // Rewrite to relative to increase hit rate.
   args_info.input_file =
-    Util::make_relative_path(ctx, args_info.orig_input_file);
-  args_info.normalized_input_file =
-    Util::normalize_concrete_absolute_path(args_info.input_file);
+    pstr(core::make_relative_path(ctx, args_info.orig_input_file)).str();
 
   // Bail out on too hard combinations of options.
   if (state.found_mf_opt && state.found_wp_md_or_mmd_opt) {
@@ -1323,7 +1323,8 @@ process_args(Context& ctx)
   }
 
   args_info.orig_output_obj = args_info.output_obj;
-  args_info.output_obj = Util::make_relative_path(ctx, args_info.output_obj);
+  args_info.output_obj =
+    pstr(core::make_relative_path(ctx, args_info.output_obj)).str();
 
   // Determine output dependency file.
 
@@ -1362,7 +1363,7 @@ process_args(Context& ctx)
   }
 
   if (args_info.profile_path.empty()) {
-    args_info.profile_path = ctx.apparent_cwd;
+    args_info.profile_path = pstr(ctx.apparent_cwd).str();
   }
 
   if (!state.explicit_language.empty() && state.explicit_language == "none") {
@@ -1386,7 +1387,7 @@ process_args(Context& ctx)
   if (args_info.output_is_precompiled_header && output_obj_by_source) {
     args_info.orig_output_obj = args_info.orig_input_file + ".gch";
     args_info.output_obj =
-      Util::make_relative_path(ctx, args_info.orig_output_obj);
+      pstr(core::make_relative_path(ctx, args_info.orig_output_obj)).str();
   }
 
   if (args_info.output_is_precompiled_header
@@ -1564,13 +1565,15 @@ process_args(Context& ctx)
   if (args_info.generating_stackusage) {
     std::string default_sufile_name =
       fs::path(args_info.output_obj).replace_extension(".su").string();
-    args_info.output_su = Util::make_relative_path(ctx, default_sufile_name);
+    args_info.output_su =
+      pstr(core::make_relative_path(ctx, default_sufile_name)).str();
   }
 
   if (args_info.generating_callgraphinfo) {
     std::string default_cifile_name =
       fs::path(args_info.output_obj).replace_extension(".ci").string();
-    args_info.output_ci = Util::make_relative_path(ctx, default_cifile_name);
+    args_info.output_ci =
+      pstr(core::make_relative_path(ctx, default_cifile_name)).str();
   }
 
   Args compiler_args = state.common_args;
index a6beb2028b50c92eb7f07c9a3f3c593a04d648a9..594c0d8a6f4564c15ae17baa7a88df04c3ab832f 100644 (file)
@@ -25,7 +25,6 @@
 #include <ccache/Depfile.hpp>
 #include <ccache/Hash.hpp>
 #include <ccache/SignalHandler.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/argprocessing.hpp>
 #include <ccache/compopt.hpp>
 #include <ccache/core/CacheEntry.hpp>
@@ -313,7 +312,7 @@ remember_include_file(Context& ctx,
     return {};
   }
 
-  if (path == ctx.args_info.normalized_input_file) {
+  if (fs::path(path) == ctx.args_info.input_file) {
     // Don't remember the input file.
     return {};
   }
@@ -550,18 +549,23 @@ process_preprocessed_file(Context& ctx, Hash& hash, const std::string& path)
 
       // p and q span the include file path.
       std::string inc_path(p, q - p);
-      auto it = relative_inc_path_cache.find(inc_path);
-      if (it == relative_inc_path_cache.end()) {
-        auto rel_inc_path = Util::make_relative_path(
-          ctx, Util::normalize_concrete_absolute_path(inc_path));
-        relative_inc_path_cache.emplace(inc_path, rel_inc_path);
-        inc_path = std::move(rel_inc_path);
-      } else {
-        inc_path = it->second;
+      while (!inc_path.empty() && inc_path.back() == '/') {
+        inc_path.pop_back();
+      }
+      if (!ctx.config.base_dir().empty()) {
+        auto it = relative_inc_path_cache.find(inc_path);
+        if (it == relative_inc_path_cache.end()) {
+          std::string rel_inc_path =
+            pstr(core::make_relative_path(ctx, inc_path)).str();
+          relative_inc_path_cache.emplace(inc_path, rel_inc_path);
+          inc_path = pstr(rel_inc_path).str();
+        } else {
+          inc_path = it->second;
+        }
       }
 
-      if ((inc_path != ctx.apparent_cwd) || ctx.config.hash_dir()) {
-        hash.hash(inc_path);
+      if (inc_path != ctx.apparent_cwd || ctx.config.hash_dir()) {
+        hash.hash(pstr(inc_path).str());
       }
 
       TRY(remember_include_file(ctx, inc_path, hash, system, nullptr));
@@ -615,7 +619,7 @@ process_preprocessed_file(Context& ctx, Hash& hash, const std::string& path)
   if (!ctx.args_info.included_pch_file.empty()
       && !ctx.args_info.generating_pch) {
     std::string pch_path =
-      Util::make_relative_path(ctx, ctx.args_info.included_pch_file);
+      pstr(core::make_relative_path(ctx, ctx.args_info.included_pch_file));
     hash.hash(pch_path);
     TRY(remember_include_file(ctx, pch_path, hash, false, nullptr));
   }
@@ -654,8 +658,8 @@ result_key_from_depfile(Context& ctx, Hash& hash)
       continue;
     }
     if (seen_colon) {
-      std::string path = Util::make_relative_path(ctx, token);
-      TRY(remember_include_file(ctx, path, hash, false, &hash));
+      fs::path path = core::make_relative_path(ctx, token);
+      TRY(remember_include_file(ctx, pstr(path), hash, false, &hash));
     } else if (token == ":") {
       seen_colon = true;
     }
@@ -665,10 +669,10 @@ result_key_from_depfile(Context& ctx, Hash& hash)
   // dependencies output.
   if (!ctx.args_info.included_pch_file.empty()
       && !ctx.args_info.generating_pch) {
-    std::string pch_path =
-      Util::make_relative_path(ctx, ctx.args_info.included_pch_file);
-    hash.hash(pch_path);
-    TRY(remember_include_file(ctx, pch_path, hash, false, nullptr));
+    fs::path pch_path =
+      core::make_relative_path(ctx, ctx.args_info.included_pch_file);
+    hash.hash(pstr(pch_path).str());
+    TRY(remember_include_file(ctx, pstr(pch_path), hash, false, nullptr));
   }
 
   bool debug_included = getenv("CCACHE_DEBUG_INCLUDED");
@@ -716,19 +720,18 @@ result_key_from_includes(Context& ctx, Hash& hash, std::string_view stdout_data)
 {
   for (std::string_view include : core::MsvcShowIncludesOutput::get_includes(
          stdout_data, ctx.config.msvc_dep_prefix())) {
-    const std::string path = Util::make_relative_path(
-      ctx, Util::normalize_abstract_absolute_path(include));
-    TRY(remember_include_file(ctx, path, hash, false, &hash));
+    const fs::path path = core::make_relative_path(ctx, include);
+    TRY(remember_include_file(ctx, pstr(path), hash, false, &hash));
   }
 
   // Explicitly check the .pch file as it is not mentioned in the
   // includes output.
   if (!ctx.args_info.included_pch_file.empty()
       && !ctx.args_info.generating_pch) {
-    std::string pch_path =
-      Util::make_relative_path(ctx, ctx.args_info.included_pch_file);
-    hash.hash(pch_path);
-    TRY(remember_include_file(ctx, pch_path, hash, false, nullptr));
+    fs::path pch_path =
+      core::make_relative_path(ctx, ctx.args_info.included_pch_file);
+    hash.hash(pstr(pch_path).str());
+    TRY(remember_include_file(ctx, pstr(pch_path), hash, false, nullptr));
   }
 
   const bool debug_included = getenv("CCACHE_DEBUG_INCLUDED");
@@ -1017,10 +1020,9 @@ rewrite_stdout_from_compiler(const Context& ctx, util::Bytes&& stdout_data)
         std::string abs_inc_path =
           util::replace_first(orig_line, ctx.config.msvc_dep_prefix(), "");
         abs_inc_path = util::strip_whitespace(abs_inc_path);
-        std::string rel_inc_path = Util::make_relative_path(
-          ctx, Util::normalize_concrete_absolute_path(abs_inc_path));
-        std::string line_with_rel_inc =
-          util::replace_first(orig_line, abs_inc_path, rel_inc_path);
+        fs::path rel_inc_path = core::make_relative_path(ctx, abs_inc_path);
+        std::string line_with_rel_inc = util::replace_first(
+          orig_line, abs_inc_path, pstr(rel_inc_path).str());
         new_stdout_data.insert(new_stdout_data.end(),
                                line_with_rel_inc.data(),
                                line_with_rel_inc.size());
@@ -1506,7 +1508,7 @@ hash_common_info(const Context& ctx,
 
   // Possibly hash the current working directory.
   if (args_info.generating_debuginfo && ctx.config.hash_dir()) {
-    std::string dir_to_hash = ctx.apparent_cwd;
+    std::string dir_to_hash = pstr(ctx.apparent_cwd).str();
     for (const auto& map : args_info.debug_prefix_maps) {
       size_t sep_pos = map.find('=');
       if (sep_pos != std::string::npos) {
@@ -1516,8 +1518,9 @@ hash_common_info(const Context& ctx,
             old_path,
             new_path,
             ctx.apparent_cwd);
-        if (util::starts_with(ctx.apparent_cwd, old_path)) {
-          dir_to_hash = new_path + ctx.apparent_cwd.substr(old_path.size());
+        if (util::starts_with(pstr(ctx.apparent_cwd), old_path)) {
+          dir_to_hash =
+            new_path + pstr(ctx.apparent_cwd).str().substr(old_path.size());
         }
       }
     }
@@ -1533,7 +1536,7 @@ hash_common_info(const Context& ctx,
     const std::string output_obj_dir =
       fs::path(args_info.output_obj).is_absolute()
         ? fs::path(args_info.output_obj).parent_path().string()
-        : ctx.actual_cwd;
+        : pstr(ctx.actual_cwd);
     LOG("Hashing object file directory {}", output_obj_dir);
     hash.hash_delimiter("source path");
     hash.hash(output_obj_dir);
@@ -1558,7 +1561,7 @@ hash_common_info(const Context& ctx,
     // the directory in the hash for now.
     LOG_RAW("Hashing apparent CWD due to generating a .gcno file");
     hash.hash_delimiter("CWD in .gcno");
-    hash.hash(ctx.apparent_cwd);
+    hash.hash(pstr(ctx.apparent_cwd).str());
   }
 
   // Possibly hash the coverage data file path.
@@ -1940,7 +1943,7 @@ hash_profile_data_file(const Context& ctx, Hash& hash)
   const std::string& profile_path = ctx.args_info.profile_path;
   const std::string base_name =
     pstr(fs::path(ctx.args_info.output_obj).replace_extension("")).str();
-  std::string hashified_cwd = ctx.apparent_cwd;
+  std::string hashified_cwd = pstr(ctx.apparent_cwd).str();
   std::replace(hashified_cwd.begin(), hashified_cwd.end(), '/', '#');
 
   std::vector<std::string> paths_to_try{
@@ -2599,7 +2602,7 @@ do_cache_compilation(Context& ctx)
 
   if (processed.hash_actual_cwd) {
     common_hash.hash_delimiter("actual_cwd");
-    common_hash.hash(ctx.actual_cwd);
+    common_hash.hash(pstr(ctx.actual_cwd).str());
   }
 
   // Try to find the hash using the manifest.
index 945d902cc76ed1ef7949d9ac4f42dc96d393b725..cb3f6fea11970edecf97f8294fcff8aa7b0d2c42 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "FileRecompressor.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/core/AtomicFile.hpp>
 #include <ccache/core/CacheEntry.hpp>
 #include <ccache/core/exceptions.hpp>
index f765da4b4fde7a385a3f98dca41df21ad894ac2c..18c58d70024697ecad66c2fa7caaa3af0b87aba1 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "ResultExtractor.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/core/exceptions.hpp>
 #include <ccache/util/Bytes.hpp>
 #include <ccache/util/DirEntry.hpp>
index 1f012a428edf0b7cecdcc106063c77e4ab52e2b9..ab368af876673b91820279409ced79b9805934d9 100644 (file)
@@ -19,7 +19,7 @@
 #include "Statistics.hpp"
 
 #include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
+#include <ccache/util/PathString.hpp>
 #include <ccache/util/TextTable.hpp>
 #include <ccache/util/format.hpp>
 #include <ccache/util/logging.hpp>
@@ -30,6 +30,7 @@
 
 namespace core {
 
+using pstr = util::PathString;
 using core::Statistic;
 
 const unsigned FLAG_NOZERO = 1U << 0;      // don't zero with --zero-stats
@@ -390,9 +391,10 @@ Statistics::format_human_readable(const Config& config,
 
   if (verbosity > 0 && !from_log) {
     table.add_row({"Cache directory:", C(config.cache_dir()).colspan(4)});
-    table.add_row({"Config file:", C(config.config_path()).colspan(4)});
     table.add_row(
-      {"System config file:", C(config.system_config_path()).colspan(4)});
+      {"Config file:", C(pstr(config.config_path()).str()).colspan(4)});
+    table.add_row({"System config file:",
+                   C(pstr(config.system_config_path()).str()).colspan(4)});
     table.add_row(
       {"Stats updated:", C(format_timestamp(last_updated)).colspan(4)});
     if (verbosity > 1) {
index 4018123448576c378f5a37701e6b0cdbdbe9b4d1..a716b54486bc7a20b79345069a565360e503b0e7 100644 (file)
@@ -81,6 +81,17 @@ ensure_dir_exists(const fs::path& dir)
   }
 }
 
+std::filesystem::path
+make_relative_path(const Context& ctx, const std::filesystem::path& path)
+{
+  if (!ctx.config.base_dir().empty() && path.is_absolute()
+      && util::path_starts_with(path, ctx.config.base_dir())) {
+    return util::make_relative_path(ctx.actual_cwd, ctx.apparent_cwd, path);
+  } else {
+    return path;
+  }
+}
+
 std::string
 rewrite_stderr_to_absolute_paths(std::string_view text)
 {
index f8d0b339c40d61f36ba72d6865ad73ef0e3a72cb..78734a8fa8b5ee063e938f8fed11c9e85c850a14 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2023 Joel Rosdahl and other contributors
+// Copyright (C) 2023-2024 Joel Rosdahl and other contributors
 //
 // See doc/AUTHORS.adoc for a complete list of contributors.
 //
@@ -29,6 +29,10 @@ namespace core {
 // Like std::filesystem::create_directories but throws core::Fatal on error.
 void ensure_dir_exists(const std::filesystem::path& dir);
 
+// Make a `path` relative to CWD if it's under base_dir.
+std::filesystem::path make_relative_path(const Context& ctx,
+                                         const std::filesystem::path& path);
+
 // Rewrite path to absolute path in `text` in the following two cases, where X
 // may be optional ANSI CSI sequences:
 //
index f180dccf71e3906954b7b42a9dfc7369b556fb28..2137939bfde3a7455b274f23b816b6d582e8788c 100644 (file)
@@ -22,7 +22,6 @@
 #include <ccache/Hash.hpp>
 #include <ccache/InodeCache.hpp>
 #include <ccache/ProgressBar.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/ccache.hpp>
 #include <ccache/core/CacheEntry.hpp>
 #include <ccache/core/FileRecompressor.hpp>
@@ -717,7 +716,8 @@ process_main_options(int argc, const char* const* argv)
 
     case 'F': { // --max-files
       auto files = util::value_or_throw<Error>(util::parse_unsigned(arg));
-      config.set_value_in_file(config.config_path(), "max_files", arg);
+      config.set_value_in_file(
+        pstr(config.config_path()).str(), "max_files", arg);
       if (files == 0) {
         PRINT_RAW(stdout, "Unset cache file limit\n");
       } else {
@@ -730,7 +730,8 @@ process_main_options(int argc, const char* const* argv)
       auto [size, suffix_type] =
         util::value_or_throw<Error>(util::parse_size(arg));
       uint64_t max_size = size;
-      config.set_value_in_file(config.config_path(), "max_size", arg);
+      config.set_value_in_file(
+        pstr(config.config_path()).str(), "max_size", arg);
       if (max_size == 0) {
         PRINT_RAW(stdout, "Unset cache size limit\n");
       } else {
@@ -750,7 +751,7 @@ process_main_options(int argc, const char* const* argv)
       }
       std::string key = arg.substr(0, eq_pos);
       std::string value = arg.substr(eq_pos + 1);
-      config.set_value_in_file(config.config_path(), key, value);
+      config.set_value_in_file(pstr(config.config_path()).str(), key, value);
       break;
     }
 
index 11c2750d365b9d01ded71af6c7e1a9f0888eb8a6..198fbe5ebb6c7276bb3d7523048a06f531b1d0e2 100644 (file)
@@ -22,7 +22,6 @@
 #include <ccache/Config.hpp>
 #include <ccache/Context.hpp>
 #include <ccache/SignalHandler.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/ccache.hpp>
 #include <ccache/core/exceptions.hpp>
 #include <ccache/util/DirEntry.hpp>
index 3d6f82f5a84e83b7577a0b26624c4d9c6114a4b3..dc8cf00c7ae08dd1b80a7ed1d74ce93ac5fdddf3 100644 (file)
@@ -19,7 +19,6 @@
 #include "Storage.hpp"
 
 #include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/core/CacheEntry.hpp>
 #include <ccache/core/Statistic.hpp>
 #include <ccache/core/exceptions.hpp>
index 7e9b979c5dd290df43b4fa88fbfa86a6e5bee42f..00469e887a7c3604a055ae4f8ebec1c70ff8b271 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "util.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/core/exceptions.hpp>
 #include <ccache/util/PathString.hpp>
 #include <ccache/util/expected.hpp>
index 51d7f1d65cbe257ce4785351f82cfbba9a9fe564..b7a6375f5e0301ec365cc5a9523e76e4390868b0 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "FileStorage.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/core/AtomicFile.hpp>
 #include <ccache/core/exceptions.hpp>
 #include <ccache/util/Bytes.hpp>
index b92a2354c4579c98f7edfd54226666afd536480e..2767dd0b5e7a4582ee8166a7e4bd1aa2ead60295 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "LockFile.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/util/DirEntry.hpp>
 #include <ccache/util/PathString.hpp>
 #include <ccache/util/assertions.hpp>
index bf6e22791b7d2fafbf9ca397eed7d8b52f477a4a..e0122eb60ff4ed4feed111e0275033e59fee2f82 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "LongLivedLockFileManager.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/util/file.hpp>
 #include <ccache/util/filesystem.hpp>
 #include <ccache/util/logging.hpp>
index c0609eaecee7c563ec598e5f9f9dcb74ac830aa6..f430fb67259d95eed6a6e4cba14aa50e52bc145f 100644 (file)
@@ -16,7 +16,6 @@
 // this program; if not, write to the Free Software Foundation, Inc., 51
 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-#include <ccache/Util.hpp>
 #include <ccache/util/assertions.hpp>
 #include <ccache/util/filesystem.hpp>
 #include <ccache/util/format.hpp>
index b32056f5a5530da6f67363cc2ed274f2a75e2ba4..824fa17dbb22b89bfbe3cc9dd5783868c00b8a35 100644 (file)
@@ -18,8 +18,8 @@
 
 #include "path.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/util/DirEntry.hpp>
+#include <ccache/util/PathString.hpp>
 #include <ccache/util/filesystem.hpp>
 #include <ccache/util/format.hpp>
 #include <ccache/util/string.hpp>
@@ -32,21 +32,9 @@ const char k_dev_null_path[] = "/dev/null";
 
 namespace fs = util::filesystem;
 
-namespace util {
+using pstr = util::PathString;
 
-std::string
-actual_cwd()
-{
-  auto cwd = fs::current_path();
-  if (!cwd) {
-    return {};
-  }
-  auto cwd_str = cwd->string();
-#ifdef _WIN32
-  std::replace(cwd_str.begin(), cwd_str.end(), '\\', '/');
-#endif
-  return cwd_str;
-}
+namespace util {
 
 std::string
 add_exe_suffix(const std::string& program)
@@ -59,8 +47,8 @@ add_exe_suffix(const std::string& program)
   }
 }
 
-std::string
-apparent_cwd(const std::string& actual_cwd)
+fs::path
+apparent_cwd(const fs::path& actual_cwd)
 {
 #ifdef _WIN32
   return actual_cwd;
@@ -72,9 +60,7 @@ apparent_cwd(const std::string& actual_cwd)
 
   DirEntry pwd_de(pwd);
   DirEntry cwd_de(actual_cwd);
-  return !pwd_de || !cwd_de || !pwd_de.same_inode_as(cwd_de)
-           ? actual_cwd
-           : Util::normalize_concrete_absolute_path(pwd);
+  return !pwd_de || !cwd_de || !pwd_de.same_inode_as(cwd_de) ? actual_cwd : pwd;
 #endif
 }
 
@@ -84,40 +70,74 @@ get_dev_null_path()
   return k_dev_null_path;
 }
 
-bool
-path_starts_with(std::string_view path, std::string_view prefix)
+std::filesystem::path
+make_relative_path(const fs::path& actual_cwd,
+                   const fs::path& apparent_cwd,
+                   const fs::path& path)
 {
-  if (path.empty()) {
-    return false;
-  }
-  for (size_t i = 0, j = 0; i < path.length() && j < prefix.length();
-       ++i, ++j) {
-#ifdef _WIN32
-    // Skip escaped backslashes \\\\ as seen by the preprocessor.
-    if (i > 0 && path[i] == '\\' && path[i - 1] == '\\') {
-      ++i;
-    }
-    if (j > 0 && prefix[j] == '\\' && prefix[j - 1] == '\\') {
-      ++j;
-    }
+  DEBUG_ASSERT(actual_cwd.is_absolute());
+  DEBUG_ASSERT(apparent_cwd.is_absolute());
+  DEBUG_ASSERT(path.is_absolute());
 
-    // Handle back and forward slashes as equal.
-    if (path[i] == '/' && prefix[j] == '\\') {
-      continue;
+  fs::path normalized_path = path.lexically_normal();
+  fs::path closest_existing_path = normalized_path;
+  std::vector<fs::path> relpath_candidates;
+  fs::path path_suffix;
+  while (!fs::exists(closest_existing_path)) {
+    if (path_suffix.empty()) {
+      path_suffix = closest_existing_path.filename();
+    } else {
+      path_suffix = closest_existing_path.filename() / path_suffix;
     }
-    if (path[i] == '\\' && prefix[j] == '/') {
-      continue;
+    closest_existing_path = closest_existing_path.parent_path();
+  }
+
+  const auto add_relpath_candidates = [&](auto p) {
+    relpath_candidates.push_back(p.lexically_relative(actual_cwd));
+    if (apparent_cwd != actual_cwd) {
+      relpath_candidates.emplace_back(p.lexically_relative(apparent_cwd));
     }
-    if (std::tolower(path[i]) != std::tolower(prefix[j])) {
-      return false;
+  };
+
+  add_relpath_candidates(closest_existing_path);
+  const fs::path real_closest_existing_path =
+    fs::canonical(closest_existing_path).value_or(closest_existing_path);
+  if (real_closest_existing_path != closest_existing_path) {
+    add_relpath_candidates(real_closest_existing_path);
+  }
+
+  // Find best (i.e. shortest existing) match:
+  std::sort(relpath_candidates.begin(),
+            relpath_candidates.end(),
+            [](const auto& path1, const auto& path2) {
+              return pstr(path1).str().length() < pstr(path2).str().length();
+            });
+  for (const auto& relpath : relpath_candidates) {
+    if (fs::equivalent(relpath, closest_existing_path)) {
+      return path_suffix.empty() ? relpath
+                                 : (relpath / path_suffix).lexically_normal();
     }
+  }
+
+  // No match so nothing else to do than to return the unmodified path.
+  return path;
+}
+
+bool
+path_starts_with(const fs::path& path, const fs::path& prefix)
+{
+#ifdef _WIN32
+  // Note: Not all paths on Windows are case insensitive, but for our purposes
+  // (checking whether a path is below the base directory) users will expect
+  // them to be.
+  fs::path p1 = util::to_lowercase(path.string());
+  fs::path p2 = util::to_lowercase(prefix.string());
 #else
-    if (path[i] != prefix[j]) {
-      return false;
-    }
+  const fs::path& p1 = path;
+  const fs::path& p2 = prefix;
 #endif
-  }
-  return true;
+  return std::mismatch(p1.begin(), p1.end(), p2.begin(), p2.end()).second
+         == p2.end();
 }
 
 } // namespace util
index c445d37d417e9277a352b4bad2c21339e0631102..039c58a21e47939d4998fcc3b5ac04d9ba2c71d4 100644 (file)
@@ -29,10 +29,6 @@ namespace util {
 
 // --- Interface ---
 
-// Return current working directory (CWD) as returned from getcwd(3) (i.e.,
-// normalized path without symlink parts). Returns the empty string on error.
-std::string actual_cwd();
-
 // Add ".exe" suffix to `program` if it doesn't already end with ".exe", ".bat"
 // or ".sh".
 std::string add_exe_suffix(const std::string& program);
@@ -41,7 +37,7 @@ std::string add_exe_suffix(const std::string& program);
 // PWD (thus keeping any symlink parts in the path and potentially ".." or "//"
 // parts). If PWD does not resolve to the same inode as `actual_cwd` then
 // `actual_cwd` is returned instead.
-std::string apparent_cwd(const std::string& actual_cwd);
+std::filesystem::path apparent_cwd(const std::filesystem::path& actual_cwd);
 
 const char* get_dev_null_path();
 
@@ -51,9 +47,29 @@ bool is_dev_null_path(const std::filesystem::path& path);
 // Return whether `path` includes at least one directory separator.
 bool is_full_path(std::string_view path);
 
+// Make a relative path from current working directory (either `actual_cwd` or
+// `apparent_cwd`) to `path` if `path` is under `base_dir`.
+std::filesystem::path
+make_relative_path(const std::filesystem::path& actual_cwd,
+                   const std::filesystem::path& apparent_cwd,
+                   const std::filesystem::path& path);
+
+// Construct a normalized native path.
+//
+// Example:
+//
+//   std::string path = make_path("usr", "local", "bin");
+template<typename... T>
+std::filesystem::path
+make_path(const T&... args)
+{
+  return (std::filesystem::path{} / ... / args).lexically_normal();
+}
+
 // Return whether `path` starts with `prefix` considering path specifics on
-// Windows
-bool path_starts_with(std::string_view path, std::string_view prefix);
+// Windows.
+bool path_starts_with(const std::filesystem::path& path,
+                      const std::filesystem::path& prefix);
 
 // --- Inline implementations ---
 
index 7a27d38a745535162c2b7a86895b294b6dd761e5..f62521f532144d6a4f071d5491ca1bdf4df32503 100755 (executable)
--- a/test/run
+++ b/test/run
@@ -3,7 +3,7 @@
 # A simple test suite for ccache.
 #
 # Copyright (C) 2002-2007 Andrew Tridgell
-# Copyright (C) 2009-2023 Joel Rosdahl and other contributors
+# Copyright (C) 2009-2024 Joel Rosdahl and other contributors
 #
 # See doc/AUTHORS.adoc for a complete list of contributors.
 #
index 37cff1309cf119b05183e1a41927326ea4206dd5..0ff0d3d37a098e5b447da4ac403fd3a8c1f7652e 100644 (file)
@@ -162,8 +162,8 @@ SUITE_depend() {
     # -------------------------------------------------------------------------
     TEST "Dependency file paths converted to relative if CCACHE_BASEDIR specified"
 
-    CCACHE_DEPEND=1 CCACHE_BASEDIR="`pwd`" $CCACHE_COMPILE $DEPSFLAGS_CCACHE -c "`pwd`/test.c"
-    if grep -q "[^.]/test.c" "test.d"; then
+    CCACHE_DEPEND=1 CCACHE_BASEDIR="$(pwd)" $CCACHE_COMPILE $DEPSFLAGS_CCACHE -c "$(pwd)/test.c"
+    if ! grep -q " test.c" test.d; then
         test_failed "Dependency file does not contain relative path to test.c"
     fi
 
index 5414267f447827490dbf54a3139b030fe109dd7f..842f3d4d8341dadcb4a65ff6b68ce372b6324377 100644 (file)
@@ -6,7 +6,6 @@ set(
   test_Config.cpp
   test_Depfile.cpp
   test_Hash.cpp
-  test_Util.cpp
   test_argprocessing.cpp
   test_ccache.cpp
   test_compopt.cpp
index 6314de885cd86eb25cf38d152742855a0023cd4e..0baf62aebd8a3f60dba4ae46be09e5af4145039f 100644 (file)
@@ -39,7 +39,7 @@ main(int argc, char** argv)
 #endif
   util::unsetenv("GCC_COLORS"); // Don't confuse argument processing tests.
 
-  std::string dir_before = util::actual_cwd();
+  auto dir_before = *fs::current_path();
   std::string testdir = FMT("testdir/{}", getpid());
   fs::remove_all(testdir);
   fs::create_directories(testdir);
index 1fac22c6d0950f9961f3cd4229e3ed449cc0d618..a6cfddcd1c34c6eafc3bd8a34fd8026866a23cfb 100644 (file)
@@ -19,7 +19,6 @@
 #include "TestUtil.hpp"
 
 #include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/core/exceptions.hpp>
 #include <ccache/util/environment.hpp>
 #include <ccache/util/file.hpp>
@@ -393,7 +392,7 @@ TEST_CASE("Config::visit_items")
 #ifndef _WIN32
     "base_dir = /bd\n"
 #else
-    "base_dir = C:/bd\n"
+    "base_dir = C:\\bd\n"
 #endif
     "cache_dir = cd\n"
     "compiler = c\n"
@@ -455,7 +454,7 @@ TEST_CASE("Config::visit_items")
 #ifndef _WIN32
     "(test.conf) base_dir = /bd",
 #else
-    "(test.conf) base_dir = C:/bd",
+    "(test.conf) base_dir = C:\\bd",
 #endif
     "(test.conf) cache_dir = cd",
     "(test.conf) compiler = c",
index 6e5d91a98942a59f77890ca459f58f5275836b55..2576b082462b500877412cb5a5aa706dddec3ba2 100644 (file)
@@ -84,12 +84,14 @@ TEST_CASE("Depfile::rewrite_source_paths")
     const auto expected = FMT(
       "{0}/foo.o: \\\n"
       " bar.c \\\n"
-      " ./bar/bar.h \\\n"
+      " {2} \\\n"
       " {1}/fie.h\n"
-      "./bar/bar.h:\n"
+      "{2}:\n"
       "{1}/fie.h:\n",
       Depfile::escape_filename(pstr(cwd).str()),
-      Depfile::escape_filename(pstr(cwd.parent_path()).str()));
+      Depfile::escape_filename(pstr(cwd.parent_path()).str()),
+      Depfile::escape_filename(
+        pstr(fs::path("bar/bar.h").lexically_normal()).str()));
     REQUIRE(actual);
     CHECK(*actual == expected);
   }
index a045fdab6608756c2fabd371b0ae2a6071a8700f..6607343779813c2ea796ea9231e0b7e5c49ffe2f 100644 (file)
@@ -23,6 +23,7 @@
 #include <ccache/Hash.hpp>
 #include <ccache/InodeCache.hpp>
 #include <ccache/util/Fd.hpp>
+#include <ccache/util/PathString.hpp>
 #include <ccache/util/TemporaryFile.hpp>
 #include <ccache/util/file.hpp>
 #include <ccache/util/filesystem.hpp>
 namespace fs = util::filesystem;
 
 using TestUtil::TestContext;
+using pstr = util::PathString;
 
 namespace {
 
 bool
 inode_cache_available()
 {
-  auto tmp_file = util::TemporaryFile::create(util::actual_cwd() + "/fs_test");
+  auto tmp_file =
+    util::TemporaryFile::create((*fs::current_path()) / "fs_test");
   if (!tmp_file) {
     return false;
   }
@@ -57,7 +60,7 @@ init(Config& config)
 {
   config.set_debug(true);
   config.set_inode_cache(true);
-  config.set_temporary_dir(util::actual_cwd());
+  config.set_temporary_dir(pstr(*fs::current_path()).str());
 }
 
 bool
diff --git a/unittest/test_Util.cpp b/unittest/test_Util.cpp
deleted file mode 100644 (file)
index a669c1d..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (C) 2019-2024 Joel Rosdahl and other contributors
-//
-// See doc/AUTHORS.adoc for a complete list of contributors.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-// more details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program; if not, write to the Free Software Foundation, Inc., 51
-// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-#include "TestUtil.hpp"
-
-#include <ccache/Config.hpp>
-#include <ccache/Util.hpp>
-#include <ccache/core/exceptions.hpp>
-#include <ccache/util/environment.hpp>
-#include <ccache/util/file.hpp>
-#include <ccache/util/filesystem.hpp>
-#include <ccache/util/format.hpp>
-#include <ccache/util/path.hpp>
-#include <ccache/util/wincompat.hpp>
-
-#include <doctest/doctest.h>
-
-#include <fcntl.h>
-
-#include <optional>
-#include <string>
-#include <vector>
-
-#ifdef HAVE_UNISTD_H
-#  include <unistd.h>
-#endif
-
-#include <algorithm>
-
-using TestUtil::TestContext;
-
-namespace fs = util::filesystem;
-
-TEST_SUITE_BEGIN("Util");
-
-TEST_CASE("Util::common_dir_prefix_length")
-{
-  CHECK(Util::common_dir_prefix_length("", "") == 0);
-  CHECK(Util::common_dir_prefix_length("/", "") == 0);
-  CHECK(Util::common_dir_prefix_length("", "/") == 0);
-  CHECK(Util::common_dir_prefix_length("/", "/") == 0);
-  CHECK(Util::common_dir_prefix_length("/", "/b") == 0);
-  CHECK(Util::common_dir_prefix_length("/a", "/") == 0);
-  CHECK(Util::common_dir_prefix_length("/a", "/b") == 0);
-  CHECK(Util::common_dir_prefix_length("/a", "/a") == 2);
-  CHECK(Util::common_dir_prefix_length("/a", "/a/b") == 2);
-  CHECK(Util::common_dir_prefix_length("/a/b", "/a") == 2);
-  CHECK(Util::common_dir_prefix_length("/a/b", "/a/c") == 2);
-  CHECK(Util::common_dir_prefix_length("/a/b", "/a/b") == 4);
-  CHECK(Util::common_dir_prefix_length("/a/bc", "/a/b") == 2);
-  CHECK(Util::common_dir_prefix_length("/a/b", "/a/bc") == 2);
-}
-
-TEST_CASE("Util::get_relative_path")
-{
-#ifdef _WIN32
-  CHECK(Util::get_relative_path("C:/a", "C:/a") == ".");
-  CHECK(Util::get_relative_path("C:/a", "Z:/a") == "Z:/a");
-  CHECK(Util::get_relative_path("C:/a/b", "C:/a") == "..");
-  CHECK(Util::get_relative_path("C:/a", "C:/a/b") == "b");
-  CHECK(Util::get_relative_path("C:/a", "C:/a/b/c") == "b/c");
-  CHECK(Util::get_relative_path("C:/a/b", "C:/a/c") == "../c");
-  CHECK(Util::get_relative_path("C:/a/b", "C:/a/c/d") == "../c/d");
-  CHECK(Util::get_relative_path("C:/a/b/c", "C:/a/c/d") == "../../c/d");
-  CHECK(Util::get_relative_path("C:/a/b", "C:/") == "../..");
-  CHECK(Util::get_relative_path("C:/a/b", "C:/c") == "../../c");
-  CHECK(Util::get_relative_path("C:/", "C:/a/b") == "a/b");
-  CHECK(Util::get_relative_path("C:/a", "D:/a/b") == "D:/a/b");
-#else
-  CHECK(Util::get_relative_path("/a", "/a") == ".");
-  CHECK(Util::get_relative_path("/a/b", "/a") == "..");
-  CHECK(Util::get_relative_path("/a", "/a/b") == "b");
-  CHECK(Util::get_relative_path("/a", "/a/b/c") == "b/c");
-  CHECK(Util::get_relative_path("/a/b", "/a/c") == "../c");
-  CHECK(Util::get_relative_path("/a/b", "/a/c/d") == "../c/d");
-  CHECK(Util::get_relative_path("/a/b/c", "/a/c/d") == "../../c/d");
-  CHECK(Util::get_relative_path("/a/b", "/") == "../..");
-  CHECK(Util::get_relative_path("/a/b", "/c") == "../../c");
-  CHECK(Util::get_relative_path("/", "/a/b") == "a/b");
-#endif
-}
-
-TEST_CASE("Util::make_relative_path")
-{
-  using Util::make_relative_path;
-
-  const TestContext test_context;
-
-  const std::string cwd = util::actual_cwd();
-  const std::string actual_cwd = FMT("{}/d", cwd);
-#if defined(_WIN32) || defined(__CYGWIN__)
-  const std::string apparent_cwd = actual_cwd;
-#else
-  const std::string apparent_cwd = FMT("{}/s", cwd);
-#endif
-
-  REQUIRE(fs::create_directory("d"));
-#ifndef _WIN32
-  REQUIRE(symlink("d", "s") == 0);
-#endif
-  REQUIRE(fs::current_path("d"));
-  util::setenv("PWD", apparent_cwd);
-  SUBCASE("No base directory")
-  {
-    CHECK(make_relative_path("", "/a", "/a", "/a/x") == "/a/x");
-  }
-
-  SUBCASE("Path matches neither actual nor apparent CWD")
-  {
-#ifdef _WIN32
-    CHECK(make_relative_path("C:/", "C:/a", "C:/b", "C:/x") == "C:/x");
-#else
-    CHECK(make_relative_path("/", "/a", "/b", "/x") == "/x");
-#endif
-  }
-
-  SUBCASE("Match of actual CWD")
-  {
-#ifdef _WIN32
-    CHECK(
-      make_relative_path(
-        actual_cwd.substr(0, 3), actual_cwd, apparent_cwd, actual_cwd + "/x")
-      == "./x");
-    CHECK(
-      make_relative_path(
-        actual_cwd.substr(0, 3), actual_cwd, apparent_cwd, actual_cwd + "\\x")
-      == ".\\x");
-    CHECK(
-      make_relative_path(
-        actual_cwd.substr(0, 3), actual_cwd, apparent_cwd, actual_cwd + "\\\\x")
-      == ".\\\\x");
-#else
-    CHECK(make_relative_path("/", actual_cwd, apparent_cwd, actual_cwd + "/x")
-          == "./x");
-#endif
-  }
-
-#ifndef _WIN32
-  SUBCASE("Match of apparent CWD")
-  {
-    CHECK(make_relative_path("/", actual_cwd, apparent_cwd, apparent_cwd + "/x")
-          == "./x");
-  }
-
-  SUBCASE("Match if using resolved (using realpath(3)) path")
-  {
-    CHECK(make_relative_path("/", actual_cwd, actual_cwd, apparent_cwd + "/x")
-          == "./x");
-  }
-#endif
-}
-
-TEST_CASE("Util::normalize_abstract_absolute_path")
-{
-  CHECK(Util::normalize_abstract_absolute_path("") == "");
-  CHECK(Util::normalize_abstract_absolute_path(".") == ".");
-  CHECK(Util::normalize_abstract_absolute_path("..") == "..");
-  CHECK(Util::normalize_abstract_absolute_path("...") == "...");
-  CHECK(Util::normalize_abstract_absolute_path("x/./") == "x/./");
-
-#ifdef _WIN32
-  CHECK(Util::normalize_abstract_absolute_path("c:/") == "c:/");
-  CHECK(Util::normalize_abstract_absolute_path("c:\\") == "c:/");
-  CHECK(Util::normalize_abstract_absolute_path("c:/.") == "c:/");
-  CHECK(Util::normalize_abstract_absolute_path("c:\\..") == "c:/");
-  CHECK(Util::normalize_abstract_absolute_path("c:\\x/..") == "c:/");
-  CHECK(Util::normalize_abstract_absolute_path("c:\\x/./y\\..\\\\z")
-        == "c:/x/z");
-#else
-  CHECK(Util::normalize_abstract_absolute_path("/") == "/");
-  CHECK(Util::normalize_abstract_absolute_path("/.") == "/");
-  CHECK(Util::normalize_abstract_absolute_path("/..") == "/");
-  CHECK(Util::normalize_abstract_absolute_path("/./") == "/");
-  CHECK(Util::normalize_abstract_absolute_path("//") == "/");
-  CHECK(Util::normalize_abstract_absolute_path("/../x") == "/x");
-  CHECK(Util::normalize_abstract_absolute_path("/x/./y/z") == "/x/y/z");
-  CHECK(Util::normalize_abstract_absolute_path("/x/../y/z/") == "/y/z");
-  CHECK(Util::normalize_abstract_absolute_path("/x/.../y/z") == "/x/.../y/z");
-  CHECK(Util::normalize_abstract_absolute_path("/x/yyy/../zz") == "/x/zz");
-  CHECK(Util::normalize_abstract_absolute_path("//x/yyy///.././zz") == "/x/zz");
-#endif
-}
-
-TEST_CASE("Util::normalize_concrete_absolute_path")
-{
-#if !defined(_WIN32) && !defined(__CYGWIN__)
-  TestContext test_context;
-
-  util::write_file("file", "");
-  REQUIRE(fs::create_directories("dir1/dir2"));
-  REQUIRE(symlink("dir1/dir2", "symlink") == 0);
-  const auto cwd = util::actual_cwd();
-
-  CHECK(Util::normalize_concrete_absolute_path(FMT("{}/file", cwd))
-        == FMT("{}/file", cwd));
-  CHECK(Util::normalize_concrete_absolute_path(FMT("{}/dir1/../file", cwd))
-        == FMT("{}/file", cwd));
-  CHECK(Util::normalize_concrete_absolute_path(FMT("{}/symlink/../file", cwd))
-        == FMT("{}/symlink/../file", cwd));
-#endif
-}
-
-TEST_SUITE_END();
index 19a6b91ef098a79f274da3b0a7770c07e3e78d49..3de398dd630c6ec4e28a48109cb0e30b6fce1215 100644 (file)
 #include <ccache/Args.hpp>
 #include <ccache/Config.hpp>
 #include <ccache/Context.hpp>
-#include <ccache/Util.hpp>
 #include <ccache/argprocessing.hpp>
 #include <ccache/core/Statistic.hpp>
+#include <ccache/util/PathString.hpp>
 #include <ccache/util/file.hpp>
+#include <ccache/util/filesystem.hpp>
 #include <ccache/util/format.hpp>
 #include <ccache/util/path.hpp>
 #include <ccache/util/string.hpp>
 
 #include <algorithm>
 
+namespace fs = util::filesystem;
+
 using core::Statistic;
 using TestUtil::TestContext;
+using pstr = util::PathString;
 
 namespace {
 
@@ -46,34 +50,11 @@ get_root()
   return "/";
 #else
   char volume[4]; // "C:\"
-  GetVolumePathName(util::actual_cwd().c_str(), volume, sizeof(volume));
-  volume[2] = '/'; // Since base directory is normalized to forward slashes
+  GetVolumePathName(pstr(*fs::current_path()).c_str(), volume, sizeof(volume));
   return volume;
 #endif
 }
 
-std::string
-get_posix_path(const std::string& path)
-{
-#ifndef _WIN32
-  return path;
-#else
-  std::string posix;
-
-  // /-escape volume.
-  if (path[1] == ':'
-      && ((path[0] >= 'A' && path[0] <= 'Z')
-          || (path[0] >= 'a' && path[0] <= 'z'))) {
-    posix = "/" + path;
-  } else {
-    posix = path;
-  }
-  // Convert slashes.
-  std::replace(posix.begin(), posix.end(), '\\', '/');
-  return posix;
-#endif
-}
-
 } // namespace
 
 TEST_SUITE_BEGIN("argprocessing");
@@ -312,7 +293,11 @@ TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used")
 
   const ProcessArgsResult result = process_args(ctx);
   CHECK(!result.error);
-  CHECK(result.preprocessor_args[1] == "--sysroot=./foo/bar");
+#ifdef _WIN32
+  CHECK(result.preprocessor_args[1] == "--sysroot=foo\\bar");
+#else
+  CHECK(result.preprocessor_args[1] == "--sysroot=foo/bar");
+#endif
 }
 
 TEST_CASE(
@@ -330,7 +315,7 @@ TEST_CASE(
   const ProcessArgsResult result = process_args(ctx);
   CHECK(!result.error);
   CHECK(result.preprocessor_args[1] == "--sysroot");
-  CHECK(result.preprocessor_args[2] == "./foo");
+  CHECK(result.preprocessor_args[2] == "foo");
 }
 
 TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
@@ -464,9 +449,10 @@ TEST_CASE(
 
   const ProcessArgsResult result = process_args(ctx);
   CHECK(!result.error);
-  CHECK(result.preprocessor_args[2] == "./foo");
+  CHECK(result.preprocessor_args[2] == "foo");
 }
 
+#ifndef _WIN32
 TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
 {
   TestContext test_context;
@@ -474,15 +460,14 @@ TEST_CASE("isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
   Context ctx;
 
   util::write_file("foo.c", "");
-  ctx.config.set_base_dir("/"); // posix
-  // Windows path doesn't work concatenated.
-  std::string cwd = get_posix_path(ctx.actual_cwd);
+  ctx.config.set_base_dir("/");
+  std::string cwd = ctx.actual_cwd;
   std::string arg_string = FMT("cc -isystem{}/foo -c foo.c", cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
   const ProcessArgsResult result = process_args(ctx);
   CHECK(!result.error);
-  CHECK(result.preprocessor_args[1] == "-isystem./foo");
+  CHECK(result.preprocessor_args[1] == "-isystemfoo");
 }
 
 TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
@@ -492,16 +477,16 @@ TEST_CASE("I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used")
   Context ctx;
 
   util::write_file("foo.c", "");
-  ctx.config.set_base_dir("/"); // posix
-  // Windows path doesn't work concatenated.
-  std::string cwd = get_posix_path(ctx.actual_cwd);
+  ctx.config.set_base_dir("/");
+  std::string cwd = *fs::current_path();
   std::string arg_string = FMT("cc -I{}/foo -c foo.c", cwd);
   ctx.orig_args = Args::from_string(arg_string);
 
   const ProcessArgsResult result = process_args(ctx);
   CHECK(!result.error);
-  CHECK(result.preprocessor_args[1] == "-I./foo");
+  CHECK(result.preprocessor_args[1] == "-Ifoo");
 }
+#endif // _WIN32
 
 TEST_CASE("debug_flag_order_with_known_option_first")
 {
@@ -719,7 +704,8 @@ TEST_CASE("-x")
 // MSVC's /U option, so disable the test case there. This will be possible to
 // improve when/if a compiler abstraction is introduced (issue #956).
 TEST_CASE("MSVC options"
-          * doctest::skip(util::starts_with(util::actual_cwd(), "/U")))
+          * doctest::skip(util::starts_with(fs::current_path()->string(),
+                                            "/U")))
 {
   TestContext test_context;
   Context ctx;
index 9a33346d0279763c2b3a6380ed152980bbccd62b..ecf21635a2abbb03180f359b56b778c7790480fc 100644 (file)
@@ -195,7 +195,7 @@ TEST_CASE("guess_compiler")
 #ifndef _WIN32
   SUBCASE("Follow symlink to actual compiler")
   {
-    const auto cwd = fs::path(util::actual_cwd());
+    const auto cwd = *fs::current_path();
     util::write_file(cwd / "gcc", "");
     CHECK(fs::create_symlink("gcc", cwd / "intermediate"));
     const auto cc = cwd / "cc";
@@ -206,7 +206,7 @@ TEST_CASE("guess_compiler")
 
   SUBCASE("Classify clang-cl symlink to clang")
   {
-    const auto cwd = fs::path(util::actual_cwd());
+    const auto cwd = *fs::current_path();
     util::write_file(cwd / "clang", "");
     const auto clang_cl = cwd / "clang-cl";
     CHECK(fs::create_symlink("clang", clang_cl));
index 34aaac1d4aaa59ee9f360c0948111a1ee214e5d3..db262d0fdf1ed827a0d81ea31ca8f2839e11918e 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "TestUtil.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/core/StatsLog.hpp>
 #include <ccache/util/file.hpp>
 
index 89f049953042cebfbdbef69e7e37d7af60c96e8a..ae0721b167ad33278a0871988e20f9eef8a1400b 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "TestUtil.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/core/Statistic.hpp>
 #include <ccache/storage/local/StatsFile.hpp>
 #include <ccache/util/file.hpp>
index 0953a4d80b6a28e5eeb703042c9fd326be896fb1..efdec653d145687faa84bd9a0b219e0344f7030e 100644 (file)
@@ -18,7 +18,6 @@
 
 #include "TestUtil.hpp"
 
-#include <ccache/Util.hpp>
 #include <ccache/util/DirEntry.hpp>
 #include <ccache/util/LockFile.hpp>
 #include <ccache/util/file.hpp>
index 3daedc4869315ba9d21e3ea47dee832493d9562d..d8f34a16b360679a9a340a260558cfcf4fa3b181 100644 (file)
@@ -16,7 +16,6 @@
 // this program; if not, write to the Free Software Foundation, Inc., 51
 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-#include <ccache/Util.hpp>
 #include <ccache/util/string.hpp>
 
 #include <doctest/doctest.h>
index e839c8aee5b94a5c691d4a132fa50ffa38f2b6e7..76738ad82de6508bd0c4842a25a469e53d8c6ecd 100644 (file)
 // this program; if not, write to the Free Software Foundation, Inc., 51
 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
-#include <ccache/Util.hpp>
+#include "TestUtil.hpp"
+
+#include <ccache/util/PathString.hpp>
+#include <ccache/util/environment.hpp>
+#include <ccache/util/filesystem.hpp>
 #include <ccache/util/format.hpp>
 #include <ccache/util/path.hpp>
 
 
 #include <ostream> // https://github.com/doctest/doctest/issues/618
 
+namespace fs = util::filesystem;
+
+using pstr = util::PathString;
+using TestUtil::TestContext;
+
 TEST_CASE("util::add_exe_suffix")
 {
   CHECK(util::add_exe_suffix("foo") == "foo.exe");
@@ -56,9 +65,66 @@ TEST_CASE("util::is_dev_null_path")
 #endif
 }
 
+TEST_CASE("util::make_relative_path")
+{
+  using util::make_relative_path;
+
+  const TestContext test_context;
+
+  const std::string cwd = pstr(*fs::current_path()).str();
+  const std::string actual_cwd = FMT("{}/d", cwd);
+#if defined(_WIN32) || defined(__CYGWIN__)
+  const std::string apparent_cwd = actual_cwd;
+#else
+  const std::string apparent_cwd = FMT("{}/s", cwd);
+#endif
+
+  REQUIRE(fs::create_directory("d"));
+#ifndef _WIN32
+  REQUIRE(fs::create_symlink("d", "s"));
+#endif
+  REQUIRE(fs::current_path("d"));
+  util::setenv("PWD", apparent_cwd);
+
+  SUBCASE("Path matches neither actual nor apparent CWD")
+  {
+#ifdef _WIN32
+    CHECK(make_relative_path("C:/a", "C:/b", "C:/x") == "C:/x");
+#else
+    CHECK(make_relative_path("/a", "/b", "/x") == "/x");
+#endif
+  }
+
+  SUBCASE("Match of actual CWD")
+  {
+    CHECK(make_relative_path(actual_cwd, apparent_cwd, actual_cwd + "/x")
+          == "x");
+#ifdef _WIN32
+    CHECK(make_relative_path(actual_cwd, apparent_cwd, actual_cwd + "\\x")
+          == "x");
+    CHECK(make_relative_path(actual_cwd, apparent_cwd, actual_cwd + "\\\\x")
+          == "x");
+#endif
+  }
+
+#ifndef _WIN32
+  SUBCASE("Match of apparent CWD")
+  {
+    CHECK(make_relative_path(actual_cwd, apparent_cwd, apparent_cwd + "/x")
+          == "x");
+  }
+
+  SUBCASE("Match if using resolved (using realpath(3)) path")
+  {
+    CHECK(make_relative_path(actual_cwd, actual_cwd, apparent_cwd + "/x")
+          == "x");
+  }
+#endif
+}
+
 TEST_CASE("util::path_starts_with")
 {
-  CHECK(!util::path_starts_with("", ""));
+  CHECK(util::path_starts_with("", ""));
   CHECK(!util::path_starts_with("", "/"));
   CHECK(util::path_starts_with("/foo/bar", "/foo"));
   CHECK(!util::path_starts_with("/batz/bar", "/foo"));