ccache tends to quickly fill up the cache directory. You may find the
ccache_clean utility for removing old cache files. If called with no
-arguments it will remove any cache files older than a week. You can
-also pass a single argument specifying the number of days old files
-need to be to be cleaned. For example:
-
- ccache_clean 3
-
-will remove any files older than 3 days. You may wish to call
-ccache_clean from a cron job to keep your disk space usage reasonable.
+arguments it will trim the cache to be less than 1 Gigabyte. You can
+also pass a single argument specifying the size limit on the cache,
+for example:
+ ccache_clean 2G
+would clear the oldest files to bring the cache below 2G in size. You
+can use 'M' for megabytes, 'G' for gigabytes or 'K' for kilobytes.
+
+You may wish to call ccache_clean from a cron job to keep your disk
+space usage reasonable.
How it works
------------
#include "ccache.h"
-static time_t threshold;
+#define BLOCK_SIZE 1024
+
char *cache_logfile = NULL;
-static void clean_fn(const char *fname)
+static struct files {
+ char *fname;
+ time_t mtime;
+ size_t size;
+} **files;
+static unsigned allocated;
+static unsigned num_files;
+static size_t total_size;
+static size_t size_threshold = 1024*1024; /* 1G default */
+
+static int files_compare(struct files *f1, struct files *f2)
{
- struct stat st;
- if (stat(fname, &st) != 0 || !S_ISREG(st.st_mode)) return;
+ return f2->mtime - f1->mtime;
+}
- if (st.st_mtime >= threshold) return;
+/* this builds the list of files in the cache */
+static void traverse_fn(const char *fname, struct stat *st)
+{
+ if (!S_ISREG(st->st_mode)) return;
- if (unlink(fname) != 0) {
- cc_log("unlink %s - %s\n", fname, strerror(errno));
- return;
+ if (num_files == allocated) {
+ allocated = 10000 + num_files*2;
+ files = x_realloc(files, sizeof(struct files *)*allocated);
}
- cc_log("cleaned %s\n", fname);
+
+ files[num_files] = x_malloc(sizeof(struct files *));
+ files[num_files]->fname = x_strdup(fname);
+ files[num_files]->mtime = st->st_mtime;
+ /* we deliberately overestimate by up to 1 block */
+ files[num_files]->size = 1 + (st->st_size/BLOCK_SIZE);
+ total_size += files[num_files]->size;
+ num_files++;
+}
+
+static void sort_and_clean(void)
+{
+ unsigned i;
+
+ if (num_files > 1) {
+ /* sort in ascending data order */
+ qsort(files, num_files, sizeof(struct files *), files_compare);
+ }
+
+ /* delete enough files to bring us below the threshold */
+ for (i=0;i<num_files && total_size >= size_threshold;i++) {
+ if (unlink(files[i]->fname) != 0) {
+ fprintf(stderr, "unlink %s - %s\n",
+ files[i]->fname, strerror(errno));
+ continue;
+ }
+ total_size -= files[i]->size;
+ }
+
+ printf("cleaned %d of %d files (cache is now %.1f MByte)\n",
+ i, num_files, total_size/1024.0);
}
int main(int argc, char *argv[])
{
char *cache_dir;
- int num_days = 7;
-
+
cache_dir = getenv("CCACHE_DIR");
if (!cache_dir) {
x_asprintf(&cache_dir, "%s/.ccache", getenv("HOME"));
exit(1);
}
+ /* work out what size cache they want */
if (argc > 1) {
- num_days = atoi(argv[1]);
+ char *s = argv[1];
+ char m;
+ size_threshold = atoi(s);
+ m = s[strlen(s)-1];
+ switch (m) {
+ case 'G':
+ case 'g':
+ size_threshold *= 1024*1024;
+ break;
+ case 'M':
+ case 'm':
+ size_threshold *= 1024;
+ break;
+ case 'K':
+ case 'k':
+ size_threshold *= 1;
+ break;
+ }
}
- threshold = time(NULL) - num_days*24*60*60;
+ /* build a list of files */
+ traverse(cache_dir, traverse_fn);
- traverse(cache_dir, clean_fn);
+ /* clean the cache */
+ sort_and_clean();
return 0;
}
return ret;
}
+/*
+ this is like malloc() but dies if the malloc fails
+*/
+void *x_malloc(size_t size)
+{
+ void *ret;
+ ret = malloc(size);
+ if (!ret) {
+ fatal("out of memory in malloc\n");
+ }
+ return ret;
+}
+
+/*
+ this is like strdup() but dies if the malloc fails
+*/
+void *x_realloc(void *ptr, size_t size)
+{
+ if (!ptr) return x_malloc(size);
+ ptr = realloc(ptr, size);
+ if (!ptr) {
+ fatal("out of memory in x_realloc");
+ }
+ return ptr;
+}
+
/*
revsusive directory traversal - used for cleanup
fn() is called on all files/dirs in the tree
*/
-void traverse(const char *dir, void (*fn)(const char *))
+void traverse(const char *dir, void (*fn)(const char *, struct stat *))
{
DIR *d;
struct dirent *de;
traverse(fname, fn);
}
- fn(fname);
+ fn(fname, &st);
free(fname);
}