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(std::string...) to
construct native paths using std::filesystem.

Throw a fatal error if a cache is detected in APPDATA.

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

Update manual.

Fix ccache#1023
Fix ccache#946

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
  • Loading branch information
rkitover committed Jul 22, 2022
1 parent af59e2f commit 0dbf05c
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 29 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
if(WIN32)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(program_files "$ENV{ProgramFiles}")

# For 32 bit builds.
if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4 AND ENV{ProgramFiles\(x86\)})
set(program_files "$ENV{ProgramFiles\(x86\)}")
endif()

if(NOT program_files)
if(NOT CMAKE_C_SIZEOF_DATA_PTR 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
Original file line number Diff line number Diff line change
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
22 changes: 20 additions & 2 deletions doc/MANUAL.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ configuration file won't be read.
=== Location of the primary configuration file

The location of the primary (cache-specific) configuration is determined like
this:
this 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,28 @@ 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:

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 default cache dir on Windows is `<LOCALAPPDATA>\ccache` unless specified
otherwise by the config. If a cache is detected in `<APPDATA>\ccache`, which
was the default previously, a fatal error is thrown.

=== Configuration file syntax

Expand Down
80 changes: 65 additions & 15 deletions src/Config.cpp
Original file line number Diff line number Diff line change
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,36 @@ 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
}

// Check for legacy default cachedir in APPDATA and throw a fatal error if it
// exists.
#ifdef _WIN32
if (env_appdata) {
if (Stat::stat(PATH(env_appdata, "ccache", "tmp")).is_directory())
throw core::Fatal(
"legacy cache detected in APPDATA directory {}, "
"please move it to the LOCALAPPDATA directory {}",
PATH(env_appdata, "ccache"),
PATH(env_local_appdata, "ccache"));
}
#endif

// else: cache_dir was set explicitly via environment or via secondary
// config.

Expand Down
18 changes: 8 additions & 10 deletions src/Util.cpp
Original file line number Diff line number Diff line change
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,16 +661,17 @@ 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
{
struct passwd* pwd = getpwuid(getuid());
Expand All @@ -685,6 +682,7 @@ get_home_directory()
#endif
throw core::Fatal(
"Could not determine home directory from $HOME or getpwuid(3)");
#endif
}

const char*
Expand Down
17 changes: 17 additions & 0 deletions src/Util.hpp
Original file line number Diff line number Diff line change
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,22 @@

class Context;

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

for (auto&& p : paths) {
full = full / p;
}

return full.string();
}

namespace Util {

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

0 comments on commit 0dbf05c

Please sign in to comment.