From: Joel Rosdahl Date: Wed, 8 Jan 2014 20:28:40 +0000 (+0100) Subject: Merge branch 'maint' X-Git-Tag: v3.2~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c0b40b8b634ff966a097598447317a9f7754a53a;p=thirdparty%2Fccache.git Merge branch 'maint' * maint: Require CCACHE_SLOPPINESS=pch_defines,time_macros opt-in to enable PCH handling doc: Mention that --ccache-skip currently does not mean "skip hashing option" Don't hash compiler option -fdebug-prefix-map= Conflicts: MANUAL.txt ccache.c ccache.h test.sh --- c0b40b8b634ff966a097598447317a9f7754a53a diff --cc MANUAL.txt index 1c4ecb379,5bc1ca395..a36c50503 --- a/MANUAL.txt +++ b/MANUAL.txt @@@ -425,18 -373,13 +428,21 @@@ WRAPPERS>> -- *file_macro*:: Ignore *\_\_FILE__* being present in the source. - *include_file_mtime*:: - By default, ccache will not cache a file if it includes a header whose - mtime is too new. This option disables that check. ++*file_stat_matches*:: ++ ccache normally examines a file's contents to determine whether it matches ++ the cached version. With this option set, ccache will consider a file as ++ matching its cached version if the sizes, mtimes and ctimes match. +*include_file_ctime*:: + By default, ccache also will not cache a file if it includes a header whose + ctime is too new. This option disables that check. + *include_file_mtime*:: - Don't check the modification time of include files in the direct mode. ++ By default, ccache will not cache a file if it includes a header whose ++ mtime is too new. This option disables that check. + *pch_defines*:: + Be sloppy about #defines when precompiling a header file. See + <<_precompiled_headers,PRECOMPILED HEADERS>> for more information. *time_macros*:: Ignore *\_\_DATE\__* and *\_\_TIME__* being present in the source code. - *file_stat_matches*:: - ccache normally examines a file's contents to determine whether it matches - the cached version. With this option set, ccache will consider a file as - matching its cached version if the sizes, mtimes and ctimes match. -- + See the discussion under <<_troubleshooting,TROUBLESHOOTING>> for more @@@ -635,9 -573,11 +641,11 @@@ Precompiled header ccache has support for GCC's precompiled headers. However, you have to do some things to make it work properly: - * You must set *sloppiness* to *time_macros*. The reason is that ccache can't - tell whether *\_\_TIME\__* or *\_\_DATE__* is used when using a precompiled - header. -* You must set *CCACHE_SLOPPINESS* to *pch_defines,time_macros*. The reason is - that ccache can't tell whether *\_\_TIME\__* or *\_\_DATE__* is used when - using a precompiled header. Further, it can't detect changes in #defines in - the source code because of how preprocessing works in combination with ++* You must set *sloppiness* to *pch_defines,time_macros*. The reason is that ++ ccache can't tell whether *\_\_TIME\__* or *\_\_DATE__* is used when using a ++ precompiled header. Further, it can't detect changes in #defines in the ++ source code because of how preprocessing works in combination with + precompiled headers. * You must either: + -- diff --cc ccache.c index 4b63c68c4,dc5498d67..c395fadfd --- a/ccache.c +++ b/ccache.c @@@ -2177,9 -1705,10 +2184,10 @@@ cc_process_args(struct args *args, stru if (found_pch || found_fpch_preprocess) { using_precompiled_header = true; - if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) { - if (!(sloppiness & SLOPPY_PCH_DEFINES) - || !(sloppiness & SLOPPY_TIME_MACROS)) { - cc_log("You have to specify \"pch_defines,time_macros\" sloppiness when" - " using precompiled headers to get direct hits"); ++ if (!(conf->sloppiness & SLOPPY_PCH_DEFINES) ++ || !(conf->sloppiness & SLOPPY_TIME_MACROS)) { + cc_log("You have to specify \"time_macros\" sloppiness when using" + " precompiled headers to get direct hits"); cc_log("Disabling direct mode"); stats_update(STATS_CANTUSEPCH); result = false; diff --cc ccache.h index 48812df05,282f9811a..1d86a9dc7 --- a/ccache.h +++ b/ccache.h @@@ -54,15 -53,9 +54,15 @@@ enum stats }; #define SLOPPY_INCLUDE_FILE_MTIME 1 -#define SLOPPY_FILE_MACRO 2 -#define SLOPPY_TIME_MACROS 4 -#define SLOPPY_PCH_DEFINES 8 +#define SLOPPY_INCLUDE_FILE_CTIME 2 +#define SLOPPY_FILE_MACRO 4 +#define SLOPPY_TIME_MACROS 8 - ++#define SLOPPY_PCH_DEFINES 16 +/* + * Allow us to match files based on their stats (size, mtime, ctime), without + * looking at their contents. + */ - #define SLOPPY_FILE_STAT_MATCHES 16 ++#define SLOPPY_FILE_STAT_MATCHES 32 #define str_eq(s1, s2) (strcmp((s1), (s2)) == 0) #define str_startswith(s, p) (strncmp((s), (p), strlen((p))) == 0) diff --cc conf.c index b49ba3c87,000000000..0252f5cf8 mode 100644,000000..100644 --- a/conf.c +++ b/conf.c @@@ -1,624 -1,0 +1,626 @@@ +/* - * Copyright (C) 2011-2013 Joel Rosdahl ++ * Copyright (C) 2011-2014 Joel Rosdahl + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "conf.h" +#include "ccache.h" + +typedef bool (*conf_item_parser)(const char *str, void *result, char **errmsg); +typedef bool (*conf_item_verifier)(void *value, char **errmsg); + +struct conf_item { + const char *name; + size_t number; + conf_item_parser parser; + size_t offset; + conf_item_verifier verifier; +}; + +struct env_to_conf_item { + const char *env_name; + const char *conf_name; +}; + +static bool +parse_bool(const char *str, void *result, char **errmsg) +{ + bool *value = (bool *)result; + + if (str_eq(str, "true")) { + *value = true; + return true; + } else if (str_eq(str, "false")) { + *value = false; + return true; + } else { + *errmsg = format("not a boolean value: \"%s\"", str); + return false; + } +} + +static bool +parse_env_string(const char *str, void *result, char **errmsg) +{ + char **value = (char **)result; + free(*value); + *value = subst_env_in_string(str, errmsg); + return *value; +} + +static bool +parse_size(const char *str, void *result, char **errmsg) +{ + uint64_t *value = (uint64_t *)result; + uint64_t size; + *errmsg = NULL; + if (parse_size_with_suffix(str, &size)) { + *value = size; + return true; + } else { + *errmsg = format("invalid size: \"%s\"", str); + return false; + } +} + +static bool +parse_sloppiness(const char *str, void *result, char **errmsg) +{ + unsigned *value = (unsigned *)result; + char *word, *p, *q, *saveptr = NULL; + + if (!str) { + return *value; + } + p = x_strdup(str); + q = p; + while ((word = strtok_r(q, ", ", &saveptr))) { + if (str_eq(word, "file_macro")) { + *value |= SLOPPY_FILE_MACRO; - } else if (str_eq(word, "include_file_mtime")) { - *value |= SLOPPY_INCLUDE_FILE_MTIME; ++ } else if (str_eq(word, "file_stat_matches")) { ++ *value |= SLOPPY_FILE_STAT_MATCHES; + } else if (str_eq(word, "include_file_ctime")) { + *value |= SLOPPY_INCLUDE_FILE_CTIME; ++ } else if (str_eq(word, "include_file_mtime")) { ++ *value |= SLOPPY_INCLUDE_FILE_MTIME; ++ } else if (str_eq(word, "pch_defines")) { ++ *value |= SLOPPY_PCH_DEFINES; + } else if (str_eq(word, "time_macros")) { + *value |= SLOPPY_TIME_MACROS; - } else if (str_eq(word, "file_stat_matches")) { - *value |= SLOPPY_FILE_STAT_MATCHES; + } else { + *errmsg = format("unknown sloppiness: \"%s\"", word); + free(p); + return false; + } + q = NULL; + } + free(p); + return true; +} + +static bool +parse_string(const char *str, void *result, char **errmsg) +{ + char **value = (char **)result; + (void)errmsg; + free(*value); + *value = x_strdup(str); + return true; +} + +static bool +parse_umask(const char *str, void *result, char **errmsg) +{ + unsigned *value = (unsigned *)result; + char *endptr; + if (str_eq(str, "")) { + *value = UINT_MAX; + return true; + } + errno = 0; + *value = strtoul(str, &endptr, 8); + if (errno == 0 && *str != '\0' && *endptr == '\0') { + return true; + } else { + *errmsg = format("not an octal integer: \"%s\"", str); + return false; + } +} + +static bool +parse_unsigned(const char *str, void *result, char **errmsg) +{ + unsigned *value = (unsigned *)result; + long x; + char *endptr; + errno = 0; + x = strtol(str, &endptr, 10); + if (errno == 0 && x >= 0 && *str != '\0' && *endptr == '\0') { + *value = x; + return true; + } else { + *errmsg = format("invalid unsigned integer: \"%s\"", str); + return false; + } +} + +static bool +verify_absolute_path(void *value, char **errmsg) +{ + char **path = (char **)value; + assert(*path); + if (str_eq(*path, "")) { + /* The empty string means "disable" in this case. */ + return true; + } else if (is_absolute_path(*path)) { + return true; + } else { + *errmsg = format("not an absolute path: \"%s\"", *path); + return false; + } +} + +static bool +verify_dir_levels(void *value, char **errmsg) +{ + unsigned *levels = (unsigned *)value; + assert(levels); + if (*levels >= 1 && *levels <= 8) { + return true; + } else { + *errmsg = format("cache directory levels must be between 1 and 8"); + return false; + } +} + +#define ITEM(name, type) \ + parse_##type, offsetof(struct conf, name), NULL +#define ITEM_V(name, type, verification) \ + parse_##type, offsetof(struct conf, name), verify_##verification + +#include "confitems_lookup.c" +#include "envtoconfitems_lookup.c" + +static const struct conf_item * +find_conf(const char *name) +{ + return confitems_get(name, strlen(name)); +} + +static const struct env_to_conf_item * +find_env_to_conf(const char *name) +{ + return envtoconfitems_get(name, strlen(name)); +} + +static bool +handle_conf_setting(struct conf *conf, const char *key, const char *value, + char **errmsg, bool from_env_variable, bool negate_boolean, + const char *origin) +{ + const struct conf_item *item; + + item = find_conf(key); + if (!item) { + *errmsg = format("unknown configuration option \"%s\"", key); + return false; + } + + if (from_env_variable && item->parser == parse_bool) { + /* + * Special rule for boolean settings from the environment: any value means + * true. + */ + bool *value = (bool *)((char *)conf + item->offset); + *value = !negate_boolean; + goto out; + } + + if (!item->parser(value, (char *)conf + item->offset, errmsg)) { + return false; + } + if (item->verifier && !item->verifier((char *)conf + item->offset, errmsg)) { + return false; + } + +out: + conf->item_origins[item->number] = origin; + return true; +} + +static bool +parse_line(const char *line, char **key, char **value, char **errmsg) +{ + const char *p, *q; + +#define SKIP_WS(x) while (isspace(*x)) { ++x; } + + *key = NULL; + *value = NULL; + + p = line; + SKIP_WS(p); + if (*p == '\0' || *p == '#') { + return true; + } + q = p; + while (isalpha(*q) || *q == '_') { + ++q; + } + *key = x_strndup(p, q - p); + p = q; + SKIP_WS(p); + if (*p != '=') { + *errmsg = x_strdup("missing equal sign"); + free(*key); + *key = NULL; + return false; + } + ++p; + + /* Skip leading whitespace. */ + SKIP_WS(p); + q = p; + while (*q) { + ++q; + } + /* Skip trailing whitespace. */ + while (isspace(q[-1])) { + --q; + } + *value = x_strndup(p, q - p); + + return true; + +#undef SKIP_WS +} + +/* Create a conf struct with default values. */ +struct conf * +conf_create(void) +{ + size_t i; + struct conf *conf = x_malloc(sizeof(*conf)); + conf->base_dir = x_strdup(""); + conf->cache_dir = format("%s/.ccache", get_home_directory()); + conf->cache_dir_levels = 2; + conf->compiler = x_strdup(""); + conf->compiler_check = x_strdup("mtime"); + conf->compression = false; + conf->compression_level = 6; + conf->cpp_extension = x_strdup(""); + conf->direct_mode = true; + conf->disable = false; + conf->extra_files_to_hash = x_strdup(""); + conf->hard_link = false; + conf->hash_dir = false; + conf->log_file = x_strdup(""); + conf->max_files = 0; + conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000; + conf->path = x_strdup(""); + conf->prefix_command = x_strdup(""); + conf->read_only = false; + conf->recache = false; + conf->run_second_cpp = false; + conf->sloppiness = 0; + conf->stats = true; + conf->temporary_dir = x_strdup(""); + conf->umask = UINT_MAX; /* default: don't set umask */ + conf->unify = false; + conf->item_origins = x_malloc(CONFITEMS_TOTAL_KEYWORDS * sizeof(char *)); + for (i = 0; i < CONFITEMS_TOTAL_KEYWORDS; ++i) { + conf->item_origins[i] = "default"; + } + return conf; +} + +void +conf_free(struct conf *conf) +{ + if (!conf) { + return; + } + free(conf->base_dir); + free(conf->cache_dir); + free(conf->compiler); + free(conf->compiler_check); + free(conf->cpp_extension); + free(conf->extra_files_to_hash); + free(conf->log_file); + free(conf->path); + free(conf->prefix_command); + free(conf->temporary_dir); + free(conf->item_origins); + free(conf); +} + +/* Note: The path pointer is stored in conf, so path must outlive conf. */ +bool +conf_read(struct conf *conf, const char *path, char **errmsg) +{ + FILE *f; + char buf[10000]; + bool result = true; + unsigned line_number; + + assert(errmsg); + *errmsg = NULL; + + f = fopen(path, "r"); + if (!f) { + *errmsg = format("%s: %s", path, strerror(errno)); + return false; + } + + line_number = 0; + while (fgets(buf, sizeof(buf), f)) { + char *errmsg2, *key, *value; + bool ok; + ++line_number; + ok = parse_line(buf, &key, &value, &errmsg2); + if (ok && key) { /* key == NULL if comment or blank line */ + ok = handle_conf_setting(conf, key, value, &errmsg2, false, false, path); + } + free(key); + free(value); + if (!ok) { + *errmsg = format("%s:%u: %s", path, line_number, errmsg2); + free(errmsg2); + result = false; + goto out; + } + } + if (ferror(f)) { + *errmsg = x_strdup(strerror(errno)); + result = false; + } + +out: + fclose(f); + return result; +} + +bool +conf_update_from_environment(struct conf *conf, char **errmsg) +{ + char **p; + char *q; + char *key; + char *errmsg2; + const struct env_to_conf_item *env_to_conf_item; + bool negate; + size_t key_start; + + for (p = environ; *p; ++p) { + if (!str_startswith(*p, "CCACHE_")) { + continue; + } + q = strchr(*p, '='); + if (!q) { + continue; + } + + if (str_startswith(*p + 7, "NO")) { + negate = true; + key_start = 9; + } else { + negate = false; + key_start = 7; + } + key = x_strndup(*p + key_start, q - *p - key_start); + + ++q; /* Now points to the value. */ + + env_to_conf_item = find_env_to_conf(key); + if (!env_to_conf_item) { + free(key); + continue; + } + + if (!handle_conf_setting( + conf, env_to_conf_item->conf_name, q, &errmsg2, true, negate, + "environment")) { + *errmsg = format("%s: %s", key, errmsg2); + free(errmsg2); + free(key); + return false; + } + + free(key); + } + + return true; +} + +bool +conf_set_value_in_file(const char *path, const char *key, const char *value, + char **errmsg) +{ + FILE *infile, *outfile; + char *outpath; + char buf[10000]; + bool found; + const struct conf_item *item; + + item = find_conf(key); + if (!item) { + *errmsg = format("unknown configuration option \"%s\"", key); + return false; + } + + infile = fopen(path, "r"); + if (!infile) { + *errmsg = format("%s: %s", path, strerror(errno)); + return false; + } + + outpath = format("%s.tmp.%s", path, tmp_string()); + outfile = fopen(outpath, "w"); + if (!outfile) { + *errmsg = format("%s: %s", outpath, strerror(errno)); + free(outpath); + fclose(infile); + return false; + } + + found = false; + while (fgets(buf, sizeof(buf), infile)) { + char *errmsg2, *key2, *value2; + bool ok; + ok = parse_line(buf, &key2, &value2, &errmsg2); + if (ok && key2 && str_eq(key2, key)) { + found = true; + fprintf(outfile, "%s = %s\n", key, value); + } else { + fputs(buf, outfile); + } + free(key2); + free(value2); + } + + if (!found) { + fprintf(outfile, "%s = %s\n", key, value); + } + + fclose(infile); + fclose(outfile); + if (x_rename(outpath, path) != 0) { + *errmsg = format("rename %s to %s: %s", outpath, path, strerror(errno)); + return false; + } + free(outpath); + + return true; +} + +bool +conf_print_items(struct conf *conf, + void (*printer)(const char *descr, const char *origin, + void *context), + void *context) +{ + char *s = x_strdup(""); + char *s2; + + reformat(&s, "base_dir = %s", conf->base_dir); + printer(s, conf->item_origins[find_conf("base_dir")->number], context); + + reformat(&s, "cache_dir = %s", conf->cache_dir); + printer(s, conf->item_origins[find_conf("cache_dir")->number], context); + + reformat(&s, "cache_dir_levels = %u", conf->cache_dir_levels); + printer(s, conf->item_origins[find_conf("cache_dir_levels")->number], + context); + + reformat(&s, "compiler = %s", conf->compiler); + printer(s, conf->item_origins[find_conf("compiler")->number], context); + + reformat(&s, "compiler_check = %s", conf->compiler_check); + printer(s, conf->item_origins[find_conf("compiler_check")->number], context); + + reformat(&s, "compression = %s", conf->compression ? "true" : "false"); + printer(s, conf->item_origins[find_conf("compression")->number], context); + + reformat(&s, "compression_level = %u", conf->compression_level); + printer(s, conf->item_origins[find_conf("compression_level")->number], + context); + + reformat(&s, "cpp_extension = %s", conf->cpp_extension); + printer(s, conf->item_origins[find_conf("cpp_extension")->number], context); + + reformat(&s, "direct_mode = %s", conf->direct_mode ? "true" : "false"); + printer(s, conf->item_origins[find_conf("direct_mode")->number], context); + + reformat(&s, "disable = %s", conf->disable ? "true" : "false"); + printer(s, conf->item_origins[find_conf("disable")->number], context); + + reformat(&s, "extra_files_to_hash = %s", conf->extra_files_to_hash); + printer(s, conf->item_origins[find_conf("extra_files_to_hash")->number], + context); + + reformat(&s, "hard_link = %s", conf->hard_link ? "true" : "false"); + printer(s, conf->item_origins[find_conf("hard_link")->number], context); + + reformat(&s, "hash_dir = %s", conf->hash_dir ? "true" : "false"); + printer(s, conf->item_origins[find_conf("hash_dir")->number], context); + + reformat(&s, "log_file = %s", conf->log_file); + printer(s, conf->item_origins[find_conf("log_file")->number], context); + + reformat(&s, "max_files = %u", conf->max_files); + printer(s, conf->item_origins[find_conf("max_files")->number], context); + + s2 = format_parsable_size_with_suffix(conf->max_size); + reformat(&s, "max_size = %s", s2); + printer(s, conf->item_origins[find_conf("max_size")->number], context); + free(s2); + + reformat(&s, "path = %s", conf->path); + printer(s, conf->item_origins[find_conf("path")->number], context); + + reformat(&s, "prefix_command = %s", conf->prefix_command); + printer(s, conf->item_origins[find_conf("prefix_command")->number], context); + + reformat(&s, "read_only = %s", conf->read_only ? "true" : "false"); + printer(s, conf->item_origins[find_conf("read_only")->number], context); + + reformat(&s, "recache = %s", conf->recache ? "true" : "false"); + printer(s, conf->item_origins[find_conf("recache")->number], context); + + reformat(&s, "run_second_cpp = %s", conf->run_second_cpp ? "true" : "false"); + printer(s, conf->item_origins[find_conf("run_second_cpp")->number], context); + + reformat(&s, "sloppiness = "); + if (conf->sloppiness & SLOPPY_FILE_MACRO) { + reformat(&s, "%sfile_macro, ", s); + } + if (conf->sloppiness & SLOPPY_INCLUDE_FILE_MTIME) { + reformat(&s, "%sinclude_file_mtime, ", s); + } + if (conf->sloppiness & SLOPPY_INCLUDE_FILE_CTIME) { + reformat(&s, "%sinclude_file_ctime, ", s); + } + if (conf->sloppiness & SLOPPY_TIME_MACROS) { + reformat(&s, "%stime_macros, ", s); + } + if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) { + reformat(&s, "%sfile_stat_matches, ", s); + } + if (conf->sloppiness) { + /* Strip last ", ". */ + s[strlen(s) - 2] = '\0'; + } + printer(s, conf->item_origins[find_conf("sloppiness")->number], context); + + reformat(&s, "stats = %s", conf->stats ? "true" : "false"); + printer(s, conf->item_origins[find_conf("stats")->number], context); + + reformat(&s, "temporary_dir = %s", conf->temporary_dir); + printer(s, conf->item_origins[find_conf("temporary_dir")->number], context); + + if (conf->umask == UINT_MAX) { + reformat(&s, "umask = "); + } else { + reformat(&s, "umask = %03o", conf->umask); + } + printer(s, conf->item_origins[find_conf("umask")->number], context); + + reformat(&s, "unify = %s", conf->unify ? "true" : "false"); + printer(s, conf->item_origins[find_conf("unify")->number], context); + + free(s); + return true; +} diff --cc test.sh index b8d116580,b4360e81d..51b9c8822 --- a/test.sh +++ b/test.sh @@@ -1238,11 -1102,11 +1238,11 @@@ EO #define time __TIME__ int test; EOF - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER -c time.c - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c time.c ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time.c checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER -c time.c - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c time.c ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time.c checkstat 'cache hit (direct)' 1 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 @@@ -1257,11 -1121,11 +1257,11 @@@ EO cat <time_h.c #include "time.h" EOF - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER -c time_h.c - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c time_h.c ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time_h.c checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER -c time_h.c - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c time_h.c ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER -c time_h.c checkstat 'cache hit (direct)' 1 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 @@@ -1975,9 -1761,9 +1975,9 @@@ gcc_pch_suite() # trying to preprocess: checkstat 'preprocessor error' 1 - testname="no -fpch-preprocess, -include, no sloppy time macros" + testname="no -fpch-preprocess, -include, no sloppiness" $CCACHE -Cz >/dev/null - $CCACHE $COMPILER -c -include pch.h pch2.c 2>/dev/null + $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 0 @@@ -1986,30 -1772,30 +1986,30 @@@ testname="no -fpch-preprocess, -include" $CCACHE -Cz >/dev/null - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -include pch.h pch2.c 2>/dev/null ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -include pch.h pch2.c 2>/dev/null ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null checkstat 'cache hit (direct)' 1 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 - testname="-fpch-preprocess, #include, no sloppy time macros" + testname="-fpch-preprocess, #include, no sloppiness" $CCACHE -Cz >/dev/null - $CCACHE $COMPILER -c -fpch-preprocess pch.c + $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 0 # Must enable sloppy time macros: checkstat "can't use precompiled header" 1 - testname="-fpch-preprocess, #include, sloppy time macros" + testname="-fpch-preprocess, #include, sloppiness" $CCACHE -Cz >/dev/null - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -fpch-preprocess pch.c ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -fpch-preprocess pch.c ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c checkstat 'cache hit (direct)' 1 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 @@@ -2017,18 -1803,18 +2017,18 @@@ testname="-fpch-preprocess, #include, file changed" echo "updated" >>pch.h.gch # GCC seems to cope with this... backdate pch.h.gch - CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c - CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -fpch-preprocess pch.c ++ CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c checkstat 'cache hit (direct)' 1 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 2 testname="preprocessor mode" $CCACHE -Cz >/dev/null - CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c - CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -fpch-preprocess pch.c ++ CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 0 checkstat 'cache miss' 1 - CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c - CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -fpch-preprocess pch.c ++ CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 1 checkstat 'cache miss' 1 @@@ -2036,178 -1822,7 +2036,178 @@@ testname="preprocessor mode, file changed" echo "updated" >>pch.h.gch # GCC seems to cope with this... backdate pch.h.gch - CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c - CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=pch_defines,time_macros $CCACHE $COMPILER -c -fpch-preprocess pch.c ++ CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 1 + checkstat 'cache miss' 2 - CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c ++ CCACHE_NODIRECT=1 CCACHE_SLOPPINESS="$default_sloppiness pch_defines time_macros" $CCACHE $COMPILER $SYSROOT -c -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 2 + checkstat 'cache miss' 2 +} + +clang_pch_suite() { + ################################################################## + # Tests for creating a .gch. + + backdate pch.h + + testname="create .gch, -c, no -o" + $CCACHE -zC >/dev/null + $CCACHE $COMPILER $SYSROOT -c pch.h + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + rm -f pch.h.gch + $CCACHE $COMPILER $SYSROOT -c pch.h + checkstat 'cache hit (direct)' 1 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + if [ ! -f pch.h.gch ]; then + test_failed "pch.h.gch missing" + fi + + testname="create .gch, no -c, -o" + $CCACHE -Cz >/dev/null + $CCACHE $COMPILER $SYSROOT pch.h -o pch.gch + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + $CCACHE $COMPILER $SYSROOT pch.h -o pch.gch + checkstat 'cache hit (direct)' 1 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + if [ ! -f pch.gch ]; then + test_failed "pch.gch missing" + fi + rm pch.gch + + ################################################################## + # Tests for using a .gch. + + backdate pch.h.gch + + testname="gch, no -fpch-preprocess, -include, no sloppy time macros" + $CCACHE -Cz >/dev/null + $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 0 + # Must enable sloppy time macros: + checkstat "can't use precompiled header" 1 + + testname="gch, no -fpch-preprocess, -include, sloppy time macros" + $CCACHE -Cz >/dev/null + CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null + checkstat 'cache hit (direct)' 1 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + + testname="gch, -fpch-preprocess, -include, file changed" + echo "updated" >>pch.h.gch # clang seems to cope with this... + backdate pch.h.gch + CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 1 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 2 + + testname="gch, preprocessor mode" + $CCACHE -Cz >/dev/null + CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 1 + checkstat 'cache miss' 1 + + testname="gch, preprocessor mode, file changed" + echo "updated" >>pch.h.gch # clang seems to cope with this... + backdate pch.h.gch + CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 1 + checkstat 'cache miss' 2 + CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 2 + checkstat 'cache miss' 2 + + rm pch.h.gch + + ################################################################## + # Tests for creating a .pth. + + backdate pch.h + + testname="create .pth, -c, -o" + $CCACHE -zC >/dev/null + $CCACHE $COMPILER $SYSROOT -c pch.h -o pch.h.pth + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + rm -f pch.h.pth + $CCACHE $COMPILER $SYSROOT -c pch.h -o pch.h.pth + checkstat 'cache hit (direct)' 1 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + if [ ! -f pch.h.pth ]; then + test_failed "pch.h.pth missing" + fi + + ################################################################## + # Tests for using a .pth. + + backdate pch.h.pth + + testname="pth, no -fpch-preprocess, -include, no sloppy time macros" + $CCACHE -Cz >/dev/null + $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 0 + # Must enable sloppy time macros: + checkstat "can't use precompiled header" 1 + + testname="pth, no -fpch-preprocess, -include, sloppy time macros" + $CCACHE -Cz >/dev/null + CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h pch2.c 2>/dev/null + checkstat 'cache hit (direct)' 1 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + + testname="pth, -fpch-preprocess, -include, file changed" + echo "updated" >>pch.h.pth # clang seems to cope with this... + backdate pch.h.pth + CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 1 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 2 + + testname="pth, preprocessor mode" + $CCACHE -Cz >/dev/null + CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 0 + checkstat 'cache miss' 1 + CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c + checkstat 'cache hit (direct)' 0 + checkstat 'cache hit (preprocessed)' 1 + checkstat 'cache miss' 1 + + testname="pth, preprocessor mode, file changed" + echo "updated" >>pch.h.pth # clang seems to cope with this... + backdate pch.h.pth + CCACHE_NODIRECT=1 CCACHE_SLOPPINESS=time_macros $CCACHE $COMPILER $SYSROOT -c -include pch.h -fpch-preprocess pch.c checkstat 'cache hit (direct)' 0 checkstat 'cache hit (preprocessed)' 1 checkstat 'cache miss' 2 diff --cc test/test_conf.c index c06a66a83,000000000..30bfe4328 mode 100644,000000..100644 --- a/test/test_conf.c +++ b/test/test_conf.c @@@ -1,420 -1,0 +1,420 @@@ +/* - * Copyright (C) 2011-2013 Joel Rosdahl ++ * Copyright (C) 2011-2014 Joel Rosdahl + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "conf.h" +#include "test/framework.h" +#include "test/util.h" + +#define N_CONFIG_ITEMS 26 +static struct { + char *descr; + const char *origin; +} received_conf_items[N_CONFIG_ITEMS]; +static size_t n_received_conf_items = 0; + +static void +conf_item_receiver(const char *descr, const char *origin, void *context) +{ + (void)context; + received_conf_items[n_received_conf_items].descr = x_strdup(descr); + received_conf_items[n_received_conf_items].origin = origin; + ++n_received_conf_items; +} + +static void +free_received_conf_items(void) +{ + while (n_received_conf_items > 0) { + --n_received_conf_items; + free(received_conf_items[n_received_conf_items].descr); + } +} + +TEST_SUITE(conf) + +TEST(conf_create) +{ + struct conf *conf = conf_create(); + CHECK_STR_EQ("", conf->base_dir); + CHECK_STR_EQ_FREE1(format("%s/.ccache", get_home_directory()), + conf->cache_dir); + CHECK_INT_EQ(2, conf->cache_dir_levels); + CHECK_STR_EQ("", conf->compiler); + CHECK_STR_EQ("mtime", conf->compiler_check); + CHECK(!conf->compression); + CHECK_INT_EQ(6, conf->compression_level); + CHECK_STR_EQ("", conf->cpp_extension); + CHECK(conf->direct_mode); + CHECK(!conf->disable); + CHECK_STR_EQ("", conf->extra_files_to_hash); + CHECK(!conf->hard_link); + CHECK(!conf->hash_dir); + CHECK_STR_EQ("", conf->log_file); + CHECK_INT_EQ(0, conf->max_files); + CHECK_INT_EQ((uint64_t)5 * 1000 * 1000 * 1000, conf->max_size); + CHECK_STR_EQ("", conf->path); + CHECK_STR_EQ("", conf->prefix_command); + CHECK(!conf->read_only); + CHECK(!conf->recache); + CHECK(!conf->run_second_cpp); + CHECK_INT_EQ(0, conf->sloppiness); + CHECK(conf->stats); + CHECK_STR_EQ("", conf->temporary_dir); + CHECK_INT_EQ(UINT_MAX, conf->umask); + CHECK(!conf->unify); + conf_free(conf); +} + +TEST(conf_read_valid_config) +{ + struct conf *conf = conf_create(); + char *errmsg, *user; + putenv("USER=rabbit"); + user = getenv("USER"); + CHECK_STR_EQ("rabbit", user); + create_file( + "ccache.conf", + "base_dir = /$USER/foo/${USER} \n" + "cache_dir=\n" + "cache_dir = $USER$/${USER}/.ccache\n" + "\n" + "\n" + " #A comment\n" + " cache_dir_levels = 4\n" + "\t compiler = foo\n" + "compiler_check = none\n" + "compression=true\n" + "compression_level= 2\n" + "cpp_extension = .foo\n" + "direct_mode = false\n" + "disable = true\n" + "extra_files_to_hash = a:b c:$USER\n" + "hard_link = true\n" + "hash_dir = true\n" + "log_file = $USER${USER} \n" + "max_files = 17\n" + "max_size = 123M\n" + "path = $USER.x\n" + "prefix_command = x$USER\n" + "read_only = true\n" + "recache = true\n" + "run_second_cpp = true\n" - "sloppiness = file_macro ,time_macros, include_file_mtime,include_file_ctime,file_stat_matches \n" ++ "sloppiness = file_macro ,time_macros, include_file_mtime,include_file_ctime,file_stat_matches pch_defines \n" + "stats = false\n" + "temporary_dir = ${USER}_foo\n" + "umask = 777\n" + "unify = true"); /* Note: no newline */ + CHECK(conf_read(conf, "ccache.conf", &errmsg)); + CHECK(!errmsg); + + CHECK_STR_EQ_FREE1(format("/%s/foo/%s", user, user), conf->base_dir); + CHECK_STR_EQ_FREE1(format("%s$/%s/.ccache", user, user), conf->cache_dir); + CHECK_INT_EQ(4, conf->cache_dir_levels); + CHECK_STR_EQ("foo", conf->compiler); + CHECK_STR_EQ("none", conf->compiler_check); + CHECK(conf->compression); + CHECK_INT_EQ(2, conf->compression_level); + CHECK_STR_EQ(".foo", conf->cpp_extension); + CHECK(!conf->direct_mode); + CHECK(conf->disable); + CHECK_STR_EQ_FREE1(format("a:b c:%s", user), conf->extra_files_to_hash); + CHECK(conf->hard_link); + CHECK(conf->hash_dir); + CHECK_STR_EQ_FREE1(format("%s%s", user, user), conf->log_file); + CHECK_INT_EQ(17, conf->max_files); + CHECK_INT_EQ(123 * 1000 * 1000, conf->max_size); + CHECK_STR_EQ_FREE1(format("%s.x", user), conf->path); + CHECK_STR_EQ_FREE1(format("x%s", user), conf->prefix_command); + CHECK(conf->read_only); + CHECK(conf->recache); + CHECK(conf->run_second_cpp); + CHECK_INT_EQ(SLOPPY_INCLUDE_FILE_MTIME|SLOPPY_INCLUDE_FILE_CTIME| + SLOPPY_FILE_MACRO|SLOPPY_TIME_MACROS| - SLOPPY_FILE_STAT_MATCHES, ++ SLOPPY_FILE_STAT_MATCHES|SLOPPY_PCH_DEFINES, + conf->sloppiness); + CHECK(!conf->stats); + CHECK_STR_EQ_FREE1(format("%s_foo", user), conf->temporary_dir); + CHECK_INT_EQ(0777, conf->umask); + CHECK(conf->unify); + + conf_free(conf); +} + +TEST(conf_read_with_missing_equal_sign) +{ + struct conf *conf = conf_create(); + char *errmsg; + create_file("ccache.conf", "no equal sign"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: missing equal sign", + errmsg); + conf_free(conf); +} + +TEST(conf_read_with_bad_config_key) +{ + struct conf *conf = conf_create(); + char *errmsg; + create_file("ccache.conf", "# Comment\nfoo = bar"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:2: unknown configuration option \"foo\"", + errmsg); + conf_free(conf); +} + +TEST(conf_read_invalid_bool) +{ + struct conf *conf = conf_create(); + char *errmsg; + + create_file("ccache.conf", "disable="); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: not a boolean value: \"\"", + errmsg); + + create_file("ccache.conf", "disable=foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: not a boolean value: \"foo\"", + errmsg); + conf_free(conf); +} + +TEST(conf_read_invalid_env_string) +{ + struct conf *conf = conf_create(); + char *errmsg; + create_file("ccache.conf", "base_dir = ${foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: syntax error: missing '}' after \"foo\"", + errmsg); + /* Other cases tested in test_util.c. */ + conf_free(conf); +} + +TEST(conf_read_empty_umask) +{ + struct conf *conf = conf_create(); + char *errmsg; + create_file("ccache.conf", "umask = "); + CHECK(conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(conf->umask, UINT_MAX); + conf_free(conf); +} + +TEST(conf_read_invalid_size) +{ + struct conf *conf = conf_create(); + char *errmsg; + create_file("ccache.conf", "max_size = foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: invalid size: \"foo\"", + errmsg); + /* Other cases tested in test_util.c. */ + conf_free(conf); +} + +TEST(conf_read_invalid_sloppiness) +{ + struct conf *conf = conf_create(); + char *errmsg; + create_file("ccache.conf", "sloppiness = file_macro, foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: unknown sloppiness: \"foo\"", + errmsg); + conf_free(conf); +} + +TEST(conf_read_invalid_unsigned) +{ + struct conf *conf = conf_create(); + char *errmsg; + + create_file("ccache.conf", "max_files ="); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"\"", + errmsg); + + create_file("ccache.conf", "max_files = -42"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"-42\"", + errmsg); + + create_file("ccache.conf", "max_files = foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: invalid unsigned integer: \"foo\"", + errmsg); + + conf_free(conf); +} + +TEST(verify_absolute_base_dir) +{ + struct conf *conf = conf_create(); + char *errmsg; + + create_file("ccache.conf", "base_dir = relative/path"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: not an absolute path: \"relative/path\"", + errmsg); + + create_file("ccache.conf", "base_dir ="); + CHECK(conf_read(conf, "ccache.conf", &errmsg)); + + conf_free(conf); +} + +TEST(verify_dir_levels) +{ + struct conf *conf = conf_create(); + char *errmsg; + + create_file("ccache.conf", "cache_dir_levels = 0"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: cache directory levels must be between 1 and 8", + errmsg); + create_file("ccache.conf", "cache_dir_levels = 9"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_STR_EQ_FREE2("ccache.conf:1: cache directory levels must be between 1 and 8", + errmsg); + + conf_free(conf); +} + +TEST(conf_update_from_environment) +{ + struct conf *conf = conf_create(); + char *errmsg; + + putenv("CCACHE_COMPRESS=1"); + CHECK(conf_update_from_environment(conf, &errmsg)); + CHECK(conf->compression); + + x_unsetenv("CCACHE_COMPRESS"); + putenv("CCACHE_NOCOMPRESS=1"); + CHECK(conf_update_from_environment(conf, &errmsg)); + CHECK(!conf->compression); + + conf_free(conf); +} + +TEST(conf_set_new_value) +{ + char *errmsg; + char *data; + + create_file("ccache.conf", "path = vanilla\n"); + CHECK(conf_set_value_in_file("ccache.conf", "stats", "chocolate", &errmsg)); + data = read_text_file("ccache.conf", 0); + CHECK(data); + CHECK_STR_EQ_FREE2("path = vanilla\nstats = chocolate\n", data); +} + +TEST(conf_set_existing_value) +{ + char *errmsg; + char *data; + + create_file("ccache.conf", "path = chocolate\nstats = chocolate\n"); + CHECK(conf_set_value_in_file("ccache.conf", "path", "vanilla", &errmsg)); + data = read_text_file("ccache.conf", 0); + CHECK(data); + CHECK_STR_EQ_FREE2("path = vanilla\nstats = chocolate\n", data); +} + +TEST(conf_print_items) +{ + size_t i; + struct conf conf = { + "bd", + "cd", + 7, + "c", + "cc", + true, + 8, + "ce", + false, + true, + "efth", + true, + true, + "lf", + 4711, + 98.7 * 1000 * 1000, + "p", + "pc", + true, + true, + true, + SLOPPY_FILE_MACRO|SLOPPY_INCLUDE_FILE_MTIME| + SLOPPY_INCLUDE_FILE_CTIME|SLOPPY_TIME_MACROS| + SLOPPY_FILE_STAT_MATCHES, + false, + "td", + 022, + true, + NULL + }; + size_t n = 0; + + conf.item_origins = x_malloc(N_CONFIG_ITEMS * sizeof(char *)); + for (i = 0; i < N_CONFIG_ITEMS; ++i) { + conf.item_origins[i] = format("origin%zu", i); + } + + conf_print_items(&conf, conf_item_receiver, NULL); + CHECK_INT_EQ(N_CONFIG_ITEMS, n_received_conf_items); + CHECK_STR_EQ("base_dir = bd", received_conf_items[n++].descr); + CHECK_STR_EQ("cache_dir = cd", received_conf_items[n++].descr); + CHECK_STR_EQ("cache_dir_levels = 7", received_conf_items[n++].descr); + CHECK_STR_EQ("compiler = c", received_conf_items[n++].descr); + CHECK_STR_EQ("compiler_check = cc", received_conf_items[n++].descr); + CHECK_STR_EQ("compression = true", received_conf_items[n++].descr); + CHECK_STR_EQ("compression_level = 8", received_conf_items[n++].descr); + CHECK_STR_EQ("cpp_extension = ce", received_conf_items[n++].descr); + CHECK_STR_EQ("direct_mode = false", received_conf_items[n++].descr); + CHECK_STR_EQ("disable = true", received_conf_items[n++].descr); + CHECK_STR_EQ("extra_files_to_hash = efth", received_conf_items[n++].descr); + CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr); + CHECK_STR_EQ("hash_dir = true", received_conf_items[n++].descr); + CHECK_STR_EQ("log_file = lf", received_conf_items[n++].descr); + CHECK_STR_EQ("max_files = 4711", received_conf_items[n++].descr); + CHECK_STR_EQ("max_size = 98.7M", received_conf_items[n++].descr); + CHECK_STR_EQ("path = p", received_conf_items[n++].descr); + CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++].descr); + CHECK_STR_EQ("read_only = true", received_conf_items[n++].descr); + CHECK_STR_EQ("recache = true", received_conf_items[n++].descr); + CHECK_STR_EQ("run_second_cpp = true", received_conf_items[n++].descr); + CHECK_STR_EQ("sloppiness = file_macro, include_file_mtime," + " include_file_ctime, time_macros, file_stat_matches", + received_conf_items[n++].descr); + CHECK_STR_EQ("stats = false", received_conf_items[n++].descr); + CHECK_STR_EQ("temporary_dir = td", received_conf_items[n++].descr); + CHECK_STR_EQ("umask = 022", received_conf_items[n++].descr); + CHECK_STR_EQ("unify = true", received_conf_items[n++].descr); + + for (i = 0; i < N_CONFIG_ITEMS; ++i) { + char *expected = format("origin%zu", i); + CHECK_STR_EQ(expected, received_conf_items[i].origin); + } + + free_received_conf_items(); + free(conf.item_origins); +} + +TEST_SUITE_END