]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
feat: Move Windows config and cache to LOCALAPPDATA (#1124)
authorRafael Kitover <rkitover@gmail.com>
Mon, 1 Aug 2022 12:34:16 +0000 (15:34 +0300)
committerGitHub <noreply@github.com>
Mon, 1 Aug 2022 12:34:16 +0000 (14:34 +0200)
CMakeLists.txt
cmake/GenerateConfigurationFile.cmake
cmake/InstallDirs.cmake [new file with mode: 0644]
cmake/config.h.in
doc/MANUAL.adoc
src/Config.cpp
src/Util.cpp
src/Util.hpp

index 064787198e1e2719d70ac23edb4062719b74e601..8c7b45839c28e7f9840d92bec6055bb620c4ebb1 100644 (file)
@@ -75,7 +75,8 @@ include(DefaultBuildType)
 #
 # Configuration
 #
-include(GNUInstallDirs)
+
+include(InstallDirs)
 include(GenerateConfigurationFile)
 include(GenerateVersionFile)
 
index bfa0dca285740e56b243485d3532bf33129b03ce..fe290b8f0aefbe0e3752949c493ad74e5b3d0ca4 100644 (file)
@@ -102,5 +102,9 @@ if(HAVE_SYS_MMAN_H AND HAVE_PTHREAD_MUTEXATTR_SETPSHARED)
   set(INODE_CACHE_SUPPORTED 1)
 endif()
 
+# Escape backslashes in SYSCONFDIR for C.
+file(TO_NATIVE_PATH "${CMAKE_INSTALL_FULL_SYSCONFDIR}" CONFIG_SYSCONFDIR_C_ESCAPED)
+string(REPLACE "\\" "\\\\" CONFIG_SYSCONFDIR_C_ESCAPED "${CONFIG_SYSCONFDIR_C_ESCAPED}")
+
 configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.in
                ${CMAKE_BINARY_DIR}/config.h @ONLY)
