highest):
1. Environment variables.
-2. The cache-specific configuration file *_<ccachedir>_/ccache.conf* (typically
- *$HOME/.ccache/ccache.conf*).
-3. The system-wide configuration file *_<sysconfdir>_/ccache.conf* (typically
- */etc/ccache.conf* or */usr/local/etc/ccache.conf*).
+2. The primary (cache-specific) configuration file (see below).
+3. The secondary (system-wide read-only) configuration file
+ *_<sysconfdir>_/ccache.conf* (typically */etc/ccache.conf* or
+ */usr/local/etc/ccache.conf*).
4. Compile-time defaults.
-As a special case, if the environment variable *CCACHE_CONFIGPATH* is set,
-ccache reads configuration from the specified path instead of the default
-paths.
+As a special case, if the the environment variable *CCACHE_CONFIGPATH* is set
+it specifies the primary configuration file and the secondary (system-wide)
+configuration file won't be read.
+
+
+Location of the primary configuration file
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The location of the primary (cache-specific) configuration is determined like
+this:
+
+1. If *CCACHE_CONFIGPATH* is set, use that path.
+2. Otherwise, if <<config_ccache_dir,*ccache_dir*>> (*CCACHE_DIR*) is set then
+ use *<ccache_dir>/ccache.conf*.
+3. Otherwise, if there is a legacy *$HOME/.ccache* directory then use
+ *$HOME/.ccache/ccache.conf.
+4. Otherwise, if *XDG_CONFIG_HOME* is set then use
+ *$XDG_CONFIG_HOME/ccache/ccache.conf*.
+5. Otherwise, use *%APPDATA%/ccache/ccache.conf* (Windows),
+ *$HOME/Library/Preferences/ccache/ccache.conf (macOS) or
+ *$HOME/.config/ccache/ccache.conf* (other systems).
Configuration file syntax
This setting specifies where ccache will keep its cached compiler outputs.
It will only take effect if set in the system-wide configuration file or as
- an environment variable. The default is *$HOME/.ccache*.
+ an environment variable. The default is *$XDG_CACHE_HOME/ccache* if
+ *XDG_CACHE_HOME* is set, otherwise *$HOME/.cache/ccache*. Exception: If the
+ legacy directory *$HOME/.ccache* exists then that directory is the default.
+
+ See also <<_location_of_the_primary_configuration_file,LOCATION OF THE
+ PRIMARY CONFIGURATION FILE>>.
[[config_cache_dir_levels]] *cache_dir_levels* (*CCACHE_NLEVELS*)::
class Config;
-class Config : NonCopyable
+class Config
{
public:
Config() = default;
+ Config(Config&) = default;
+ Config& operator=(const Config&) = default;
const std::string& base_dir() const;
const std::string& cache_dir() const;
std::string m_secondary_config_path;
std::string m_base_dir = "";
- std::string m_cache_dir = Util::get_home_directory() + "/.ccache";
+ std::string m_cache_dir;
uint32_t m_cache_dir_levels = 2;
std::string m_compiler = "";
std::string m_compiler_check = "mtime";
bool m_run_second_cpp = true;
uint32_t m_sloppiness = 0;
bool m_stats = true;
- std::string m_temporary_dir = default_temporary_dir(m_cache_dir);
+ std::string m_temporary_dir;
uint32_t m_umask = std::numeric_limits<uint32_t>::max(); // Don't set umask
bool m_temporary_dir_configured_explicitly = false;
fclose(f);
}
+static std::string
+default_cache_dir(const std::string& home_dir)
+{
+#ifdef _WIN32
+ return home_dir + "/ccache";
+#elif defined(__APPLE__)
+ return home_dir + "/Library/Caches/ccache";
+#else
+ return home_dir + "/.cache/ccache";
+#endif
+}
+
+static std::string
+default_config_dir(const std::string& home_dir)
+{
+#ifdef _WIN32
+ return home_dir + "/ccache";
+#elif defined(__APPLE__)
+ return home_dir + "/Library/Preferences/ccache";
+#else
+ return home_dir + "/.config/ccache";
+#endif
+}
+
// Read config file(s), populate variables, create configuration file in cache
// directory if missing, etc. Returns whether the primary configuration file
// exists.
static bool
set_up_config(Config& config)
{
- const char* p = getenv("CCACHE_CONFIGPATH");
- if (p) {
- config.set_primary_config_path(p);
+ const std::string home_dir = Util::get_home_directory();
+ const std::string legacy_ccache_dir = home_dir + "/.ccache";
+ const bool legacy_ccache_dir_exists =
+ Stat::stat(legacy_ccache_dir).is_directory();
+ const char* const env_xdg_cache_home = getenv("XDG_CACHE_HOME");
+ const char* const env_xdg_config_home = getenv("XDG_CONFIG_HOME");
+
+ const char* env_ccache_configpath = getenv("CCACHE_CONFIGPATH");
+ if (env_ccache_configpath) {
+ config.set_primary_config_path(env_ccache_configpath);
} else {
- config.set_secondary_config_path(fmt::format("{}/ccache.conf", SYSCONFDIR));
+ // Only used for ccache tests:
+ const char* const env_ccache_configpath2 = getenv("CCACHE_CONFIGPATH2");
+
+ config.set_secondary_config_path(
+ env_ccache_configpath2 ? env_ccache_configpath2
+ : fmt::format("{}/ccache.conf", SYSCONFDIR));
MTR_BEGIN("config", "conf_read_secondary");
// A missing config file in SYSCONFDIR is OK so don't check return value.
config.update_from_file(config.secondary_config_path());
MTR_END("config", "conf_read_secondary");
- if (config.cache_dir().empty()) {
- throw FatalError(
- "configuration setting \"cache_dir\" must not be the empty string");
- }
- if ((p = getenv("CCACHE_DIR"))) {
- config.set_cache_dir(p);
- }
- if (config.cache_dir().empty()) {
- throw FatalError("CCACHE_DIR must not be the empty string");
+ const char* const env_ccache_dir = getenv("CCACHE_DIR");
+ std::string primary_config_dir;
+ if (env_ccache_dir && *env_ccache_dir) {
+ primary_config_dir = env_ccache_dir;
+ } else if (!config.cache_dir().empty() && !env_ccache_dir) {
+ primary_config_dir = config.cache_dir();
+ } else if (legacy_ccache_dir_exists) {
+ primary_config_dir = legacy_ccache_dir;
+ } else if (env_xdg_config_home) {
+ primary_config_dir = fmt::format("{}/ccache", env_xdg_config_home);
+ } else {
+ primary_config_dir = default_config_dir(home_dir);
}
-
- config.set_primary_config_path(
- fmt::format("{}/ccache.conf", config.cache_dir()));
+ config.set_primary_config_path(primary_config_dir + "/ccache.conf");
}
+ const std::string& cache_dir_before_primary_config = config.cache_dir();
+
MTR_BEGIN("config", "conf_read_primary");
- bool primary_config_exists =
+ const bool primary_config_exists =
config.update_from_file(config.primary_config_path());
MTR_END("config", "conf_read_primary");
+ // Ignore cache_dir set in primary config.
+ config.set_cache_dir(cache_dir_before_primary_config);
+
MTR_BEGIN("config", "conf_update_from_environment");
config.update_from_environment();
+ // (config.cache_dir is set above if CCACHE_DIR is set.)
MTR_END("config", "conf_update_from_environment");
+ if (config.cache_dir().empty()) {
+ if (legacy_ccache_dir_exists) {
+ config.set_cache_dir(legacy_ccache_dir);
+ } else if (env_xdg_cache_home) {
+ config.set_cache_dir(fmt::format("{}/ccache", env_xdg_cache_home));
+ } else {
+ config.set_cache_dir(default_cache_dir(home_dir));
+ }
+ }
+ // else: cache_dir was set explicitly via environment or via secondary config.
+
+ // We have now determined config.cache_dir and populated the rest of config in
+ // prio order (1. environment, 2. primary config, 3. secondary config).
+
return primary_config_exists;
}
// Some of the above switches might have changed config settings, so run the
// setup again.
+ ctx.config = Config();
set_up_config(ctx.config);
}
EOF
unset GCC_COLORS
unset TERM
+ unset XDG_CACHE_HOME
+ unset XDG_CONFIG_HOME
export CCACHE_DETECT_SHEBANG=1
export CCACHE_DIR=$ABS_TESTDIR/.ccache
SUITE_upgrade() {
+ # -------------------------------------------------------------------------
TEST "Keep maxfiles and maxsize settings"
rm -f $CCACHE_CONFIGPATH
echo "0 0 0 0 0 0 0 0 0 0 0 0 0 2000 131072" >$CCACHE_DIR/0/stats
expect_stat 'max files' 32000
expect_stat 'max cache size' '2.1 GB'
+
+ # -------------------------------------------------------------------------
+ TEST "Default cache config/directory without XDG variables"
+
+ unset CCACHE_CONFIGPATH
+ unset CCACHE_DIR
+ export HOME=/home/user
+
+ if $HOST_OS_APPLE; then
+ expected=$HOME/Library/Caches/ccache
+ else
+ expected=$HOME/.cache/ccache
+ fi
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ if $HOST_OS_APPLE; then
+ expected=$HOME/Library/Preferences/ccache/ccache.conf
+ else
+ expected=$HOME/.config/ccache/ccache.conf
+ fi
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Default cache config/directory with XDG variables"
+
+ unset CCACHE_CONFIGPATH
+ unset CCACHE_DIR
+ export HOME=$PWD
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+
+ expected=$XDG_CACHE_HOME/ccache
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$XDG_CONFIG_HOME/ccache/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Cache config/directory with XDG variables and legacy directory"
+
+ unset CCACHE_CONFIGPATH
+ unset CCACHE_DIR
+ export HOME=$PWD
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+ mkdir $HOME/.ccache
+
+ expected=$HOME/.ccache
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$HOME/.ccache/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Cache config/directory with XDG variables and CCACHE_DIR"
+
+ unset CCACHE_CONFIGPATH
+ export CCACHE_DIR=$PWD/test
+ export HOME=/home/user
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+
+ expected=$CCACHE_DIR
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$CCACHE_DIR/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
+
+ # -------------------------------------------------------------------------
+ TEST "Cache config/directory with empty CCACHE_DIR"
+
+ # Empty (but set) CCACHE_DIR means "use defaults" and should thus override
+ # cache_dir set in the secondary config.
+
+ unset CCACHE_CONFIGPATH
+ export CCACHE_CONFIGPATH2=$PWD/ccache.conf2
+ export HOME=/home/user
+ export XDG_CACHE_HOME=/somewhere/cache
+ export XDG_CONFIG_HOME=/elsewhere/config
+ export CCACHE_DIR= # Set but empty
+ echo 'cache_dir = /nowhere' > $CCACHE_CONFIGPATH2
+
+ expected=$XDG_CACHE_HOME/ccache
+ actual=$($CCACHE -s | sed -n 's/^cache directory *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected cache directory $expected, actual $actual"
+ fi
+
+ expected=$XDG_CONFIG_HOME/ccache/ccache.conf
+ actual=$($CCACHE -s | sed -n 's/^primary config *//p')
+ if [ "$actual" != "$expected" ]; then
+ test_failed "expected primary config $expected actual $actual"
+ fi
}
{
Config config;
- std::string expected_cache_dir = Util::get_home_directory() + "/.ccache";
-
CHECK(config.base_dir().empty());
- CHECK(config.cache_dir() == expected_cache_dir);
+ CHECK(config.cache_dir().empty()); // Set later
CHECK(config.cache_dir_levels() == 2);
CHECK(config.compiler().empty());
CHECK(config.compiler_check() == "mtime");
CHECK(config.run_second_cpp());
CHECK(config.sloppiness() == 0);
CHECK(config.stats());
-#ifdef HAVE_GETEUID
- if (Stat::stat(fmt::format("/run/user/{}", geteuid())).is_directory()) {
- CHECK(config.temporary_dir()
- == fmt::format("/run/user/{}/ccache-tmp", geteuid()));
- } else {
-#endif
- CHECK(config.temporary_dir() == expected_cache_dir + "/tmp");
-#ifdef HAVE_GETEUID
- }
-#endif
+ CHECK(config.temporary_dir().empty()); // Set later
CHECK(config.umask() == std::numeric_limits<uint32_t>::max());
}
namespace {
+void
+init(Context& ctx)
+{
+ ctx.config.set_debug(true);
+ ctx.config.set_inode_cache(true);
+ ctx.config.set_cache_dir(Util::get_home_directory());
+}
+
bool
put(const Context& ctx,
const std::string& filename,
TestContext test_context;
Context ctx;
- ctx.config.set_debug(true);
+ init(ctx);
ctx.config.set_inode_cache(false);
Digest digest;
TestContext test_context;
Context ctx;
- ctx.config.set_debug(true);
- ctx.config.set_inode_cache(true);
+ init(ctx);
ctx.inode_cache.drop();
Util::write_file("a", "");
TestContext test_context;
Context ctx;
- ctx.config.set_debug(true);
- ctx.config.set_inode_cache(true);
+ init(ctx);
ctx.inode_cache.drop();
Util::write_file("a", "a text");
TestContext test_context;
Context ctx;
- ctx.config.set_debug(true);
- ctx.config.set_inode_cache(true);
+ init(ctx);
Digest digest;
TestContext test_context;
Context ctx;
- ctx.config.set_debug(true);
+ init(ctx);
ctx.inode_cache.drop();
- ctx.config.set_inode_cache(true);
Util::write_file("a", "a text");
Digest binary_digest = Hash().hash("binary").digest();
Digest code_digest = Hash().hash("code").digest();