Zero the cache statistics (but not the configuration options).
+=== Options for secondary storage
+
+*--trim-dir* _PATH_::
+
+ Remove old files from directory _PATH_ until it is at most the size specified
+ by `--trim-max-size`.
++
+WARNING: Don't use this option to trim the primary cache. To trim the primary
+cache directory to a certain size, use `CCACHE_MAXSIZE=_SIZE_ ccache -c`.
+
+*--trim-max-size* _SIZE_::
+
+ Specify the maximum size for `--trim-dir`. _SIZE_ should be a number followed
+ by an optional suffix: k, M, G, T (decimal), Ki, Mi, Gi or Ti (binary). The
+ default suffix is G.
+
+*--trim-method* _METHOD_::
+
+ Specify the method to trim a directory with `--trim-dir`. Possible values
+ are:
++
+--
+*atime*::
+ LRU (least recently used) using the file access timestamp. This is the
+ default.
+*mtime*::
+ LRU (least recently used) using the file modification timestamp.
+--
+
=== Options for scripting or debugging
*--checksum-file* _PATH_::
This backend stores data as separate files in a directory structure below
*DIRECTORY* (an absolute path), similar (but not identical) to the primary cache
storage. A typical use case for this backend would be sharing a cache on an NFS
-directory. Note that ccache will not perform any cleanup of the storage -- that
-has to be done by other means.
+directory.
+
+IMPORTANT: ccache will not perform any cleanup of the storage -- that has to be
+done by other means, for instance by running `ccache --trim-dir` periodically.
Examples:
This backend stores data in an HTTP-compatible server. The required HTTP methods
are `GET`, `PUT` and `DELETE`.
-IMPORTANT: ccache will not perform any cleanup of the HTTP storage.
+IMPORTANT: ccache will not perform any cleanup of the storage -- that has to be
+done by other means, for instance by running `ccache --trim-dir` periodically.
NOTE: HTTPS is not supported.
-h, --help print this help text
-V, --version print version and copyright information
+Options for secondary storage:
+ --trim-dir PATH remove old files from directory _PATH_ until it
+ is at most the size specified by --trim-max-size
+ (note: don't use this option to trim the primary
+ cache)
+ --trim-max-size SIZE specify the maximum size for --trim-dir;
+ available suffixes: k, M, G, T (decimal) and Ki,
+ Mi, Gi, Ti (binary); default suffix: G
+ --trim-method METHOD specify the method (atime or mtime) for
+ --trim-dir; default: atime
+
Options for scripting or debugging:
--checksum-file PATH print the checksum (64 bit XXH3) of the file at
PATH
PRINT_RAW(stdout, table.render());
}
+static void
+trim_dir(const std::string& dir,
+ const uint64_t trim_max_size,
+ const bool trim_lru_mtime)
+{
+ struct File
+ {
+ std::string path;
+ Stat stat;
+ };
+ std::vector<File> files;
+ uint64_t size_before = 0;
+
+ Util::traverse(dir, [&](const std::string& path, const bool is_dir) {
+ const auto stat = Stat::lstat(path);
+ if (!stat) {
+ // Probably some race, ignore.
+ return;
+ }
+ size_before += stat.size_on_disk();
+ if (!is_dir) {
+ const auto name = Util::base_name(path);
+ if (name == "ccache.conf" || name == "stats") {
+ throw Fatal("this looks like a primary cache directory (found {})",
+ path);
+ }
+ files.push_back({path, stat});
+ }
+ });
+
+ std::sort(files.begin(), files.end(), [&](const auto& f1, const auto& f2) {
+ if (trim_lru_mtime) {
+ return f1.stat.mtime() < f2.stat.mtime();
+ } else {
+ return f1.stat.atime() < f2.stat.atime();
+ }
+ });
+
+ uint64_t size_after = size_before;
+
+ for (const auto& file : files) {
+ if (size_after <= trim_max_size) {
+ break;
+ }
+ Util::unlink_tmp(file.path);
+ size_after -= file.stat.size();
+ }
+
+ PRINT(stdout,
+ "Removed {} ({} -> {})\n",
+ Util::format_human_readable_size(size_before - size_after),
+ Util::format_human_readable_size(size_before),
+ Util::format_human_readable_size(size_after));
+}
+
static std::string
get_version_text()
{
HASH_FILE,
PRINT_STATS,
SHOW_LOG_STATS,
+ TRIM_DIR,
+ TRIM_MAX_SIZE,
+ TRIM_METHOD,
};
const char options_string[] = "cCd:k:hF:M:po:sVxX:z";
{"show-config", no_argument, nullptr, 'p'},
{"show-log-stats", no_argument, nullptr, SHOW_LOG_STATS},
{"show-stats", no_argument, nullptr, 's'},
+ {"trim-dir", required_argument, nullptr, TRIM_DIR},
+ {"trim-max-size", required_argument, nullptr, TRIM_MAX_SIZE},
+ {"trim-method", required_argument, nullptr, TRIM_METHOD},
{"version", no_argument, nullptr, 'V'},
{"zero-stats", no_argument, nullptr, 'z'},
{nullptr, 0, nullptr, 0}};
process_main_options(int argc, const char* const* argv)
{
int c;
+ nonstd::optional<uint64_t> trim_max_size;
+ bool trim_lru_mtime = false;
// First pass: Handle non-command options that affect command options.
while ((c = getopt_long(argc,
long_options,
nullptr))
!= -1) {
+ const std::string arg = optarg ? optarg : std::string();
+
switch (c) {
case 'd': // --directory
- Util::setenv("CCACHE_DIR", optarg);
+ Util::setenv("CCACHE_DIR", arg);
break;
case CONFIG_PATH:
- Util::setenv("CCACHE_CONFIGPATH", optarg);
+ Util::setenv("CCACHE_CONFIGPATH", arg);
+ break;
+
+ case TRIM_MAX_SIZE:
+ trim_max_size = Util::parse_size(arg);
+ break;
+
+ case TRIM_METHOD:
+ trim_lru_mtime = (arg == "ctime");
break;
}
}
Config config;
config.read();
- std::string arg = optarg ? optarg : std::string();
+ const std::string arg = optarg ? optarg : std::string();
switch (c) {
case CONFIG_PATH:
- break; // Already handled in the first pass.
-
case 'd': // --directory
- break; // Already handled in the first pass.
+ case TRIM_MAX_SIZE:
+ case TRIM_METHOD:
+ // Already handled in the first pass.
+ break;
case CHECKSUM_FILE: {
Checksum checksum;
break;
}
+ case TRIM_DIR:
+ if (!trim_max_size) {
+ throw Error("please specify --trim-max-size when using --trim-dir");
+ }
+ trim_dir(arg, *trim_max_size, trim_lru_mtime);
+ break;
+
case 'V': // --version
PRINT_RAW(stdout, get_version_text());
exit(EXIT_SUCCESS);
--- /dev/null
+SUITE_trim_dir() {
+ # -------------------------------------------------------------------------
+ TEST "Trim secondary cache directory"
+
+ if $HOST_OS_APPLE; then
+ one_mb=1m
+ else
+ one_mb=1M
+ for subdir in aa bb cc; do
+ mkdir -p secondary/$subdir
+ dd if=/dev/zero of=secondary/$subdir/1 count=1 bs=$one_mb 2/dev/null
+ dd if=/dev/zero of=secondary/$subdir/2 count=1 bs=$one_mb 2/dev/null
+ done
+
+ backdate secondary/bb/2 secondary/cc/1
+ $CCACHE --trim-dir secondary --trim-max-size 4.5M --trim-method mtime \
+ >/dev/null
+
+ expect_exists secondary/aa/1
+ expect_exists secondary/aa/2
+ expect_exists secondary/bb/1
+ expect_missing secondary/bb/2
+ expect_missing secondary/cc/1
+ expect_exists secondary/cc/2
+
+ # -------------------------------------------------------------------------
+ TEST "Trim primary cache directory"
+
+ mkdir -p primary/0
+ touch primary/0/stats
+ if $CCACHE --trim-dir primary --trim-max-size 0 &>/dev/null; then
+ test_failed "Expected failure"
+ fi
+
+ rm -rf primary
+ mkdir primary
+ touch primary/ccache.conf
+ if $CCACHE --trim-dir primary --trim-max-size 0 &>/dev/null; then
+ test_failed "Expected failure"
+ fi
+}