diff --git a/cmake/InstallDirs.cmake b/cmake/InstallDirs.cmake
new file mode 100644 (file)
index 0000000..eb719cc
--- /dev/null
@@ -0,0 +1,41 @@
+if(WIN32)
+  if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+    set(program_files "$ENV{ProgramFiles}")
+
+    # For 32 bit builds.
+    if(CMAKE_SIZEOF_VOID_P EQUAL 4 AND ENV{ProgramFiles\(x86\)})
+      set(program_files "$ENV{ProgramFiles\(x86\)}")
+    endif()
+
+    if(NOT program_files)
+      if(NOT CMAKE_SIZEOF_VOID_P EQUAL 4)
+        set(program_files "/Program Files")
+      else()
+        set(program_files "/Program Files (x86)")
+      endif()
+    endif()
+
+    file(TO_CMAKE_PATH "${program_files}/ccache" CMAKE_INSTALL_PREFIX)
+
+    set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE STRING "System-wide installation prefix" FORCE)
+  endif()
+
+  if(NOT CMAKE_INSTALL_SYSCONFDIR)
+    set(program_data "$ENV{ALLUSERSPROFILE}")
+
+    if(NOT program_data)
+      set(program_data "/ProgramData")
+    endif()
+
+    file(TO_CMAKE_PATH "${program_data}/ccache" CMAKE_INSTALL_SYSCONFDIR)
+
+    set(CMAKE_INSTALL_SYSCONFDIR "${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH "System-wide config file location" FORCE)
+  endif()
+
+  set(CMAKE_INSTALL_BINDIR     "" CACHE PATH "executables subdirectory" FORCE)
+  set(CMAKE_INSTALL_SBINDIR    "" CACHE PATH "system administration executables subdirectory" FORCE)
+  set(CMAKE_INSTALL_LIBEXECDIR "" CACHE PATH "dependent executables subdirectory" FORCE)
+  set(CMAKE_INSTALL_LIBDIR     "" CACHE PATH "object libraries subdirectory" FORCE)
+endif()
+
+include(GNUInstallDirs)
index 649a44e45245a5311463d153576ef01f65367fe5..ed572b671463725c0fb73319181994d8730f93c1 100644 (file)
@@ -206,7 +206,7 @@ typedef int pid_t;
 #  define ESTALE -1
 #endif
 
-#define SYSCONFDIR "@CMAKE_INSTALL_FULL_SYSCONFDIR@"
+#define SYSCONFDIR "@CONFIG_SYSCONFDIR_C_ESCAPED@"
 
 #cmakedefine INODE_CACHE_SUPPORTED
 
index 0688108b90fbefafec41ebc1cc947c30b6e892bb..31e4c148d7255d6a0cdbface42498a19f4be01c9 100644 (file)
@@ -302,8 +302,8 @@ configuration file won't be read.
 
 === Location of the primary configuration file
 
-The location of the primary (cache-specific) configuration is determined like
-this:
+The location of the primary (cache-specific) configuration is determined as
+follows on non-Windows systems:
 
 1. If `CCACHE_CONFIGPATH` is set, use that path.
 2. Otherwise, if the environment variable `CCACHE_DIR` is set then use
@@ -314,10 +314,27 @@ this:
    `$HOME/.ccache/ccache.conf`.
 5. Otherwise, if `XDG_CONFIG_HOME` is set then use
    `$XDG_CONFIG_HOME/ccache/ccache.conf`.
-6. Otherwise, use `%APPDATA%/ccache/ccache.conf` (Windows),
+6. Otherwise, use
    `$HOME/Library/Preferences/ccache/ccache.conf` (macOS) or
    `$HOME/.config/ccache/ccache.conf` (other systems).
 
+On Windows, this is the method used to find the configuration file:
+
+1. If `CCACHE_CONFIGPATH` is set, use that path.
+2. Otherwise, if the environment variable `CCACHE_DIR` is set then use
+   `%CCACHE_DIR%/ccache.conf`.
+3. Otherwise, if <<config_cache_dir,*cache_dir*>> is set in the secondary
+   (system-wide) configuration file then use `<cache_dir>\ccache.conf`. The
+   system-wide configuration on Windows is
+   `%ALLUSERSPROFILE%\ccache\ccache.conf` by default, the `ALLUSERSPROFILE`
+   environment variable is usually `C:\ProgramData`.
+4. Otherwise, if there is a legacy `%USERPROFILE%\.ccache` directory then use
+   `%USERPROFILE%\.ccache\ccache.conf`.
+5. Otherwise, if `%LOCALAPPDATA%\ccache\ccache.conf` exists it is used.
+6. Otherwise, if `%APPDATA%\ccache\ccache.conf` exists it is used.
+
+See also the <<config_cache_dir,*cache_dir*>> configuration option for how the
+cache directory location is determined.
 
 === Configuration file syntax
 
@@ -428,11 +445,35 @@ relative paths in the first place instead instead of using *base_dir*.
 [#config_cache_dir]
 *cache_dir* (*CCACHE_DIR*)::
 
-    This option specifies where ccache will keep its cached compiler outputs.
-    The default is `$XDG_CACHE_HOME/ccache` if `XDG_CACHE_HOME` is set,
-    otherwise `%APPDATA%/ccache` (Windows), `$HOME/Library/Caches/ccache`
-    (macOS) or `$HOME/.config/ccache` (other systems). Exception: If the legacy
-    directory `$HOME/.ccache` exists then that directory is the default.
+    This option specifies where ccache will keep its cached compiler outputs if
+    specified in the first found configuration files, with the `$CCACHE_DIR`
+    environment variable taking precedence over it.
++
+The location of the cache directory is determined as follows on non-Windows
+systems:
++
+--
+1. `$CCACHE_DIR` if set.
+2. `cache_dir` in the first found configuration if specified.
+3. `$HOME/.ccache` if it exists.
+4. `$XDG_CACHE_HOME/ccache` if `$XDG_CACHE_HOME` is set.
+5. Otherwise `~/.cache/ccache` or `~/Library/Caches/ccache` on Apple systems.
+--
++
+The cache directory on Windows is determined as follows:
++
+--
+1. `%CCACHE_DIR%` if set.
+2. `cache_dir` in the first found configuration if specified.
+3. `%USERPROFILE%\.ccache` if it exists.
+4. Otherwise `%LOCALAPPDATA%\ccache`.
+--
++
+WARNING: Previous builds of ccache for Windows defaulted to storing the cache in
+`%APPDATA%\ccache`. This can result in large network file transfers of the cache
+in domain environments and similar problems. Please check this directory for
+cache directories and either delete them or the whole directory, or move them to
+the `%LOCALAPPDATA%\ccache` directory.
 +
 See also _<<Location of the primary configuration file>>_.
 +
index 2bc55e3158ac6b6ee9c55735e37cc50a152a1c48..a6cefe14fb3503346b2e65b34fa8a0decbb62291 100644 (file)
@@ -424,29 +424,27 @@ parse_config_file(const std::string& path,
 
 } // namespace
 
+#ifndef _WIN32
 static std::string
 default_cache_dir(const std::string& home_dir)
 {
-#ifdef _WIN32
-  return home_dir + "/ccache";
-#elif defined(__APPLE__)
+#  ifdef __APPLE__
   return home_dir + "/Library/Caches/ccache";
-#else
+#  else
   return home_dir + "/.cache/ccache";
-#endif
+#  endif
 }
 
 static std::string
 default_config_dir(const std::string& home_dir)
 {
-#ifdef _WIN32
-  return home_dir + "/ccache";
-#elif defined(__APPLE__)
+#  ifdef __APPLE__
   return home_dir + "/Library/Preferences/ccache";
-#else
+#  else
   return home_dir + "/.config/ccache";
-#endif
+#  endif
 }
+#endif
 
 std::string
 compiler_type_to_string(CompilerType compiler_type)
@@ -477,11 +475,16 @@ void
 Config::read()
 {
   const std::string home_dir = Util::get_home_directory();
-  const std::string legacy_ccache_dir = home_dir + "/.ccache";
+  const std::string legacy_ccache_dir = Util::make_path(home_dir, ".ccache");
   const bool legacy_ccache_dir_exists =
     Stat::stat(legacy_ccache_dir).is_directory();
+#ifdef _WIN32
+  const char* const env_appdata = getenv("APPDATA");
+  const char* const env_local_appdata = getenv("LOCALAPPDATA");
+#else
   const char* const env_xdg_cache_home = getenv("XDG_CACHE_HOME");
   const char* const env_xdg_config_home = getenv("XDG_CONFIG_HOME");
+#endif
 
   const char* env_ccache_configpath = getenv("CCACHE_CONFIGPATH");
   if (env_ccache_configpath) {
@@ -490,9 +493,15 @@ Config::read()
     // Only used for ccache tests:
     const char* const env_ccache_configpath2 = getenv("CCACHE_CONFIGPATH2");
 
+    std::string sysconfdir = Util::make_path(k_sysconfdir);
+#ifdef _WIN32
+    if (const char* program_data = getenv("ALLUSERSPROFILE"))
+      sysconfdir = Util::make_path(program_data, "ccache");
+#endif
+
     set_secondary_config_path(env_ccache_configpath2
                                 ? env_ccache_configpath2
-                                : FMT("{}/ccache.conf", k_sysconfdir));
+                                : Util::make_path(sysconfdir, "ccache.conf"));
     MTR_BEGIN("config", "conf_read_secondary");
     // A missing config file in SYSCONFDIR is OK so don't check return value.
     update_from_file(secondary_config_path());
@@ -506,12 +515,30 @@ Config::read()
       primary_config_dir = cache_dir();
     } else if (legacy_ccache_dir_exists) {
       primary_config_dir = legacy_ccache_dir;
+#ifdef _WIN32
+    } else if (env_local_appdata
+               && Stat::stat(
+                 Util::make_path(env_local_appdata, "ccache", "ccache.conf"))) {
+      primary_config_dir = Util::make_path(env_local_appdata, "ccache");
+    } else if (env_appdata
+               && Stat::stat(
+                 Util::make_path(env_appdata, "ccache", "ccache.conf"))) {
+      primary_config_dir = Util::make_path(env_appdata, "ccache");
+    } else if (env_local_appdata) {
+      primary_config_dir = Util::make_path(env_local_appdata, "ccache");
+    } else {
+      throw core::Fatal(
+        "could not find config file and the LOCALAPPDATA "
+        "environment variable is not set");
+    }
+#else
     } else if (env_xdg_config_home) {
-      primary_config_dir = FMT("{}/ccache", env_xdg_config_home);
+      primary_config_dir = Util::make_path(env_xdg_config_home, "ccache");
     } else {
       primary_config_dir = default_config_dir(home_dir);
     }
-    set_primary_config_path(primary_config_dir + "/ccache.conf");
+#endif
+    set_primary_config_path(Util::make_path(primary_config_dir, "ccache.conf"));
   }
 
   const std::string& cache_dir_before_primary_config = cache_dir();
