Skip to content

Commit

Permalink
fix: Move Windows config and cache to LOCALAPPDATA
Browse files Browse the repository at this point in the history
Add a variadic template helper function PATH(...) to construct native
paths using std::filesystem.

Fix-up default cmake install directories and set system-wide config to
C:\ProgramData\ccache\ccache.conf on Windows.

Update manual to describe behavior specific to Windows and cache
directory finding heuristics on all systems.

Fix ccache#1023
Fix ccache#946

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
  • Loading branch information
rkitover committed Jul 25, 2022
1 parent df9d855 commit 1485bda
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 33 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Expand Up @@ -75,7 +75,8 @@ include(DefaultBuildType)
#
# Configuration
#
include(GNUInstallDirs)

include(InstallDirs)
include(GenerateConfigurationFile)
include(GenerateVersionFile)

Expand Down
52 changes: 52 additions & 0 deletions cmake/InstallDirs.cmake
@@ -0,0 +1,52 @@
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)

# Escape SYSCONFDIR for a C macro in config.h .
file(TO_NATIVE_PATH "${CMAKE_INSTALL_FULL_SYSCONFDIR}" CONFIG_SYSCONFDIR_C_ESCAPED)

string(REPLACE "\\" "\\\\" CONFIG_SYSCONFDIR_C_ESCAPED "${CONFIG_SYSCONFDIR_C_ESCAPED}")

set(
CONFIG_SYSCONFDIR_C_ESCAPED "${CONFIG_SYSCONFDIR_C_ESCAPED}" CACHE STRING
"System-wide config file location, CMAKE_INSTALL_FULL_SYSCONFDIR, escaped for a C macro in config.h"
FORCE
)
2 changes: 1 addition & 1 deletion cmake/config.h.in
Expand Up @@ -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

Expand Down
45 changes: 41 additions & 4 deletions doc/MANUAL.adoc
Expand Up @@ -300,10 +300,10 @@ it specifies the primary configuration file and the secondary (system-wide)
configuration file won't be read.


=== Location of the primary configuration file
=== Location of the primary configuration file and cache

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 systems other than Windows:

1. If `CCACHE_CONFIGPATH` is set, use that path.
2. Otherwise, if the environment variable `CCACHE_DIR` is set then use
Expand All @@ -314,10 +314,47 @@ 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).

The location of the cache directory is determined as follows:

1. `cache_dir` in the above found configuration if specified.
2. `$CCACHE_DIR` if set.
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.

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`.
6. Otherwise, if `%LOCALAPPDATA%\ccache\ccache.conf` exists it is used.
7. Otherwise, if `%APPDATA%\ccache\ccache.conf` exists it is used.

The cache directory on Windows is determined as follows:

1. `cache_dir` in the above found configuration if specified.
2. `%CCACHE_DIR%` if set.
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. Having a roaming ccache
configuration in `%APPDATA%\ccache\ccache.conf` is perfectly fine, the new
default cache directory will still be `%LOCALAPPDATA%\ccache`.

=== Configuration file syntax

Expand Down
67 changes: 52 additions & 15 deletions src/Config.cpp
Expand Up @@ -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)
Expand Down Expand Up @@ -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 = 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) {
Expand All @@ -490,9 +493,15 @@ Config::read()
// Only used for ccache tests:
const char* const env_ccache_configpath2 = getenv("CCACHE_CONFIGPATH2");

std::string sysconfdir = k_sysconfdir;
#ifdef _WIN32
if (const char* program_data = getenv("ALLUSERSPROFILE"))
sysconfdir = PATH(program_data, "ccache");
#endif

set_secondary_config_path(env_ccache_configpath2
? env_ccache_configpath2
: FMT("{}/ccache.conf", k_sysconfdir));
: 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());
Expand All @@ -506,12 +515,29 @@ 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(
PATH(env_local_appdata, "ccache", "ccache.conf"))) {
primary_config_dir = PATH(env_local_appdata, "ccache");
} else if (env_appdata
&& Stat::stat(PATH(env_appdata, "ccache", "ccache.conf"))) {
primary_config_dir = PATH(env_appdata, "ccache");
} else if (env_local_appdata) {
primary_config_dir = 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 = 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(PATH(primary_config_dir, "ccache.conf"));
}

const std::string& cache_dir_before_primary_config = cache_dir();
Expand All @@ -531,12 +557,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(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(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.

Expand Down
22 changes: 10 additions & 12 deletions src/Util.cpp
Expand Up @@ -54,10 +54,6 @@ extern "C" {
#include <fstream>
#include <locale>

#ifndef HAVE_DIRENT_H
# include <filesystem>
#endif

#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
Expand Down Expand Up @@ -665,26 +661,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*
Expand Down
10 changes: 10 additions & 0 deletions src/Util.hpp
Expand Up @@ -22,6 +22,7 @@
#include <util/Tokenizer.hpp>

#include <cstdint>
#include <filesystem>
#include <functional>
#include <ios>
#include <memory>
Expand All @@ -33,6 +34,15 @@

class Context;

// Helper variadic template function to construct native paths.
// Used like: PATH("usr", "local", "bin");
template<typename... T>
std::string
PATH(const T&... args)
{
return (std::filesystem::path{} / ... / args).lexically_normal().string();
}

namespace Util {

using DataReceiver = std::function<void(const void* data, size_t size)>;
Expand Down

0 comments on commit 1485bda

Please sign in to comment.