@@ -531,12 +558,23 @@ Config::read()
   if (cache_dir().empty()) {
     if (legacy_ccache_dir_exists) {
       set_cache_dir(legacy_ccache_dir);
+#ifdef _WIN32
+    } else if (env_local_appdata) {
+      set_cache_dir(Util::make_path(env_local_appdata, "ccache"));
+    } else {
+      throw core::Fatal(
+        "could not find cache dir and the LOCALAPPDATA "
+        "environment variable is not set");
+    }
+#else
     } else if (env_xdg_cache_home) {
-      set_cache_dir(FMT("{}/ccache", env_xdg_cache_home));
+      set_cache_dir(Util::make_path(env_xdg_cache_home, "ccache"));
     } else {
       set_cache_dir(default_cache_dir(home_dir));
     }
+#endif
   }
+
   // else: cache_dir was set explicitly via environment or via secondary
   // config.
 
index 45b3f28413fbf19b095fcbeed8b785371d5ad472..2464ae1a36285ffb2e07538630456514f7b3246a 100644 (file)
@@ -46,10 +46,6 @@ extern "C" {
 
 #include <fcntl.h>
 
-#ifndef HAVE_DIRENT_H
-#  include <filesystem>
-#endif
-
 #ifdef HAVE_PWD_H
 #  include <pwd.h>
 #endif
@@ -663,26 +659,28 @@ get_extension(std::string_view path)
 std::string
 get_home_directory()
 {
-  const char* p = getenv("HOME");
-  if (p) {
+#ifdef _WIN32
+  if (const char* p = getenv("USERPROFILE")) {
     return p;
   }
-#ifdef _WIN32
-  p = getenv("APPDATA");
-  if (p) {
+  throw core::Fatal(
+    "The USERPROFILE environment variable must be set to your user profile "
+    "folder");
+#else
+  if (const char* p = getenv("HOME")) {
     return p;
   }
-#endif
-#ifdef HAVE_GETPWUID
+#  ifdef HAVE_GETPWUID
   {
     struct passwd* pwd = getpwuid(getuid());
     if (pwd) {
       return pwd->pw_dir;
     }
   }
-#endif
+#  endif
   throw core::Fatal(
     "Could not determine home directory from $HOME or getpwuid(3)");
+#endif
 }
 
 const char*
index 8f06d8c8f2992b90b3ed535995da1128ecc8a0d9..b12c1405fabe2fa965d2acaff7fff6c84849f765 100644 (file)
@@ -22,6 +22,7 @@
 #include <util/Tokenizer.hpp>
 
 #include <cstdint>
+#include <filesystem>
 #include <functional>
 #include <ios>
 #include <memory>
@@ -245,6 +246,15 @@ bool is_precompiled_header(std::string_view path);
 // time of day is used.
 std::optional<tm> localtime(std::optional<time_t> time = {});
 
+// Construct a normalized native path, used like:
+// 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,