diff options
author | Joel Rosdahl <joel@rosdahl.net> | 2019-08-02 00:04:30 +0200 |
---|---|---|
committer | Joel Rosdahl <joel@rosdahl.net> | 2019-08-14 21:42:33 +0200 |
commit | f5795cdbc0703d80ab21f39c49bb2384ea2429ba (patch) | |
tree | c1a18e6743bd8875284cb45b36c2db6399c6371b | |
parent | 80ff3c27a787973dc6284b1d6f85a236f573a866 (diff) | |
download | ccache-f5795cdbc0703d80ab21f39c49bb2384ea2429ba.tar.gz |
Run clang-format on all code to follow the new code style
If you end up on this commit when running “git blame ...”, it’s probably
a good idea to use “git blame -w ...” to ignore whitespace changes.
60 files changed, 11791 insertions, 11794 deletions
diff --git a/.editorconfig b/.editorconfig index da20c97f..8f4a31e1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,7 +6,7 @@ insert_final_newline = true charset = utf-8 [*.{c,cpp,h,hpp}] -indent_style = tab +indent_style = space indent_size = 2 [*.{bash,py}] diff --git a/src/args.cpp b/src/args.cpp index 9ff7d5c3..deb4309d 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -19,116 +19,116 @@ #include "ccache.hpp" -struct args * -args_init(int init_argc, const char * const*init_args) +struct args* +args_init(int init_argc, const char* const* init_args) { - struct args *args = (struct args *)x_malloc(sizeof(struct args)); - args->argc = 0; - args->argv = (char **)x_malloc(sizeof(char *)); - args->argv[0] = NULL; - for (int i = 0; i < init_argc; i++) { - args_add(args, init_args[i]); - } - return args; + struct args* args = (struct args*)x_malloc(sizeof(struct args)); + args->argc = 0; + args->argv = (char**)x_malloc(sizeof(char*)); + args->argv[0] = NULL; + for (int i = 0; i < init_argc; i++) { + args_add(args, init_args[i]); + } + return args; } -struct args * -args_init_from_string(const char *command) +struct args* +args_init_from_string(const char* command) { - char *p = x_strdup(command); - char *q = p; - char *word, *saveptr = NULL; - struct args *args = args_init(0, NULL); - while ((word = strtok_r(q, " \t\r\n", &saveptr))) { - args_add(args, word); - q = NULL; - } - - free(p); - return args; + char* p = x_strdup(command); + char* q = p; + char *word, *saveptr = NULL; + struct args* args = args_init(0, NULL); + while ((word = strtok_r(q, " \t\r\n", &saveptr))) { + args_add(args, word); + q = NULL; + } + + free(p); + return args; } -struct args * -args_init_from_gcc_atfile(const char *filename) +struct args* +args_init_from_gcc_atfile(const char* filename) { - char *argtext; - if (!(argtext = read_text_file(filename, 0))) { - return NULL; - } - - struct args *args = args_init(0, NULL); - char *pos = argtext; - char* argbuf = static_cast<char*>(x_malloc(strlen(argtext) + 1)); - char *argpos = argbuf; - - // Used to track quoting state; if \0, we are not inside quotes. Otherwise - // stores the quoting character that started it, for matching the end quote. - char quoting = '\0'; - - while (1) { - switch (*pos) { - case '\\': - pos++; - if (*pos == '\0') { - continue; - } - break; - - case '\"': - case '\'': - if (quoting != '\0') { - if (quoting == *pos) { - quoting = '\0'; - pos++; - continue; - } else { - break; - } - } else { - quoting = *pos; - pos++; - continue; - } - - case '\n': - case '\r': - case '\t': - case ' ': - if (quoting) { - break; - } - // Fall through. - - case '\0': - // End of token - *argpos = '\0'; - if (argbuf[0] != '\0') { - args_add(args, argbuf); - } - argpos = argbuf; - if (*pos == '\0') { - goto out; - } else { - pos++; - continue; - } - } - - *argpos = *pos; - pos++; - argpos++; - } + char* argtext; + if (!(argtext = read_text_file(filename, 0))) { + return NULL; + } + + struct args* args = args_init(0, NULL); + char* pos = argtext; + char* argbuf = static_cast<char*>(x_malloc(strlen(argtext) + 1)); + char* argpos = argbuf; + + // Used to track quoting state; if \0, we are not inside quotes. Otherwise + // stores the quoting character that started it, for matching the end quote. + char quoting = '\0'; + + while (1) { + switch (*pos) { + case '\\': + pos++; + if (*pos == '\0') { + continue; + } + break; + + case '\"': + case '\'': + if (quoting != '\0') { + if (quoting == *pos) { + quoting = '\0'; + pos++; + continue; + } else { + break; + } + } else { + quoting = *pos; + pos++; + continue; + } + + case '\n': + case '\r': + case '\t': + case ' ': + if (quoting) { + break; + } + // Fall through. + + case '\0': + // End of token + *argpos = '\0'; + if (argbuf[0] != '\0') { + args_add(args, argbuf); + } + argpos = argbuf; + if (*pos == '\0') { + goto out; + } else { + pos++; + continue; + } + } + + *argpos = *pos; + pos++; + argpos++; + } out: - free(argbuf); - free(argtext); - return args; + free(argbuf); + free(argtext); + return args; } -struct args * -args_copy(struct args *args) +struct args* +args_copy(struct args* args) { - return args_init(args->argc, args->argv); + return args_init(args->argc, args->argv); } // Insert all arguments in src into dest at position index. If replace is true, @@ -138,178 +138,175 @@ args_copy(struct args *args) // src is consumed by this operation and should not be freed or used again by // the caller. void -args_insert(struct args *dest, int index, struct args *src, bool replace) +args_insert(struct args* dest, int index, struct args* src, bool replace) { - // Adjustments made if we are replacing or shifting the element currently at - // dest->argv[index]. - int offset = replace ? 1 : 0; - - if (replace) { - free(dest->argv[index]); - } - - if (src->argc == 0) { - if (replace) { - // Have to shift everything down by 1 since we replaced with an empty - // list. - for (int i = index; i < dest->argc; i++) { - dest->argv[i] = dest->argv[i + 1]; - } - dest->argc--; - } - args_free(src); - return; - } - - if (src->argc == 1 && replace) { - // Trivial case; replace with 1 element. - dest->argv[index] = src->argv[0]; - src->argc = 0; - args_free(src); - return; - } - - dest->argv = (char **)x_realloc( - dest->argv, - (src->argc + dest->argc + 1 - offset) * sizeof(char *)); - - // Shift arguments over. - for (int i = dest->argc; i >= index + offset; i--) { - dest->argv[i + src->argc - offset] = dest->argv[i]; - } - - // Copy the new arguments into place. - for (int i = 0; i < src->argc; i++) { - dest->argv[i + index] = src->argv[i]; - } - - dest->argc += src->argc - offset; - src->argc = 0; - args_free(src); + // Adjustments made if we are replacing or shifting the element currently at + // dest->argv[index]. + int offset = replace ? 1 : 0; + + if (replace) { + free(dest->argv[index]); + } + + if (src->argc == 0) { + if (replace) { + // Have to shift everything down by 1 since we replaced with an empty + // list. + for (int i = index; i < dest->argc; i++) { + dest->argv[i] = dest->argv[i + 1]; + } + dest->argc--; + } + args_free(src); + return; + } + + if (src->argc == 1 && replace) { + // Trivial case; replace with 1 element. + dest->argv[index] = src->argv[0]; + src->argc = 0; + args_free(src); + return; + } + + dest->argv = (char**)x_realloc( + dest->argv, (src->argc + dest->argc + 1 - offset) * sizeof(char*)); + + // Shift arguments over. + for (int i = dest->argc; i >= index + offset; i--) { + dest->argv[i + src->argc - offset] = dest->argv[i]; + } + + // Copy the new arguments into place. + for (int i = 0; i < src->argc; i++) { + dest->argv[i + index] = src->argv[i]; + } + + dest->argc += src->argc - offset; + src->argc = 0; + args_free(src); } void -args_free(struct args *args) +args_free(struct args* args) { - if (!args) { - return; - } - for (int i = 0; i < args->argc; ++i) { - if (args->argv[i]) { - free(args->argv[i]); - } - } - free(args->argv); - free(args); + if (!args) { + return; + } + for (int i = 0; i < args->argc; ++i) { + if (args->argv[i]) { + free(args->argv[i]); + } + } + free(args->argv); + free(args); } void -args_add(struct args *args, const char *s) +args_add(struct args* args, const char* s) { - args->argv = (char **)x_realloc(args->argv, - (args->argc + 2) * sizeof(char *)); - args->argv[args->argc] = x_strdup(s); - args->argc++; - args->argv[args->argc] = NULL; + args->argv = (char**)x_realloc(args->argv, (args->argc + 2) * sizeof(char*)); + args->argv[args->argc] = x_strdup(s); + args->argc++; + args->argv[args->argc] = NULL; } // Add all arguments in to_append to args. void -args_extend(struct args *args, struct args *to_append) +args_extend(struct args* args, struct args* to_append) { - for (int i = 0; i < to_append->argc; i++) { - args_add(args, to_append->argv[i]); - } + for (int i = 0; i < to_append->argc; i++) { + args_add(args, to_append->argv[i]); + } } // Pop the last element off the args list. void -args_pop(struct args *args, int n) +args_pop(struct args* args, int n) { - while (n--) { - args->argc--; - free(args->argv[args->argc]); - args->argv[args->argc] = NULL; - } + while (n--) { + args->argc--; + free(args->argv[args->argc]); + args->argv[args->argc] = NULL; + } } // Set argument at given index. void -args_set(struct args *args, int index, const char *value) +args_set(struct args* args, int index, const char* value) { - assert(index < args->argc); - free(args->argv[index]); - args->argv[index] = x_strdup(value); + assert(index < args->argc); + free(args->argv[index]); + args->argv[index] = x_strdup(value); } // Remove the first element of the argument list. void -args_remove_first(struct args *args) +args_remove_first(struct args* args) { - free(args->argv[0]); - memmove(&args->argv[0], &args->argv[1], args->argc * sizeof(args->argv[0])); - args->argc--; + free(args->argv[0]); + memmove(&args->argv[0], &args->argv[1], args->argc * sizeof(args->argv[0])); + args->argc--; } // Add an argument into the front of the argument list. void -args_add_prefix(struct args *args, const char *s) +args_add_prefix(struct args* args, const char* s) { - args->argv = (char **)x_realloc(args->argv, - (args->argc + 2) * sizeof(char *)); - memmove(&args->argv[1], &args->argv[0], - (args->argc+1) * sizeof(args->argv[0])); - args->argv[0] = x_strdup(s); - args->argc++; + args->argv = (char**)x_realloc(args->argv, (args->argc + 2) * sizeof(char*)); + memmove( + &args->argv[1], &args->argv[0], (args->argc + 1) * sizeof(args->argv[0])); + args->argv[0] = x_strdup(s); + args->argc++; } // Strip any arguments beginning with the specified prefix. void -args_strip(struct args *args, const char *prefix) +args_strip(struct args* args, const char* prefix) { - for (int i = 0; i < args->argc;) { - if (str_startswith(args->argv[i], prefix)) { - free(args->argv[i]); - memmove(&args->argv[i], - &args->argv[i+1], - (args->argc - i) * sizeof(args->argv[i])); - args->argc--; - } else { - i++; - } - } + for (int i = 0; i < args->argc;) { + if (str_startswith(args->argv[i], prefix)) { + free(args->argv[i]); + memmove(&args->argv[i], + &args->argv[i + 1], + (args->argc - i) * sizeof(args->argv[i])); + args->argc--; + } else { + i++; + } + } } // Format args to a space-separated string. Does not quote spaces. Caller // frees. -char * -args_to_string(const struct args *args) +char* +args_to_string(const struct args* args) { - unsigned size = 0; - for (char **p = args->argv; *p; p++) { - size += strlen(*p) + 1; - } - - char* result = static_cast<char*>(x_malloc(size + 1)); - int pos = 0; - for (char **p = args->argv; *p; p++) { - pos += sprintf(&result[pos], "%s ", *p); - } - result[pos - 1] = '\0'; - return result; + unsigned size = 0; + for (char** p = args->argv; *p; p++) { + size += strlen(*p) + 1; + } + + char* result = static_cast<char*>(x_malloc(size + 1)); + int pos = 0; + for (char** p = args->argv; *p; p++) { + pos += sprintf(&result[pos], "%s ", *p); + } + result[pos - 1] = '\0'; + return result; } // Returns true if args1 equals args2, else false. bool -args_equal(const struct args *args1, const struct args *args2) +args_equal(const struct args* args1, const struct args* args2) { - if (args1->argc != args2->argc) { - return false; - } - for (int i = 0; i < args1->argc; i++) { - if (!str_eq(args1->argv[i], args2->argv[i])) { - return false; - } - } - return true; + if (args1->argc != args2->argc) { + return false; + } + for (int i = 0; i < args1->argc; i++) { + if (!str_eq(args1->argv[i], args2->argv[i])) { + return false; + } + } + return true; } diff --git a/src/ccache.cpp b/src/ccache.cpp index 32e0477c..74207601 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -18,137 +18,154 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "ccache.hpp" + #include "compopt.hpp" #ifdef HAVE_GETOPT_LONG -#include <getopt.h> +# include <getopt.h> #else -#include "third_party/getopt_long.h" +# include "third_party/getopt_long.h" #endif #include "hash.hpp" -#include "third_party/hashtable.h" -#include "third_party/hashtable_itr.h" #include "hashutil.hpp" #include "language.hpp" #include "manifest.hpp" #include "result.hpp" #include "unify.hpp" +#include "third_party/hashtable.h" +#include "third_party/hashtable_itr.h" + #define STRINGIFY(x) #x #define TO_STRING(x) STRINGIFY(x) // Global variables used by other compilation units. -extern struct conf *conf; -extern char *primary_config_path; -extern char *secondary_config_path; -extern char *current_working_dir; -extern char *stats_file; +extern struct conf* conf; +extern char* primary_config_path; +extern char* secondary_config_path; +extern char* current_working_dir; +extern char* stats_file; extern unsigned lock_staleness_limit; -static const char VERSION_TEXT[] = - MYNAME " version %s\n" - "\n" - "Copyright (C) 2002-2007 Andrew Tridgell\n" - "Copyright (C) 2009-2019 Joel Rosdahl and other contributors\n" - "\n" - "See <https://ccache.dev/credits.html> for a complete list of contributors.\n" - "\n" - "This program is free software; you can redistribute it and/or modify it under\n" - "the terms of the GNU General Public License as published by the Free Software\n" - "Foundation; either version 3 of the License, or (at your option) any later\n" - "version.\n"; +static const char VERSION_TEXT[] = MYNAME + " version %s\n" + "\n" + "Copyright (C) 2002-2007 Andrew Tridgell\n" + "Copyright (C) 2009-2019 Joel Rosdahl and other contributors\n" + "\n" + "See <https://ccache.dev/credits.html> for a complete list of " + "contributors.\n" + "\n" + "This program is free software; you can redistribute it and/or modify it " + "under\n" + "the terms of the GNU General Public License as published by the Free " + "Software\n" + "Foundation; either version 3 of the License, or (at your option) any " + "later\n" + "version.\n"; static const char USAGE_TEXT[] = - "Usage:\n" - " " MYNAME " [options]\n" - " " MYNAME " compiler [compiler options]\n" - " compiler [compiler options] (via symbolic link)\n" - "\n" - "Common options:\n" - " -c, --cleanup delete old files and recalculate size counters\n" - " (normally not needed as this is done\n" - " automatically)\n" - " -C, --clear clear the cache completely (except configuration)\n" - " -F, --max-files=N set maximum number of files in cache to N (use 0\n" - " for no limit)\n" - " -M, --max-size=SIZE set maximum size of cache to SIZE (use 0 for no\n" - " limit); available suffixes: k, M, G, T (decimal)\n" - " and Ki, Mi, Gi, Ti (binary); default suffix: G\n" - " -x, --show-compression show compression statistics\n" - " -p, --show-config show current configuration options in\n" - " human-readable format\n" - " -s, --show-stats show summary of configuration and statistics\n" - " counters in human-readable format\n" - " -z, --zero-stats zero statistics counters\n" - "\n" - " -h, --help print this help text\n" - " -V, --version print version and copyright information\n" - "\n" - "Options for scripting or debugging:\n" - " --dump-manifest=PATH dump manifest file at PATH in text format\n" - " -k, --get-config=K print the value of configuration key K\n" - " --hash-file=PATH print the hash (160 bit BLAKE2b) of the file at\n" - " PATH\n" - " --print-stats print statistics counter IDs and corresponding\n" - " values in machine-parsable format\n" - " -o, --set-config=K=V set configuration item K to value V\n" - "\n" - "See also <https://ccache.dev>.\n"; + "Usage:\n" + " " MYNAME + " [options]\n" + " " MYNAME + " compiler [compiler options]\n" + " compiler [compiler options] (via symbolic link)\n" + "\n" + "Common options:\n" + " -c, --cleanup delete old files and recalculate size " + "counters\n" + " (normally not needed as this is done\n" + " automatically)\n" + " -C, --clear clear the cache completely (except " + "configuration)\n" + " -F, --max-files=N set maximum number of files in cache to N " + "(use 0\n" + " for no limit)\n" + " -M, --max-size=SIZE set maximum size of cache to SIZE (use 0 " + "for no\n" + " limit); available suffixes: k, M, G, T " + "(decimal)\n" + " and Ki, Mi, Gi, Ti (binary); default " + "suffix: G\n" + " -x, --show-compression show compression statistics\n" + " -p, --show-config show current configuration options in\n" + " human-readable format\n" + " -s, --show-stats show summary of configuration and " + "statistics\n" + " counters in human-readable format\n" + " -z, --zero-stats zero statistics counters\n" + "\n" + " -h, --help print this help text\n" + " -V, --version print version and copyright information\n" + "\n" + "Options for scripting or debugging:\n" + " --dump-manifest=PATH dump manifest file at PATH in text format\n" + " -k, --get-config=K print the value of configuration key K\n" + " --hash-file=PATH print the hash (160 bit BLAKE2b) of the " + "file at\n" + " PATH\n" + " --print-stats print statistics counter IDs and " + "corresponding\n" + " values in machine-parsable format\n" + " -o, --set-config=K=V set configuration item K to value V\n" + "\n" + "See also <https://ccache.dev>.\n"; // Global configuration data. -struct conf *conf = NULL; +struct conf* conf = NULL; // Where to write configuration changes. -char *primary_config_path = NULL; +char* primary_config_path = NULL; // Secondary, read-only configuration file (if any). -char *secondary_config_path = NULL; +char* secondary_config_path = NULL; // Current working directory taken from $PWD, or getcwd() if $PWD is bad. -char *current_working_dir = NULL; +char* current_working_dir = NULL; // The original argument list. -static struct args *orig_args; +static struct args* orig_args; // The source file. -static char *input_file; +static char* input_file; // The output file being compiled to. -static char *output_obj; +static char* output_obj; // The path to the dependency file (implicit or specified with -MF). -static char *output_dep; +static char* output_dep; // The path to the coverage file (implicit when using -ftest-coverage). -static char *output_cov; +static char* output_cov; // The path to the stack usage (implicit when using -fstack-usage). -static char *output_su; +static char* output_su; // Diagnostic generation information (clang). Contains pathname if not NULL. -static char *output_dia; +static char* output_dia; // Split dwarf information (GCC 4.8 and up). Contains pathname if not NULL. -static char *output_dwo; +static char* output_dwo; // Language to use for the compilation target (see language.c). -static const char *actual_language; +static const char* actual_language; // Array for storing -arch options. #define MAX_ARCH_ARGS 10 static size_t arch_args_size = 0; -static char *arch_args[MAX_ARCH_ARGS] = {NULL}; +static char* arch_args[MAX_ARCH_ARGS] = {NULL}; // Name (represented as a struct digest) of the file containing the cached // result. -static struct digest *cached_result_name; +static struct digest* cached_result_name; // Full path to the file containing the result // (cachedir/a/b/cdef[...]-size.result). -static char *cached_result_path; +static char* cached_result_path; // Full path to the file containing the manifest // (cachedir/a/b/cdef[...]-size.manifest). -static char *manifest_path; +static char* manifest_path; // Time of compilation. Used to see if include files have changed after // compilation. @@ -156,13 +173,13 @@ time_t time_of_compilation; // Files included by the preprocessor and their hashes. Key: file path. Value: // struct digest. -static struct hashtable *included_files = NULL; +static struct hashtable* included_files = NULL; // Uses absolute path for some include files. static bool has_absolute_include_headers = false; // List of headers to ignore. -static char **ignore_headers; +static char** ignore_headers; // Size of headers to ignore list. static size_t ignore_headers_len; @@ -190,7 +207,7 @@ static bool generating_diagnostics; static bool seen_split_dwarf; // Relocating debuginfo in the format old=new. -static char **debug_prefix_maps = NULL; +static char** debug_prefix_maps = NULL; // Size of debug_prefix_maps list. static size_t debug_prefix_maps_len = 0; @@ -199,23 +216,23 @@ static size_t debug_prefix_maps_len = 0; static bool profile_arcs; // Name of the custom profile directory (default: object dirname). -static char *profile_dir; +static char* profile_dir; // The name of the temporary preprocessed file. -static char *i_tmpfile; +static char* i_tmpfile; // Are we compiling a .i or .ii file directly? static bool direct_i_file; // The name of the cpp stderr file. -static char *cpp_stderr; +static char* cpp_stderr; // Full path to the statistics file in the subdirectory where the cached result // belongs (<cache_dir>/<x>/stats). -char *stats_file = NULL; +char* stats_file = NULL; // The stats file to use for the manifest. -static char *manifest_stats_file; +static char* manifest_stats_file; // Whether the output is a precompiled header. bool output_is_precompiled_header = false; @@ -229,7 +246,7 @@ static bool profile_use = false; static bool profile_generate = false; // Sanitize blacklist -static char **sanitize_blacklists = NULL; +static char** sanitize_blacklists = NULL; // Size of sanitize_blacklists static size_t sanitize_blacklists_len = 0; @@ -239,23 +256,21 @@ static size_t sanitize_blacklists_len = 0; static bool using_precompiled_header = false; // The .gch/.pch/.pth file used for compilation. -static char *included_pch_file = NULL; +static char* included_pch_file = NULL; // How long (in microseconds) to wait before breaking a stale lock. unsigned lock_staleness_limit = 2000000; -enum fromcache_call_mode { - FROMCACHE_DIRECT_MODE, - FROMCACHE_CPP_MODE -}; +enum fromcache_call_mode { FROMCACHE_DIRECT_MODE, FROMCACHE_CPP_MODE }; -struct pending_tmp_file { - char *path; - struct pending_tmp_file *next; +struct pending_tmp_file +{ + char* path; + struct pending_tmp_file* next; }; // Temporary files to remove at program exit. -static struct pending_tmp_file *pending_tmp_files = NULL; +static struct pending_tmp_file* pending_tmp_files = NULL; #ifndef _WIN32 static sigset_t fatal_signal_set; @@ -273,75 +288,73 @@ static pid_t compiler_pid = 0; static const char HASH_PREFIX[] = "3"; static void -add_prefix(struct args *args, char *prefix_command) +add_prefix(struct args* args, char* prefix_command) { - if (str_eq(prefix_command, "")) { - return; - } - - struct args *prefix = args_init(0, NULL); - char *e = x_strdup(prefix_command); - char *saveptr = NULL; - for (char *tok = strtok_r(e, " ", &saveptr); - tok; - tok = strtok_r(NULL, " ", &saveptr)) { - char *p; - - p = find_executable(tok, MYNAME); - if (!p) { - fatal("%s: %s", tok, strerror(errno)); - } - - args_add(prefix, p); - free(p); - } - free(e); - - cc_log("Using command-line prefix %s", prefix_command); - for (int i = prefix->argc; i != 0; i--) { - args_add_prefix(args, prefix->argv[i-1]); - } - args_free(prefix); + if (str_eq(prefix_command, "")) { + return; + } + + struct args* prefix = args_init(0, NULL); + char* e = x_strdup(prefix_command); + char* saveptr = NULL; + for (char* tok = strtok_r(e, " ", &saveptr); tok; + tok = strtok_r(NULL, " ", &saveptr)) { + char* p; + + p = find_executable(tok, MYNAME); + if (!p) { + fatal("%s: %s", tok, strerror(errno)); + } + + args_add(prefix, p); + free(p); + } + free(e); + + cc_log("Using command-line prefix %s", prefix_command); + for (int i = prefix->argc; i != 0; i--) { + args_add_prefix(args, prefix->argv[i - 1]); + } + args_free(prefix); } - static void failed(void) ATTR_NORETURN; // Something went badly wrong - just execute the real compiler. static void failed(void) { - assert(orig_args); + assert(orig_args); - args_strip(orig_args, "--ccache-"); - add_prefix(orig_args, conf->prefix_command); + args_strip(orig_args, "--ccache-"); + add_prefix(orig_args, conf->prefix_command); - cc_log("Failed; falling back to running the real compiler"); - cc_log_argv("Executing ", orig_args->argv); - exitfn_call(); - execv(orig_args->argv[0], orig_args->argv); - fatal("execv of %s failed: %s", orig_args->argv[0], strerror(errno)); + cc_log("Failed; falling back to running the real compiler"); + cc_log_argv("Executing ", orig_args->argv); + exitfn_call(); + execv(orig_args->argv[0], orig_args->argv); + fatal("execv of %s failed: %s", orig_args->argv[0], strerror(errno)); } -static const char * +static const char* temp_dir() { - static char *path = NULL; - if (path) { - return path; // Memoize - } - path = conf->temporary_dir; - if (str_eq(path, "")) { - path = format("%s/tmp", conf->cache_dir); - } - return path; + static char* path = NULL; + if (path) { + return path; // Memoize + } + path = conf->temporary_dir; + if (str_eq(path, "")) { + path = format("%s/tmp", conf->cache_dir); + } + return path; } void block_signals(void) { #ifndef _WIN32 - sigprocmask(SIG_BLOCK, &fatal_signal_set, NULL); + sigprocmask(SIG_BLOCK, &fatal_signal_set, NULL); #endif } @@ -349,470 +362,474 @@ void unblock_signals(void) { #ifndef _WIN32 - sigset_t empty; - sigemptyset(&empty); - sigprocmask(SIG_SETMASK, &empty, NULL); + sigset_t empty; + sigemptyset(&empty); + sigprocmask(SIG_SETMASK, &empty, NULL); #endif } static void -add_pending_tmp_file(const char *path) +add_pending_tmp_file(const char* path) { - block_signals(); - auto e = static_cast<pending_tmp_file*>(x_malloc(sizeof(pending_tmp_file))); - e->path = x_strdup(path); - e->next = pending_tmp_files; - pending_tmp_files = e; - unblock_signals(); + block_signals(); + auto e = static_cast<pending_tmp_file*>(x_malloc(sizeof(pending_tmp_file))); + e->path = x_strdup(path); + e->next = pending_tmp_files; + pending_tmp_files = e; + unblock_signals(); } static void do_clean_up_pending_tmp_files(void) { - struct pending_tmp_file *p = pending_tmp_files; - while (p) { - // Can't call tmp_unlink here since its cc_log calls aren't signal safe. - unlink(p->path); - p = p->next; - // Leak p->path and p here because clean_up_pending_tmp_files needs to be - // signal safe. - } + struct pending_tmp_file* p = pending_tmp_files; + while (p) { + // Can't call tmp_unlink here since its cc_log calls aren't signal safe. + unlink(p->path); + p = p->next; + // Leak p->path and p here because clean_up_pending_tmp_files needs to be + // signal safe. + } } static void clean_up_pending_tmp_files(void) { - block_signals(); - do_clean_up_pending_tmp_files(); - unblock_signals(); + block_signals(); + do_clean_up_pending_tmp_files(); + unblock_signals(); } #ifndef _WIN32 static void signal_handler(int signum) { - // Unregister handler for this signal so that we can send the signal to - // ourselves at the end of the handler. - signal(signum, SIG_DFL); - - // If ccache was killed explicitly, then bring the compiler subprocess (if - // any) with us as well. - if (signum == SIGTERM - && compiler_pid != 0 - && waitpid(compiler_pid, NULL, WNOHANG) == 0) { - kill(compiler_pid, signum); - } - - do_clean_up_pending_tmp_files(); - - if (compiler_pid != 0) { - // Wait for compiler subprocess to exit before we snuff it. - waitpid(compiler_pid, NULL, 0); - } - - // Resend signal to ourselves to exit properly after returning from the - // handler. - kill(getpid(), signum); + // Unregister handler for this signal so that we can send the signal to + // ourselves at the end of the handler. + signal(signum, SIG_DFL); + + // If ccache was killed explicitly, then bring the compiler subprocess (if + // any) with us as well. + if (signum == SIGTERM && compiler_pid != 0 + && waitpid(compiler_pid, NULL, WNOHANG) == 0) { + kill(compiler_pid, signum); + } + + do_clean_up_pending_tmp_files(); + + if (compiler_pid != 0) { + // Wait for compiler subprocess to exit before we snuff it. + waitpid(compiler_pid, NULL, 0); + } + + // Resend signal to ourselves to exit properly after returning from the + // handler. + kill(getpid(), signum); } static void register_signal_handler(int signum) { - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = signal_handler; - act.sa_mask = fatal_signal_set; -#ifdef SA_RESTART - act.sa_flags = SA_RESTART; -#endif - sigaction(signum, &act, NULL); + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + act.sa_mask = fatal_signal_set; +# ifdef SA_RESTART + act.sa_flags = SA_RESTART; +# endif + sigaction(signum, &act, NULL); } static void set_up_signal_handlers(void) { - sigemptyset(&fatal_signal_set); - sigaddset(&fatal_signal_set, SIGINT); - sigaddset(&fatal_signal_set, SIGTERM); -#ifdef SIGHUP - sigaddset(&fatal_signal_set, SIGHUP); -#endif -#ifdef SIGQUIT - sigaddset(&fatal_signal_set, SIGQUIT); -#endif - - register_signal_handler(SIGINT); - register_signal_handler(SIGTERM); -#ifdef SIGHUP - register_signal_handler(SIGHUP); -#endif -#ifdef SIGQUIT - register_signal_handler(SIGQUIT); -#endif + sigemptyset(&fatal_signal_set); + sigaddset(&fatal_signal_set, SIGINT); + sigaddset(&fatal_signal_set, SIGTERM); +# ifdef SIGHUP + sigaddset(&fatal_signal_set, SIGHUP); +# endif +# ifdef SIGQUIT + sigaddset(&fatal_signal_set, SIGQUIT); +# endif + + register_signal_handler(SIGINT); + register_signal_handler(SIGTERM); +# ifdef SIGHUP + register_signal_handler(SIGHUP); +# endif +# ifdef SIGQUIT + register_signal_handler(SIGQUIT); +# endif } #endif // _WIN32 static void clean_up_internal_tempdir(void) { - time_t now = time(NULL); - struct stat st; - if (x_stat(conf->cache_dir, &st) != 0 || st.st_mtime + 3600 >= now) { - // No cleanup needed. - return; - } - - update_mtime(conf->cache_dir); - - DIR *dir = opendir(temp_dir()); - if (!dir) { - return; - } - - struct dirent *entry; - while ((entry = readdir(dir))) { - if (str_eq(entry->d_name, ".") || str_eq(entry->d_name, "..")) { - continue; - } - - char *path = format("%s/%s", temp_dir(), entry->d_name); - if (x_lstat(path, &st) == 0 && st.st_mtime + 3600 < now) { - tmp_unlink(path); - } - free(path); - } - - closedir(dir); + time_t now = time(NULL); + struct stat st; + if (x_stat(conf->cache_dir, &st) != 0 || st.st_mtime + 3600 >= now) { + // No cleanup needed. + return; + } + + update_mtime(conf->cache_dir); + + DIR* dir = opendir(temp_dir()); + if (!dir) { + return; + } + + struct dirent* entry; + while ((entry = readdir(dir))) { + if (str_eq(entry->d_name, ".") || str_eq(entry->d_name, "..")) { + continue; + } + + char* path = format("%s/%s", temp_dir(), entry->d_name); + if (x_lstat(path, &st) == 0 && st.st_mtime + 3600 < now) { + tmp_unlink(path); + } + free(path); + } + + closedir(dir); } static void -fclose_exitfn(void *context) +fclose_exitfn(void* context) { - fclose((FILE *)context); + fclose((FILE*)context); } static void -dump_debug_log_buffer_exitfn(void *context) +dump_debug_log_buffer_exitfn(void* context) { - if (!conf->debug) { - return; - } + if (!conf->debug) { + return; + } - char *path = format("%s.ccache-log", (const char *)context); - cc_dump_debug_log_buffer(path); - free(path); + char* path = format("%s.ccache-log", (const char*)context); + cc_dump_debug_log_buffer(path); + free(path); } static void -init_hash_debug(struct hash *hash, const char *obj_path, char type, - const char *section_name, FILE *debug_text_file) +init_hash_debug(struct hash* hash, + const char* obj_path, + char type, + const char* section_name, + FILE* debug_text_file) { - if (!conf->debug) { - return; - } - - char *path = format("%s.ccache-input-%c", obj_path, type); - FILE *debug_binary_file = fopen(path, "wb"); - if (debug_binary_file) { - hash_enable_debug(hash, section_name, debug_binary_file, debug_text_file); - exitfn_add(fclose_exitfn, debug_binary_file); - } else { - cc_log("Failed to open %s: %s", path, strerror(errno)); - } - free(path); + if (!conf->debug) { + return; + } + + char* path = format("%s.ccache-input-%c", obj_path, type); + FILE* debug_binary_file = fopen(path, "wb"); + if (debug_binary_file) { + hash_enable_debug(hash, section_name, debug_binary_file, debug_text_file); + exitfn_add(fclose_exitfn, debug_binary_file); + } else { + cc_log("Failed to open %s: %s", path, strerror(errno)); + } + free(path); } static enum guessed_compiler -guess_compiler(const char *path) +guess_compiler(const char* path) { - char *name = x_basename(path); - enum guessed_compiler result = GUESSED_UNKNOWN; - if (strstr(name, "clang")) { - result = GUESSED_CLANG; - } else if (strstr(name, "gcc") || strstr(name, "g++")) { - result = GUESSED_GCC; - } else if (strstr(name, "nvcc")) { - result = GUESSED_NVCC; - } else if (str_eq(name, "pump") || str_eq(name, "distcc-pump")) { - result = GUESSED_PUMP; - } - free(name); - return result; + char* name = x_basename(path); + enum guessed_compiler result = GUESSED_UNKNOWN; + if (strstr(name, "clang")) { + result = GUESSED_CLANG; + } else if (strstr(name, "gcc") || strstr(name, "g++")) { + result = GUESSED_GCC; + } else if (strstr(name, "nvcc")) { + result = GUESSED_NVCC; + } else if (str_eq(name, "pump") || str_eq(name, "distcc-pump")) { + result = GUESSED_PUMP; + } + free(name); + return result; } -static char * +static char* get_current_working_dir(void) { - if (!current_working_dir) { - char *cwd = get_cwd(); - if (cwd) { - current_working_dir = x_realpath(cwd); - free(cwd); - } - if (!current_working_dir) { - cc_log("Unable to determine current working directory: %s", - strerror(errno)); - failed(); - } - } - return current_working_dir; + if (!current_working_dir) { + char* cwd = get_cwd(); + if (cwd) { + current_working_dir = x_realpath(cwd); + free(cwd); + } + if (!current_working_dir) { + cc_log("Unable to determine current working directory: %s", + strerror(errno)); + failed(); + } + } + return current_working_dir; } // This function hashes an include file and stores the path and hash in the // global included_files variable. If the include file is a PCH, cpp_hash is // also updated. Takes over ownership of path. static void -remember_include_file(char *path, struct hash *cpp_hash, bool system, - struct hash *depend_mode_hash) +remember_include_file(char* path, + struct hash* cpp_hash, + bool system, + struct hash* depend_mode_hash) { - struct hash *fhash = NULL; - bool is_pch = false; - - size_t path_len = strlen(path); - if (path_len >= 2 && (path[0] == '<' && path[path_len - 1] == '>')) { - // Typically <built-in> or <command-line>. - goto out; - } - - if (str_eq(path, input_file)) { - // Don't remember the input file. - goto out; - } - - if (system && (conf->sloppiness & SLOPPY_SYSTEM_HEADERS)) { - // Don't remember this system header. - goto out; - } - - if (hashtable_search(included_files, path)) { - // Already known include file. - goto out; - } + struct hash* fhash = NULL; + bool is_pch = false; + + size_t path_len = strlen(path); + if (path_len >= 2 && (path[0] == '<' && path[path_len - 1] == '>')) { + // Typically <built-in> or <command-line>. + goto out; + } + + if (str_eq(path, input_file)) { + // Don't remember the input file. + goto out; + } + + if (system && (conf->sloppiness & SLOPPY_SYSTEM_HEADERS)) { + // Don't remember this system header. + goto out; + } + + if (hashtable_search(included_files, path)) { + // Already known include file. + goto out; + } #ifdef _WIN32 - { - // stat fails on directories on win32. - DWORD attributes = GetFileAttributes(path); - if (attributes != INVALID_FILE_ATTRIBUTES && - attributes & FILE_ATTRIBUTE_DIRECTORY) { - goto out; - } - } + { + // stat fails on directories on win32. + DWORD attributes = GetFileAttributes(path); + if (attributes != INVALID_FILE_ATTRIBUTES + && attributes & FILE_ATTRIBUTE_DIRECTORY) { + goto out; + } + } #endif - struct stat st; - if (x_stat(path, &st) != 0) { - goto failure; - } - if (S_ISDIR(st.st_mode)) { - // Ignore directory, typically $PWD. - goto out; - } - if (!S_ISREG(st.st_mode)) { - // Device, pipe, socket or other strange creature. - cc_log("Non-regular include file %s", path); - goto failure; - } - - // Canonicalize path for comparison; clang uses ./header.h. - { - char *canonical = path; - size_t canonical_len = path_len; - if (canonical[0] == '.' && canonical[1] == '/') { - canonical += 2; - canonical_len -= 2; - } - - for (size_t i = 0; i < ignore_headers_len; i++) { - char *ignore = ignore_headers[i]; - size_t ignore_len = strlen(ignore); - if (ignore_len > canonical_len) { - continue; - } - if (strncmp(canonical, ignore, ignore_len) == 0 - && (ignore[ignore_len-1] == DIR_DELIM_CH - || canonical[ignore_len] == DIR_DELIM_CH - || canonical[ignore_len] == '\0')) { - goto out; - } - } - } - - // The comparison using >= is intentional, due to a possible race between - // starting compilation and writing the include file. See also the notes - // under "Performance" in doc/MANUAL.adoc. - if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_MTIME) - && st.st_mtime >= time_of_compilation) { - cc_log("Include file %s too new", path); - goto failure; - } - - // The same >= logic as above applies to the change time of the file. - if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_CTIME) - && st.st_ctime >= time_of_compilation) { - cc_log("Include file %s ctime too new", path); - goto failure; - } - - // Let's hash the include file content. - fhash = hash_init(); - - is_pch = is_precompiled_header(path); - if (is_pch) { - if (!included_pch_file) { - cc_log("Detected use of precompiled header: %s", path); - } - bool using_pch_sum = false; - if (conf->pch_external_checksum) { - // hash pch.sum instead of pch when it exists - // to prevent hashing a very large .pch file every time - char *pch_sum_path = format("%s.sum", path); - if (x_stat(pch_sum_path, &st) == 0) { - char *old_path = path; - path = pch_sum_path; - pch_sum_path = old_path; - using_pch_sum = true; - cc_log("Using pch.sum file %s", path); - } - free(pch_sum_path); - } - - if (!hash_file(fhash, path)) { - goto failure; - } - hash_delimiter(cpp_hash, using_pch_sum ? "pch_sum_hash" : "pch_hash"); - char pch_digest[DIGEST_STRING_BUFFER_SIZE]; - hash_result_as_string(fhash, pch_digest); - hash_string(cpp_hash, pch_digest); - } - - if (conf->direct_mode) { - if (!is_pch) { // else: the file has already been hashed. - char *source = NULL; - size_t size; - if (st.st_size > 0) { - if (!read_file(path, st.st_size, &source, &size)) { - goto failure; - } - } else { - source = x_strdup(""); - size = 0; - } - - int result = hash_source_code_string(conf, fhash, source, size, path); - free(source); - if (result & HASH_SOURCE_CODE_ERROR - || result & HASH_SOURCE_CODE_FOUND_TIME) { - goto failure; - } - } - - auto d = static_cast<digest*>(x_malloc(sizeof(digest))); - hash_result_as_bytes(fhash, d); - hashtable_insert(included_files, path, d); - path = NULL; // Ownership transferred to included_files. - - if (depend_mode_hash) { - hash_delimiter(depend_mode_hash, "include"); - char digest[DIGEST_STRING_BUFFER_SIZE]; - digest_as_string(d, digest); - hash_string(depend_mode_hash, digest); - } - } - - goto out; + struct stat st; + if (x_stat(path, &st) != 0) { + goto failure; + } + if (S_ISDIR(st.st_mode)) { + // Ignore directory, typically $PWD. + goto out; + } + if (!S_ISREG(st.st_mode)) { + // Device, pipe, socket or other strange creature. + cc_log("Non-regular include file %s", path); + goto failure; + } + + // Canonicalize path for comparison; clang uses ./header.h. + { + char* canonical = path; + size_t canonical_len = path_len; + if (canonical[0] == '.' && canonical[1] == '/') { + canonical += 2; + canonical_len -= 2; + } + + for (size_t i = 0; i < ignore_headers_len; i++) { + char* ignore = ignore_headers[i]; + size_t ignore_len = strlen(ignore); + if (ignore_len > canonical_len) { + continue; + } + if (strncmp(canonical, ignore, ignore_len) == 0 + && (ignore[ignore_len - 1] == DIR_DELIM_CH + || canonical[ignore_len] == DIR_DELIM_CH + || canonical[ignore_len] == '\0')) { + goto out; + } + } + } + + // The comparison using >= is intentional, due to a possible race between + // starting compilation and writing the include file. See also the notes + // under "Performance" in doc/MANUAL.adoc. + if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_MTIME) + && st.st_mtime >= time_of_compilation) { + cc_log("Include file %s too new", path); + goto failure; + } + + // The same >= logic as above applies to the change time of the file. + if (!(conf->sloppiness & SLOPPY_INCLUDE_FILE_CTIME) + && st.st_ctime >= time_of_compilation) { + cc_log("Include file %s ctime too new", path); + goto failure; + } + + // Let's hash the include file content. + fhash = hash_init(); + + is_pch = is_precompiled_header(path); + if (is_pch) { + if (!included_pch_file) { + cc_log("Detected use of precompiled header: %s", path); + } + bool using_pch_sum = false; + if (conf->pch_external_checksum) { + // hash pch.sum instead of pch when it exists + // to prevent hashing a very large .pch file every time + char* pch_sum_path = format("%s.sum", path); + if (x_stat(pch_sum_path, &st) == 0) { + char* old_path = path; + path = pch_sum_path; + pch_sum_path = old_path; + using_pch_sum = true; + cc_log("Using pch.sum file %s", path); + } + free(pch_sum_path); + } + + if (!hash_file(fhash, path)) { + goto failure; + } + hash_delimiter(cpp_hash, using_pch_sum ? "pch_sum_hash" : "pch_hash"); + char pch_digest[DIGEST_STRING_BUFFER_SIZE]; + hash_result_as_string(fhash, pch_digest); + hash_string(cpp_hash, pch_digest); + } + + if (conf->direct_mode) { + if (!is_pch) { // else: the file has already been hashed. + char* source = NULL; + size_t size; + if (st.st_size > 0) { + if (!read_file(path, st.st_size, &source, &size)) { + goto failure; + } + } else { + source = x_strdup(""); + size = 0; + } + + int result = hash_source_code_string(conf, fhash, source, size, path); + free(source); + if (result & HASH_SOURCE_CODE_ERROR + || result & HASH_SOURCE_CODE_FOUND_TIME) { + goto failure; + } + } + + auto d = static_cast<digest*>(x_malloc(sizeof(digest))); + hash_result_as_bytes(fhash, d); + hashtable_insert(included_files, path, d); + path = NULL; // Ownership transferred to included_files. + + if (depend_mode_hash) { + hash_delimiter(depend_mode_hash, "include"); + char digest[DIGEST_STRING_BUFFER_SIZE]; + digest_as_string(d, digest); + hash_string(depend_mode_hash, digest); + } + } + + goto out; failure: - if (conf->direct_mode) { - cc_log("Disabling direct mode"); - conf->direct_mode = false; - } - // Fall through. + if (conf->direct_mode) { + cc_log("Disabling direct mode"); + conf->direct_mode = false; + } + // Fall through. out: - hash_free(fhash); - free(path); + hash_free(fhash); + free(path); } static void -print_included_files(FILE *fp) +print_included_files(FILE* fp) { - struct hashtable_itr *iter = hashtable_iterator(included_files); - do { - char* path = static_cast<char*>(hashtable_iterator_key(iter)); - fprintf(fp, "%s\n", path); - } while (hashtable_iterator_advance(iter)); + struct hashtable_itr* iter = hashtable_iterator(included_files); + do { + char* path = static_cast<char*>(hashtable_iterator_key(iter)); + fprintf(fp, "%s\n", path); + } while (hashtable_iterator_advance(iter)); } // Make a relative path from current working directory to path if path is under // the base directory. Takes over ownership of path. Caller frees. -static char * -make_relative_path(char *path) +static char* +make_relative_path(char* path) { - if (str_eq(conf->base_dir, "") || !str_startswith(path, conf->base_dir)) { - return path; - } + if (str_eq(conf->base_dir, "") || !str_startswith(path, conf->base_dir)) { + return path; + } #ifdef _WIN32 - if (path[0] == '/') { - path++; // Skip leading slash. - } + if (path[0] == '/') { + path++; // Skip leading slash. + } #endif - // x_realpath only works for existing paths, so if path doesn't exist, try - // x_dirname(path) and assemble the path afterwards. We only bother to try - // canonicalizing one of these two paths since a compiler path argument - // typically only makes sense if path or x_dirname(path) exists. - char *path_suffix = NULL; - struct stat st; - if (stat(path, &st) != 0) { - // path doesn't exist. - char *dir = x_dirname(path); - // find the nearest existing directory in path - while (stat(dir, &st) != 0) { - char *parent_dir = x_dirname(dir); - free(dir); - dir = parent_dir; - } - - // suffix is the remaining of the path, skip the first delimiter - size_t dir_len = strlen(dir); - if (path[dir_len] == '/' || path[dir_len] == '\\') { - dir_len++; - } - path_suffix = x_strdup(&path[dir_len]); - char *p = path; - path = dir; - free(p); - } - - char *canon_path = x_realpath(path); - if (canon_path) { - free(path); - char *relpath = get_relative_path(get_current_working_dir(), canon_path); - free(canon_path); - if (path_suffix) { - path = format("%s/%s", relpath, path_suffix); - free(relpath); - free(path_suffix); - return path; - } else { - return relpath; - } - } else { - // path doesn't exist, so leave it as it is. - free(path_suffix); - return path; - } + // x_realpath only works for existing paths, so if path doesn't exist, try + // x_dirname(path) and assemble the path afterwards. We only bother to try + // canonicalizing one of these two paths since a compiler path argument + // typically only makes sense if path or x_dirname(path) exists. + char* path_suffix = NULL; + struct stat st; + if (stat(path, &st) != 0) { + // path doesn't exist. + char* dir = x_dirname(path); + // find the nearest existing directory in path + while (stat(dir, &st) != 0) { + char* parent_dir = x_dirname(dir); + free(dir); + dir = parent_dir; + } + + // suffix is the remaining of the path, skip the first delimiter + size_t dir_len = strlen(dir); + if (path[dir_len] == '/' || path[dir_len] == '\\') { + dir_len++; + } + path_suffix = x_strdup(&path[dir_len]); + char* p = path; + path = dir; + free(p); + } + + char* canon_path = x_realpath(path); + if (canon_path) { + free(path); + char* relpath = get_relative_path(get_current_working_dir(), canon_path); + free(canon_path); + if (path_suffix) { + path = format("%s/%s", relpath, path_suffix); + free(relpath); + free(path_suffix); + return path; + } else { + return relpath; + } + } else { + // path doesn't exist, so leave it as it is. + free(path_suffix); + return path; + } } static void init_included_files_table(void) { - // (This function may be called multiple times if several -arch options are - // used.) - if (!included_files) { - included_files = create_hashtable(1000, hash_from_string, strings_equal); - } + // (This function may be called multiple times if several -arch options are + // used.) + if (!included_files) { + included_files = create_hashtable(1000, hash_from_string, strings_equal); + } } // This function reads and hashes a file. While doing this, it also does these @@ -823,779 +840,788 @@ init_included_files_table(void) // - Stores the paths and hashes of included files in the global variable // included_files. static bool -process_preprocessed_file(struct hash *hash, const char *path, bool pump) +process_preprocessed_file(struct hash* hash, const char* path, bool pump) { - char *data; - size_t size; - if (!read_file(path, 0, &data, &size)) { - return false; - } - - ignore_headers = NULL; - ignore_headers_len = 0; - if (!str_eq(conf->ignore_headers_in_manifest, "")) { - char *header, *p, *q, *saveptr = NULL; - p = x_strdup(conf->ignore_headers_in_manifest); - q = p; - while ((header = strtok_r(q, PATH_DELIM, &saveptr))) { - ignore_headers = static_cast<char**>( - x_realloc(ignore_headers, (ignore_headers_len+1) * sizeof(char *))); - ignore_headers[ignore_headers_len++] = x_strdup(header); - q = NULL; - } - free(p); - } - - init_included_files_table(); - - char *cwd = gnu_getcwd(); - - // Bytes between p and q are pending to be hashed. - char *p = data; - char *q = data; - char *end = data + size; - - // There must be at least 7 characters (# 1 "x") left to potentially find an - // include file path. - while (q < end - 7) { - // Check if we look at a line containing the file name of an included file. - // At least the following formats exist (where N is a positive integer): - // - // GCC: - // - // # N "file" - // # N "file" N - // #pragma GCC pch_preprocess "file" - // - // HP's compiler: - // - // #line N "file" - // - // AIX's compiler: - // - // #line N "file" - // #line N - // - // Note that there may be other lines starting with '#' left after - // preprocessing as well, for instance "# pragma". - if (q[0] == '#' - // GCC: - && ((q[1] == ' ' && q[2] >= '0' && q[2] <= '9') - // GCC precompiled header: - || (q[1] == 'p' - && str_startswith(&q[2], "ragma GCC pch_preprocess ")) - // HP/AIX: - || (q[1] == 'l' && q[2] == 'i' && q[3] == 'n' && q[4] == 'e' - && q[5] == ' ')) - && (q == data || q[-1] == '\n')) { - // Workarounds for preprocessor linemarker bugs in GCC version 6. - if (q[2] == '3') { - if (str_startswith(q, "# 31 \"<command-line>\"\n")) { - // Bogus extra line with #31, after the regular #1: Ignore the whole - // line, and continue parsing. - hash_string_buffer(hash, p, q - p); - while (q < end && *q != '\n') { - q++; - } - q++; - p = q; - continue; - } else if (str_startswith(q, "# 32 \"<command-line>\" 2\n")) { - // Bogus wrong line with #32, instead of regular #1: Replace the line - // number with the usual one. - hash_string_buffer(hash, p, q - p); - q += 1; - q[0] = '#'; - q[1] = ' '; - q[2] = '1'; - p = q; - } - } - - while (q < end && *q != '"' && *q != '\n') { - q++; - } - if (q < end && *q == '\n') { - // A newline before the quotation mark -> no match. - continue; - } - q++; - if (q >= end) { - cc_log("Failed to parse included file path"); - free(data); - free(cwd); - return false; - } - // q points to the beginning of an include file path - hash_string_buffer(hash, p, q - p); - p = q; - while (q < end && *q != '"') { - q++; - } - // Look for preprocessor flags, after the "filename". - bool system = false; - char *r = q + 1; - while (r < end && *r != '\n') { - if (*r == '3') { // System header. - system = true; - } - r++; - } - // p and q span the include file path. - char *inc_path = x_strndup(p, q - p); - if (!has_absolute_include_headers) { - has_absolute_include_headers = is_absolute_path(inc_path); - } - inc_path = make_relative_path(inc_path); - - bool should_hash_inc_path = true; - if (!conf->hash_dir) { - if (str_startswith(inc_path, cwd) && str_endswith(inc_path, "//")) { - // When compiling with -g or similar, GCC adds the absolute path to - // CWD like this: - // - // # 1 "CWD//" - // - // If the user has opted out of including the CWD in the hash, don't - // hash it. See also how debug_prefix_map is handled. - should_hash_inc_path = false; - } - } - if (should_hash_inc_path) { - hash_string_buffer(hash, inc_path, strlen(inc_path)); - } - - remember_include_file(inc_path, hash, system, NULL); - p = q; // Everything of interest between p and q has been hashed now. - } else if (q[0] == '.' && q[1] == 'i' && q[2] == 'n' && q[3] == 'c' - && q[4] == 'b' && q[5] == 'i' && q[6] == 'n') { - // An assembler .inc bin (without the space) statement, which could be - // part of inline assembly, refers to an external file. If the file - // changes, the hash should change as well, but finding out what file to - // hash is too hard for ccache, so just bail out. - cc_log("Found unsupported .inc" "bin directive in source code"); - stats_update(STATS_UNSUPPORTED_DIRECTIVE); - failed(); - } else if (pump && strncmp(q, "_________", 9) == 0) { - // Unfortunately the distcc-pump wrapper outputs standard output lines: - // __________Using distcc-pump from /usr/bin - // __________Using # distcc servers in pump mode - // __________Shutting down distcc-pump include server - while (q < end && *q != '\n') { - q++; - } - if (*q == '\n') { - q++; - } - p = q; - continue; - } else { - q++; - } - } - - hash_string_buffer(hash, p, (end - p)); - free(data); - free(cwd); - - // Explicitly check the .gch/.pch/.pth file as Clang does not include any - // mention of it in the preprocessed output. - if (included_pch_file) { - char *pch_path = x_strdup(included_pch_file); - pch_path = make_relative_path(pch_path); - hash_string(hash, pch_path); - remember_include_file(pch_path, hash, false, NULL); - } - - bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); - if (debug_included) { - print_included_files(stdout); - } - - return true; + char* data; + size_t size; + if (!read_file(path, 0, &data, &size)) { + return false; + } + + ignore_headers = NULL; + ignore_headers_len = 0; + if (!str_eq(conf->ignore_headers_in_manifest, "")) { + char *header, *p, *q, *saveptr = NULL; + p = x_strdup(conf->ignore_headers_in_manifest); + q = p; + while ((header = strtok_r(q, PATH_DELIM, &saveptr))) { + ignore_headers = static_cast<char**>( + x_realloc(ignore_headers, (ignore_headers_len + 1) * sizeof(char*))); + ignore_headers[ignore_headers_len++] = x_strdup(header); + q = NULL; + } + free(p); + } + + init_included_files_table(); + + char* cwd = gnu_getcwd(); + + // Bytes between p and q are pending to be hashed. + char* p = data; + char* q = data; + char* end = data + size; + + // There must be at least 7 characters (# 1 "x") left to potentially find an + // include file path. + while (q < end - 7) { + // Check if we look at a line containing the file name of an included file. + // At least the following formats exist (where N is a positive integer): + // + // GCC: + // + // # N "file" + // # N "file" N + // #pragma GCC pch_preprocess "file" + // + // HP's compiler: + // + // #line N "file" + // + // AIX's compiler: + // + // #line N "file" + // #line N + // + // Note that there may be other lines starting with '#' left after + // preprocessing as well, for instance "# pragma". + if (q[0] == '#' + // GCC: + && ((q[1] == ' ' && q[2] >= '0' && q[2] <= '9') + // GCC precompiled header: + || (q[1] == 'p' + && str_startswith(&q[2], "ragma GCC pch_preprocess ")) + // HP/AIX: + || (q[1] == 'l' && q[2] == 'i' && q[3] == 'n' && q[4] == 'e' + && q[5] == ' ')) + && (q == data || q[-1] == '\n')) { + // Workarounds for preprocessor linemarker bugs in GCC version 6. + if (q[2] == '3') { + if (str_startswith(q, "# 31 \"<command-line>\"\n")) { + // Bogus extra line with #31, after the regular #1: Ignore the whole + // line, and continue parsing. + hash_string_buffer(hash, p, q - p); + while (q < end && *q != '\n') { + q++; + } + q++; + p = q; + continue; + } else if (str_startswith(q, "# 32 \"<command-line>\" 2\n")) { + // Bogus wrong line with #32, instead of regular #1: Replace the line + // number with the usual one. + hash_string_buffer(hash, p, q - p); + q += 1; + q[0] = '#'; + q[1] = ' '; + q[2] = '1'; + p = q; + } + } + + while (q < end && *q != '"' && *q != '\n') { + q++; + } + if (q < end && *q == '\n') { + // A newline before the quotation mark -> no match. + continue; + } + q++; + if (q >= end) { + cc_log("Failed to parse included file path"); + free(data); + free(cwd); + return false; + } + // q points to the beginning of an include file path + hash_string_buffer(hash, p, q - p); + p = q; + while (q < end && *q != '"') { + q++; + } + // Look for preprocessor flags, after the "filename". + bool system = false; + char* r = q + 1; + while (r < end && *r != '\n') { + if (*r == '3') { // System header. + system = true; + } + r++; + } + // p and q span the include file path. + char* inc_path = x_strndup(p, q - p); + if (!has_absolute_include_headers) { + has_absolute_include_headers = is_absolute_path(inc_path); + } + inc_path = make_relative_path(inc_path); + + bool should_hash_inc_path = true; + if (!conf->hash_dir) { + if (str_startswith(inc_path, cwd) && str_endswith(inc_path, "//")) { + // When compiling with -g or similar, GCC adds the absolute path to + // CWD like this: + // + // # 1 "CWD//" + // + // If the user has opted out of including the CWD in the hash, don't + // hash it. See also how debug_prefix_map is handled. + should_hash_inc_path = false; + } + } + if (should_hash_inc_path) { + hash_string_buffer(hash, inc_path, strlen(inc_path)); + } + + remember_include_file(inc_path, hash, system, NULL); + p = q; // Everything of interest between p and q has been hashed now. + } else if (q[0] == '.' && q[1] == 'i' && q[2] == 'n' && q[3] == 'c' + && q[4] == 'b' && q[5] == 'i' && q[6] == 'n') { + // An assembler .inc bin (without the space) statement, which could be + // part of inline assembly, refers to an external file. If the file + // changes, the hash should change as well, but finding out what file to + // hash is too hard for ccache, so just bail out. + cc_log( + "Found unsupported .inc" + "bin directive in source code"); + stats_update(STATS_UNSUPPORTED_DIRECTIVE); + failed(); + } else if (pump && strncmp(q, "_________", 9) == 0) { + // Unfortunately the distcc-pump wrapper outputs standard output lines: + // __________Using distcc-pump from /usr/bin + // __________Using # distcc servers in pump mode + // __________Shutting down distcc-pump include server + while (q < end && *q != '\n') { + q++; + } + if (*q == '\n') { + q++; + } + p = q; + continue; + } else { + q++; + } + } + + hash_string_buffer(hash, p, (end - p)); + free(data); + free(cwd); + + // Explicitly check the .gch/.pch/.pth file as Clang does not include any + // mention of it in the preprocessed output. + if (included_pch_file) { + char* pch_path = x_strdup(included_pch_file); + pch_path = make_relative_path(pch_path); + hash_string(hash, pch_path); + remember_include_file(pch_path, hash, false, NULL); + } + + bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); + if (debug_included) { + print_included_files(stdout); + } + + return true; } // Replace absolute paths with relative paths in the provided dependency file. static void -use_relative_paths_in_depfile(const char *depfile) +use_relative_paths_in_depfile(const char* depfile) { - if (str_eq(conf->base_dir, "")) { - cc_log("Base dir not set, skip using relative paths"); - return; // nothing to do - } - if (!has_absolute_include_headers) { - cc_log("No absolute path for included files found, skip using relative" - " paths"); - return; // nothing to do - } - - FILE *f; - f = fopen(depfile, "r"); - if (!f) { - cc_log("Cannot open dependency file: %s (%s)", depfile, strerror(errno)); - return; - } - - char *tmp_file = format("%s.tmp", depfile); - FILE *tmpf = create_tmp_file(&tmp_file, "w"); - - bool result = false; - char buf[10000]; - while (fgets(buf, sizeof(buf), f) && !ferror(tmpf)) { - char *saveptr; - char *token = strtok_r(buf, " \t", &saveptr); - while (token) { - char *relpath; - if (is_absolute_path(token) && str_startswith(token, conf->base_dir)) { - relpath = make_relative_path(x_strdup(token)); - result = true; - } else { - relpath = token; - } - if (token != buf) { // This is a dependency file. - fputc(' ', tmpf); - } - fputs(relpath, tmpf); - if (relpath != token) { - free(relpath); - } - token = strtok_r(NULL, " \t", &saveptr); - } - } - - if (ferror(f)) { - cc_log("Error reading dependency file: %s, skip relative path usage", - depfile); - result = false; - goto out; - } - if (ferror(tmpf)) { - cc_log("Error writing temporary dependency file: %s, skip relative path" - " usage", tmp_file); - result = false; - goto out; - } + if (str_eq(conf->base_dir, "")) { + cc_log("Base dir not set, skip using relative paths"); + return; // nothing to do + } + if (!has_absolute_include_headers) { + cc_log( + "No absolute path for included files found, skip using relative" + " paths"); + return; // nothing to do + } + + FILE* f; + f = fopen(depfile, "r"); + if (!f) { + cc_log("Cannot open dependency file: %s (%s)", depfile, strerror(errno)); + return; + } + + char* tmp_file = format("%s.tmp", depfile); + FILE* tmpf = create_tmp_file(&tmp_file, "w"); + + bool result = false; + char buf[10000]; + while (fgets(buf, sizeof(buf), f) && !ferror(tmpf)) { + char* saveptr; + char* token = strtok_r(buf, " \t", &saveptr); + while (token) { + char* relpath; + if (is_absolute_path(token) && str_startswith(token, conf->base_dir)) { + relpath = make_relative_path(x_strdup(token)); + result = true; + } else { + relpath = token; + } + if (token != buf) { // This is a dependency file. + fputc(' ', tmpf); + } + fputs(relpath, tmpf); + if (relpath != token) { + free(relpath); + } + token = strtok_r(NULL, " \t", &saveptr); + } + } + + if (ferror(f)) { + cc_log("Error reading dependency file: %s, skip relative path usage", + depfile); + result = false; + goto out; + } + if (ferror(tmpf)) { + cc_log( + "Error writing temporary dependency file: %s, skip relative path" + " usage", + tmp_file); + result = false; + goto out; + } out: - fclose(tmpf); - fclose(f); - if (result) { - if (x_rename(tmp_file, depfile) != 0) { - cc_log("Error renaming dependency file: %s -> %s (%s), skip relative" - " path usage", tmp_file, depfile, strerror(errno)); - result = false; - } else { - cc_log("Renamed dependency file: %s -> %s", tmp_file, depfile); - } - } - if (!result) { - cc_log("Removing temporary dependency file: %s", tmp_file); - x_unlink(tmp_file); - } - free(tmp_file); + fclose(tmpf); + fclose(f); + if (result) { + if (x_rename(tmp_file, depfile) != 0) { + cc_log( + "Error renaming dependency file: %s -> %s (%s), skip relative" + " path usage", + tmp_file, + depfile, + strerror(errno)); + result = false; + } else { + cc_log("Renamed dependency file: %s -> %s", tmp_file, depfile); + } + } + if (!result) { + cc_log("Removing temporary dependency file: %s", tmp_file); + x_unlink(tmp_file); + } + free(tmp_file); } // Extract the used includes from the dependency file. Note that we cannot // distinguish system headers from other includes here. -static struct digest * -result_name_from_depfile(const char *depfile, struct hash *hash) +static struct digest* +result_name_from_depfile(const char* depfile, struct hash* hash) { - FILE *f = fopen(depfile, "r"); - if (!f) { - cc_log("Cannot open dependency file %s: %s", depfile, strerror(errno)); - return NULL; - } - - init_included_files_table(); - - char buf[10000]; - while (fgets(buf, sizeof(buf), f) && !ferror(f)) { - char *saveptr; - char *token; - for (token = strtok_r(buf, " \t\n", &saveptr); - token; - token = strtok_r(NULL, " \t\n", &saveptr)) { - if (str_endswith(token, ":") || str_eq(token, "\\")) { - continue; - } - if (!has_absolute_include_headers) { - has_absolute_include_headers = is_absolute_path(token); - } - char *path = make_relative_path(x_strdup(token)); - remember_include_file(path, hash, false, hash); - } - } - - fclose(f); - - // Explicitly check the .gch/.pch/.pth file as it may not be mentioned in the - // dependencies output. - if (included_pch_file) { - char *pch_path = x_strdup(included_pch_file); - pch_path = make_relative_path(pch_path); - hash_string(hash, pch_path); - remember_include_file(pch_path, hash, false, NULL); - } - - bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); - if (debug_included) { - print_included_files(stdout); - } - - auto d = static_cast<digest*>(x_malloc(sizeof(digest))); - hash_result_as_bytes(hash, d); - return d; + FILE* f = fopen(depfile, "r"); + if (!f) { + cc_log("Cannot open dependency file %s: %s", depfile, strerror(errno)); + return NULL; + } + + init_included_files_table(); + + char buf[10000]; + while (fgets(buf, sizeof(buf), f) && !ferror(f)) { + char* saveptr; + char* token; + for (token = strtok_r(buf, " \t\n", &saveptr); token; + token = strtok_r(NULL, " \t\n", &saveptr)) { + if (str_endswith(token, ":") || str_eq(token, "\\")) { + continue; + } + if (!has_absolute_include_headers) { + has_absolute_include_headers = is_absolute_path(token); + } + char* path = make_relative_path(x_strdup(token)); + remember_include_file(path, hash, false, hash); + } + } + + fclose(f); + + // Explicitly check the .gch/.pch/.pth file as it may not be mentioned in the + // dependencies output. + if (included_pch_file) { + char* pch_path = x_strdup(included_pch_file); + pch_path = make_relative_path(pch_path); + hash_string(hash, pch_path); + remember_include_file(pch_path, hash, false, NULL); + } + + bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); + if (debug_included) { + print_included_files(stdout); + } + + auto d = static_cast<digest*>(x_malloc(sizeof(digest))); + hash_result_as_bytes(hash, d); + return d; } // Send cached stderr, if any, to stderr. static void -send_cached_stderr(const char *path_stderr) +send_cached_stderr(const char* path_stderr) { - int fd_stderr = open(path_stderr, O_RDONLY | O_BINARY); - if (fd_stderr != -1) { - copy_fd(fd_stderr, 2); - close(fd_stderr); - } + int fd_stderr = open(path_stderr, O_RDONLY | O_BINARY); + if (fd_stderr != -1) { + copy_fd(fd_stderr, 2); + close(fd_stderr); + } } // Create or update the manifest file. static void update_manifest_file(void) { - if (!conf->direct_mode - || !included_files - || conf->read_only - || conf->read_only_direct) { - return; - } - - struct stat st; - size_t old_size = 0; // in bytes - if (stat(manifest_path, &st) == 0) { - old_size = file_size(&st); - } - - MTR_BEGIN("manifest", "manifest_put"); - cc_log("Adding result name to %s", manifest_path); - if (manifest_put(manifest_path, cached_result_name, included_files)) { - if (x_stat(manifest_path, &st) == 0) { - stats_update_size( - manifest_stats_file, - file_size(&st) - old_size, - old_size == 0 ? 1 : 0); - } - } else { - cc_log("Failed to add result name to %s", manifest_path); - } - MTR_END("manifest", "manifest_put"); + if (!conf->direct_mode || !included_files || conf->read_only + || conf->read_only_direct) { + return; + } + + struct stat st; + size_t old_size = 0; // in bytes + if (stat(manifest_path, &st) == 0) { + old_size = file_size(&st); + } + + MTR_BEGIN("manifest", "manifest_put"); + cc_log("Adding result name to %s", manifest_path); + if (manifest_put(manifest_path, cached_result_name, included_files)) { + if (x_stat(manifest_path, &st) == 0) { + stats_update_size( + manifest_stats_file, file_size(&st) - old_size, old_size == 0 ? 1 : 0); + } + } else { + cc_log("Failed to add result name to %s", manifest_path); + } + MTR_END("manifest", "manifest_put"); } static void -update_cached_result_globals(struct digest *result_name) +update_cached_result_globals(struct digest* result_name) { - char result_name_string[DIGEST_STRING_BUFFER_SIZE]; - digest_as_string(result_name, result_name_string); - cached_result_name = result_name; - cached_result_path = get_path_in_cache(result_name_string, ".result"); - stats_file = format("%s/%c/stats", conf->cache_dir, result_name_string[0]); + char result_name_string[DIGEST_STRING_BUFFER_SIZE]; + digest_as_string(result_name, result_name_string); + cached_result_name = result_name; + cached_result_path = get_path_in_cache(result_name_string, ".result"); + stats_file = format("%s/%c/stats", conf->cache_dir, result_name_string[0]); } // Run the real compiler and put the result in cache. static void -to_cache(struct args *args, struct hash *depend_mode_hash) +to_cache(struct args* args, struct hash* depend_mode_hash) { - args_add(args, "-o"); - args_add(args, output_obj); - - if (conf->hard_link) { - // Workaround for Clang bug where it overwrites an existing object file - // when it's compiling an assembler file, see - // <https://bugs.llvm.org/show_bug.cgi?id=39782>. - x_unlink(output_obj); - } - - if (generating_diagnostics) { - args_add(args, "--serialize-diagnostics"); - args_add(args, output_dia); - } - - // Turn off DEPENDENCIES_OUTPUT when running cc1, because otherwise it will - // emit a line like this: - // - // tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i - x_unsetenv("DEPENDENCIES_OUTPUT"); - x_unsetenv("SUNPRO_DEPENDENCIES"); - - if (conf->run_second_cpp) { - args_add(args, input_file); - } else { - args_add(args, i_tmpfile); - } - - if (seen_split_dwarf) { - // Remove any pre-existing .dwo file since we want to check if the compiler - // produced one, intentionally not using x_unlink or tmp_unlink since we're - // not interested in logging successful deletions or failures due to - // non-existent .dwo files. - if (unlink(output_dwo) == -1 && errno != ENOENT) { - cc_log("Failed to unlink %s: %s", output_dwo, strerror(errno)); - stats_update(STATS_BADOUTPUTFILE); - } - } - - cc_log("Running real compiler"); - MTR_BEGIN("execute", "compiler"); - char *tmp_stdout; - int tmp_stdout_fd; - char *tmp_stderr; - int tmp_stderr_fd; - int status; - if (!conf->depend_mode) { - tmp_stdout = format("%s/tmp.stdout", temp_dir()); - tmp_stdout_fd = create_tmp_fd(&tmp_stdout); - tmp_stderr = format("%s/tmp.stderr", temp_dir()); - tmp_stderr_fd = create_tmp_fd(&tmp_stderr); - status = execute(args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid); - args_pop(args, 3); - } else { - // The cached result path is not known yet, use temporary files. - tmp_stdout = format("%s/tmp.stdout", temp_dir()); - tmp_stdout_fd = create_tmp_fd(&tmp_stdout); - tmp_stderr = format("%s/tmp.stderr", temp_dir()); - tmp_stderr_fd = create_tmp_fd(&tmp_stderr); - - // Use the original arguments (including dependency options) in depend - // mode. - assert(orig_args); - struct args *depend_mode_args = args_copy(orig_args); - args_strip(depend_mode_args, "--ccache-"); - add_prefix(depend_mode_args, conf->prefix_command); - - time_of_compilation = time(NULL); - status = execute( - depend_mode_args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid); - args_free(depend_mode_args); - } - MTR_END("execute", "compiler"); - - struct stat st; - if (x_stat(tmp_stdout, &st) != 0) { - // The stdout file was removed - cleanup in progress? Better bail out. - stats_update(STATS_MISSING); - tmp_unlink(tmp_stdout); - tmp_unlink(tmp_stderr); - failed(); - } - - // distcc-pump outputs lines like this: - // __________Using # distcc servers in pump mode - if (st.st_size != 0 && guessed_compiler != GUESSED_PUMP) { - cc_log("Compiler produced stdout"); - stats_update(STATS_STDOUT); - tmp_unlink(tmp_stdout); - tmp_unlink(tmp_stderr); - failed(); - } - tmp_unlink(tmp_stdout); - - // Merge stderr from the preprocessor (if any) and stderr from the real - // compiler into tmp_stderr. - if (cpp_stderr) { - char *tmp_stderr2 = format("%s.2", tmp_stderr); - if (x_rename(tmp_stderr, tmp_stderr2)) { - cc_log("Failed to rename %s to %s: %s", tmp_stderr, tmp_stderr2, - strerror(errno)); - failed(); - } - - int fd_cpp_stderr = open(cpp_stderr, O_RDONLY | O_BINARY); - if (fd_cpp_stderr == -1) { - cc_log("Failed opening %s: %s", cpp_stderr, strerror(errno)); - failed(); - } - - int fd_real_stderr = open(tmp_stderr2, O_RDONLY | O_BINARY); - if (fd_real_stderr == -1) { - cc_log("Failed opening %s: %s", tmp_stderr2, strerror(errno)); - failed(); - } - - int fd_result = - open(tmp_stderr, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); - if (fd_result == -1) { - cc_log("Failed opening %s: %s", tmp_stderr, strerror(errno)); - failed(); - } - - copy_fd(fd_cpp_stderr, fd_result); - copy_fd(fd_real_stderr, fd_result); - close(fd_cpp_stderr); - close(fd_real_stderr); - close(fd_result); - tmp_unlink(tmp_stderr2); - free(tmp_stderr2); - } - - if (status != 0) { - cc_log("Compiler gave exit status %d", status); - stats_update(STATS_STATUS); - - int fd = open(tmp_stderr, O_RDONLY | O_BINARY); - if (fd != -1) { - // We can output stderr immediately instead of rerunning the compiler. - copy_fd(fd, 2); - close(fd); - tmp_unlink(tmp_stderr); - - x_exit(status); - } - - tmp_unlink(tmp_stderr); - failed(); - } - - if (conf->depend_mode) { - struct digest *result_name = - result_name_from_depfile(output_dep, depend_mode_hash); - if (!result_name) { - failed(); - } - update_cached_result_globals(result_name); - } - - bool produce_dep_file = - generating_dependencies && !str_eq(output_dep, "/dev/null"); - - if (produce_dep_file) { - use_relative_paths_in_depfile(output_dep); - } - - if (stat(output_obj, &st) != 0) { - cc_log("Compiler didn't produce an object file"); - stats_update(STATS_NOOUTPUT); - failed(); - } - if (st.st_size == 0) { - cc_log("Compiler produced an empty object file"); - stats_update(STATS_EMPTYOUTPUT); - failed(); - } - - if (x_stat(tmp_stderr, &st) != 0) { - stats_update(STATS_ERROR); - failed(); - } - struct result_files *result_files = result_files_init(); - if (st.st_size > 0) { - result_files_add(result_files, tmp_stderr, RESULT_STDERR_NAME); - } - result_files_add(result_files, output_obj, ".o"); - if (generating_dependencies) { - result_files_add(result_files, output_dep, ".d"); - } - if (generating_coverage) { - result_files_add(result_files, output_cov, ".gcno"); - } - if (generating_stackusage) { - result_files_add(result_files, output_su, ".su"); - } - if (generating_diagnostics) { - result_files_add(result_files, output_dia, ".dia"); - } - if (seen_split_dwarf && stat(output_dwo, &st) == 0) { - // Only copy .dwo file if it was created by the compiler (GCC and Clang - // behave differently e.g. for "-gsplit-dwarf -g1"). - result_files_add(result_files, output_dwo, ".dwo"); - } - struct stat orig_dest_st; - bool orig_dest_existed = stat(cached_result_path, &orig_dest_st) == 0; - result_put(cached_result_path, result_files); - result_files_free(result_files); - - cc_log("Stored in cache: %s", cached_result_path); - - if (x_stat(cached_result_path, &st) != 0) { - stats_update(STATS_ERROR); - failed(); - } - stats_update_size( - stats_file, - file_size(&st) - (orig_dest_existed ? file_size(&orig_dest_st) : 0), - orig_dest_existed ? 0 : 1); - - MTR_END("file", "file_put"); - - stats_update(STATS_TOCACHE); - - // Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can - // be done almost anywhere, but we might as well do it near the end as we - // save the stat call if we exit early. - { - char *first_level_dir = x_dirname(stats_file); - if (create_cachedirtag(first_level_dir) != 0) { - cc_log("Failed to create %s/CACHEDIR.TAG (%s)\n", - first_level_dir, strerror(errno)); - stats_update(STATS_ERROR); - failed(); - } - free(first_level_dir); - - // Remove any CACHEDIR.TAG on the cache_dir level where it was located in - // previous ccache versions. - if (getpid() % 1000 == 0) { - char *path = format("%s/CACHEDIR.TAG", conf->cache_dir); - x_unlink(path); - free(path); - } - } - - // Everything OK. - send_cached_stderr(tmp_stderr); - tmp_unlink(tmp_stderr); - - update_manifest_file(); - - free(tmp_stderr); - free(tmp_stdout); + args_add(args, "-o"); + args_add(args, output_obj); + + if (conf->hard_link) { + // Workaround for Clang bug where it overwrites an existing object file + // when it's compiling an assembler file, see + // <https://bugs.llvm.org/show_bug.cgi?id=39782>. + x_unlink(output_obj); + } + + if (generating_diagnostics) { + args_add(args, "--serialize-diagnostics"); + args_add(args, output_dia); + } + + // Turn off DEPENDENCIES_OUTPUT when running cc1, because otherwise it will + // emit a line like this: + // + // tmp.stdout.vexed.732.o: /home/mbp/.ccache/tmp.stdout.vexed.732.i + x_unsetenv("DEPENDENCIES_OUTPUT"); + x_unsetenv("SUNPRO_DEPENDENCIES"); + + if (conf->run_second_cpp) { + args_add(args, input_file); + } else { + args_add(args, i_tmpfile); + } + + if (seen_split_dwarf) { + // Remove any pre-existing .dwo file since we want to check if the compiler + // produced one, intentionally not using x_unlink or tmp_unlink since we're + // not interested in logging successful deletions or failures due to + // non-existent .dwo files. + if (unlink(output_dwo) == -1 && errno != ENOENT) { + cc_log("Failed to unlink %s: %s", output_dwo, strerror(errno)); + stats_update(STATS_BADOUTPUTFILE); + } + } + + cc_log("Running real compiler"); + MTR_BEGIN("execute", "compiler"); + char* tmp_stdout; + int tmp_stdout_fd; + char* tmp_stderr; + int tmp_stderr_fd; + int status; + if (!conf->depend_mode) { + tmp_stdout = format("%s/tmp.stdout", temp_dir()); + tmp_stdout_fd = create_tmp_fd(&tmp_stdout); + tmp_stderr = format("%s/tmp.stderr", temp_dir()); + tmp_stderr_fd = create_tmp_fd(&tmp_stderr); + status = execute(args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid); + args_pop(args, 3); + } else { + // The cached result path is not known yet, use temporary files. + tmp_stdout = format("%s/tmp.stdout", temp_dir()); + tmp_stdout_fd = create_tmp_fd(&tmp_stdout); + tmp_stderr = format("%s/tmp.stderr", temp_dir()); + tmp_stderr_fd = create_tmp_fd(&tmp_stderr); + + // Use the original arguments (including dependency options) in depend + // mode. + assert(orig_args); + struct args* depend_mode_args = args_copy(orig_args); + args_strip(depend_mode_args, "--ccache-"); + add_prefix(depend_mode_args, conf->prefix_command); + + time_of_compilation = time(NULL); + status = execute( + depend_mode_args->argv, tmp_stdout_fd, tmp_stderr_fd, &compiler_pid); + args_free(depend_mode_args); + } + MTR_END("execute", "compiler"); + + struct stat st; + if (x_stat(tmp_stdout, &st) != 0) { + // The stdout file was removed - cleanup in progress? Better bail out. + stats_update(STATS_MISSING); + tmp_unlink(tmp_stdout); + tmp_unlink(tmp_stderr); + failed(); + } + + // distcc-pump outputs lines like this: + // __________Using # distcc servers in pump mode + if (st.st_size != 0 && guessed_compiler != GUESSED_PUMP) { + cc_log("Compiler produced stdout"); + stats_update(STATS_STDOUT); + tmp_unlink(tmp_stdout); + tmp_unlink(tmp_stderr); + failed(); + } + tmp_unlink(tmp_stdout); + + // Merge stderr from the preprocessor (if any) and stderr from the real + // compiler into tmp_stderr. + if (cpp_stderr) { + char* tmp_stderr2 = format("%s.2", tmp_stderr); + if (x_rename(tmp_stderr, tmp_stderr2)) { + cc_log("Failed to rename %s to %s: %s", + tmp_stderr, + tmp_stderr2, + strerror(errno)); + failed(); + } + + int fd_cpp_stderr = open(cpp_stderr, O_RDONLY | O_BINARY); + if (fd_cpp_stderr == -1) { + cc_log("Failed opening %s: %s", cpp_stderr, strerror(errno)); + failed(); + } + + int fd_real_stderr = open(tmp_stderr2, O_RDONLY | O_BINARY); + if (fd_real_stderr == -1) { + cc_log("Failed opening %s: %s", tmp_stderr2, strerror(errno)); + failed(); + } + + int fd_result = + open(tmp_stderr, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + if (fd_result == -1) { + cc_log("Failed opening %s: %s", tmp_stderr, strerror(errno)); + failed(); + } + + copy_fd(fd_cpp_stderr, fd_result); + copy_fd(fd_real_stderr, fd_result); + close(fd_cpp_stderr); + close(fd_real_stderr); + close(fd_result); + tmp_unlink(tmp_stderr2); + free(tmp_stderr2); + } + + if (status != 0) { + cc_log("Compiler gave exit status %d", status); + stats_update(STATS_STATUS); + + int fd = open(tmp_stderr, O_RDONLY | O_BINARY); + if (fd != -1) { + // We can output stderr immediately instead of rerunning the compiler. + copy_fd(fd, 2); + close(fd); + tmp_unlink(tmp_stderr); + + x_exit(status); + } + + tmp_unlink(tmp_stderr); + failed(); + } + + if (conf->depend_mode) { + struct digest* result_name = + result_name_from_depfile(output_dep, depend_mode_hash); + if (!result_name) { + failed(); + } + update_cached_result_globals(result_name); + } + + bool produce_dep_file = + generating_dependencies && !str_eq(output_dep, "/dev/null"); + + if (produce_dep_file) { + use_relative_paths_in_depfile(output_dep); + } + + if (stat(output_obj, &st) != 0) { + cc_log("Compiler didn't produce an object file"); + stats_update(STATS_NOOUTPUT); + failed(); + } + if (st.st_size == 0) { + cc_log("Compiler produced an empty object file"); + stats_update(STATS_EMPTYOUTPUT); + failed(); + } + + if (x_stat(tmp_stderr, &st) != 0) { + stats_update(STATS_ERROR); + failed(); + } + struct result_files* result_files = result_files_init(); + if (st.st_size > 0) { + result_files_add(result_files, tmp_stderr, RESULT_STDERR_NAME); + } + result_files_add(result_files, output_obj, ".o"); + if (generating_dependencies) { + result_files_add(result_files, output_dep, ".d"); + } + if (generating_coverage) { + result_files_add(result_files, output_cov, ".gcno"); + } + if (generating_stackusage) { + result_files_add(result_files, output_su, ".su"); + } + if (generating_diagnostics) { + result_files_add(result_files, output_dia, ".dia"); + } + if (seen_split_dwarf && stat(output_dwo, &st) == 0) { + // Only copy .dwo file if it was created by the compiler (GCC and Clang + // behave differently e.g. for "-gsplit-dwarf -g1"). + result_files_add(result_files, output_dwo, ".dwo"); + } + struct stat orig_dest_st; + bool orig_dest_existed = stat(cached_result_path, &orig_dest_st) == 0; + result_put(cached_result_path, result_files); + result_files_free(result_files); + + cc_log("Stored in cache: %s", cached_result_path); + + if (x_stat(cached_result_path, &st) != 0) { + stats_update(STATS_ERROR); + failed(); + } + stats_update_size(stats_file, + file_size(&st) + - (orig_dest_existed ? file_size(&orig_dest_st) : 0), + orig_dest_existed ? 0 : 1); + + MTR_END("file", "file_put"); + + stats_update(STATS_TOCACHE); + + // Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can + // be done almost anywhere, but we might as well do it near the end as we + // save the stat call if we exit early. + { + char* first_level_dir = x_dirname(stats_file); + if (create_cachedirtag(first_level_dir) != 0) { + cc_log("Failed to create %s/CACHEDIR.TAG (%s)\n", + first_level_dir, + strerror(errno)); + stats_update(STATS_ERROR); + failed(); + } + free(first_level_dir); + + // Remove any CACHEDIR.TAG on the cache_dir level where it was located in + // previous ccache versions. + if (getpid() % 1000 == 0) { + char* path = format("%s/CACHEDIR.TAG", conf->cache_dir); + x_unlink(path); + free(path); + } + } + + // Everything OK. + send_cached_stderr(tmp_stderr); + tmp_unlink(tmp_stderr); + + update_manifest_file(); + + free(tmp_stderr); + free(tmp_stdout); } // Find the result name by running the compiler in preprocessor mode and // hashing the result. -static struct digest * -get_result_name_from_cpp(struct args *args, struct hash *hash) +static struct digest* +get_result_name_from_cpp(struct args* args, struct hash* hash) { - time_of_compilation = time(NULL); - - char *path_stderr = NULL; - char *path_stdout; - int status; - if (direct_i_file) { - // We are compiling a .i or .ii file - that means we can skip the cpp stage - // and directly form the correct i_tmpfile. - path_stdout = input_file; - status = 0; - } else { - // Run cpp on the input file to obtain the .i. - - // Limit the basename to 10 characters in order to cope with filesystem with - // small maximum filename length limits. - char *input_base = x_basename(input_file); - char *tmp = strchr(input_base, '.'); - if (tmp) { - *tmp = 0; - } - if (strlen(input_base) > 10) { - input_base[10] = 0; - } - - path_stdout = format("%s/%s.stdout", temp_dir(), input_base); - free(input_base); - int path_stdout_fd = create_tmp_fd(&path_stdout); - add_pending_tmp_file(path_stdout); - - path_stderr = format("%s/tmp.cpp_stderr", temp_dir()); - int path_stderr_fd = create_tmp_fd(&path_stderr); - add_pending_tmp_file(path_stderr); - - int args_added = 2; - args_add(args, "-E"); - if (conf->keep_comments_cpp) { - args_add(args, "-C"); - args_added = 3; - } - args_add(args, input_file); - add_prefix(args, conf->prefix_command_cpp); - cc_log("Running preprocessor"); - MTR_BEGIN("execute", "preprocessor"); - status = execute(args->argv, path_stdout_fd, path_stderr_fd, &compiler_pid); - MTR_END("execute", "preprocessor"); - args_pop(args, args_added); - } - - if (status != 0) { - cc_log("Preprocessor gave exit status %d", status); - stats_update(STATS_PREPROCESSOR); - failed(); - } - - if (conf->unify) { - // When we are doing the unifying tricks we need to include the input file - // name in the hash to get the warnings right. - hash_delimiter(hash, "unifyfilename"); - hash_string(hash, input_file); - - hash_delimiter(hash, "unifycpp"); - - bool debug_unify = getenv("CCACHE_DEBUG_UNIFY"); - if (unify_hash(hash, path_stdout, debug_unify) != 0) { - stats_update(STATS_ERROR); - cc_log("Failed to unify %s", path_stdout); - failed(); - } - } else { - hash_delimiter(hash, "cpp"); - if (!process_preprocessed_file(hash, path_stdout, - guessed_compiler == GUESSED_PUMP)) { - stats_update(STATS_ERROR); - failed(); - } - } - - hash_delimiter(hash, "cppstderr"); - if (!direct_i_file && !hash_file(hash, path_stderr)) { - fatal("Failed to open %s: %s", path_stderr, strerror(errno)); - } - - if (direct_i_file) { - i_tmpfile = input_file; - } else { - // i_tmpfile needs the proper cpp_extension for the compiler to do its - // thing correctly - i_tmpfile = format("%s.%s", path_stdout, conf->cpp_extension); - x_rename(path_stdout, i_tmpfile); - add_pending_tmp_file(i_tmpfile); - } - - if (conf->run_second_cpp) { - free(path_stderr); - } else { - // If we are using the CPP trick, we need to remember this stderr data and - // output it just before the main stderr from the compiler pass. - cpp_stderr = path_stderr; - hash_delimiter(hash, "runsecondcpp"); - hash_string(hash, "false"); - } - - auto name = static_cast<digest*>(x_malloc(sizeof(digest))); - hash_result_as_bytes(hash, name); - return name; + time_of_compilation = time(NULL); + + char* path_stderr = NULL; + char* path_stdout; + int status; + if (direct_i_file) { + // We are compiling a .i or .ii file - that means we can skip the cpp stage + // and directly form the correct i_tmpfile. + path_stdout = input_file; + status = 0; + } else { + // Run cpp on the input file to obtain the .i. + + // Limit the basename to 10 characters in order to cope with filesystem with + // small maximum filename length limits. + char* input_base = x_basename(input_file); + char* tmp = strchr(input_base, '.'); + if (tmp) { + *tmp = 0; + } + if (strlen(input_base) > 10) { + input_base[10] = 0; + } + + path_stdout = format("%s/%s.stdout", temp_dir(), input_base); + free(input_base); + int path_stdout_fd = create_tmp_fd(&path_stdout); + add_pending_tmp_file(path_stdout); + + path_stderr = format("%s/tmp.cpp_stderr", temp_dir()); + int path_stderr_fd = create_tmp_fd(&path_stderr); + add_pending_tmp_file(path_stderr); + + int args_added = 2; + args_add(args, "-E"); + if (conf->keep_comments_cpp) { + args_add(args, "-C"); + args_added = 3; + } + args_add(args, input_file); + add_prefix(args, conf->prefix_command_cpp); + cc_log("Running preprocessor"); + MTR_BEGIN("execute", "preprocessor"); + status = execute(args->argv, path_stdout_fd, path_stderr_fd, &compiler_pid); + MTR_END("execute", "preprocessor"); + args_pop(args, args_added); + } + + if (status != 0) { + cc_log("Preprocessor gave exit status %d", status); + stats_update(STATS_PREPROCESSOR); + failed(); + } + + if (conf->unify) { + // When we are doing the unifying tricks we need to include the input file + // name in the hash to get the warnings right. + hash_delimiter(hash, "unifyfilename"); + hash_string(hash, input_file); + + hash_delimiter(hash, "unifycpp"); + + bool debug_unify = getenv("CCACHE_DEBUG_UNIFY"); + if (unify_hash(hash, path_stdout, debug_unify) != 0) { + stats_update(STATS_ERROR); + cc_log("Failed to unify %s", path_stdout); + failed(); + } + } else { + hash_delimiter(hash, "cpp"); + if (!process_preprocessed_file( + hash, path_stdout, guessed_compiler == GUESSED_PUMP)) { + stats_update(STATS_ERROR); + failed(); + } + } + + hash_delimiter(hash, "cppstderr"); + if (!direct_i_file && !hash_file(hash, path_stderr)) { + fatal("Failed to open %s: %s", path_stderr, strerror(errno)); + } + + if (direct_i_file) { + i_tmpfile = input_file; + } else { + // i_tmpfile needs the proper cpp_extension for the compiler to do its + // thing correctly + i_tmpfile = format("%s.%s", path_stdout, conf->cpp_extension); + x_rename(path_stdout, i_tmpfile); + add_pending_tmp_file(i_tmpfile); + } + + if (conf->run_second_cpp) { + free(path_stderr); + } else { + // If we are using the CPP trick, we need to remember this stderr data and + // output it just before the main stderr from the compiler pass. + cpp_stderr = path_stderr; + hash_delimiter(hash, "runsecondcpp"); + hash_string(hash, "false"); + } + + auto name = static_cast<digest*>(x_malloc(sizeof(digest))); + hash_result_as_bytes(hash, name); + return name; } // Hash mtime or content of a file, or the output of a command, according to // the CCACHE_COMPILERCHECK setting. static void -hash_compiler(struct hash *hash, struct stat *st, const char *path, +hash_compiler(struct hash* hash, + struct stat* st, + const char* path, bool allow_command) { - if (str_eq(conf->compiler_check, "none")) { - // Do nothing. - } else if (str_eq(conf->compiler_check, "mtime")) { - hash_delimiter(hash, "cc_mtime"); - hash_int(hash, st->st_size); - hash_int(hash, st->st_mtime); - } else if (str_startswith(conf->compiler_check, "string:")) { - hash_delimiter(hash, "cc_hash"); - hash_string(hash, conf->compiler_check + strlen("string:")); - } else if (str_eq(conf->compiler_check, "content") || !allow_command) { - hash_delimiter(hash, "cc_content"); - hash_file(hash, path); - } else { // command string - bool ok = hash_multicommand_output( - hash, conf->compiler_check, orig_args->argv[0]); - if (!ok) { - fatal("Failure running compiler check command: %s", conf->compiler_check); - } - } + if (str_eq(conf->compiler_check, "none")) { + // Do nothing. + } else if (str_eq(conf->compiler_check, "mtime")) { + hash_delimiter(hash, "cc_mtime"); + hash_int(hash, st->st_size); + hash_int(hash, st->st_mtime); + } else if (str_startswith(conf->compiler_check, "string:")) { + hash_delimiter(hash, "cc_hash"); + hash_string(hash, conf->compiler_check + strlen("string:")); + } else if (str_eq(conf->compiler_check, "content") || !allow_command) { + hash_delimiter(hash, "cc_content"); + hash_file(hash, path); + } else { // command string + bool ok = + hash_multicommand_output(hash, conf->compiler_check, orig_args->argv[0]); + if (!ok) { + fatal("Failure running compiler check command: %s", conf->compiler_check); + } + } } // Hash the host compiler(s) invoked by nvcc. @@ -1604,498 +1630,485 @@ hash_compiler(struct hash *hash, struct stat *st, const char *path, // with -ccbin/--compiler-bindir. If they are NULL, the compilers are looked up // in PATH instead. static void -hash_nvcc_host_compiler(struct hash *hash, struct stat *ccbin_st, - const char *ccbin) +hash_nvcc_host_compiler(struct hash* hash, + struct stat* ccbin_st, + const char* ccbin) { - // From <http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html>: - // - // "[...] Specify the directory in which the compiler executable resides. - // The host compiler executable name can be also specified to ensure that - // the correct host compiler is selected." - // - // and - // - // "On all platforms, the default host compiler executable (gcc and g++ on - // Linux, clang and clang++ on Mac OS X, and cl.exe on Windows) found in - // the current execution search path will be used". - - if (!ccbin || S_ISDIR(ccbin_st->st_mode)) { + // From <http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html>: + // + // "[...] Specify the directory in which the compiler executable resides. + // The host compiler executable name can be also specified to ensure that + // the correct host compiler is selected." + // + // and + // + // "On all platforms, the default host compiler executable (gcc and g++ on + // Linux, clang and clang++ on Mac OS X, and cl.exe on Windows) found in + // the current execution search path will be used". + + if (!ccbin || S_ISDIR(ccbin_st->st_mode)) { #if defined(__APPLE__) - const char *compilers[] = {"clang", "clang++"}; + const char* compilers[] = {"clang", "clang++"}; #elif defined(_WIN32) - const char *compilers[] = {"cl.exe"}; + const char* compilers[] = {"cl.exe"}; #else - const char *compilers[] = {"gcc", "g++"}; + const char* compilers[] = {"gcc", "g++"}; #endif - for (size_t i = 0; i < ARRAY_SIZE(compilers); i++) { - if (ccbin) { - char *path = format("%s/%s", ccbin, compilers[i]); - struct stat st; - if (stat(path, &st) == 0) { - hash_compiler(hash, &st, path, false); - } - free(path); - } else { - char *path = find_executable(compilers[i], MYNAME); - if (path) { - struct stat st; - x_stat(path, &st); - hash_compiler(hash, &st, ccbin, false); - free(path); - } - } - } - } else { - hash_compiler(hash, ccbin_st, ccbin, false); - } + for (size_t i = 0; i < ARRAY_SIZE(compilers); i++) { + if (ccbin) { + char* path = format("%s/%s", ccbin, compilers[i]); + struct stat st; + if (stat(path, &st) == 0) { + hash_compiler(hash, &st, path, false); + } + free(path); + } else { + char* path = find_executable(compilers[i], MYNAME); + if (path) { + struct stat st; + x_stat(path, &st); + hash_compiler(hash, &st, ccbin, false); + free(path); + } + } + } + } else { + hash_compiler(hash, ccbin_st, ccbin, false); + } } // Update a hash with information common for the direct and preprocessor modes. static void -hash_common_info(struct args *args, struct hash *hash) +hash_common_info(struct args* args, struct hash* hash) { - hash_string(hash, HASH_PREFIX); + hash_string(hash, HASH_PREFIX); - // We have to hash the extension, as a .i file isn't treated the same by the - // compiler as a .ii file. - hash_delimiter(hash, "ext"); - hash_string(hash, conf->cpp_extension); + // We have to hash the extension, as a .i file isn't treated the same by the + // compiler as a .ii file. + hash_delimiter(hash, "ext"); + hash_string(hash, conf->cpp_extension); #ifdef _WIN32 - const char *ext = strrchr(args->argv[0], '.'); - char full_path_win_ext[MAX_PATH + 1] = {0}; - add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, - args->argv[0]); - const char *full_path = full_path_win_ext; + const char* ext = strrchr(args->argv[0], '.'); + char full_path_win_ext[MAX_PATH + 1] = {0}; + add_exe_ext_if_no_to_fullpath( + full_path_win_ext, MAX_PATH, ext, args->argv[0]); + const char* full_path = full_path_win_ext; #else - const char *full_path = args->argv[0]; + const char* full_path = args->argv[0]; #endif - struct stat st; - if (x_stat(full_path, &st) != 0) { - stats_update(STATS_COMPILER); - failed(); - } - - // Hash information about the compiler. - hash_compiler(hash, &st, args->argv[0], true); - - // Also hash the compiler name as some compilers use hard links and behave - // differently depending on the real name. - hash_delimiter(hash, "cc_name"); - char *base = x_basename(args->argv[0]); - hash_string(hash, base); - free(base); - - if (!(conf->sloppiness & SLOPPY_LOCALE)) { - // Hash environment variables that may affect localization of compiler - // warning messages. - const char *envvars[] = { - "LANG", - "LC_ALL", - "LC_CTYPE", - "LC_MESSAGES", - NULL - }; - for (const char **p = envvars; *p; ++p) { - char *v = getenv(*p); - if (v) { - hash_delimiter(hash, *p); - hash_string(hash, v); - } - } - } - - // Possibly hash the current working directory. - if (generating_debuginfo && conf->hash_dir) { - char *cwd = gnu_getcwd(); - for (size_t i = 0; i < debug_prefix_maps_len; i++) { - char *map = debug_prefix_maps[i]; - char *sep = strchr(map, '='); - if (sep) { - char *old_path = x_strndup(map, sep - map); - char* new_path = static_cast<char*>(x_strdup(sep + 1)); - cc_log( - "Relocating debuginfo CWD %s from %s to %s", - cwd, - old_path, - new_path); - if (str_startswith(cwd, old_path)) { - char *dir = format("%s%s", new_path, cwd + strlen(old_path)); - free(cwd); - cwd = dir; - } - free(old_path); - free(new_path); - } - } - if (cwd) { - cc_log("Hashing CWD %s", cwd); - hash_delimiter(hash, "cwd"); - hash_string(hash, cwd); - free(cwd); - } - } - - if (seen_split_dwarf) { - // When using -gsplit-dwarf, object files include a link to the - // corresponding .dwo file based on the target object filename, so we need - // to include the target filename in the hash to avoid handing out an - // object file with an incorrect .dwo link. - hash_delimiter(hash, "filename"); - hash_string(hash, x_basename(output_obj)); - } - - // Possibly hash the coverage data file path. - if (generating_coverage && profile_arcs) { - char *dir = x_dirname(output_obj); - if (profile_dir) { - dir = x_strdup(profile_dir); - } else { - char *real_dir = x_realpath(dir); - free(dir); - dir = real_dir; - } - if (dir) { - char *base_name = x_basename(output_obj); - char *p = remove_extension(base_name); - free(base_name); - char *gcda_path = format("%s/%s.gcda", dir, p); - cc_log("Hashing coverage path %s", gcda_path); - free(p); - hash_delimiter(hash, "gcda"); - hash_string(hash, gcda_path); - free(dir); - } - } - - // Possibly hash the sanitize blacklist file path. - for (size_t i = 0; i < sanitize_blacklists_len; i++) { - char *sanitize_blacklist = sanitize_blacklists[i]; - cc_log("Hashing sanitize blacklist %s", sanitize_blacklist); - hash_delimiter(hash, "sanitizeblacklist"); - if (!hash_file(hash, sanitize_blacklist)) { - stats_update(STATS_BADEXTRAFILE); - failed(); - } - } - - if (!str_eq(conf->extra_files_to_hash, "")) { - char *p = x_strdup(conf->extra_files_to_hash); - char *q = p; - char *path; - char *saveptr = NULL; - while ((path = strtok_r(q, PATH_DELIM, &saveptr))) { - cc_log("Hashing extra file %s", path); - hash_delimiter(hash, "extrafile"); - if (!hash_file(hash, path)) { - stats_update(STATS_BADEXTRAFILE); - failed(); - } - q = NULL; - } - free(p); - } - - // Possibly hash GCC_COLORS (for color diagnostics). - if (guessed_compiler == GUESSED_GCC) { - const char *gcc_colors = getenv("GCC_COLORS"); - if (gcc_colors) { - hash_delimiter(hash, "gcccolors"); - hash_string(hash, gcc_colors); - } - } + struct stat st; + if (x_stat(full_path, &st) != 0) { + stats_update(STATS_COMPILER); + failed(); + } + + // Hash information about the compiler. + hash_compiler(hash, &st, args->argv[0], true); + + // Also hash the compiler name as some compilers use hard links and behave + // differently depending on the real name. + hash_delimiter(hash, "cc_name"); + char* base = x_basename(args->argv[0]); + hash_string(hash, base); + free(base); + + if (!(conf->sloppiness & SLOPPY_LOCALE)) { + // Hash environment variables that may affect localization of compiler + // warning messages. + const char* envvars[] = {"LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", NULL}; + for (const char** p = envvars; *p; ++p) { + char* v = getenv(*p); + if (v) { + hash_delimiter(hash, *p); + hash_string(hash, v); + } + } + } + + // Possibly hash the current working directory. + if (generating_debuginfo && conf->hash_dir) { + char* cwd = gnu_getcwd(); + for (size_t i = 0; i < debug_prefix_maps_len; i++) { + char* map = debug_prefix_maps[i]; + char* sep = strchr(map, '='); + if (sep) { + char* old_path = x_strndup(map, sep - map); + char* new_path = static_cast<char*>(x_strdup(sep + 1)); + cc_log( + "Relocating debuginfo CWD %s from %s to %s", cwd, old_path, new_path); + if (str_startswith(cwd, old_path)) { + char* dir = format("%s%s", new_path, cwd + strlen(old_path)); + free(cwd); + cwd = dir; + } + free(old_path); + free(new_path); + } + } + if (cwd) { + cc_log("Hashing CWD %s", cwd); + hash_delimiter(hash, "cwd"); + hash_string(hash, cwd); + free(cwd); + } + } + + if (seen_split_dwarf) { + // When using -gsplit-dwarf, object files include a link to the + // corresponding .dwo file based on the target object filename, so we need + // to include the target filename in the hash to avoid handing out an + // object file with an incorrect .dwo link. + hash_delimiter(hash, "filename"); + hash_string(hash, x_basename(output_obj)); + } + + // Possibly hash the coverage data file path. + if (generating_coverage && profile_arcs) { + char* dir = x_dirname(output_obj); + if (profile_dir) { + dir = x_strdup(profile_dir); + } else { + char* real_dir = x_realpath(dir); + free(dir); + dir = real_dir; + } + if (dir) { + char* base_name = x_basename(output_obj); + char* p = remove_extension(base_name); + free(base_name); + char* gcda_path = format("%s/%s.gcda", dir, p); + cc_log("Hashing coverage path %s", gcda_path); + free(p); + hash_delimiter(hash, "gcda"); + hash_string(hash, gcda_path); + free(dir); + } + } + + // Possibly hash the sanitize blacklist file path. + for (size_t i = 0; i < sanitize_blacklists_len; i++) { + char* sanitize_blacklist = sanitize_blacklists[i]; + cc_log("Hashing sanitize blacklist %s", sanitize_blacklist); + hash_delimiter(hash, "sanitizeblacklist"); + if (!hash_file(hash, sanitize_blacklist)) { + stats_update(STATS_BADEXTRAFILE); + failed(); + } + } + + if (!str_eq(conf->extra_files_to_hash, "")) { + char* p = x_strdup(conf->extra_files_to_hash); + char* q = p; + char* path; + char* saveptr = NULL; + while ((path = strtok_r(q, PATH_DELIM, &saveptr))) { + cc_log("Hashing extra file %s", path); + hash_delimiter(hash, "extrafile"); + if (!hash_file(hash, path)) { + stats_update(STATS_BADEXTRAFILE); + failed(); + } + q = NULL; + } + free(p); + } + + // Possibly hash GCC_COLORS (for color diagnostics). + if (guessed_compiler == GUESSED_GCC) { + const char* gcc_colors = getenv("GCC_COLORS"); + if (gcc_colors) { + hash_delimiter(hash, "gcccolors"); + hash_string(hash, gcc_colors); + } + } } // Update a hash sum with information specific to the direct and preprocessor // modes and calculate the result name. Returns the result name on success, // otherwise NULL. Caller frees. -static struct digest * -calculate_result_name(struct args *args, struct hash *hash, int direct_mode) +static struct digest* +calculate_result_name(struct args* args, struct hash* hash, int direct_mode) { - bool found_ccbin = false; - - hash_delimiter(hash, "result version"); - hash_int(hash, RESULT_VERSION); - - if (direct_mode) { - hash_delimiter(hash, "manifest version"); - hash_int(hash, MANIFEST_VERSION); - } - - // clang will emit warnings for unused linker flags, so we shouldn't skip - // those arguments. - int is_clang = - guessed_compiler == GUESSED_CLANG || guessed_compiler == GUESSED_UNKNOWN; - - // First the arguments. - for (int i = 1; i < args->argc; i++) { - // -L doesn't affect compilation (except for clang). - if (i < args->argc-1 && str_eq(args->argv[i], "-L") && !is_clang) { - i++; - continue; - } - if (str_startswith(args->argv[i], "-L") && !is_clang) { - continue; - } - - // -Wl,... doesn't affect compilation (except for clang). - if (str_startswith(args->argv[i], "-Wl,") && !is_clang) { - continue; - } - - // The -fdebug-prefix-map option may be used in combination with - // CCACHE_BASEDIR to reuse results across different directories. Skip using - // the value of the option from hashing but still hash the existence of the - // option. - if (str_startswith(args->argv[i], "-fdebug-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-fdebug-prefix-map="); - continue; - } - if (str_startswith(args->argv[i], "-ffile-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-ffile-prefix-map="); - continue; - } - if (str_startswith(args->argv[i], "-fmacro-prefix-map=")) { - hash_delimiter(hash, "arg"); - hash_string(hash, "-fmacro-prefix-map="); - continue; - } - - // When using the preprocessor, some arguments don't contribute to the - // hash. The theory is that these arguments will change the output of -E if - // they are going to have any effect at all. For precompiled headers this - // might not be the case. - if (!direct_mode && !output_is_precompiled_header - && !using_precompiled_header) { - if (compopt_affects_cpp(args->argv[i])) { - if (compopt_takes_arg(args->argv[i])) { - i++; - } - continue; - } - if (compopt_short(compopt_affects_cpp, args->argv[i])) { - continue; - } - } - - // If we're generating dependencies, we make sure to skip the filename of - // the dependency file, since it doesn't impact the output. - if (generating_dependencies) { - if (str_startswith(args->argv[i], "-Wp,")) { - if (str_startswith(args->argv[i], "-Wp,-MD,") - && !strchr(args->argv[i] + 8, ',')) { - hash_string_buffer(hash, args->argv[i], 8); - continue; - } else if (str_startswith(args->argv[i], "-Wp,-MMD,") - && !strchr(args->argv[i] + 9, ',')) { - hash_string_buffer(hash, args->argv[i], 9); - continue; - } - } else if (str_startswith(args->argv[i], "-MF")) { - // In either case, hash the "-MF" part. - hash_delimiter(hash, "arg"); - hash_string_buffer(hash, args->argv[i], 3); - - if (!str_eq(output_dep, "/dev/null")) { - bool separate_argument = (strlen(args->argv[i]) == 3); - if (separate_argument) { - // Next argument is dependency name, so skip it. - i++; - } - } - continue; - } - } - - char *p = NULL; - if (str_startswith(args->argv[i], "-specs=")) { - p = args->argv[i] + 7; - } else if (str_startswith(args->argv[i], "--specs=")) { - p = args->argv[i] + 8; - } - - struct stat st; - if (p && x_stat(p, &st) == 0) { - // If given an explicit specs file, then hash that file, but don't - // include the path to it in the hash. - hash_delimiter(hash, "specs"); - hash_compiler(hash, &st, p, false); - continue; - } - - if (str_startswith(args->argv[i], "-fplugin=") - && x_stat(args->argv[i] + 9, &st) == 0) { - hash_delimiter(hash, "plugin"); - hash_compiler(hash, &st, args->argv[i] + 9, false); - continue; - } - - if (str_eq(args->argv[i], "-Xclang") - && i + 3 < args->argc - && str_eq(args->argv[i+1], "-load") - && str_eq(args->argv[i+2], "-Xclang") - && x_stat(args->argv[i+3], &st) == 0) { - hash_delimiter(hash, "plugin"); - hash_compiler(hash, &st, args->argv[i+3], false); - i += 3; - continue; - } - - if ((str_eq(args->argv[i], "-ccbin") - || str_eq(args->argv[i], "--compiler-bindir")) - && i + 1 < args->argc - && x_stat(args->argv[i+1], &st) == 0) { - found_ccbin = true; - hash_delimiter(hash, "ccbin"); - hash_nvcc_host_compiler(hash, &st, args->argv[i+1]); - i++; - continue; - } - - // All other arguments are included in the hash. - hash_delimiter(hash, "arg"); - hash_string(hash, args->argv[i]); - if (i + 1 < args->argc && compopt_takes_arg(args->argv[i])) { - i++; - hash_delimiter(hash, "arg"); - hash_string(hash, args->argv[i]); - } - } - - // Make results with dependency file /dev/null different from those without - // it. - if (generating_dependencies && str_eq(output_dep, "/dev/null")) { - hash_delimiter(hash, "/dev/null dependency file"); - } - - if (!found_ccbin && str_eq(actual_language, "cu")) { - hash_nvcc_host_compiler(hash, NULL, NULL); - } - - // For profile generation (-fprofile-arcs, -fprofile-generate): - // - hash profile directory - // - // For profile usage (-fprofile-use): - // - hash profile data - // - // -fbranch-probabilities and -fvpt usage is covered by - // -fprofile-generate/-fprofile-use. - // - // The profile directory can be specified as an argument to - // -fprofile-generate=, -fprofile-use= or -fprofile-dir=. - if (profile_generate) { - if (!profile_dir) { - profile_dir = get_cwd(); - } - cc_log("Adding profile directory %s to our hash", profile_dir); - hash_delimiter(hash, "-fprofile-dir"); - hash_string(hash, profile_dir); - } - - if (profile_use) { - // Calculate gcda name. - if (!profile_dir) { - profile_dir = get_cwd(); - } - char *base_name = remove_extension(output_obj); - char *gcda_name = format("%s/%s.gcda", profile_dir, base_name); - cc_log("Adding profile data %s to our hash", gcda_name); - // Add the gcda to our hash. - hash_delimiter(hash, "-fprofile-use"); - hash_file(hash, gcda_name); - free(base_name); - free(gcda_name); - } - - // Adding -arch to hash since cpp output is affected. - for (size_t i = 0; i < arch_args_size; ++i) { - hash_delimiter(hash, "-arch"); - hash_string(hash, arch_args[i]); - } - - struct digest *result_name = NULL; - if (direct_mode) { - // Hash environment variables that affect the preprocessor output. - const char *envvars[] = { - "CPATH", - "C_INCLUDE_PATH", - "CPLUS_INCLUDE_PATH", - "OBJC_INCLUDE_PATH", - "OBJCPLUS_INCLUDE_PATH", // clang - NULL - }; - for (const char **p = envvars; *p; ++p) { - char *v = getenv(*p); - if (v) { - hash_delimiter(hash, *p); - hash_string(hash, v); - } - } - - if (!(conf->sloppiness & SLOPPY_FILE_MACRO)) { - // The source code file or an include file may contain __FILE__, so make - // sure that the hash is unique for the file name. - hash_delimiter(hash, "inputfile"); - hash_string(hash, input_file); - } - - hash_delimiter(hash, "sourcecode"); - int result = hash_source_code_file(conf, hash, input_file); - if (result & HASH_SOURCE_CODE_ERROR) { - failed(); - } - if (result & HASH_SOURCE_CODE_FOUND_TIME) { - cc_log("Disabling direct mode"); - conf->direct_mode = false; - return NULL; - } - - char manifest_name_string[DIGEST_STRING_BUFFER_SIZE]; - hash_result_as_string(hash, manifest_name_string); - manifest_path = get_path_in_cache(manifest_name_string, ".manifest"); - manifest_stats_file = - format("%s/%c/stats", conf->cache_dir, manifest_name_string[0]); - - cc_log("Looking for result name in %s", manifest_path); - MTR_BEGIN("manifest", "manifest_get"); - result_name = manifest_get(conf, manifest_path); - MTR_END("manifest", "manifest_get"); - if (result_name) { - cc_log("Got result name from manifest"); - } else { - cc_log("Did not find result name in manifest"); - } - } else { - if (arch_args_size == 0) { - result_name = get_result_name_from_cpp(args, hash); - cc_log("Got result name from preprocessor"); - } else { - args_add(args, "-arch"); - for (size_t i = 0; i < arch_args_size; ++i) { - args_add(args, arch_args[i]); - result_name = get_result_name_from_cpp(args, hash); - cc_log("Got result name from preprocessor with -arch %s", - arch_args[i]); - if (i != arch_args_size - 1) { - free(result_name); - result_name = NULL; - } - args_pop(args, 1); - } - args_pop(args, 1); - } - if (generating_dependencies) { - // Nothing is actually created with -MF /dev/null - if (!str_eq(output_dep, "/dev/null")) { - cc_log("Preprocessor created %s", output_dep); - } - } - } - - return result_name; + bool found_ccbin = false; + + hash_delimiter(hash, "result version"); + hash_int(hash, RESULT_VERSION); + + if (direct_mode) { + hash_delimiter(hash, "manifest version"); + hash_int(hash, MANIFEST_VERSION); + } + + // clang will emit warnings for unused linker flags, so we shouldn't skip + // those arguments. + int is_clang = + guessed_compiler == GUESSED_CLANG || guessed_compiler == GUESSED_UNKNOWN; + + // First the arguments. + for (int i = 1; i < args->argc; i++) { + // -L doesn't affect compilation (except for clang). + if (i < args->argc - 1 && str_eq(args->argv[i], "-L") && !is_clang) { + i++; + continue; + } + if (str_startswith(args->argv[i], "-L") && !is_clang) { + continue; + } + + // -Wl,... doesn't affect compilation (except for clang). + if (str_startswith(args->argv[i], "-Wl,") && !is_clang) { + continue; + } + + // The -fdebug-prefix-map option may be used in combination with + // CCACHE_BASEDIR to reuse results across different directories. Skip using + // the value of the option from hashing but still hash the existence of the + // option. + if (str_startswith(args->argv[i], "-fdebug-prefix-map=")) { + hash_delimiter(hash, "arg"); + hash_string(hash, "-fdebug-prefix-map="); + continue; + } + if (str_startswith(args->argv[i], "-ffile-prefix-map=")) { + hash_delimiter(hash, "arg"); + hash_string(hash, "-ffile-prefix-map="); + continue; + } + if (str_startswith(args->argv[i], "-fmacro-prefix-map=")) { + hash_delimiter(hash, "arg"); + hash_string(hash, "-fmacro-prefix-map="); + continue; + } + + // When using the preprocessor, some arguments don't contribute to the + // hash. The theory is that these arguments will change the output of -E if + // they are going to have any effect at all. For precompiled headers this + // might not be the case. + if (!direct_mode && !output_is_precompiled_header + && !using_precompiled_header) { + if (compopt_affects_cpp(args->argv[i])) { + if (compopt_takes_arg(args->argv[i])) { + i++; + } + continue; + } + if (compopt_short(compopt_affects_cpp, args->argv[i])) { + continue; + } + } + + // If we're generating dependencies, we make sure to skip the filename of + // the dependency file, since it doesn't impact the output. + if (generating_dependencies) { + if (str_startswith(args->argv[i], "-Wp,")) { + if (str_startswith(args->argv[i], "-Wp,-MD,") + && !strchr(args->argv[i] + 8, ',')) { + hash_string_buffer(hash, args->argv[i], 8); + continue; + } else if (str_startswith(args->argv[i], "-Wp,-MMD,") + && !strchr(args->argv[i] + 9, ',')) { + hash_string_buffer(hash, args->argv[i], 9); + continue; + } + } else if (str_startswith(args->argv[i], "-MF")) { + // In either case, hash the "-MF" part. + hash_delimiter(hash, "arg"); + hash_string_buffer(hash, args->argv[i], 3); + + if (!str_eq(output_dep, "/dev/null")) { + bool separate_argument = (strlen(args->argv[i]) == 3); + if (separate_argument) { + // Next argument is dependency name, so skip it. + i++; + } + } + continue; + } + } + + char* p = NULL; + if (str_startswith(args->argv[i], "-specs=")) { + p = args->argv[i] + 7; + } else if (str_startswith(args->argv[i], "--specs=")) { + p = args->argv[i] + 8; + } + + struct stat st; + if (p && x_stat(p, &st) == 0) { + // If given an explicit specs file, then hash that file, but don't + // include the path to it in the hash. + hash_delimiter(hash, "specs"); + hash_compiler(hash, &st, p, false); + continue; + } + + if (str_startswith(args->argv[i], "-fplugin=") + && x_stat(args->argv[i] + 9, &st) == 0) { + hash_delimiter(hash, "plugin"); + hash_compiler(hash, &st, args->argv[i] + 9, false); + continue; + } + + if (str_eq(args->argv[i], "-Xclang") && i + 3 < args->argc + && str_eq(args->argv[i + 1], "-load") + && str_eq(args->argv[i + 2], "-Xclang") + && x_stat(args->argv[i + 3], &st) == 0) { + hash_delimiter(hash, "plugin"); + hash_compiler(hash, &st, args->argv[i + 3], false); + i += 3; + continue; + } + + if ((str_eq(args->argv[i], "-ccbin") + || str_eq(args->argv[i], "--compiler-bindir")) + && i + 1 < args->argc && x_stat(args->argv[i + 1], &st) == 0) { + found_ccbin = true; + hash_delimiter(hash, "ccbin"); + hash_nvcc_host_compiler(hash, &st, args->argv[i + 1]); + i++; + continue; + } + + // All other arguments are included in the hash. + hash_delimiter(hash, "arg"); + hash_string(hash, args->argv[i]); + if (i + 1 < args->argc && compopt_takes_arg(args->argv[i])) { + i++; + hash_delimiter(hash, "arg"); + hash_string(hash, args->argv[i]); + } + } + + // Make results with dependency file /dev/null different from those without + // it. + if (generating_dependencies && str_eq(output_dep, "/dev/null")) { + hash_delimiter(hash, "/dev/null dependency file"); + } + + if (!found_ccbin && str_eq(actual_language, "cu")) { + hash_nvcc_host_compiler(hash, NULL, NULL); + } + + // For profile generation (-fprofile-arcs, -fprofile-generate): + // - hash profile directory + // + // For profile usage (-fprofile-use): + // - hash profile data + // + // -fbranch-probabilities and -fvpt usage is covered by + // -fprofile-generate/-fprofile-use. + // + // The profile directory can be specified as an argument to + // -fprofile-generate=, -fprofile-use= or -fprofile-dir=. + if (profile_generate) { + if (!profile_dir) { + profile_dir = get_cwd(); + } + cc_log("Adding profile directory %s to our hash", profile_dir); + hash_delimiter(hash, "-fprofile-dir"); + hash_string(hash, profile_dir); + } + + if (profile_use) { + // Calculate gcda name. + if (!profile_dir) { + profile_dir = get_cwd(); + } + char* base_name = remove_extension(output_obj); + char* gcda_name = format("%s/%s.gcda", profile_dir, base_name); + cc_log("Adding profile data %s to our hash", gcda_name); + // Add the gcda to our hash. + hash_delimiter(hash, "-fprofile-use"); + hash_file(hash, gcda_name); + free(base_name); + free(gcda_name); + } + + // Adding -arch to hash since cpp output is affected. + for (size_t i = 0; i < arch_args_size; ++i) { + hash_delimiter(hash, "-arch"); + hash_string(hash, arch_args[i]); + } + + struct digest* result_name = NULL; + if (direct_mode) { + // Hash environment variables that affect the preprocessor output. + const char* envvars[] = {"CPATH", + "C_INCLUDE_PATH", + "CPLUS_INCLUDE_PATH", + "OBJC_INCLUDE_PATH", + "OBJCPLUS_INCLUDE_PATH", // clang + NULL}; + for (const char** p = envvars; *p; ++p) { + char* v = getenv(*p); + if (v) { + hash_delimiter(hash, *p); + hash_string(hash, v); + } + } + + if (!(conf->sloppiness & SLOPPY_FILE_MACRO)) { + // The source code file or an include file may contain __FILE__, so make + // sure that the hash is unique for the file name. + hash_delimiter(hash, "inputfile"); + hash_string(hash, input_file); + } + + hash_delimiter(hash, "sourcecode"); + int result = hash_source_code_file(conf, hash, input_file); + if (result & HASH_SOURCE_CODE_ERROR) { + failed(); + } + if (result & HASH_SOURCE_CODE_FOUND_TIME) { + cc_log("Disabling direct mode"); + conf->direct_mode = false; + return NULL; + } + + char manifest_name_string[DIGEST_STRING_BUFFER_SIZE]; + hash_result_as_string(hash, manifest_name_string); + manifest_path = get_path_in_cache(manifest_name_string, ".manifest"); + manifest_stats_file = + format("%s/%c/stats", conf->cache_dir, manifest_name_string[0]); + + cc_log("Looking for result name in %s", manifest_path); + MTR_BEGIN("manifest", "manifest_get"); + result_name = manifest_get(conf, manifest_path); + MTR_END("manifest", "manifest_get"); + if (result_name) { + cc_log("Got result name from manifest"); + } else { + cc_log("Did not find result name in manifest"); + } + } else { + if (arch_args_size == 0) { + result_name = get_result_name_from_cpp(args, hash); + cc_log("Got result name from preprocessor"); + } else { + args_add(args, "-arch"); + for (size_t i = 0; i < arch_args_size; ++i) { + args_add(args, arch_args[i]); + result_name = get_result_name_from_cpp(args, hash); + cc_log("Got result name from preprocessor with -arch %s", arch_args[i]); + if (i != arch_args_size - 1) { + free(result_name); + result_name = NULL; + } + args_pop(args, 1); + } + args_pop(args, 1); + } + if (generating_dependencies) { + // Nothing is actually created with -MF /dev/null + if (!str_eq(output_dep, "/dev/null")) { + cc_log("Preprocessor created %s", output_dep); + } + } + } + + return result_name; } // Try to return the compile result from cache. If we can return from cache @@ -2103,1392 +2116,1383 @@ calculate_result_name(struct args *args, struct hash *hash, int direct_mode) static void from_cache(enum fromcache_call_mode mode, bool put_result_in_manifest) { - // The user might be disabling cache hits. - if (conf->recache) { - return; - } - - // If we're using Clang, we can't trust a precompiled header object based on - // running the preprocessor since clang will produce a fatal error when the - // precompiled header is used and one of the included files has an updated - // timestamp: - // - // file 'foo.h' has been modified since the precompiled header 'foo.pch' - // was built - if ((guessed_compiler == GUESSED_CLANG || guessed_compiler == GUESSED_UNKNOWN) - && output_is_precompiled_header - && mode == FROMCACHE_CPP_MODE) { - cc_log("Not considering cached precompiled header in preprocessor mode"); - return; - } - - MTR_BEGIN("cache", "from_cache"); - - // (If mode != FROMCACHE_DIRECT_MODE, the dependency file is created by gcc.) - bool produce_dep_file = - generating_dependencies && mode == FROMCACHE_DIRECT_MODE - && !str_eq(output_dep, "/dev/null"); - - MTR_BEGIN("file", "file_get"); - - // Get result from cache. - char *tmp_stderr = format("%s/tmp.stderr", temp_dir()); - int tmp_stderr_fd = create_tmp_fd(&tmp_stderr); - close(tmp_stderr_fd); - - struct result_files *result_files = result_files_init(); - if (!str_eq(output_obj, "/dev/null")) { - result_files_add(result_files, output_obj, ".o"); - if (seen_split_dwarf) { - result_files_add(result_files, output_dwo, ".dwo"); - } - } - result_files_add(result_files, tmp_stderr, RESULT_STDERR_NAME); - if (produce_dep_file) { - result_files_add(result_files, output_dep, ".d"); - } - if (generating_coverage) { - result_files_add(result_files, output_cov, ".gcno"); - } - if (generating_stackusage) { - result_files_add(result_files, output_su, ".su"); - } - if (generating_diagnostics) { - result_files_add(result_files, output_dia, ".dia"); - } - bool ok = result_get(cached_result_path, result_files); - result_files_free(result_files); - if (!ok) { - cc_log("Failed to get result from cache"); - tmp_unlink(tmp_stderr); - free(tmp_stderr); - return; - } - - MTR_END("file", "file_get"); - - send_cached_stderr(tmp_stderr); - - if (put_result_in_manifest) { - update_manifest_file(); - } - - tmp_unlink(tmp_stderr); - free(tmp_stderr); - - // Log the cache hit. - switch (mode) { - case FROMCACHE_DIRECT_MODE: - cc_log("Succeeded getting cached result"); - stats_update(STATS_CACHEHIT_DIR); - break; - - case FROMCACHE_CPP_MODE: - cc_log("Succeeded getting cached result"); - stats_update(STATS_CACHEHIT_CPP); - break; - } - - MTR_END("cache", "from_cache"); - - // And exit with the right status code. - x_exit(0); + // The user might be disabling cache hits. + if (conf->recache) { + return; + } + + // If we're using Clang, we can't trust a precompiled header object based on + // running the preprocessor since clang will produce a fatal error when the + // precompiled header is used and one of the included files has an updated + // timestamp: + // + // file 'foo.h' has been modified since the precompiled header 'foo.pch' + // was built + if ((guessed_compiler == GUESSED_CLANG || guessed_compiler == GUESSED_UNKNOWN) + && output_is_precompiled_header && mode == FROMCACHE_CPP_MODE) { + cc_log("Not considering cached precompiled header in preprocessor mode"); + return; + } + + MTR_BEGIN("cache", "from_cache"); + + // (If mode != FROMCACHE_DIRECT_MODE, the dependency file is created by gcc.) + bool produce_dep_file = generating_dependencies + && mode == FROMCACHE_DIRECT_MODE + && !str_eq(output_dep, "/dev/null"); + + MTR_BEGIN("file", "file_get"); + + // Get result from cache. + char* tmp_stderr = format("%s/tmp.stderr", temp_dir()); + int tmp_stderr_fd = create_tmp_fd(&tmp_stderr); + close(tmp_stderr_fd); + + struct result_files* result_files = result_files_init(); + if (!str_eq(output_obj, "/dev/null")) { + result_files_add(result_files, output_obj, ".o"); + if (seen_split_dwarf) { + result_files_add(result_files, output_dwo, ".dwo"); + } + } + result_files_add(result_files, tmp_stderr, RESULT_STDERR_NAME); + if (produce_dep_file) { + result_files_add(result_files, output_dep, ".d"); + } + if (generating_coverage) { + result_files_add(result_files, output_cov, ".gcno"); + } + if (generating_stackusage) { + result_files_add(result_files, output_su, ".su"); + } + if (generating_diagnostics) { + result_files_add(result_files, output_dia, ".dia"); + } + bool ok = result_get(cached_result_path, result_files); + result_files_free(result_files); + if (!ok) { + cc_log("Failed to get result from cache"); + tmp_unlink(tmp_stderr); + free(tmp_stderr); + return; + } + + MTR_END("file", "file_get"); + + send_cached_stderr(tmp_stderr); + + if (put_result_in_manifest) { + update_manifest_file(); + } + + tmp_unlink(tmp_stderr); + free(tmp_stderr); + + // Log the cache hit. + switch (mode) { + case FROMCACHE_DIRECT_MODE: + cc_log("Succeeded getting cached result"); + stats_update(STATS_CACHEHIT_DIR); + break; + + case FROMCACHE_CPP_MODE: + cc_log("Succeeded getting cached result"); + stats_update(STATS_CACHEHIT_CPP); + break; + } + + MTR_END("cache", "from_cache"); + + // And exit with the right status code. + x_exit(0); } // Find the real compiler. We just search the PATH to find an executable of the // same name that isn't a link to ourselves. static void -find_compiler(char **argv) +find_compiler(char** argv) { - // We might be being invoked like "ccache gcc -c foo.c". - char *base = x_basename(argv[0]); - if (same_executable_name(base, MYNAME)) { - args_remove_first(orig_args); - free(base); - if (is_full_path(orig_args->argv[0])) { - // A full path was given. - return; - } - base = x_basename(orig_args->argv[0]); - } - - // Support user override of the compiler. - if (!str_eq(conf->compiler, "")) { - base = conf->compiler; - } - - char *compiler = find_executable(base, MYNAME); - if (!compiler) { - stats_update(STATS_COMPILER); - fatal("Could not find compiler \"%s\" in PATH", base); - } - if (str_eq(compiler, argv[0])) { - fatal("Recursive invocation (the name of the ccache binary must be \"%s\")", - MYNAME); - } - orig_args->argv[0] = compiler; + // We might be being invoked like "ccache gcc -c foo.c". + char* base = x_basename(argv[0]); + if (same_executable_name(base, MYNAME)) { + args_remove_first(orig_args); + free(base); + if (is_full_path(orig_args->argv[0])) { + // A full path was given. + return; + } + base = x_basename(orig_args->argv[0]); + } + + // Support user override of the compiler. + if (!str_eq(conf->compiler, "")) { + base = conf->compiler; + } + + char* compiler = find_executable(base, MYNAME); + if (!compiler) { + stats_update(STATS_COMPILER); + fatal("Could not find compiler \"%s\" in PATH", base); + } + if (str_eq(compiler, argv[0])) { + fatal("Recursive invocation (the name of the ccache binary must be \"%s\")", + MYNAME); + } + orig_args->argv[0] = compiler; } bool -is_precompiled_header(const char *path) +is_precompiled_header(const char* path) { - const char *ext = get_extension(path); - char *dir = x_dirname(path); - const char *dir_ext = get_extension(dir); - bool result = - str_eq(ext, ".gch") - || str_eq(ext, ".pch") - || str_eq(ext, ".pth") - || str_eq(dir_ext, ".gch"); // See "Precompiled Headers" in GCC docs. - free(dir); - return result; + const char* ext = get_extension(path); + char* dir = x_dirname(path); + const char* dir_ext = get_extension(dir); + bool result = + str_eq(ext, ".gch") || str_eq(ext, ".pch") || str_eq(ext, ".pth") + || str_eq(dir_ext, ".gch"); // See "Precompiled Headers" in GCC docs. + free(dir); + return result; } static bool color_output_possible(void) { - const char *term_env = getenv("TERM"); - return isatty(STDERR_FILENO) && term_env && strcasecmp(term_env, "DUMB") != 0; + const char* term_env = getenv("TERM"); + return isatty(STDERR_FILENO) && term_env && strcasecmp(term_env, "DUMB") != 0; } static bool -detect_pch(const char *option, const char *arg, bool *found_pch) +detect_pch(const char* option, const char* arg, bool* found_pch) { - struct stat st; - - // Try to be smart about detecting precompiled headers. - char *pch_file = NULL; - if (str_eq(option, "-include-pch") || str_eq(option, "-include-pth")) { - if (stat(arg, &st) == 0) { - cc_log("Detected use of precompiled header: %s", arg); - pch_file = x_strdup(arg); - } - } else { - char *gchpath = format("%s.gch", arg); - if (stat(gchpath, &st) == 0) { - cc_log("Detected use of precompiled header: %s", gchpath); - pch_file = x_strdup(gchpath); - } else { - char *pchpath = format("%s.pch", arg); - if (stat(pchpath, &st) == 0) { - cc_log("Detected use of precompiled header: %s", pchpath); - pch_file = x_strdup(pchpath); - } else { - // clang may use pretokenized headers. - char *pthpath = format("%s.pth", arg); - if (stat(pthpath, &st) == 0) { - cc_log("Detected use of pretokenized header: %s", pthpath); - pch_file = x_strdup(pthpath); - } - free(pthpath); - } - free(pchpath); - } - free(gchpath); - } - - if (pch_file) { - if (included_pch_file) { - cc_log("Multiple precompiled headers used: %s and %s\n", - included_pch_file, pch_file); - stats_update(STATS_ARGS); - return false; - } - included_pch_file = pch_file; - *found_pch = true; - } - return true; + struct stat st; + + // Try to be smart about detecting precompiled headers. + char* pch_file = NULL; + if (str_eq(option, "-include-pch") || str_eq(option, "-include-pth")) { + if (stat(arg, &st) == 0) { + cc_log("Detected use of precompiled header: %s", arg); + pch_file = x_strdup(arg); + } + } else { + char* gchpath = format("%s.gch", arg); + if (stat(gchpath, &st) == 0) { + cc_log("Detected use of precompiled header: %s", gchpath); + pch_file = x_strdup(gchpath); + } else { + char* pchpath = format("%s.pch", arg); + if (stat(pchpath, &st) == 0) { + cc_log("Detected use of precompiled header: %s", pchpath); + pch_file = x_strdup(pchpath); + } else { + // clang may use pretokenized headers. + char* pthpath = format("%s.pth", arg); + if (stat(pthpath, &st) == 0) { + cc_log("Detected use of pretokenized header: %s", pthpath); + pch_file = x_strdup(pthpath); + } + free(pthpath); + } + free(pchpath); + } + free(gchpath); + } + + if (pch_file) { + if (included_pch_file) { + cc_log("Multiple precompiled headers used: %s and %s\n", + included_pch_file, + pch_file); + stats_update(STATS_ARGS); + return false; + } + included_pch_file = pch_file; + *found_pch = true; + } + return true; } // Process the compiler options into options suitable for passing to the // preprocessor and the real compiler. The preprocessor options don't include // -E; this is added later. Returns true on success, otherwise false. bool -cc_process_args(struct args *args, struct args **preprocessor_args, - struct args **compiler_args) +cc_process_args(struct args* args, + struct args** preprocessor_args, + struct args** compiler_args) { - bool found_c_opt = false; - bool found_dc_opt = false; - bool found_S_opt = false; - bool found_pch = false; - bool found_fpch_preprocess = false; - const char *explicit_language = NULL; // As specified with -x. - const char *file_language; // As deduced from file extension. - const char *input_charset = NULL; - - // Is the dependency makefile name overridden with -MF? - bool dependency_filename_specified = false; - - // Is the dependency makefile target name specified with -MT or -MQ? - bool dependency_target_specified = false; - - // Is the dependency target name implicitly specified using - // DEPENDENCIES_OUTPUT or SUNPRO_DEPENDENCIES? - bool dependency_implicit_target_specified = false; - - // expanded_args is a copy of the original arguments given to the compiler - // but with arguments from @file and similar constructs expanded. It's only - // used as a temporary data structure to loop over. - struct args *expanded_args = args_copy(args); - - // common_args contains all original arguments except: - // * those that never should be passed to the preprocessor, - // * those that only should be passed to the preprocessor (if run_second_cpp - // is false), and - // * dependency options (like -MD and friends). - struct args *common_args = args_init(0, NULL); - - // cpp_args contains arguments that were not added to common_args, i.e. those - // that should only be passed to the preprocessor if run_second_cpp is false. - // If run_second_cpp is true, they will be passed to the compiler as well. - struct args *cpp_args = args_init(0, NULL); - - // dep_args contains dependency options like -MD. They are only passed to the - // preprocessor, never to the compiler. - struct args *dep_args = args_init(0, NULL); - - // compiler_only_args contains arguments that should only be passed to the - // compiler, not the preprocessor. - struct args *compiler_only_args = args_init(0, NULL); - - bool found_color_diagnostics = false; - bool found_directives_only = false; - bool found_rewrite_includes = false; - - int argc = expanded_args->argc; - char **argv = expanded_args->argv; - args_add(common_args, argv[0]); - - bool result = true; - for (int i = 1; i < argc; i++) { - // The user knows best: just swallow the next arg. - if (str_eq(argv[i], "--ccache-skip")) { - i++; - if (i == argc) { - cc_log("--ccache-skip lacks an argument"); - result = false; - goto out; - } - args_add(common_args, argv[i]); - continue; - } - - // Special case for -E. - if (str_eq(argv[i], "-E")) { - stats_update(STATS_PREPROCESSING); - result = false; - goto out; - } - - // Handle "@file" argument. - if (str_startswith(argv[i], "@") || str_startswith(argv[i], "-@")) { - char *argpath = argv[i] + 1; - - if (argpath[-1] == '-') { - ++argpath; - } - struct args *file_args = args_init_from_gcc_atfile(argpath); - if (!file_args) { - cc_log("Couldn't read arg file %s", argpath); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - args_insert(expanded_args, i, file_args, true); - argc = expanded_args->argc; - argv = expanded_args->argv; - i--; - continue; - } - - // Handle cuda "-optf" and "--options-file" argument. - if (guessed_compiler == GUESSED_NVCC - && (str_eq(argv[i], "-optf") || str_eq(argv[i], "--options-file"))) { - if (i == argc - 1) { - cc_log("Expected argument after %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - ++i; - - // Argument is a comma-separated list of files. - char *str_start = argv[i]; - char *str_end = strchr(str_start, ','); - int index = i + 1; - - if (!str_end) { - str_end = str_start + strlen(str_start); - } - - while (str_end) { - *str_end = '\0'; - struct args *file_args = args_init_from_gcc_atfile(str_start); - if (!file_args) { - cc_log("Couldn't read cuda options file %s", str_start); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - int new_index = file_args->argc + index; - args_insert(expanded_args, index, file_args, false); - index = new_index; - str_start = str_end; - str_end = strchr(str_start, ','); - } - - argc = expanded_args->argc; - argv = expanded_args->argv; - continue; - } - - // These are always too hard. - if (compopt_too_hard(argv[i]) || str_startswith(argv[i], "-fdump-")) { - cc_log("Compiler option %s is unsupported", argv[i]); - stats_update(STATS_UNSUPPORTED_OPTION); - result = false; - goto out; - } - - // These are too hard in direct mode. - if (conf->direct_mode && compopt_too_hard_for_direct_mode(argv[i])) { - cc_log("Unsupported compiler option for direct mode: %s", argv[i]); - conf->direct_mode = false; - } - - // -Xarch_* options are too hard. - if (str_startswith(argv[i], "-Xarch_")) { - cc_log("Unsupported compiler option :%s", argv[i]); - stats_update(STATS_UNSUPPORTED_OPTION); - result = false; - goto out; - } - - // Handle -arch options. - if (str_eq(argv[i], "-arch")) { - if (arch_args_size == MAX_ARCH_ARGS - 1) { - cc_log("Too many -arch compiler options; ccache supports at most %d", - MAX_ARCH_ARGS); - stats_update(STATS_UNSUPPORTED_OPTION); - result = false; - goto out; - } - - ++i; - arch_args[arch_args_size] = x_strdup(argv[i]); // It will leak. - ++arch_args_size; - if (arch_args_size == 2) { - conf->run_second_cpp = true; - } - continue; - } - - // Handle options that should not be passed to the preprocessor. - if (compopt_affects_comp(argv[i])) { - args_add(compiler_only_args, argv[i]); - if (compopt_takes_arg(argv[i])) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(compiler_only_args, argv[i + 1]); - ++i; - } - continue; - } - if (compopt_prefix_affects_comp(argv[i])) { - args_add(compiler_only_args, argv[i]); - continue; - } - - if (str_eq(argv[i], "-fpch-preprocess") - || str_eq(argv[i], "-emit-pch") - || str_eq(argv[i], "-emit-pth")) { - found_fpch_preprocess = true; - } - - // We must have -c. - if (str_eq(argv[i], "-c")) { - found_c_opt = true; - continue; - } - - // when using nvcc with separable compilation, -dc implies -c - if ((str_eq(argv[i], "-dc") || str_eq(argv[i], "--device-c")) && guessed_compiler == GUESSED_NVCC) { - found_dc_opt = true; - continue; - } - - // -S changes the default extension. - if (str_eq(argv[i], "-S")) { - args_add(common_args, argv[i]); - found_S_opt = true; - continue; - } - - // Special handling for -x: remember the last specified language before the - // input file and strip all -x options from the arguments. - if (str_eq(argv[i], "-x")) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - if (!input_file) { - explicit_language = argv[i+1]; - } - i++; - continue; - } - if (str_startswith(argv[i], "-x")) { - if (!input_file) { - explicit_language = &argv[i][2]; - } - continue; - } - - // We need to work out where the output was meant to go. - if (str_eq(argv[i], "-o")) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - output_obj = make_relative_path(x_strdup(argv[i+1])); - i++; - continue; - } - - // Alternate form of -o with no space. Nvcc does not support this. - if (str_startswith(argv[i], "-o") && guessed_compiler != GUESSED_NVCC) { - output_obj = make_relative_path(x_strdup(&argv[i][2])); - continue; - } - - if (str_startswith(argv[i], "-fdebug-prefix-map=") - || str_startswith(argv[i], "-ffile-prefix-map=")) { - debug_prefix_maps = static_cast<char**>( - x_realloc( - debug_prefix_maps, - (debug_prefix_maps_len + 1) * sizeof(char *))); - debug_prefix_maps[debug_prefix_maps_len++] = - x_strdup(&argv[i][argv[i][2] == 'f' ? 18 : 19]); - args_add(common_args, argv[i]); - continue; - } - - // Debugging is handled specially, so that we know if we can strip line - // number info. - if (str_startswith(argv[i], "-g")) { - args_add(common_args, argv[i]); - - if (str_startswith(argv[i], "-gdwarf")) { - // Selection of DWARF format (-gdwarf or -gdwarf-<version>) enables - // debug info on level 2. - generating_debuginfo = true; - continue; - } - - char last_char = argv[i][strlen(argv[i]) - 1]; - if (last_char == '0') { - // "-g0", "-ggdb0" or similar: All debug information disabled. - generating_debuginfo = false; - generating_debuginfo_level_3 = false; - } else { - generating_debuginfo = true; - if (last_char == '3') { - generating_debuginfo_level_3 = true; - } - if (str_eq(argv[i], "-gsplit-dwarf")) { - seen_split_dwarf = true; - } - } - continue; - } - - // These options require special handling, because they behave differently - // with gcc -E, when the output file is not specified. - if (str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD")) { - generating_dependencies = true; - args_add(dep_args, argv[i]); - continue; - } - if (str_startswith(argv[i], "-MF")) { - dependency_filename_specified = true; - free(output_dep); - - char *arg; - bool separate_argument = (strlen(argv[i]) == 3); - if (separate_argument) { - // -MF arg - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - arg = argv[i + 1]; - i++; - } else { - // -MFarg - arg = &argv[i][3]; - } - output_dep = make_relative_path(x_strdup(arg)); - // Keep the format of the args the same. - if (separate_argument) { - args_add(dep_args, "-MF"); - args_add(dep_args, output_dep); - } else { - char *option = format("-MF%s", output_dep); - args_add(dep_args, option); - free(option); - } - continue; - } - if (str_startswith(argv[i], "-MQ") || str_startswith(argv[i], "-MT")) { - dependency_target_specified = true; - - char *relpath; - if (strlen(argv[i]) == 3) { - // -MQ arg or -MT arg - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(dep_args, argv[i]); - relpath = make_relative_path(x_strdup(argv[i + 1])); - args_add(dep_args, relpath); - free(relpath); - i++; - } else { - char *arg_opt = x_strndup(argv[i], 3); - relpath = make_relative_path(x_strdup(argv[i] + 3)); - char *option = format("%s%s", arg_opt, relpath); - args_add(dep_args, option); - free(arg_opt); - free(relpath); - free(option); - } - continue; - } - if (str_eq(argv[i], "-fprofile-arcs")) { - profile_arcs = true; - args_add(common_args, argv[i]); - continue; - } - if (str_eq(argv[i], "-ftest-coverage")) { - generating_coverage = true; - args_add(common_args, argv[i]); - continue; - } - if (str_eq(argv[i], "-fstack-usage")) { - generating_stackusage = true; - args_add(common_args, argv[i]); - continue; - } - if (str_eq(argv[i], "--coverage") // = -fprofile-arcs -ftest-coverage - || str_eq(argv[i], "-coverage")) { // Undocumented but still works. - profile_arcs = true; - generating_coverage = true; - args_add(common_args, argv[i]); - continue; - } - if (str_startswith(argv[i], "-fprofile-dir=")) { - profile_dir = x_strdup(argv[i] + 14); - args_add(common_args, argv[i]); - continue; - } - if (str_startswith(argv[i], "-fsanitize-blacklist=")) { - sanitize_blacklists = static_cast<char**>( - x_realloc( - sanitize_blacklists, - (sanitize_blacklists_len + 1) * sizeof(char *))); - sanitize_blacklists[sanitize_blacklists_len++] = x_strdup(argv[i] + 21); - args_add(common_args, argv[i]); - continue; - } - if (str_startswith(argv[i], "--sysroot=")) { - char *relpath = make_relative_path(x_strdup(argv[i] + 10)); - char *option = format("--sysroot=%s", relpath); - args_add(common_args, option); - free(relpath); - free(option); - continue; - } - // Alternate form of specifying sysroot without = - if (str_eq(argv[i], "--sysroot")) { - if (i == argc-1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(common_args, argv[i]); - char *relpath = make_relative_path(x_strdup(argv[i+1])); - args_add(common_args, relpath); - i++; - free(relpath); - continue; - } - // Alternate form of specifying target without = - if (str_eq(argv[i], "-target")) { - if (i == argc-1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - args_add(common_args, argv[i]); - args_add(common_args, argv[i+1]); - i++; - continue; - } - if (str_startswith(argv[i], "-Wp,")) { - if (str_eq(argv[i], "-Wp,-P") - || strstr(argv[i], ",-P,") - || str_endswith(argv[i], ",-P")) { - // -P removes preprocessor information in such a way that the object - // file from compiling the preprocessed file will not be equal to the - // object file produced when compiling without ccache. - cc_log("Too hard option -Wp,-P detected"); - stats_update(STATS_UNSUPPORTED_OPTION); - failed(); - } else if (str_startswith(argv[i], "-Wp,-MD,") - && !strchr(argv[i] + 8, ',')) { - generating_dependencies = true; - dependency_filename_specified = true; - free(output_dep); - output_dep = make_relative_path(x_strdup(argv[i] + 8)); - args_add(dep_args, argv[i]); - continue; - } else if (str_startswith(argv[i], "-Wp,-MMD,") - && !strchr(argv[i] + 9, ',')) { - generating_dependencies = true; - dependency_filename_specified = true; - free(output_dep); - output_dep = make_relative_path(x_strdup(argv[i] + 9)); - args_add(dep_args, argv[i]); - continue; - } else if (str_startswith(argv[i], "-Wp,-D") - && !strchr(argv[i] + 6, ',')) { - // Treat it like -D. - args_add(cpp_args, argv[i] + 4); - continue; - } else if (str_eq(argv[i], "-Wp,-MP") - || (strlen(argv[i]) > 8 - && str_startswith(argv[i], "-Wp,-M") - && argv[i][7] == ',' - && (argv[i][6] == 'F' - || argv[i][6] == 'Q' - || argv[i][6] == 'T') - && !strchr(argv[i] + 8, ','))) { - // TODO: Make argument to MF/MQ/MT relative. - args_add(dep_args, argv[i]); - continue; - } else if (conf->direct_mode) { - // -Wp, can be used to pass too hard options to the preprocessor. - // Hence, disable direct mode. - cc_log("Unsupported compiler option for direct mode: %s", argv[i]); - conf->direct_mode = false; - } - - // Any other -Wp,* arguments are only relevant for the preprocessor. - args_add(cpp_args, argv[i]); - continue; - } - if (str_eq(argv[i], "-MP")) { - args_add(dep_args, argv[i]); - continue; - } - - // Input charset needs to be handled specially. - if (str_startswith(argv[i], "-finput-charset=")) { - input_charset = argv[i]; - continue; - } - - if (str_eq(argv[i], "--serialize-diagnostics")) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - generating_diagnostics = true; - output_dia = make_relative_path(x_strdup(argv[i+1])); - i++; - continue; - } - - if (str_startswith(argv[i], "-fprofile-")) { - char *arg = x_strdup(argv[i]); - const char *arg_profile_dir = strchr(argv[i], '='); - if (arg_profile_dir) { - // Convert to absolute path. - char *dir = x_realpath(arg_profile_dir + 1); - if (!dir) { - // Directory doesn't exist. - dir = x_strdup(arg_profile_dir + 1); - } - - // We can get a better hit rate by using the real path here. - free(arg); - char *option = x_strndup(argv[i], arg_profile_dir - argv[i]); - arg = format("%s=%s", option, dir); - cc_log("Rewriting %s to %s", argv[i], arg); - free(option); - free(dir); - } - - bool supported_profile_option = false; - if (str_startswith(argv[i], "-fprofile-generate") - || str_eq(argv[i], "-fprofile-arcs")) { - profile_generate = true; - supported_profile_option = true; - } else if (str_startswith(argv[i], "-fprofile-use") - || str_eq(argv[i], "-fbranch-probabilities")) { - profile_use = true; - supported_profile_option = true; - } else if (str_eq(argv[i], "-fprofile-dir")) { - supported_profile_option = true; - } - - if (supported_profile_option) { - args_add(common_args, arg); - free(arg); - - // If the profile directory has already been set, give up... Hard to - // know what the user means, and what the compiler will do. - if (arg_profile_dir && profile_dir) { - cc_log("Profile directory already set; giving up"); - result = false; - goto out; - } else if (arg_profile_dir) { - cc_log("Setting profile directory to %s", arg_profile_dir); - profile_dir = x_strdup(arg_profile_dir); - } - continue; - } - cc_log("Unknown profile option: %s", argv[i]); - free(arg); - } - - if (str_eq(argv[i], "-fcolor-diagnostics") - || str_eq(argv[i], "-fno-color-diagnostics") - || str_eq(argv[i], "-fdiagnostics-color") - || str_eq(argv[i], "-fdiagnostics-color=always") - || str_eq(argv[i], "-fno-diagnostics-color") - || str_eq(argv[i], "-fdiagnostics-color=never")) { - args_add(common_args, argv[i]); - found_color_diagnostics = true; - continue; - } - if (str_eq(argv[i], "-fdiagnostics-color=auto")) { - if (color_output_possible()) { - // Output is redirected, so color output must be forced. - args_add(common_args, "-fdiagnostics-color=always"); - cc_log("Automatically forcing colors"); - } else { - args_add(common_args, argv[i]); - } - found_color_diagnostics = true; - continue; - } - - // GCC - if (str_eq(argv[i], "-fdirectives-only")) { - found_directives_only = true; - continue; - } - // Clang - if (str_eq(argv[i], "-frewrite-includes")) { - found_rewrite_includes = true; - continue; - } - - if (conf->sloppiness & SLOPPY_CLANG_INDEX_STORE - && str_eq(argv[i], "-index-store-path")) { - // Xcode 9 or later calls Clang with this option. The given path includes - // a UUID that might lead to cache misses, especially when cache is - // shared among multiple users. - i++; - if (i <= argc - 1) { - cc_log("Skipping argument -index-store-path %s", argv[i]); - } - continue; - } - - // Options taking an argument that we may want to rewrite to relative paths - // to get better hit rate. A secondary effect is that paths in the standard - // error output produced by the compiler will be normalized. - if (compopt_takes_path(argv[i])) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - if (!detect_pch(argv[i], argv[i+1], &found_pch)) { - result = false; - goto out; - } - - char *relpath = make_relative_path(x_strdup(argv[i+1])); - if (compopt_affects_cpp(argv[i])) { - args_add(cpp_args, argv[i]); - args_add(cpp_args, relpath); - } else { - args_add(common_args, argv[i]); - args_add(common_args, relpath); - } - free(relpath); - - i++; - continue; - } - - // Same as above but options with concatenated argument beginning with a - // slash. - if (argv[i][0] == '-') { - char *slash_pos = strchr(argv[i], '/'); - if (slash_pos) { - char *option = x_strndup(argv[i], slash_pos - argv[i]); - if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) { - char *relpath = make_relative_path(x_strdup(slash_pos)); - char *new_option = format("%s%s", option, relpath); - if (compopt_affects_cpp(option)) { - args_add(cpp_args, new_option); - } else { - args_add(common_args, new_option); - } - free(new_option); - free(relpath); - free(option); - continue; - } else { - free(option); - } - } - } - - // Options that take an argument. - if (compopt_takes_arg(argv[i])) { - if (i == argc - 1) { - cc_log("Missing argument to %s", argv[i]); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - if (compopt_affects_cpp(argv[i])) { - args_add(cpp_args, argv[i]); - args_add(cpp_args, argv[i+1]); - } else { - args_add(common_args, argv[i]); - args_add(common_args, argv[i+1]); - } - - i++; - continue; - } - - // Other options. - if (argv[i][0] == '-') { - if (compopt_affects_cpp(argv[i]) - || compopt_prefix_affects_cpp(argv[i])) { - args_add(cpp_args, argv[i]); - } else { - args_add(common_args, argv[i]); - } - continue; - } - - // If an argument isn't a plain file then assume its an option, not an - // input file. This allows us to cope better with unusual compiler options. - // - // Note that "/dev/null" is an exception that is sometimes used as an input - // file when code is testing compiler flags. - struct stat st; - if (!str_eq(argv[i], "/dev/null") - && (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode))) { - cc_log("%s is not a regular file, not considering as input file", - argv[i]); - args_add(common_args, argv[i]); - continue; - } - - if (input_file) { - if (language_for_file(argv[i])) { - cc_log("Multiple input files: %s and %s", input_file, argv[i]); - stats_update(STATS_MULTIPLE); - } else if (!found_c_opt && !found_dc_opt) { - cc_log("Called for link with %s", argv[i]); - if (strstr(argv[i], "conftest.")) { - stats_update(STATS_CONFTEST); - } else { - stats_update(STATS_LINK); - } - } else { - cc_log("Unsupported source extension: %s", argv[i]); - stats_update(STATS_SOURCELANG); - } - result = false; - goto out; - } - - // The source code file path gets put into the notes. - if (generating_coverage) { - input_file = x_strdup(argv[i]); - continue; - } - - if (is_symlink(argv[i])) { - // Don't rewrite source file path if it's a symlink since - // make_relative_path resolves symlinks using realpath(3) and this leads - // to potentially choosing incorrect relative header files. See the - // "symlink to source file" test. - input_file = x_strdup(argv[i]); - } else { - // Rewrite to relative to increase hit rate. - input_file = make_relative_path(x_strdup(argv[i])); - } - } // for - - if (generating_debuginfo && conf->unify) { - cc_log("Generating debug info; disabling unify mode"); - conf->unify = false; - } - - if (generating_debuginfo_level_3 && !conf->run_second_cpp) { - cc_log("Generating debug info level 3; not compiling preprocessed code"); - conf->run_second_cpp = true; - } - - // See <http://gcc.gnu.org/onlinedocs/cpp/Environment-Variables.html>. - // Contrary to what the documentation seems to imply the compiler still - // creates object files with these defined (confirmed with GCC 8.2.1), i.e. - // they work as -MMD/-MD, not -MM/-M. These environment variables do nothing - // on Clang. - { - char *dependencies_env = getenv("DEPENDENCIES_OUTPUT"); - bool using_sunpro_dependencies = false; - if (!dependencies_env) { - dependencies_env = getenv("SUNPRO_DEPENDENCIES"); - using_sunpro_dependencies = true; - } - if (dependencies_env) { - generating_dependencies = true; - dependency_filename_specified = true; - char *saveptr = nullptr; - char *abspath_file = strtok_r(dependencies_env, " ", &saveptr); - - free(output_dep); - output_dep = make_relative_path(x_strdup(abspath_file)); - - // specifying target object is optional. - char *abspath_obj = strtok_r(nullptr, " ", &saveptr); - if (abspath_obj) { - // it's the "file target" form. - - dependency_target_specified = true; - char *relpath_obj = make_relative_path(x_strdup(abspath_obj)); - // ensure compiler gets relative path. - char *relpath_both = format("%s %s", output_dep, relpath_obj); - if (using_sunpro_dependencies) { - x_setenv("SUNPRO_DEPENDENCIES", relpath_both); - } else { - x_setenv("DEPENDENCIES_OUTPUT", relpath_both); - } - free(relpath_obj); - free(relpath_both); - } else { - // it's the "file" form. - - dependency_implicit_target_specified = true; - // ensure compiler gets relative path. - if (using_sunpro_dependencies) { - x_setenv("SUNPRO_DEPENDENCIES", output_dep); - } else { - x_setenv("DEPENDENCIES_OUTPUT", output_dep); - } - } - } - } - - if (!input_file) { - cc_log("No input file found"); - stats_update(STATS_NOINPUT); - result = false; - goto out; - } - - if (found_pch || found_fpch_preprocess) { - using_precompiled_header = true; - if (!(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; - goto out; - } - } - - if (explicit_language && str_eq(explicit_language, "none")) { - explicit_language = NULL; - } - file_language = language_for_file(input_file); - if (explicit_language) { - if (!language_is_supported(explicit_language)) { - cc_log("Unsupported language: %s", explicit_language); - stats_update(STATS_SOURCELANG); - result = false; - goto out; - } - actual_language = x_strdup(explicit_language); - } else { - actual_language = file_language; - } - - output_is_precompiled_header = - actual_language && strstr(actual_language, "-header"); - - if (output_is_precompiled_header - && !(conf->sloppiness & SLOPPY_PCH_DEFINES)) { - cc_log("You have to specify \"pch_defines,time_macros\" sloppiness when" - " creating precompiled headers"); - stats_update(STATS_CANTUSEPCH); - result = false; - goto out; - } - - if (!found_c_opt && !found_dc_opt && !found_S_opt) { - if (output_is_precompiled_header) { - args_add(common_args, "-c"); - } else { - cc_log("No -c option found"); - // I find that having a separate statistic for autoconf tests is useful, - // as they are the dominant form of "called for link" in many cases. - if (strstr(input_file, "conftest.")) { - stats_update(STATS_CONFTEST); - } else { - stats_update(STATS_LINK); - } - result = false; - goto out; - } - } - - if (!actual_language) { - cc_log("Unsupported source extension: %s", input_file); - stats_update(STATS_SOURCELANG); - result = false; - goto out; - } - - if (!conf->run_second_cpp && str_eq(actual_language, "cu")) { - cc_log("Using CUDA compiler; not compiling preprocessed code"); - conf->run_second_cpp = true; - } - - direct_i_file = language_is_preprocessed(actual_language); - - if (output_is_precompiled_header && !conf->run_second_cpp) { - // It doesn't work to create the .gch from preprocessed source. - cc_log("Creating precompiled header; not compiling preprocessed code"); - conf->run_second_cpp = true; - } - - if (str_eq(conf->cpp_extension, "")) { - const char *p_language = p_language_for_language(actual_language); - free(conf->cpp_extension); - conf->cpp_extension = x_strdup(extension_for_language(p_language) + 1); - } - - // Don't try to second guess the compilers heuristics for stdout handling. - if (output_obj && str_eq(output_obj, "-")) { - stats_update(STATS_OUTSTDOUT); - cc_log("Output file is -"); - result = false; - goto out; - } - - if (!output_obj) { - if (output_is_precompiled_header) { - output_obj = format("%s.gch", input_file); - } else { - char extension = found_S_opt ? 's' : 'o'; - output_obj = x_basename(input_file); - char *p = strrchr(output_obj, '.'); - if (!p) { - reformat(&output_obj, "%s.%c", output_obj, extension); - } else if (!p[1]) { - reformat(&output_obj, "%s%c", output_obj, extension); - } else { - p[1] = extension; - p[2] = 0; - } - } - } - - if (seen_split_dwarf) { - char *p = strrchr(output_obj, '.'); - if (!p || !p[1]) { - cc_log("Badly formed object filename"); - stats_update(STATS_ARGS); - result = false; - goto out; - } - - char *base_name = remove_extension(output_obj); - output_dwo = format("%s.dwo", base_name); - free(base_name); - } - - // Cope with -o /dev/null. - struct stat st; - if (!str_eq(output_obj, "/dev/null") - && stat(output_obj, &st) == 0 - && !S_ISREG(st.st_mode)) { - cc_log("Not a regular file: %s", output_obj); - stats_update(STATS_BADOUTPUTFILE); - result = false; - goto out; - } - - { - char *output_dir = x_dirname(output_obj); - if (stat(output_dir, &st) != 0 || !S_ISDIR(st.st_mode)) { - cc_log("Directory does not exist: %s", output_dir); - stats_update(STATS_BADOUTPUTFILE); - result = false; - free(output_dir); - goto out; - } - free(output_dir); - } - - // Some options shouldn't be passed to the real compiler when it compiles - // preprocessed code: - // - // -finput-charset=XXX (otherwise conversion happens twice) - // -x XXX (otherwise the wrong language is selected) - if (input_charset) { - args_add(cpp_args, input_charset); - } - if (found_pch) { - args_add(cpp_args, "-fpch-preprocess"); - } - if (explicit_language) { - args_add(cpp_args, "-x"); - args_add(cpp_args, explicit_language); - } - - // Since output is redirected, compilers will not color their output by - // default, so force it explicitly if it would be otherwise done. - if (!found_color_diagnostics && color_output_possible()) { - if (guessed_compiler == GUESSED_CLANG) { - if (!str_eq(actual_language, "assembler")) { - args_add(common_args, "-fcolor-diagnostics"); - cc_log("Automatically enabling colors"); - } - } else if (guessed_compiler == GUESSED_GCC) { - // GCC has it since 4.9, but that'd require detecting what GCC version is - // used for the actual compile. However it requires also GCC_COLORS to be - // set (and not empty), so use that for detecting if GCC would use - // colors. - if (getenv("GCC_COLORS") && getenv("GCC_COLORS")[0] != '\0') { - args_add(common_args, "-fdiagnostics-color"); - cc_log("Automatically enabling colors"); - } - } - } - - // Add flags for dependency generation only to the preprocessor command line. - if (generating_dependencies) { - if (!dependency_filename_specified) { - char *base_name = remove_extension(output_obj); - char *default_depfile_name = format("%s.d", base_name); - free(base_name); - args_add(dep_args, "-MF"); - args_add(dep_args, default_depfile_name); - output_dep = make_relative_path(x_strdup(default_depfile_name)); - } - - if (!dependency_target_specified - && !dependency_implicit_target_specified - && !str_eq(get_extension(output_dep), ".o")) { - args_add(dep_args, "-MQ"); - args_add(dep_args, output_obj); - } - } - if (generating_coverage) { - char *base_name = remove_extension(output_obj); - char *default_covfile_name = format("%s.gcno", base_name); - free(base_name); - output_cov = make_relative_path(default_covfile_name); - } - if (generating_stackusage) { - char *base_name = remove_extension(output_obj); - char *default_sufile_name = format("%s.su", base_name); - free(base_name); - output_su = make_relative_path(default_sufile_name); - } - - *compiler_args = args_copy(common_args); - args_extend(*compiler_args, compiler_only_args); - - if (conf->run_second_cpp) { - args_extend(*compiler_args, cpp_args); - } else if (found_directives_only || found_rewrite_includes) { - // Need to pass the macros and any other preprocessor directives again. - args_extend(*compiler_args, cpp_args); - if (found_directives_only) { - args_add(cpp_args, "-fdirectives-only"); - // The preprocessed source code still needs some more preprocessing. - args_add(*compiler_args, "-fpreprocessed"); - args_add(*compiler_args, "-fdirectives-only"); - } - if (found_rewrite_includes) { - args_add(cpp_args, "-frewrite-includes"); - // The preprocessed source code still needs some more preprocessing. - args_add(*compiler_args, "-x"); - args_add(*compiler_args, actual_language); - } - } else if (explicit_language) { - // Workaround for a bug in Apple's patched distcc -- it doesn't properly - // reset the language specified with -x, so if -x is given, we have to - // specify the preprocessed language explicitly. - args_add(*compiler_args, "-x"); - args_add(*compiler_args, p_language_for_language(explicit_language)); - } - - if (found_c_opt) { - args_add(*compiler_args, "-c"); - } - - if (found_dc_opt) { - args_add(*compiler_args, "-dc"); - } - - for (size_t i = 0; i < arch_args_size; ++i) { - args_add(*compiler_args, "-arch"); - args_add(*compiler_args, arch_args[i]); - } - - // Only pass dependency arguments to the preprocessor since Intel's C++ - // compiler doesn't produce a correct .d file when compiling preprocessed - // source. - args_extend(cpp_args, dep_args); - - *preprocessor_args = args_copy(common_args); - args_extend(*preprocessor_args, cpp_args); + bool found_c_opt = false; + bool found_dc_opt = false; + bool found_S_opt = false; + bool found_pch = false; + bool found_fpch_preprocess = false; + const char* explicit_language = NULL; // As specified with -x. + const char* file_language; // As deduced from file extension. + const char* input_charset = NULL; + + // Is the dependency makefile name overridden with -MF? + bool dependency_filename_specified = false; + + // Is the dependency makefile target name specified with -MT or -MQ? + bool dependency_target_specified = false; + + // Is the dependency target name implicitly specified using + // DEPENDENCIES_OUTPUT or SUNPRO_DEPENDENCIES? + bool dependency_implicit_target_specified = false; + + // expanded_args is a copy of the original arguments given to the compiler + // but with arguments from @file and similar constructs expanded. It's only + // used as a temporary data structure to loop over. + struct args* expanded_args = args_copy(args); + + // common_args contains all original arguments except: + // * those that never should be passed to the preprocessor, + // * those that only should be passed to the preprocessor (if run_second_cpp + // is false), and + // * dependency options (like -MD and friends). + struct args* common_args = args_init(0, NULL); + + // cpp_args contains arguments that were not added to common_args, i.e. those + // that should only be passed to the preprocessor if run_second_cpp is false. + // If run_second_cpp is true, they will be passed to the compiler as well. + struct args* cpp_args = args_init(0, NULL); + + // dep_args contains dependency options like -MD. They are only passed to the + // preprocessor, never to the compiler. + struct args* dep_args = args_init(0, NULL); + + // compiler_only_args contains arguments that should only be passed to the + // compiler, not the preprocessor. + struct args* compiler_only_args = args_init(0, NULL); + + bool found_color_diagnostics = false; + bool found_directives_only = false; + bool found_rewrite_includes = false; + + int argc = expanded_args->argc; + char** argv = expanded_args->argv; + args_add(common_args, argv[0]); + + bool result = true; + for (int i = 1; i < argc; i++) { + // The user knows best: just swallow the next arg. + if (str_eq(argv[i], "--ccache-skip")) { + i++; + if (i == argc) { + cc_log("--ccache-skip lacks an argument"); + result = false; + goto out; + } + args_add(common_args, argv[i]); + continue; + } + + // Special case for -E. + if (str_eq(argv[i], "-E")) { + stats_update(STATS_PREPROCESSING); + result = false; + goto out; + } + + // Handle "@file" argument. + if (str_startswith(argv[i], "@") || str_startswith(argv[i], "-@")) { + char* argpath = argv[i] + 1; + + if (argpath[-1] == '-') { + ++argpath; + } + struct args* file_args = args_init_from_gcc_atfile(argpath); + if (!file_args) { + cc_log("Couldn't read arg file %s", argpath); + stats_update(STATS_ARGS); + result = false; + goto out; + } + + args_insert(expanded_args, i, file_args, true); + argc = expanded_args->argc; + argv = expanded_args->argv; + i--; + continue; + } + + // Handle cuda "-optf" and "--options-file" argument. + if (guessed_compiler == GUESSED_NVCC + && (str_eq(argv[i], "-optf") || str_eq(argv[i], "--options-file"))) { + if (i == argc - 1) { + cc_log("Expected argument after %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + ++i; + + // Argument is a comma-separated list of files. + char* str_start = argv[i]; + char* str_end = strchr(str_start, ','); + int index = i + 1; + + if (!str_end) { + str_end = str_start + strlen(str_start); + } + + while (str_end) { + *str_end = '\0'; + struct args* file_args = args_init_from_gcc_atfile(str_start); + if (!file_args) { + cc_log("Couldn't read cuda options file %s", str_start); + stats_update(STATS_ARGS); + result = false; + goto out; + } + + int new_index = file_args->argc + index; + args_insert(expanded_args, index, file_args, false); + index = new_index; + str_start = str_end; + str_end = strchr(str_start, ','); + } + + argc = expanded_args->argc; + argv = expanded_args->argv; + continue; + } + + // These are always too hard. + if (compopt_too_hard(argv[i]) || str_startswith(argv[i], "-fdump-")) { + cc_log("Compiler option %s is unsupported", argv[i]); + stats_update(STATS_UNSUPPORTED_OPTION); + result = false; + goto out; + } + + // These are too hard in direct mode. + if (conf->direct_mode && compopt_too_hard_for_direct_mode(argv[i])) { + cc_log("Unsupported compiler option for direct mode: %s", argv[i]); + conf->direct_mode = false; + } + + // -Xarch_* options are too hard. + if (str_startswith(argv[i], "-Xarch_")) { + cc_log("Unsupported compiler option :%s", argv[i]); + stats_update(STATS_UNSUPPORTED_OPTION); + result = false; + goto out; + } + + // Handle -arch options. + if (str_eq(argv[i], "-arch")) { + if (arch_args_size == MAX_ARCH_ARGS - 1) { + cc_log("Too many -arch compiler options; ccache supports at most %d", + MAX_ARCH_ARGS); + stats_update(STATS_UNSUPPORTED_OPTION); + result = false; + goto out; + } + + ++i; + arch_args[arch_args_size] = x_strdup(argv[i]); // It will leak. + ++arch_args_size; + if (arch_args_size == 2) { + conf->run_second_cpp = true; + } + continue; + } + + // Handle options that should not be passed to the preprocessor. + if (compopt_affects_comp(argv[i])) { + args_add(compiler_only_args, argv[i]); + if (compopt_takes_arg(argv[i])) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + args_add(compiler_only_args, argv[i + 1]); + ++i; + } + continue; + } + if (compopt_prefix_affects_comp(argv[i])) { + args_add(compiler_only_args, argv[i]); + continue; + } + + if (str_eq(argv[i], "-fpch-preprocess") || str_eq(argv[i], "-emit-pch") + || str_eq(argv[i], "-emit-pth")) { + found_fpch_preprocess = true; + } + + // We must have -c. + if (str_eq(argv[i], "-c")) { + found_c_opt = true; + continue; + } + + // when using nvcc with separable compilation, -dc implies -c + if ((str_eq(argv[i], "-dc") || str_eq(argv[i], "--device-c")) + && guessed_compiler == GUESSED_NVCC) { + found_dc_opt = true; + continue; + } + + // -S changes the default extension. + if (str_eq(argv[i], "-S")) { + args_add(common_args, argv[i]); + found_S_opt = true; + continue; + } + + // Special handling for -x: remember the last specified language before the + // input file and strip all -x options from the arguments. + if (str_eq(argv[i], "-x")) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + if (!input_file) { + explicit_language = argv[i + 1]; + } + i++; + continue; + } + if (str_startswith(argv[i], "-x")) { + if (!input_file) { + explicit_language = &argv[i][2]; + } + continue; + } + + // We need to work out where the output was meant to go. + if (str_eq(argv[i], "-o")) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + output_obj = make_relative_path(x_strdup(argv[i + 1])); + i++; + continue; + } + + // Alternate form of -o with no space. Nvcc does not support this. + if (str_startswith(argv[i], "-o") && guessed_compiler != GUESSED_NVCC) { + output_obj = make_relative_path(x_strdup(&argv[i][2])); + continue; + } + + if (str_startswith(argv[i], "-fdebug-prefix-map=") + || str_startswith(argv[i], "-ffile-prefix-map=")) { + debug_prefix_maps = static_cast<char**>(x_realloc( + debug_prefix_maps, (debug_prefix_maps_len + 1) * sizeof(char*))); + debug_prefix_maps[debug_prefix_maps_len++] = + x_strdup(&argv[i][argv[i][2] == 'f' ? 18 : 19]); + args_add(common_args, argv[i]); + continue; + } + + // Debugging is handled specially, so that we know if we can strip line + // number info. + if (str_startswith(argv[i], "-g")) { + args_add(common_args, argv[i]); + + if (str_startswith(argv[i], "-gdwarf")) { + // Selection of DWARF format (-gdwarf or -gdwarf-<version>) enables + // debug info on level 2. + generating_debuginfo = true; + continue; + } + + char last_char = argv[i][strlen(argv[i]) - 1]; + if (last_char == '0') { + // "-g0", "-ggdb0" or similar: All debug information disabled. + generating_debuginfo = false; + generating_debuginfo_level_3 = false; + } else { + generating_debuginfo = true; + if (last_char == '3') { + generating_debuginfo_level_3 = true; + } + if (str_eq(argv[i], "-gsplit-dwarf")) { + seen_split_dwarf = true; + } + } + continue; + } + + // These options require special handling, because they behave differently + // with gcc -E, when the output file is not specified. + if (str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD")) { + generating_dependencies = true; + args_add(dep_args, argv[i]); + continue; + } + if (str_startswith(argv[i], "-MF")) { + dependency_filename_specified = true; + free(output_dep); + + char* arg; + bool separate_argument = (strlen(argv[i]) == 3); + if (separate_argument) { + // -MF arg + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + arg = argv[i + 1]; + i++; + } else { + // -MFarg + arg = &argv[i][3]; + } + output_dep = make_relative_path(x_strdup(arg)); + // Keep the format of the args the same. + if (separate_argument) { + args_add(dep_args, "-MF"); + args_add(dep_args, output_dep); + } else { + char* option = format("-MF%s", output_dep); + args_add(dep_args, option); + free(option); + } + continue; + } + if (str_startswith(argv[i], "-MQ") || str_startswith(argv[i], "-MT")) { + dependency_target_specified = true; + + char* relpath; + if (strlen(argv[i]) == 3) { + // -MQ arg or -MT arg + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + args_add(dep_args, argv[i]); + relpath = make_relative_path(x_strdup(argv[i + 1])); + args_add(dep_args, relpath); + free(relpath); + i++; + } else { + char* arg_opt = x_strndup(argv[i], 3); + relpath = make_relative_path(x_strdup(argv[i] + 3)); + char* option = format("%s%s", arg_opt, relpath); + args_add(dep_args, option); + free(arg_opt); + free(relpath); + free(option); + } + continue; + } + if (str_eq(argv[i], "-fprofile-arcs")) { + profile_arcs = true; + args_add(common_args, argv[i]); + continue; + } + if (str_eq(argv[i], "-ftest-coverage")) { + generating_coverage = true; + args_add(common_args, argv[i]); + continue; + } + if (str_eq(argv[i], "-fstack-usage")) { + generating_stackusage = true; + args_add(common_args, argv[i]); + continue; + } + if (str_eq(argv[i], "--coverage") // = -fprofile-arcs -ftest-coverage + || str_eq(argv[i], "-coverage")) { // Undocumented but still works. + profile_arcs = true; + generating_coverage = true; + args_add(common_args, argv[i]); + continue; + } + if (str_startswith(argv[i], "-fprofile-dir=")) { + profile_dir = x_strdup(argv[i] + 14); + args_add(common_args, argv[i]); + continue; + } + if (str_startswith(argv[i], "-fsanitize-blacklist=")) { + sanitize_blacklists = static_cast<char**>(x_realloc( + sanitize_blacklists, (sanitize_blacklists_len + 1) * sizeof(char*))); + sanitize_blacklists[sanitize_blacklists_len++] = x_strdup(argv[i] + 21); + args_add(common_args, argv[i]); + continue; + } + if (str_startswith(argv[i], "--sysroot=")) { + char* relpath = make_relative_path(x_strdup(argv[i] + 10)); + char* option = format("--sysroot=%s", relpath); + args_add(common_args, option); + free(relpath); + free(option); + continue; + } + // Alternate form of specifying sysroot without = + if (str_eq(argv[i], "--sysroot")) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + args_add(common_args, argv[i]); + char* relpath = make_relative_path(x_strdup(argv[i + 1])); + args_add(common_args, relpath); + i++; + free(relpath); + continue; + } + // Alternate form of specifying target without = + if (str_eq(argv[i], "-target")) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + args_add(common_args, argv[i]); + args_add(common_args, argv[i + 1]); + i++; + continue; + } + if (str_startswith(argv[i], "-Wp,")) { + if (str_eq(argv[i], "-Wp,-P") || strstr(argv[i], ",-P,") + || str_endswith(argv[i], ",-P")) { + // -P removes preprocessor information in such a way that the object + // file from compiling the preprocessed file will not be equal to the + // object file produced when compiling without ccache. + cc_log("Too hard option -Wp,-P detected"); + stats_update(STATS_UNSUPPORTED_OPTION); + failed(); + } else if (str_startswith(argv[i], "-Wp,-MD,") + && !strchr(argv[i] + 8, ',')) { + generating_dependencies = true; + dependency_filename_specified = true; + free(output_dep); + output_dep = make_relative_path(x_strdup(argv[i] + 8)); + args_add(dep_args, argv[i]); + continue; + } else if (str_startswith(argv[i], "-Wp,-MMD,") + && !strchr(argv[i] + 9, ',')) { + generating_dependencies = true; + dependency_filename_specified = true; + free(output_dep); + output_dep = make_relative_path(x_strdup(argv[i] + 9)); + args_add(dep_args, argv[i]); + continue; + } else if (str_startswith(argv[i], "-Wp,-D") + && !strchr(argv[i] + 6, ',')) { + // Treat it like -D. + args_add(cpp_args, argv[i] + 4); + continue; + } else if (str_eq(argv[i], "-Wp,-MP") + || (strlen(argv[i]) > 8 && str_startswith(argv[i], "-Wp,-M") + && argv[i][7] == ',' + && (argv[i][6] == 'F' || argv[i][6] == 'Q' + || argv[i][6] == 'T') + && !strchr(argv[i] + 8, ','))) { + // TODO: Make argument to MF/MQ/MT relative. + args_add(dep_args, argv[i]); + continue; + } else if (conf->direct_mode) { + // -Wp, can be used to pass too hard options to the preprocessor. + // Hence, disable direct mode. + cc_log("Unsupported compiler option for direct mode: %s", argv[i]); + conf->direct_mode = false; + } + + // Any other -Wp,* arguments are only relevant for the preprocessor. + args_add(cpp_args, argv[i]); + continue; + } + if (str_eq(argv[i], "-MP")) { + args_add(dep_args, argv[i]); + continue; + } + + // Input charset needs to be handled specially. + if (str_startswith(argv[i], "-finput-charset=")) { + input_charset = argv[i]; + continue; + } + + if (str_eq(argv[i], "--serialize-diagnostics")) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + generating_diagnostics = true; + output_dia = make_relative_path(x_strdup(argv[i + 1])); + i++; + continue; + } + + if (str_startswith(argv[i], "-fprofile-")) { + char* arg = x_strdup(argv[i]); + const char* arg_profile_dir = strchr(argv[i], '='); + if (arg_profile_dir) { + // Convert to absolute path. + char* dir = x_realpath(arg_profile_dir + 1); + if (!dir) { + // Directory doesn't exist. + dir = x_strdup(arg_profile_dir + 1); + } + + // We can get a better hit rate by using the real path here. + free(arg); + char* option = x_strndup(argv[i], arg_profile_dir - argv[i]); + arg = format("%s=%s", option, dir); + cc_log("Rewriting %s to %s", argv[i], arg); + free(option); + free(dir); + } + + bool supported_profile_option = false; + if (str_startswith(argv[i], "-fprofile-generate") + || str_eq(argv[i], "-fprofile-arcs")) { + profile_generate = true; + supported_profile_option = true; + } else if (str_startswith(argv[i], "-fprofile-use") + || str_eq(argv[i], "-fbranch-probabilities")) { + profile_use = true; + supported_profile_option = true; + } else if (str_eq(argv[i], "-fprofile-dir")) { + supported_profile_option = true; + } + + if (supported_profile_option) { + args_add(common_args, arg); + free(arg); + + // If the profile directory has already been set, give up... Hard to + // know what the user means, and what the compiler will do. + if (arg_profile_dir && profile_dir) { + cc_log("Profile directory already set; giving up"); + result = false; + goto out; + } else if (arg_profile_dir) { + cc_log("Setting profile directory to %s", arg_profile_dir); + profile_dir = x_strdup(arg_profile_dir); + } + continue; + } + cc_log("Unknown profile option: %s", argv[i]); + free(arg); + } + + if (str_eq(argv[i], "-fcolor-diagnostics") + || str_eq(argv[i], "-fno-color-diagnostics") + || str_eq(argv[i], "-fdiagnostics-color") + || str_eq(argv[i], "-fdiagnostics-color=always") + || str_eq(argv[i], "-fno-diagnostics-color") + || str_eq(argv[i], "-fdiagnostics-color=never")) { + args_add(common_args, argv[i]); + found_color_diagnostics = true; + continue; + } + if (str_eq(argv[i], "-fdiagnostics-color=auto")) { + if (color_output_possible()) { + // Output is redirected, so color output must be forced. + args_add(common_args, "-fdiagnostics-color=always"); + cc_log("Automatically forcing colors"); + } else { + args_add(common_args, argv[i]); + } + found_color_diagnostics = true; + continue; + } + + // GCC + if (str_eq(argv[i], "-fdirectives-only")) { + found_directives_only = true; + continue; + } + // Clang + if (str_eq(argv[i], "-frewrite-includes")) { + found_rewrite_includes = true; + continue; + } + + if (conf->sloppiness & SLOPPY_CLANG_INDEX_STORE + && str_eq(argv[i], "-index-store-path")) { + // Xcode 9 or later calls Clang with this option. The given path includes + // a UUID that might lead to cache misses, especially when cache is + // shared among multiple users. + i++; + if (i <= argc - 1) { + cc_log("Skipping argument -index-store-path %s", argv[i]); + } + continue; + } + + // Options taking an argument that we may want to rewrite to relative paths + // to get better hit rate. A secondary effect is that paths in the standard + // error output produced by the compiler will be normalized. + if (compopt_takes_path(argv[i])) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + + if (!detect_pch(argv[i], argv[i + 1], &found_pch)) { + result = false; + goto out; + } + + char* relpath = make_relative_path(x_strdup(argv[i + 1])); + if (compopt_affects_cpp(argv[i])) { + args_add(cpp_args, argv[i]); + args_add(cpp_args, relpath); + } else { + args_add(common_args, argv[i]); + args_add(common_args, relpath); + } + free(relpath); + + i++; + continue; + } + + // Same as above but options with concatenated argument beginning with a + // slash. + if (argv[i][0] == '-') { + char* slash_pos = strchr(argv[i], '/'); + if (slash_pos) { + char* option = x_strndup(argv[i], slash_pos - argv[i]); + if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) { + char* relpath = make_relative_path(x_strdup(slash_pos)); + char* new_option = format("%s%s", option, relpath); + if (compopt_affects_cpp(option)) { + args_add(cpp_args, new_option); + } else { + args_add(common_args, new_option); + } + free(new_option); + free(relpath); + free(option); + continue; + } else { + free(option); + } + } + } + + // Options that take an argument. + if (compopt_takes_arg(argv[i])) { + if (i == argc - 1) { + cc_log("Missing argument to %s", argv[i]); + stats_update(STATS_ARGS); + result = false; + goto out; + } + + if (compopt_affects_cpp(argv[i])) { + args_add(cpp_args, argv[i]); + args_add(cpp_args, argv[i + 1]); + } else { + args_add(common_args, argv[i]); + args_add(common_args, argv[i + 1]); + } + + i++; + continue; + } + + // Other options. + if (argv[i][0] == '-') { + if (compopt_affects_cpp(argv[i]) || compopt_prefix_affects_cpp(argv[i])) { + args_add(cpp_args, argv[i]); + } else { + args_add(common_args, argv[i]); + } + continue; + } + + // If an argument isn't a plain file then assume its an option, not an + // input file. This allows us to cope better with unusual compiler options. + // + // Note that "/dev/null" is an exception that is sometimes used as an input + // file when code is testing compiler flags. + struct stat st; + if (!str_eq(argv[i], "/dev/null") + && (stat(argv[i], &st) != 0 || !S_ISREG(st.st_mode))) { + cc_log("%s is not a regular file, not considering as input file", + argv[i]); + args_add(common_args, argv[i]); + continue; + } + + if (input_file) { + if (language_for_file(argv[i])) { + cc_log("Multiple input files: %s and %s", input_file, argv[i]); + stats_update(STATS_MULTIPLE); + } else if (!found_c_opt && !found_dc_opt) { + cc_log("Called for link with %s", argv[i]); + if (strstr(argv[i], "conftest.")) { + stats_update(STATS_CONFTEST); + } else { + stats_update(STATS_LINK); + } + } else { + cc_log("Unsupported source extension: %s", argv[i]); + stats_update(STATS_SOURCELANG); + } + result = false; + goto out; + } + + // The source code file path gets put into the notes. + if (generating_coverage) { + input_file = x_strdup(argv[i]); + continue; + } + + if (is_symlink(argv[i])) { + // Don't rewrite source file path if it's a symlink since + // make_relative_path resolves symlinks using realpath(3) and this leads + // to potentially choosing incorrect relative header files. See the + // "symlink to source file" test. + input_file = x_strdup(argv[i]); + } else { + // Rewrite to relative to increase hit rate. + input_file = make_relative_path(x_strdup(argv[i])); + } + } // for + + if (generating_debuginfo && conf->unify) { + cc_log("Generating debug info; disabling unify mode"); + conf->unify = false; + } + + if (generating_debuginfo_level_3 && !conf->run_second_cpp) { + cc_log("Generating debug info level 3; not compiling preprocessed code"); + conf->run_second_cpp = true; + } + + // See <http://gcc.gnu.org/onlinedocs/cpp/Environment-Variables.html>. + // Contrary to what the documentation seems to imply the compiler still + // creates object files with these defined (confirmed with GCC 8.2.1), i.e. + // they work as -MMD/-MD, not -MM/-M. These environment variables do nothing + // on Clang. + { + char* dependencies_env = getenv("DEPENDENCIES_OUTPUT"); + bool using_sunpro_dependencies = false; + if (!dependencies_env) { + dependencies_env = getenv("SUNPRO_DEPENDENCIES"); + using_sunpro_dependencies = true; + } + if (dependencies_env) { + generating_dependencies = true; + dependency_filename_specified = true; + char* saveptr = nullptr; + char* abspath_file = strtok_r(dependencies_env, " ", &saveptr); + + free(output_dep); + output_dep = make_relative_path(x_strdup(abspath_file)); + + // specifying target object is optional. + char* abspath_obj = strtok_r(nullptr, " ", &saveptr); + if (abspath_obj) { + // it's the "file target" form. + + dependency_target_specified = true; + char* relpath_obj = make_relative_path(x_strdup(abspath_obj)); + // ensure compiler gets relative path. + char* relpath_both = format("%s %s", output_dep, relpath_obj); + if (using_sunpro_dependencies) { + x_setenv("SUNPRO_DEPENDENCIES", relpath_both); + } else { + x_setenv("DEPENDENCIES_OUTPUT", relpath_both); + } + free(relpath_obj); + free(relpath_both); + } else { + // it's the "file" form. + + dependency_implicit_target_specified = true; + // ensure compiler gets relative path. + if (using_sunpro_dependencies) { + x_setenv("SUNPRO_DEPENDENCIES", output_dep); + } else { + x_setenv("DEPENDENCIES_OUTPUT", output_dep); + } + } + } + } + + if (!input_file) { + cc_log("No input file found"); + stats_update(STATS_NOINPUT); + result = false; + goto out; + } + + if (found_pch || found_fpch_preprocess) { + using_precompiled_header = true; + if (!(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; + goto out; + } + } + + if (explicit_language && str_eq(explicit_language, "none")) { + explicit_language = NULL; + } + file_language = language_for_file(input_file); + if (explicit_language) { + if (!language_is_supported(explicit_language)) { + cc_log("Unsupported language: %s", explicit_language); + stats_update(STATS_SOURCELANG); + result = false; + goto out; + } + actual_language = x_strdup(explicit_language); + } else { + actual_language = file_language; + } + + output_is_precompiled_header = + actual_language && strstr(actual_language, "-header"); + + if (output_is_precompiled_header + && !(conf->sloppiness & SLOPPY_PCH_DEFINES)) { + cc_log( + "You have to specify \"pch_defines,time_macros\" sloppiness when" + " creating precompiled headers"); + stats_update(STATS_CANTUSEPCH); + result = false; + goto out; + } + + if (!found_c_opt && !found_dc_opt && !found_S_opt) { + if (output_is_precompiled_header) { + args_add(common_args, "-c"); + } else { + cc_log("No -c option found"); + // I find that having a separate statistic for autoconf tests is useful, + // as they are the dominant form of "called for link" in many cases. + if (strstr(input_file, "conftest.")) { + stats_update(STATS_CONFTEST); + } else { + stats_update(STATS_LINK); + } + result = false; + goto out; + } + } + + if (!actual_language) { + cc_log("Unsupported source extension: %s", input_file); + stats_update(STATS_SOURCELANG); + result = false; + goto out; + } + + if (!conf->run_second_cpp && str_eq(actual_language, "cu")) { + cc_log("Using CUDA compiler; not compiling preprocessed code"); + conf->run_second_cpp = true; + } + + direct_i_file = language_is_preprocessed(actual_language); + + if (output_is_precompiled_header && !conf->run_second_cpp) { + // It doesn't work to create the .gch from preprocessed source. + cc_log("Creating precompiled header; not compiling preprocessed code"); + conf->run_second_cpp = true; + } + + if (str_eq(conf->cpp_extension, "")) { + const char* p_language = p_language_for_language(actual_language); + free(conf->cpp_extension); + conf->cpp_extension = x_strdup(extension_for_language(p_language) + 1); + } + + // Don't try to second guess the compilers heuristics for stdout handling. + if (output_obj && str_eq(output_obj, "-")) { + stats_update(STATS_OUTSTDOUT); + cc_log("Output file is -"); + result = false; + goto out; + } + + if (!output_obj) { + if (output_is_precompiled_header) { + output_obj = format("%s.gch", input_file); + } else { + char extension = found_S_opt ? 's' : 'o'; + output_obj = x_basename(input_file); + char* p = strrchr(output_obj, '.'); + if (!p) { + reformat(&output_obj, "%s.%c", output_obj, extension); + } else if (!p[1]) { + reformat(&output_obj, "%s%c", output_obj, extension); + } else { + p[1] = extension; + p[2] = 0; + } + } + } + + if (seen_split_dwarf) { + char* p = strrchr(output_obj, '.'); + if (!p || !p[1]) { + cc_log("Badly formed object filename"); + stats_update(STATS_ARGS); + result = false; + goto out; + } + + char* base_name = remove_extension(output_obj); + output_dwo = format("%s.dwo", base_name); + free(base_name); + } + + // Cope with -o /dev/null. + struct stat st; + if (!str_eq(output_obj, "/dev/null") && stat(output_obj, &st) == 0 + && !S_ISREG(st.st_mode)) { + cc_log("Not a regular file: %s", output_obj); + stats_update(STATS_BADOUTPUTFILE); + result = false; + goto out; + } + + { + char* output_dir = x_dirname(output_obj); + if (stat(output_dir, &st) != 0 || !S_ISDIR(st.st_mode)) { + cc_log("Directory does not exist: %s", output_dir); + stats_update(STATS_BADOUTPUTFILE); + result = false; + free(output_dir); + goto out; + } + free(output_dir); + } + + // Some options shouldn't be passed to the real compiler when it compiles + // preprocessed code: + // + // -finput-charset=XXX (otherwise conversion happens twice) + // -x XXX (otherwise the wrong language is selected) + if (input_charset) { + args_add(cpp_args, input_charset); + } + if (found_pch) { + args_add(cpp_args, "-fpch-preprocess"); + } + if (explicit_language) { + args_add(cpp_args, "-x"); + args_add(cpp_args, explicit_language); + } + + // Since output is redirected, compilers will not color their output by + // default, so force it explicitly if it would be otherwise done. + if (!found_color_diagnostics && color_output_possible()) { + if (guessed_compiler == GUESSED_CLANG) { + if (!str_eq(actual_language, "assembler")) { + args_add(common_args, "-fcolor-diagnostics"); + cc_log("Automatically enabling colors"); + } + } else if (guessed_compiler == GUESSED_GCC) { + // GCC has it since 4.9, but that'd require detecting what GCC version is + // used for the actual compile. However it requires also GCC_COLORS to be + // set (and not empty), so use that for detecting if GCC would use + // colors. + if (getenv("GCC_COLORS") && getenv("GCC_COLORS")[0] != '\0') { + args_add(common_args, "-fdiagnostics-color"); + cc_log("Automatically enabling colors"); + } + } + } + + // Add flags for dependency generation only to the preprocessor command line. + if (generating_dependencies) { + if (!dependency_filename_specified) { + char* base_name = remove_extension(output_obj); + char* default_depfile_name = format("%s.d", base_name); + free(base_name); + args_add(dep_args, "-MF"); + args_add(dep_args, default_depfile_name); + output_dep = make_relative_path(x_strdup(default_depfile_name)); + } + + if (!dependency_target_specified && !dependency_implicit_target_specified + && !str_eq(get_extension(output_dep), ".o")) { + args_add(dep_args, "-MQ"); + args_add(dep_args, output_obj); + } + } + if (generating_coverage) { + char* base_name = remove_extension(output_obj); + char* default_covfile_name = format("%s.gcno", base_name); + free(base_name); + output_cov = make_relative_path(default_covfile_name); + } + if (generating_stackusage) { + char* base_name = remove_extension(output_obj); + char* default_sufile_name = format("%s.su", base_name); + free(base_name); + output_su = make_relative_path(default_sufile_name); + } + + *compiler_args = args_copy(common_args); + args_extend(*compiler_args, compiler_only_args); + + if (conf->run_second_cpp) { + args_extend(*compiler_args, cpp_args); + } else if (found_directives_only || found_rewrite_includes) { + // Need to pass the macros and any other preprocessor directives again. + args_extend(*compiler_args, cpp_args); + if (found_directives_only) { + args_add(cpp_args, "-fdirectives-only"); + // The preprocessed source code still needs some more preprocessing. + args_add(*compiler_args, "-fpreprocessed"); + args_add(*compiler_args, "-fdirectives-only"); + } + if (found_rewrite_includes) { + args_add(cpp_args, "-frewrite-includes"); + // The preprocessed source code still needs some more preprocessing. + args_add(*compiler_args, "-x"); + args_add(*compiler_args, actual_language); + } + } else if (explicit_language) { + // Workaround for a bug in Apple's patched distcc -- it doesn't properly + // reset the language specified with -x, so if -x is given, we have to + // specify the preprocessed language explicitly. + args_add(*compiler_args, "-x"); + args_add(*compiler_args, p_language_for_language(explicit_language)); + } + + if (found_c_opt) { + args_add(*compiler_args, "-c"); + } + + if (found_dc_opt) { + args_add(*compiler_args, "-dc"); + } + + for (size_t i = 0; i < arch_args_size; ++i) { + args_add(*compiler_args, "-arch"); + args_add(*compiler_args, arch_args[i]); + } + + // Only pass dependency arguments to the preprocessor since Intel's C++ + // compiler doesn't produce a correct .d file when compiling preprocessed + // source. + args_extend(cpp_args, dep_args); + + *preprocessor_args = args_copy(common_args); + args_extend(*preprocessor_args, cpp_args); out: - args_free(expanded_args); - args_free(common_args); - args_free(dep_args); - args_free(cpp_args); - return result; + args_free(expanded_args); + args_free(common_args); + args_free(dep_args); + args_free(cpp_args); + return result; } static void -create_initial_config_file(const char *path) +create_initial_config_file(const char* path) { - if (create_parent_dirs(path) != 0) { - return; - } - - unsigned max_files; - uint64_t max_size; - char *stats_dir = format("%s/0", conf->cache_dir); - struct stat st; - if (stat(stats_dir, &st) == 0) { - stats_get_obsolete_limits(stats_dir, &max_files, &max_size); - // STATS_MAXFILES and STATS_MAXSIZE was stored for each top directory. - max_files *= 16; - max_size *= 16; - } else { - max_files = 0; - max_size = conf->max_size; - } - free(stats_dir); - - FILE *f = fopen(path, "w"); - if (!f) { - return; - } - if (max_files != 0) { - fprintf(f, "max_files = %u\n", max_files); - conf->max_files = max_files; - } - if (max_size != 0) { - char *size = format_parsable_size_with_suffix(max_size); - fprintf(f, "max_size = %s\n", size); - free(size); - conf->max_size = max_size; - } - fclose(f); + if (create_parent_dirs(path) != 0) { + return; + } + + unsigned max_files; + uint64_t max_size; + char* stats_dir = format("%s/0", conf->cache_dir); + struct stat st; + if (stat(stats_dir, &st) == 0) { + stats_get_obsolete_limits(stats_dir, &max_files, &max_size); + // STATS_MAXFILES and STATS_MAXSIZE was stored for each top directory. + max_files *= 16; + max_size *= 16; + } else { + max_files = 0; + max_size = conf->max_size; + } + free(stats_dir); + + FILE* f = fopen(path, "w"); + if (!f) { + return; + } + if (max_files != 0) { + fprintf(f, "max_files = %u\n", max_files); + conf->max_files = max_files; + } + if (max_size != 0) { + char* size = format_parsable_size_with_suffix(max_size); + fprintf(f, "max_size = %s\n", size); + free(size); + conf->max_size = max_size; + } + fclose(f); } #ifdef MTR_ENABLED -static void *trace_id; -static char *tmp_trace_file; +static void* trace_id; +static char* tmp_trace_file; static void -trace_init(char *path) +trace_init(char* path) { - tmp_trace_file = path; - mtr_init(tmp_trace_file); - char *s = format("%f", time_seconds()); - MTR_INSTANT_C("", "", "time", s); + tmp_trace_file = path; + mtr_init(tmp_trace_file); + char* s = format("%f", time_seconds()); + MTR_INSTANT_C("", "", "time", s); } static void trace_start(void) { - MTR_META_PROCESS_NAME(MYNAME); - trace_id = (void *) ((long) getpid()); - MTR_START("program", "ccache", trace_id); + MTR_META_PROCESS_NAME(MYNAME); + trace_id = (void*)((long)getpid()); + MTR_START("program", "ccache", trace_id); } static void trace_stop(void) { - char *trace_file = format("%s.ccache-trace", output_obj); - MTR_FINISH("program", "ccache", trace_id); - mtr_flush(); - mtr_shutdown(); - move_file(tmp_trace_file, trace_file); - free(trace_file); - free(tmp_trace_file); + char* trace_file = format("%s.ccache-trace", output_obj); + MTR_FINISH("program", "ccache", trace_id); + mtr_flush(); + mtr_shutdown(); + move_file(tmp_trace_file, trace_file); + free(trace_file); + free(tmp_trace_file); } -static const char * +static const char* tmpdir() { -#ifndef _WIN32 - const char *tmpdir = getenv("TMPDIR"); - if (tmpdir != NULL) { - return tmpdir; - } -#else - static char dirbuf[PATH_MAX]; - DWORD retval = GetTempPath(PATH_MAX, dirbuf); - if (retval > 0 && retval < PATH_MAX) { - return dirbuf; - } -#endif - return "/tmp"; +# ifndef _WIN32 + const char* tmpdir = getenv("TMPDIR"); + if (tmpdir != NULL) { + return tmpdir; + } +# else + static char dirbuf[PATH_MAX]; + DWORD retval = GetTempPath(PATH_MAX, dirbuf); + if (retval > 0 && retval < PATH_MAX) { + return dirbuf; + } +# endif + return "/tmp"; } #endif // MTR_ENABLED @@ -3498,153 +3502,177 @@ tmpdir() static void initialize(void) { - bool enable_internal_trace = getenv("CCACHE_INTERNAL_TRACE"); - if (enable_internal_trace) { + bool enable_internal_trace = getenv("CCACHE_INTERNAL_TRACE"); + if (enable_internal_trace) { #ifdef MTR_ENABLED - // We don't have any conf yet, so we can't use temp_dir() here. - trace_init(format("%s/tmp.ccache-trace.%d", tmpdir(), (int)getpid())); + // We don't have any conf yet, so we can't use temp_dir() here. + trace_init(format("%s/tmp.ccache-trace.%d", tmpdir(), (int)getpid())); #endif - } - - conf_free(conf); - MTR_BEGIN("config", "conf_create"); - conf = conf_create(); - MTR_END("config", "conf_create"); - - char *errmsg; - char *p = getenv("CCACHE_CONFIGPATH"); - if (p) { - primary_config_path = x_strdup(p); - } else { - secondary_config_path = format("%s/ccache.conf", TO_STRING(SYSCONFDIR)); - MTR_BEGIN("config", "conf_read_secondary"); - if (!conf_read(conf, secondary_config_path, &errmsg)) { - if (errno == 0) { - // We could read the file but it contained errors. - fatal("%s", errmsg); - } - // A missing config file in SYSCONFDIR is OK. - free(errmsg); - } - MTR_END("config", "conf_read_secondary"); - - if (str_eq(conf->cache_dir, "")) { - fatal("configuration setting \"cache_dir\" must not be the empty string"); - } - if ((p = getenv("CCACHE_DIR"))) { - free(conf->cache_dir); - conf->cache_dir = strdup(p); - } - if (str_eq(conf->cache_dir, "")) { - fatal("CCACHE_DIR must not be the empty string"); - } - - primary_config_path = format("%s/ccache.conf", conf->cache_dir); - } - - bool should_create_initial_config = false; - MTR_BEGIN("config", "conf_read_primary"); - if (!conf_read(conf, primary_config_path, &errmsg)) { - if (errno == 0) { - // We could read the file but it contained errors. - fatal("%s", errmsg); - } - if (!conf->disable) { - should_create_initial_config = true; - } - } - MTR_END("config", "conf_read_primary"); - - MTR_BEGIN("config", "conf_update_from_environment"); - if (!conf_update_from_environment(conf, &errmsg)) { - fatal("%s", errmsg); - } - MTR_END("config", "conf_update_from_environment"); - - if (should_create_initial_config) { - create_initial_config_file(primary_config_path); - } - - exitfn_init(); - exitfn_add_nullary(stats_flush); - exitfn_add_nullary(clean_up_pending_tmp_files); - - cc_log("=== CCACHE %s STARTED =========================================", - CCACHE_VERSION); - - if (conf->umask != UINT_MAX) { - umask(conf->umask); - } - - if (enable_internal_trace) { + } + + conf_free(conf); + MTR_BEGIN("config", "conf_create"); + conf = conf_create(); + MTR_END("config", "conf_create"); + + char* errmsg; + char* p = getenv("CCACHE_CONFIGPATH"); + if (p) { + primary_config_path = x_strdup(p); + } else { + secondary_config_path = format("%s/ccache.conf", TO_STRING(SYSCONFDIR)); + MTR_BEGIN("config", "conf_read_secondary"); + if (!conf_read(conf, secondary_config_path, &errmsg)) { + if (errno == 0) { + // We could read the file but it contained errors. + fatal("%s", errmsg); + } + // A missing config file in SYSCONFDIR is OK. + free(errmsg); + } + MTR_END("config", "conf_read_secondary"); + + if (str_eq(conf->cache_dir, "")) { + fatal("configuration setting \"cache_dir\" must not be the empty string"); + } + if ((p = getenv("CCACHE_DIR"))) { + free(conf->cache_dir); + conf->cache_dir = strdup(p); + } + if (str_eq(conf->cache_dir, "")) { + fatal("CCACHE_DIR must not be the empty string"); + } + + primary_config_path = format("%s/ccache.conf", conf->cache_dir); + } + + bool should_create_initial_config = false; + MTR_BEGIN("config", "conf_read_primary"); + if (!conf_read(conf, primary_config_path, &errmsg)) { + if (errno == 0) { + // We could read the file but it contained errors. + fatal("%s", errmsg); + } + if (!conf->disable) { + should_create_initial_config = true; + } + } + MTR_END("config", "conf_read_primary"); + + MTR_BEGIN("config", "conf_update_from_environment"); + if (!conf_update_from_environment(conf, &errmsg)) { + fatal("%s", errmsg); + } + MTR_END("config", "conf_update_from_environment"); + + if (should_create_initial_config) { + create_initial_config_file(primary_config_path); + } + + exitfn_init(); + exitfn_add_nullary(stats_flush); + exitfn_add_nullary(clean_up_pending_tmp_files); + + cc_log("=== CCACHE %s STARTED =========================================", + CCACHE_VERSION); + + if (conf->umask != UINT_MAX) { + umask(conf->umask); + } + + if (enable_internal_trace) { #ifdef MTR_ENABLED - trace_start(); - exitfn_add_nullary(trace_stop); + trace_start(); + exitfn_add_nullary(trace_stop); #else - cc_log("Error: tracing is not enabled!"); + cc_log("Error: tracing is not enabled!"); #endif - } + } } // Reset the global state. Used by the test suite. void cc_reset(void) { - conf_free(conf); conf = NULL; - free(primary_config_path); primary_config_path = NULL; - free(secondary_config_path); secondary_config_path = NULL; - free(current_working_dir); current_working_dir = NULL; - for (size_t i = 0; i < debug_prefix_maps_len; i++) { - free(debug_prefix_maps[i]); - debug_prefix_maps[i] = NULL; - } - free(debug_prefix_maps); debug_prefix_maps = NULL; - debug_prefix_maps_len = 0; - free(profile_dir); profile_dir = NULL; - for (size_t i = 0; i < sanitize_blacklists_len; i++) { - free(sanitize_blacklists[i]); - sanitize_blacklists[i] = NULL; - } - free(sanitize_blacklists); sanitize_blacklists = NULL; - sanitize_blacklists_len = 0; - free(included_pch_file); included_pch_file = NULL; - args_free(orig_args); orig_args = NULL; - free(input_file); input_file = NULL; - free(output_obj); output_obj = NULL; - free(output_dep); output_dep = NULL; - free(output_cov); output_cov = NULL; - free(output_su); output_su = NULL; - free(output_dia); output_dia = NULL; - free(output_dwo); output_dwo = NULL; - free(cached_result_name); cached_result_name = NULL; - free(cached_result_path); cached_result_path = NULL; - free(manifest_path); manifest_path = NULL; - time_of_compilation = 0; - for (size_t i = 0; i < ignore_headers_len; i++) { - free(ignore_headers[i]); - ignore_headers[i] = NULL; - } - free(ignore_headers); ignore_headers = NULL; - ignore_headers_len = 0; - if (included_files) { - hashtable_destroy(included_files, 1); included_files = NULL; - } - has_absolute_include_headers = false; - generating_debuginfo = false; - generating_debuginfo_level_3 = false; - generating_dependencies = false; - generating_coverage = false; - generating_stackusage = false; - profile_arcs = false; - free(profile_dir); profile_dir = NULL; - i_tmpfile = NULL; - direct_i_file = false; - free(cpp_stderr); cpp_stderr = NULL; - free(stats_file); stats_file = NULL; - output_is_precompiled_header = false; - - conf = conf_create(); - seen_split_dwarf = false; + conf_free(conf); + conf = NULL; + free(primary_config_path); + primary_config_path = NULL; + free(secondary_config_path); + secondary_config_path = NULL; + free(current_working_dir); + current_working_dir = NULL; + for (size_t i = 0; i < debug_prefix_maps_len; i++) { + free(debug_prefix_maps[i]); + debug_prefix_maps[i] = NULL; + } + free(debug_prefix_maps); + debug_prefix_maps = NULL; + debug_prefix_maps_len = 0; + free(profile_dir); + profile_dir = NULL; + for (size_t i = 0; i < sanitize_blacklists_len; i++) { + free(sanitize_blacklists[i]); + sanitize_blacklists[i] = NULL; + } + free(sanitize_blacklists); + sanitize_blacklists = NULL; + sanitize_blacklists_len = 0; + free(included_pch_file); + included_pch_file = NULL; + args_free(orig_args); + orig_args = NULL; + free(input_file); + input_file = NULL; + free(output_obj); + output_obj = NULL; + free(output_dep); + output_dep = NULL; + free(output_cov); + output_cov = NULL; + free(output_su); + output_su = NULL; + free(output_dia); + output_dia = NULL; + free(output_dwo); + output_dwo = NULL; + free(cached_result_name); + cached_result_name = NULL; + free(cached_result_path); + cached_result_path = NULL; + free(manifest_path); + manifest_path = NULL; + time_of_compilation = 0; + for (size_t i = 0; i < ignore_headers_len; i++) { + free(ignore_headers[i]); + ignore_headers[i] = NULL; + } + free(ignore_headers); + ignore_headers = NULL; + ignore_headers_len = 0; + if (included_files) { + hashtable_destroy(included_files, 1); + included_files = NULL; + } + has_absolute_include_headers = false; + generating_debuginfo = false; + generating_debuginfo_level_3 = false; + generating_dependencies = false; + generating_coverage = false; + generating_stackusage = false; + profile_arcs = false; + free(profile_dir); + profile_dir = NULL; + i_tmpfile = NULL; + direct_i_file = false; + free(cpp_stderr); + cpp_stderr = NULL; + free(stats_file); + stats_file = NULL; + output_is_precompiled_header = false; + + conf = conf_create(); + seen_split_dwarf = false; } // Make a copy of stderr that will not be cached, so things like distcc can @@ -3652,441 +3680,434 @@ cc_reset(void) static void set_up_uncached_err(void) { - int uncached_fd = dup(2); // The file descriptor is intentionally leaked. - if (uncached_fd == -1) { - cc_log("dup(2) failed: %s", strerror(errno)); - failed(); - } - - // Leak a pointer to the environment. - char *buf = format("UNCACHED_ERR_FD=%d", uncached_fd); - if (putenv(buf) == -1) { - cc_log("putenv failed: %s", strerror(errno)); - failed(); - } + int uncached_fd = dup(2); // The file descriptor is intentionally leaked. + if (uncached_fd == -1) { + cc_log("dup(2) failed: %s", strerror(errno)); + failed(); + } + + // Leak a pointer to the environment. + char* buf = format("UNCACHED_ERR_FD=%d", uncached_fd); + if (putenv(buf) == -1) { + cc_log("putenv failed: %s", strerror(errno)); + failed(); + } } static void -configuration_logger(const char *descr, const char *origin, void *context) +configuration_logger(const char* descr, const char* origin, void* context) { - (void)context; - cc_bulklog("Config: (%s) %s", origin, descr); + (void)context; + cc_bulklog("Config: (%s) %s", origin, descr); } -static void ccache(int argc, char *argv[]) ATTR_NORETURN; +static void ccache(int argc, char* argv[]) ATTR_NORETURN; // The main ccache driver function. static void -ccache(int argc, char *argv[]) +ccache(int argc, char* argv[]) { #ifndef _WIN32 - set_up_signal_handlers(); + set_up_signal_handlers(); #endif - // Needed for portability when using localtime_r. - tzset(); - - orig_args = args_init(argc, argv); - - initialize(); - MTR_BEGIN("main", "find_compiler"); - find_compiler(argv); - MTR_END("main", "find_compiler"); - - MTR_BEGIN("main", "clean_up_internal_tempdir"); - if (str_eq(conf->temporary_dir, "")) { - clean_up_internal_tempdir(); - } - MTR_END("main", "clean_up_internal_tempdir"); - - if (!str_eq(conf->log_file, "") || conf->debug) { - conf_print_items(conf, configuration_logger, NULL); - } - - if (conf->disable) { - cc_log("ccache is disabled"); - failed(); - } - - MTR_BEGIN("main", "set_up_uncached_err"); - set_up_uncached_err(); - MTR_END("main", "set_up_uncached_err"); - - cc_log_argv("Command line: ", argv); - cc_log("Hostname: %s", get_hostname()); - cc_log("Working directory: %s", get_current_working_dir()); - - conf->limit_multiple = MIN(MAX(conf->limit_multiple, 0.0), 1.0); - - MTR_BEGIN("main", "guess_compiler"); - guessed_compiler = guess_compiler(orig_args->argv[0]); - MTR_END("main", "guess_compiler"); - - // Arguments (except -E) to send to the preprocessor. - struct args *preprocessor_args; - // Arguments to send to the real compiler. - struct args *compiler_args; - MTR_BEGIN("main", "process_args"); - if (!cc_process_args(orig_args, &preprocessor_args, &compiler_args)) { - failed(); - } - MTR_END("main", "process_args"); - - if (conf->depend_mode - && (!generating_dependencies || str_eq(output_dep, "/dev/null") - || !conf->run_second_cpp || conf->unify)) { - cc_log("Disabling depend mode"); - conf->depend_mode = false; - } - - cc_log("Source file: %s", input_file); - if (generating_dependencies) { - cc_log("Dependency file: %s", output_dep); - } - if (generating_coverage) { - cc_log("Coverage file: %s", output_cov); - } - if (generating_stackusage) { - cc_log("Stack usage file: %s", output_su); - } - if (generating_diagnostics) { - cc_log("Diagnostics file: %s", output_dia); - } - if (output_dwo) { - cc_log("Split dwarf file: %s", output_dwo); - } - - cc_log("Object file: %s", output_obj); - MTR_META_THREAD_NAME(output_obj); - - // Need to dump log buffer as the last exit function to not lose any logs. - exitfn_add_last(dump_debug_log_buffer_exitfn, output_obj); - - FILE *debug_text_file = NULL; - if (conf->debug) { - char *path = format("%s.ccache-input-text", output_obj); - debug_text_file = fopen(path, "w"); - if (debug_text_file) { - exitfn_add(fclose_exitfn, debug_text_file); - } else { - cc_log("Failed to open %s: %s", path, strerror(errno)); - } - free(path); - } - - struct hash *common_hash = hash_init(); - init_hash_debug(common_hash, output_obj, 'c', "COMMON", debug_text_file); - - MTR_BEGIN("hash", "common_hash"); - hash_common_info(preprocessor_args, common_hash); - MTR_END("hash", "common_hash"); - - // Try to find the hash using the manifest. - struct hash *direct_hash = hash_copy(common_hash); - init_hash_debug( - direct_hash, output_obj, 'd', "DIRECT MODE", debug_text_file); - - bool put_result_in_manifest = false; - struct digest *result_name = NULL; - struct digest *result_name_from_manifest = NULL; - if (conf->direct_mode) { - cc_log("Trying direct lookup"); - MTR_BEGIN("hash", "direct_hash"); - result_name = calculate_result_name(preprocessor_args, direct_hash, 1); - MTR_END("hash", "direct_hash"); - if (result_name) { - update_cached_result_globals(result_name); - - // If we can return from cache at this point then do so. - from_cache(FROMCACHE_DIRECT_MODE, 0); - - // Wasn't able to return from cache at this point. However, the result - // was already found in manifest, so don't re-add it later. - put_result_in_manifest = false; - - result_name_from_manifest = result_name; - } else { - // Add result to manifest later. - put_result_in_manifest = true; - } - } - - if (conf->read_only_direct) { - cc_log("Read-only direct mode; running real compiler"); - failed(); - } - - if (!conf->depend_mode) { - // Find the hash using the preprocessed output. Also updates - // included_files. - struct hash *cpp_hash = hash_copy(common_hash); - init_hash_debug( - cpp_hash, output_obj, 'p', "PREPROCESSOR MODE", debug_text_file); - - MTR_BEGIN("hash", "cpp_hash"); - result_name = calculate_result_name(preprocessor_args, cpp_hash, 0); - MTR_END("hash", "cpp_hash"); - if (!result_name) { - fatal("internal error: calculate_result_name returned NULL for cpp"); - } - update_cached_result_globals(result_name); - - if (result_name_from_manifest - && !digests_equal(result_name_from_manifest, result_name)) { - // The hash from manifest differs from the hash of the preprocessor - // output. This could be because: - // - // - The preprocessor produces different output for the same input (not - // likely). - // - There's a bug in ccache (maybe incorrect handling of compiler - // arguments). - // - The user has used a different CCACHE_BASEDIR (most likely). - // - // The best thing here would probably be to remove the hash entry from - // the manifest. For now, we use a simpler method: just remove the - // manifest file. - cc_log("Hash from manifest doesn't match preprocessor output"); - cc_log("Likely reason: different CCACHE_BASEDIRs used"); - cc_log("Removing manifest as a safety measure"); - x_unlink(manifest_path); - - put_result_in_manifest = true; - } - - // If we can return from cache at this point then do. - from_cache(FROMCACHE_CPP_MODE, put_result_in_manifest); - } - - if (conf->read_only) { - cc_log("Read-only mode; running real compiler"); - failed(); - } - - add_prefix(compiler_args, conf->prefix_command); - - // In depend_mode, extend the direct hash. - struct hash *depend_mode_hash = conf->depend_mode ? direct_hash : NULL; - - // Run real compiler, sending output to cache. - MTR_BEGIN("cache", "to_cache"); - to_cache(compiler_args, depend_mode_hash); - MTR_END("cache", "to_cache"); - - x_exit(0); + // Needed for portability when using localtime_r. + tzset(); + + orig_args = args_init(argc, argv); + + initialize(); + MTR_BEGIN("main", "find_compiler"); + find_compiler(argv); + MTR_END("main", "find_compiler"); + + MTR_BEGIN("main", "clean_up_internal_tempdir"); + if (str_eq(conf->temporary_dir, "")) { + clean_up_internal_tempdir(); + } + MTR_END("main", "clean_up_internal_tempdir"); + + if (!str_eq(conf->log_file, "") || conf->debug) { + conf_print_items(conf, configuration_logger, NULL); + } + + if (conf->disable) { + cc_log("ccache is disabled"); + failed(); + } + + MTR_BEGIN("main", "set_up_uncached_err"); + set_up_uncached_err(); + MTR_END("main", "set_up_uncached_err"); + + cc_log_argv("Command line: ", argv); + cc_log("Hostname: %s", get_hostname()); + cc_log("Working directory: %s", get_current_working_dir()); + + conf->limit_multiple = MIN(MAX(conf->limit_multiple, 0.0), 1.0); + + MTR_BEGIN("main", "guess_compiler"); + guessed_compiler = guess_compiler(orig_args->argv[0]); + MTR_END("main", "guess_compiler"); + + // Arguments (except -E) to send to the preprocessor. + struct args* preprocessor_args; + // Arguments to send to the real compiler. + struct args* compiler_args; + MTR_BEGIN("main", "process_args"); + if (!cc_process_args(orig_args, &preprocessor_args, &compiler_args)) { + failed(); + } + MTR_END("main", "process_args"); + + if (conf->depend_mode + && (!generating_dependencies || str_eq(output_dep, "/dev/null") + || !conf->run_second_cpp || conf->unify)) { + cc_log("Disabling depend mode"); + conf->depend_mode = false; + } + + cc_log("Source file: %s", input_file); + if (generating_dependencies) { + cc_log("Dependency file: %s", output_dep); + } + if (generating_coverage) { + cc_log("Coverage file: %s", output_cov); + } + if (generating_stackusage) { + cc_log("Stack usage file: %s", output_su); + } + if (generating_diagnostics) { + cc_log("Diagnostics file: %s", output_dia); + } + if (output_dwo) { + cc_log("Split dwarf file: %s", output_dwo); + } + + cc_log("Object file: %s", output_obj); + MTR_META_THREAD_NAME(output_obj); + + // Need to dump log buffer as the last exit function to not lose any logs. + exitfn_add_last(dump_debug_log_buffer_exitfn, output_obj); + + FILE* debug_text_file = NULL; + if (conf->debug) { + char* path = format("%s.ccache-input-text", output_obj); + debug_text_file = fopen(path, "w"); + if (debug_text_file) { + exitfn_add(fclose_exitfn, debug_text_file); + } else { + cc_log("Failed to open %s: %s", path, strerror(errno)); + } + free(path); + } + + struct hash* common_hash = hash_init(); + init_hash_debug(common_hash, output_obj, 'c', "COMMON", debug_text_file); + + MTR_BEGIN("hash", "common_hash"); + hash_common_info(preprocessor_args, common_hash); + MTR_END("hash", "common_hash"); + + // Try to find the hash using the manifest. + struct hash* direct_hash = hash_copy(common_hash); + init_hash_debug(direct_hash, output_obj, 'd', "DIRECT MODE", debug_text_file); + + bool put_result_in_manifest = false; + struct digest* result_name = NULL; + struct digest* result_name_from_manifest = NULL; + if (conf->direct_mode) { + cc_log("Trying direct lookup"); + MTR_BEGIN("hash", "direct_hash"); + result_name = calculate_result_name(preprocessor_args, direct_hash, 1); + MTR_END("hash", "direct_hash"); + if (result_name) { + update_cached_result_globals(result_name); + + // If we can return from cache at this point then do so. + from_cache(FROMCACHE_DIRECT_MODE, 0); + + // Wasn't able to return from cache at this point. However, the result + // was already found in manifest, so don't re-add it later. + put_result_in_manifest = false; + + result_name_from_manifest = result_name; + } else { + // Add result to manifest later. + put_result_in_manifest = true; + } + } + + if (conf->read_only_direct) { + cc_log("Read-only direct mode; running real compiler"); + failed(); + } + + if (!conf->depend_mode) { + // Find the hash using the preprocessed output. Also updates + // included_files. + struct hash* cpp_hash = hash_copy(common_hash); + init_hash_debug( + cpp_hash, output_obj, 'p', "PREPROCESSOR MODE", debug_text_file); + + MTR_BEGIN("hash", "cpp_hash"); + result_name = calculate_result_name(preprocessor_args, cpp_hash, 0); + MTR_END("hash", "cpp_hash"); + if (!result_name) { + fatal("internal error: calculate_result_name returned NULL for cpp"); + } + update_cached_result_globals(result_name); + + if (result_name_from_manifest + && !digests_equal(result_name_from_manifest, result_name)) { + // The hash from manifest differs from the hash of the preprocessor + // output. This could be because: + // + // - The preprocessor produces different output for the same input (not + // likely). + // - There's a bug in ccache (maybe incorrect handling of compiler + // arguments). + // - The user has used a different CCACHE_BASEDIR (most likely). + // + // The best thing here would probably be to remove the hash entry from + // the manifest. For now, we use a simpler method: just remove the + // manifest file. + cc_log("Hash from manifest doesn't match preprocessor output"); + cc_log("Likely reason: different CCACHE_BASEDIRs used"); + cc_log("Removing manifest as a safety measure"); + x_unlink(manifest_path); + + put_result_in_manifest = true; + } + + // If we can return from cache at this point then do. + from_cache(FROMCACHE_CPP_MODE, put_result_in_manifest); + } + + if (conf->read_only) { + cc_log("Read-only mode; running real compiler"); + failed(); + } + + add_prefix(compiler_args, conf->prefix_command); + + // In depend_mode, extend the direct hash. + struct hash* depend_mode_hash = conf->depend_mode ? direct_hash : NULL; + + // Run real compiler, sending output to cache. + MTR_BEGIN("cache", "to_cache"); + to_cache(compiler_args, depend_mode_hash); + MTR_END("cache", "to_cache"); + + x_exit(0); } static void -configuration_printer(const char *descr, const char *origin, void *context) +configuration_printer(const char* descr, const char* origin, void* context) { - assert(context); - auto f = static_cast<FILE*>(context); - fprintf(f, "(%s) %s\n", origin, descr); + assert(context); + auto f = static_cast<FILE*>(context); + fprintf(f, "(%s) %s\n", origin, descr); } // The main program when not doing a compile. static int -ccache_main_options(int argc, char *argv[]) +ccache_main_options(int argc, char* argv[]) { - enum longopts { - DUMP_MANIFEST, - DUMP_RESULT, - HASH_FILE, - PRINT_STATS, - }; - static const struct option options[] = { - {"cleanup", no_argument, 0, 'c'}, - {"clear", no_argument, 0, 'C'}, - {"dump-manifest", required_argument, 0, DUMP_MANIFEST}, - {"dump-result", required_argument, 0, DUMP_RESULT}, - {"get-config", required_argument, 0, 'k'}, - {"hash-file", required_argument, 0, HASH_FILE}, - {"help", no_argument, 0, 'h'}, - {"max-files", required_argument, 0, 'F'}, - {"max-size", required_argument, 0, 'M'}, - {"print-stats", no_argument, 0, PRINT_STATS}, - {"set-config", required_argument, 0, 'o'}, - {"show-compression", no_argument, 0, 'x'}, - {"show-config", no_argument, 0, 'p'}, - {"show-stats", no_argument, 0, 's'}, - {"version", no_argument, 0, 'V'}, - {"zero-stats", no_argument, 0, 'z'}, - {0, 0, 0, 0} - }; - - int c; - while ((c = getopt_long(argc, argv, "cCk:hF:M:po:sVxz", options, NULL)) - != -1) { - switch (c) { - case DUMP_MANIFEST: - initialize(); - manifest_dump(optarg, stdout); - break; - - case DUMP_RESULT: - initialize(); - if (!result_dump(optarg, stdout)) { - return 1; - } - break; - - case HASH_FILE: - { - initialize(); - struct hash *hash = hash_init(); - if (str_eq(optarg, "-")) { - hash_fd(hash, STDIN_FILENO); - } else { - hash_file(hash, optarg); - } - char digest[DIGEST_STRING_BUFFER_SIZE]; - hash_result_as_string(hash, digest); - puts(digest); - hash_free(hash); - break; - } - - case PRINT_STATS: - initialize(); - stats_print(); - break; - - case 'c': // --cleanup - initialize(); - clean_up_all(conf); - printf("Cleaned cache\n"); - break; - - case 'C': // --clear - initialize(); - wipe_all(conf); - printf("Cleared cache\n"); - break; - - case 'h': // --help - fputs(USAGE_TEXT, stdout); - x_exit(0); - - case 'k': // --get-config - { - initialize(); - char *errmsg; - if (!conf_print_value(conf, optarg, stdout, &errmsg)) { - fatal("%s", errmsg); - } - } - break; - - case 'F': // --max-files - { - initialize(); - char *errmsg; - if (conf_set_value_in_file(primary_config_path, "max_files", optarg, - &errmsg)) { - unsigned files = atoi(optarg); - if (files == 0) { - printf("Unset cache file limit\n"); - } else { - printf("Set cache file limit to %u\n", files); - } - } else { - fatal("could not set cache file limit: %s", errmsg); - } - } - break; - - case 'M': // --max-size - { - initialize(); - uint64_t size; - if (!parse_size_with_suffix(optarg, &size)) { - fatal("invalid size: %s", optarg); - } - char *errmsg; - if (conf_set_value_in_file(primary_config_path, "max_size", optarg, - &errmsg)) { - if (size == 0) { - printf("Unset cache size limit\n"); - } else { - char *s = format_human_readable_size(size); - printf("Set cache size limit to %s\n", s); - free(s); - } - } else { - fatal("could not set cache size limit: %s", errmsg); - } - } - break; - - case 'o': // --set-config - { - initialize(); - char *p = strchr(optarg, '='); - if (!p) { - fatal("missing equal sign in \"%s\"", optarg); - } - char *key = x_strndup(optarg, p - optarg); - char *value = p + 1; - char *errmsg; - if (!conf_set_value_in_file(primary_config_path, key, value, &errmsg)) { - fatal("%s", errmsg); - } - free(key); - } - break; - - case 'p': // --show-config - initialize(); - conf_print_items(conf, configuration_printer, stdout); - break; - - case 's': // --show-stats - initialize(); - stats_summary(); - break; - - case 'V': // --version - fprintf(stdout, VERSION_TEXT, CCACHE_VERSION); - x_exit(0); - - case 'x': // --show-compression - initialize(); - compress_stats(conf); - break; - - case 'z': // --zero-stats - initialize(); - stats_zero(); - printf("Statistics zeroed\n"); - break; - - default: - fputs(USAGE_TEXT, stderr); - x_exit(1); - } - } - - return 0; + enum longopts { + DUMP_MANIFEST, + DUMP_RESULT, + HASH_FILE, + PRINT_STATS, + }; + static const struct option options[] = { + {"cleanup", no_argument, 0, 'c'}, + {"clear", no_argument, 0, 'C'}, + {"dump-manifest", required_argument, 0, DUMP_MANIFEST}, + {"dump-result", required_argument, 0, DUMP_RESULT}, + {"get-config", required_argument, 0, 'k'}, + {"hash-file", required_argument, 0, HASH_FILE}, + {"help", no_argument, 0, 'h'}, + {"max-files", required_argument, 0, 'F'}, + {"max-size", required_argument, 0, 'M'}, + {"print-stats", no_argument, 0, PRINT_STATS}, + {"set-config", required_argument, 0, 'o'}, + {"show-compression", no_argument, 0, 'x'}, + {"show-config", no_argument, 0, 'p'}, + {"show-stats", no_argument, 0, 's'}, + {"version", no_argument, 0, 'V'}, + {"zero-stats", no_argument, 0, 'z'}, + {0, 0, 0, 0}}; + + int c; + while ((c = getopt_long(argc, argv, "cCk:hF:M:po:sVxz", options, NULL)) + != -1) { + switch (c) { + case DUMP_MANIFEST: + initialize(); + manifest_dump(optarg, stdout); + break; + + case DUMP_RESULT: + initialize(); + if (!result_dump(optarg, stdout)) { + return 1; + } + break; + + case HASH_FILE: { + initialize(); + struct hash* hash = hash_init(); + if (str_eq(optarg, "-")) { + hash_fd(hash, STDIN_FILENO); + } else { + hash_file(hash, optarg); + } + char digest[DIGEST_STRING_BUFFER_SIZE]; + hash_result_as_string(hash, digest); + puts(digest); + hash_free(hash); + break; + } + + case PRINT_STATS: + initialize(); + stats_print(); + break; + + case 'c': // --cleanup + initialize(); + clean_up_all(conf); + printf("Cleaned cache\n"); + break; + + case 'C': // --clear + initialize(); + wipe_all(conf); + printf("Cleared cache\n"); + break; + + case 'h': // --help + fputs(USAGE_TEXT, stdout); + x_exit(0); + + case 'k': // --get-config + { + initialize(); + char* errmsg; + if (!conf_print_value(conf, optarg, stdout, &errmsg)) { + fatal("%s", errmsg); + } + } break; + + case 'F': // --max-files + { + initialize(); + char* errmsg; + if (conf_set_value_in_file( + primary_config_path, "max_files", optarg, &errmsg)) { + unsigned files = atoi(optarg); + if (files == 0) { + printf("Unset cache file limit\n"); + } else { + printf("Set cache file limit to %u\n", files); + } + } else { + fatal("could not set cache file limit: %s", errmsg); + } + } break; + + case 'M': // --max-size + { + initialize(); + uint64_t size; + if (!parse_size_with_suffix(optarg, &size)) { + fatal("invalid size: %s", optarg); + } + char* errmsg; + if (conf_set_value_in_file( + primary_config_path, "max_size", optarg, &errmsg)) { + if (size == 0) { + printf("Unset cache size limit\n"); + } else { + char* s = format_human_readable_size(size); + printf("Set cache size limit to %s\n", s); + free(s); + } + } else { + fatal("could not set cache size limit: %s", errmsg); + } + } break; + + case 'o': // --set-config + { + initialize(); + char* p = strchr(optarg, '='); + if (!p) { + fatal("missing equal sign in \"%s\"", optarg); + } + char* key = x_strndup(optarg, p - optarg); + char* value = p + 1; + char* errmsg; + if (!conf_set_value_in_file(primary_config_path, key, value, &errmsg)) { + fatal("%s", errmsg); + } + free(key); + } break; + + case 'p': // --show-config + initialize(); + conf_print_items(conf, configuration_printer, stdout); + break; + + case 's': // --show-stats + initialize(); + stats_summary(); + break; + + case 'V': // --version + fprintf(stdout, VERSION_TEXT, CCACHE_VERSION); + x_exit(0); + + case 'x': // --show-compression + initialize(); + compress_stats(conf); + break; + + case 'z': // --zero-stats + initialize(); + stats_zero(); + printf("Statistics zeroed\n"); + break; + + default: + fputs(USAGE_TEXT, stderr); + x_exit(1); + } + } + + return 0; } -int ccache_main(int argc, char *argv[]); +int ccache_main(int argc, char* argv[]); int -ccache_main(int argc, char *argv[]) +ccache_main(int argc, char* argv[]) { - // Check if we are being invoked as "ccache". - char *program_name = x_basename(argv[0]); - if (same_executable_name(program_name, MYNAME)) { - if (argc < 2) { - fputs(USAGE_TEXT, stderr); - x_exit(1); - } - // If the first argument isn't an option, then assume we are being passed a - // compiler name and options. - if (argv[1][0] == '-') { - return ccache_main_options(argc, argv); - } - } - free(program_name); - - ccache(argc, argv); + // Check if we are being invoked as "ccache". + char* program_name = x_basename(argv[0]); + if (same_executable_name(program_name, MYNAME)) { + if (argc < 2) { + fputs(USAGE_TEXT, stderr); + x_exit(1); + } + // If the first argument isn't an option, then assume we are being passed a + // compiler name and options. + if (argv[1][0] == '-') { + return ccache_main_options(argc, argv); + } + } + free(program_name); + + ccache(argc, argv); } diff --git a/src/ccache.hpp b/src/ccache.hpp index 089f6b75..e8be9244 100644 --- a/src/ccache.hpp +++ b/src/ccache.hpp @@ -20,68 +20,71 @@ #pragma once #include "system.hpp" + #include "conf.hpp" #include "counters.hpp" + #include "third_party/minitrace.h" #ifdef __GNUC__ -#define ATTR_FORMAT(x, y, z) __attribute__((format (ATTRIBUTE_FORMAT_PRINTF, y, z))) -#define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_FORMAT(x, y, z) \ + __attribute__((format(ATTRIBUTE_FORMAT_PRINTF, y, z))) +# define ATTR_NORETURN __attribute__((noreturn)) #else -#define ATTR_FORMAT(x, y, z) -#define ATTR_NORETURN +# define ATTR_FORMAT(x, y, z) +# define ATTR_NORETURN #endif #ifndef MYNAME -#define MYNAME "ccache" +# define MYNAME "ccache" #endif extern const char CCACHE_VERSION[]; // Statistics fields in storage order. enum stats { - STATS_NONE = 0, - STATS_STDOUT = 1, - STATS_STATUS = 2, - STATS_ERROR = 3, - STATS_TOCACHE = 4, - STATS_PREPROCESSOR = 5, - STATS_COMPILER = 6, - STATS_MISSING = 7, - STATS_CACHEHIT_CPP = 8, - STATS_ARGS = 9, - STATS_LINK = 10, - STATS_NUMFILES = 11, - STATS_TOTALSIZE = 12, - STATS_OBSOLETE_MAXFILES = 13, - STATS_OBSOLETE_MAXSIZE = 14, - STATS_SOURCELANG = 15, - STATS_BADOUTPUTFILE = 16, - STATS_NOINPUT = 17, - STATS_MULTIPLE = 18, - STATS_CONFTEST = 19, - STATS_UNSUPPORTED_OPTION = 20, - STATS_OUTSTDOUT = 21, - STATS_CACHEHIT_DIR = 22, - STATS_NOOUTPUT = 23, - STATS_EMPTYOUTPUT = 24, - STATS_BADEXTRAFILE = 25, - STATS_COMPCHECK = 26, - STATS_CANTUSEPCH = 27, - STATS_PREPROCESSING = 28, - STATS_NUMCLEANUPS = 29, - STATS_UNSUPPORTED_DIRECTIVE = 30, - STATS_ZEROTIMESTAMP = 31, - - STATS_END + STATS_NONE = 0, + STATS_STDOUT = 1, + STATS_STATUS = 2, + STATS_ERROR = 3, + STATS_TOCACHE = 4, + STATS_PREPROCESSOR = 5, + STATS_COMPILER = 6, + STATS_MISSING = 7, + STATS_CACHEHIT_CPP = 8, + STATS_ARGS = 9, + STATS_LINK = 10, + STATS_NUMFILES = 11, + STATS_TOTALSIZE = 12, + STATS_OBSOLETE_MAXFILES = 13, + STATS_OBSOLETE_MAXSIZE = 14, + STATS_SOURCELANG = 15, + STATS_BADOUTPUTFILE = 16, + STATS_NOINPUT = 17, + STATS_MULTIPLE = 18, + STATS_CONFTEST = 19, + STATS_UNSUPPORTED_OPTION = 20, + STATS_OUTSTDOUT = 21, + STATS_CACHEHIT_DIR = 22, + STATS_NOOUTPUT = 23, + STATS_EMPTYOUTPUT = 24, + STATS_BADEXTRAFILE = 25, + STATS_COMPCHECK = 26, + STATS_CANTUSEPCH = 27, + STATS_PREPROCESSING = 28, + STATS_NUMCLEANUPS = 29, + STATS_UNSUPPORTED_DIRECTIVE = 30, + STATS_ZEROTIMESTAMP = 31, + + STATS_END }; enum guessed_compiler { - GUESSED_CLANG, - GUESSED_GCC, - GUESSED_NVCC, - GUESSED_PUMP, - GUESSED_UNKNOWN + GUESSED_CLANG, + GUESSED_GCC, + GUESSED_NVCC, + GUESSED_PUMP, + GUESSED_UNKNOWN }; extern enum guessed_compiler guessed_compiler; @@ -106,11 +109,11 @@ extern enum guessed_compiler guessed_compiler; #define SLOPPY_LOCALE (1U << 9) #define str_eq(s1, s2) (strcmp((s1), (s2)) == 0) -#define str_startswith(s, prefix) \ - (strncmp((s), (prefix), strlen((prefix))) == 0) -#define str_endswith(s, suffix) \ - (strlen(s) >= strlen(suffix) \ - && str_eq((s) + strlen(s) - strlen(suffix), (suffix))) +#define str_startswith(s, prefix) \ + (strncmp((s), (prefix), strlen((prefix))) == 0) +#define str_endswith(s, suffix) \ + (strlen(s) >= strlen(suffix) \ + && str_eq((s) + strlen(s) - strlen(suffix), (suffix))) #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) // Buffer size for I/O operations. Should be a multiple of 4 KiB. @@ -119,99 +122,100 @@ extern enum guessed_compiler guessed_compiler; // ---------------------------------------------------------------------------- // args.c -struct args { - char **argv; - int argc; +struct args +{ + char** argv; + int argc; }; -struct args *args_init(int, const char * const*); -struct args *args_init_from_string(const char *); -struct args *args_init_from_gcc_atfile(const char *filename); -struct args *args_copy(struct args *args); -void args_free(struct args *args); -void args_add(struct args *args, const char *s); -void args_add_prefix(struct args *args, const char *s); -void args_extend(struct args *args, struct args *to_append); -void args_insert(struct args *dest, int index, struct args *src, bool replace); -void args_pop(struct args *args, int n); -void args_set(struct args *args, int index, const char *value); -void args_strip(struct args *args, const char *prefix); -void args_remove_first(struct args *args); -char *args_to_string(const struct args *args); -bool args_equal(const struct args *args1, const struct args *args2); +struct args* args_init(int, const char* const*); +struct args* args_init_from_string(const char*); +struct args* args_init_from_gcc_atfile(const char* filename); +struct args* args_copy(struct args* args); +void args_free(struct args* args); +void args_add(struct args* args, const char* s); +void args_add_prefix(struct args* args, const char* s); +void args_extend(struct args* args, struct args* to_append); +void args_insert(struct args* dest, int index, struct args* src, bool replace); +void args_pop(struct args* args, int n); +void args_set(struct args* args, int index, const char* value); +void args_strip(struct args* args, const char* prefix); +void args_remove_first(struct args* args); +char* args_to_string(const struct args* args); +bool args_equal(const struct args* args1, const struct args* args2); // ---------------------------------------------------------------------------- // util.c -void cc_log(const char *format, ...) ATTR_FORMAT(printf, 1, 2); -void cc_bulklog(const char *format, ...) ATTR_FORMAT(printf, 1, 2); -void cc_log_argv(const char *prefix, char **argv); -void cc_dump_debug_log_buffer(const char *path); -void fatal(const char *format, ...) ATTR_FORMAT(printf, 1, 2) ATTR_NORETURN; -void warn(const char *format, ...) ATTR_FORMAT(printf, 1, 2); +void cc_log(const char* format, ...) ATTR_FORMAT(printf, 1, 2); +void cc_bulklog(const char* format, ...) ATTR_FORMAT(printf, 1, 2); +void cc_log_argv(const char* prefix, char** argv); +void cc_dump_debug_log_buffer(const char* path); +void fatal(const char* format, ...) ATTR_FORMAT(printf, 1, 2) ATTR_NORETURN; +void warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2); -char *get_path_in_cache(const char *name, const char *suffix); +char* get_path_in_cache(const char* name, const char* suffix); bool copy_fd(int fd_in, int fd_out); -bool clone_file(const char *src, const char *dest, bool via_tmp_file); -bool copy_file(const char *src, const char *dest, bool via_tmp_file); -bool move_file(const char *src, const char *dest); -int create_dir(const char *dir); -int create_parent_dirs(const char *path); -const char *get_hostname(void); -const char *tmp_string(void); -int create_cachedirtag(const char *dir); -char *format(const char *format, ...) ATTR_FORMAT(printf, 1, 2); -void format_hex(const uint8_t *data, size_t size, char *buffer); -void reformat(char **ptr, const char *format, ...) ATTR_FORMAT(printf, 2, 3); -char *x_strdup(const char *s); -char *x_strndup(const char *s, size_t n); -void *x_malloc(size_t size); -void *x_calloc(size_t nmemb, size_t size); -void *x_realloc(void *ptr, size_t size); -void x_setenv(const char *name, const char *value); -void x_unsetenv(const char *name); -int x_fstat(int fd, struct stat *buf); -int x_lstat(const char *pathname, struct stat *buf); -int x_stat(const char *pathname, struct stat *buf); -void traverse(const char *dir, void (*fn)(const char *, struct stat *)); -char *x_basename(const char *path); -char *x_dirname(const char *path); -const char *get_extension(const char *path); -char *remove_extension(const char *path); -size_t file_size(struct stat *st); -char *format_human_readable_size(uint64_t size); -char *format_parsable_size_with_suffix(uint64_t size); -bool parse_size_with_suffix(const char *str, uint64_t *size); -char *x_realpath(const char *path); -char *gnu_getcwd(void); +bool clone_file(const char* src, const char* dest, bool via_tmp_file); +bool copy_file(const char* src, const char* dest, bool via_tmp_file); +bool move_file(const char* src, const char* dest); +int create_dir(const char* dir); +int create_parent_dirs(const char* path); +const char* get_hostname(void); +const char* tmp_string(void); +int create_cachedirtag(const char* dir); +char* format(const char* format, ...) ATTR_FORMAT(printf, 1, 2); +void format_hex(const uint8_t* data, size_t size, char* buffer); +void reformat(char** ptr, const char* format, ...) ATTR_FORMAT(printf, 2, 3); +char* x_strdup(const char* s); +char* x_strndup(const char* s, size_t n); +void* x_malloc(size_t size); +void* x_calloc(size_t nmemb, size_t size); +void* x_realloc(void* ptr, size_t size); +void x_setenv(const char* name, const char* value); +void x_unsetenv(const char* name); +int x_fstat(int fd, struct stat* buf); +int x_lstat(const char* pathname, struct stat* buf); +int x_stat(const char* pathname, struct stat* buf); +void traverse(const char* dir, void (*fn)(const char*, struct stat*)); +char* x_basename(const char* path); +char* x_dirname(const char* path); +const char* get_extension(const char* path); +char* remove_extension(const char* path); +size_t file_size(struct stat* st); +char* format_human_readable_size(uint64_t size); +char* format_parsable_size_with_suffix(uint64_t size); +bool parse_size_with_suffix(const char* str, uint64_t* size); +char* x_realpath(const char* path); +char* gnu_getcwd(void); #ifndef HAVE_LOCALTIME_R -struct tm *localtime_r(const time_t *timep, struct tm *result); +struct tm* localtime_r(const time_t* timep, struct tm* result); #endif #ifndef HAVE_STRTOK_R -char *strtok_r(char *str, const char *delim, char **saveptr); +char* strtok_r(char* str, const char* delim, char** saveptr); #endif -int create_tmp_fd(char **fname); -FILE *create_tmp_file(char **fname, const char *mode); -const char *get_home_directory(void); -char *get_cwd(void); -bool same_executable_name(const char *s1, const char *s2); -size_t common_dir_prefix_length(const char *s1, const char *s2); -char *get_relative_path(const char *from, const char *to); -bool is_absolute_path(const char *path); -bool is_full_path(const char *path); -bool is_symlink(const char *path); -void update_mtime(const char *path); +int create_tmp_fd(char** fname); +FILE* create_tmp_file(char** fname, const char* mode); +const char* get_home_directory(void); +char* get_cwd(void); +bool same_executable_name(const char* s1, const char* s2); +size_t common_dir_prefix_length(const char* s1, const char* s2); +char* get_relative_path(const char* from, const char* to); +bool is_absolute_path(const char* path); +bool is_full_path(const char* path); +bool is_symlink(const char* path); +void update_mtime(const char* path); void x_exit(int status) ATTR_NORETURN; -int x_rename(const char *oldpath, const char *newpath); -int tmp_unlink(const char *path); -int x_unlink(const char *path); -int x_try_unlink(const char *path); +int x_rename(const char* oldpath, const char* newpath); +int tmp_unlink(const char* path); +int x_unlink(const char* path); +int x_try_unlink(const char* path); #ifndef _WIN32 -char *x_readlink(const char *path); +char* x_readlink(const char* path); #endif -bool read_file(const char *path, size_t size_hint, char **data, size_t *size); -char *read_text_file(const char *path, size_t size_hint); -char *subst_env_in_string(const char *str, char **errmsg); +bool read_file(const char* path, size_t size_hint, char** data, size_t* size); +char* read_text_file(const char* path, size_t size_hint); +char* subst_env_in_string(const char* str, char** errmsg); void set_cloexec_flag(int fd); double time_seconds(void); @@ -224,49 +228,50 @@ unsigned stats_get_pending(enum stats stat); void stats_zero(void); void stats_summary(void); void stats_print(void); -void stats_update_size(const char *sfile, int64_t size, int files); -void stats_get_obsolete_limits(const char *dir, unsigned *maxfiles, - uint64_t *maxsize); -void stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size); -void stats_add_cleanup(const char *dir, unsigned count); -void stats_timestamp(time_t time, struct counters *counters); -void stats_read(const char *path, struct counters *counters); -void stats_write(const char *path, struct counters *counters); +void stats_update_size(const char* sfile, int64_t size, int files); +void stats_get_obsolete_limits(const char* dir, + unsigned* maxfiles, + uint64_t* maxsize); +void stats_set_sizes(const char* dir, unsigned num_files, uint64_t total_size); +void stats_add_cleanup(const char* dir, unsigned count); +void stats_timestamp(time_t time, struct counters* counters); +void stats_read(const char* path, struct counters* counters); +void stats_write(const char* path, struct counters* counters); // ---------------------------------------------------------------------------- // exitfn.c void exitfn_init(void); void exitfn_add_nullary(void (*function)(void)); -void exitfn_add(void (*function)(void *), void *context); -void exitfn_add_last(void (*function)(void *), void *context); +void exitfn_add(void (*function)(void*), void* context); +void exitfn_add_last(void (*function)(void*), void* context); void exitfn_call(void); // ---------------------------------------------------------------------------- // cleanup.c -void clean_up_dir(struct conf *conf, const char *dir, double limit_multiple); -void clean_up_all(struct conf *conf); -void wipe_all(struct conf *conf); +void clean_up_dir(struct conf* conf, const char* dir, double limit_multiple); +void clean_up_all(struct conf* conf); +void wipe_all(struct conf* conf); // ---------------------------------------------------------------------------- // compress.c -void compress_stats(struct conf *conf); +void compress_stats(struct conf* conf); // ---------------------------------------------------------------------------- // execute.c -int execute(char **argv, int fd_out, int fd_err, pid_t *pid); -char *find_executable(const char *name, const char *exclude_name); -void print_command(FILE *fp, char **argv); -char *format_command(const char* const* argv); +int execute(char** argv, int fd_out, int fd_err, pid_t* pid); +char* find_executable(const char* name, const char* exclude_name); +void print_command(FILE* fp, char** argv); +char* format_command(const char* const* argv); // ---------------------------------------------------------------------------- // lockfile.c -bool lockfile_acquire(const char *path, unsigned staleness_limit); -void lockfile_release(const char *path); +bool lockfile_acquire(const char* path, unsigned staleness_limit); +void lockfile_release(const char* path); // ---------------------------------------------------------------------------- // ccache.c @@ -275,52 +280,55 @@ extern time_t time_of_compilation; extern bool output_is_precompiled_header; void block_signals(void); void unblock_signals(void); -bool cc_process_args(struct args *args, struct args **preprocessor_args, - struct args **compiler_args); +bool cc_process_args(struct args* args, + struct args** preprocessor_args, + struct args** compiler_args); void cc_reset(void); -bool is_precompiled_header(const char *path); +bool is_precompiled_header(const char* path); // ---------------------------------------------------------------------------- #ifdef HAVE_COMPAR_FN_T -#define COMPAR_FN_T __compar_fn_t +# define COMPAR_FN_T __compar_fn_t #else -typedef int (*COMPAR_FN_T)(const void *, const void *); +typedef int (*COMPAR_FN_T)(const void*, const void*); #endif // Work with silly DOS binary open. #ifndef O_BINARY -#define O_BINARY 0 +# define O_BINARY 0 #endif #ifdef _WIN32 -char *win32argvtos(char *prefix, char **argv, int *length); -char *win32getshell(char *path); -int win32execute(char *path, char **argv, int doreturn, - int fd_stdout, int fd_stderr); -void add_exe_ext_if_no_to_fullpath(char *full_path_win_ext, size_t max_size, - const char *ext, const char *path); -# ifndef _WIN32_WINNT +char* win32argvtos(char* prefix, char** argv, int* length); +char* win32getshell(char* path); +int win32execute( + char* path, char** argv, int doreturn, int fd_stdout, int fd_stderr); +void add_exe_ext_if_no_to_fullpath(char* full_path_win_ext, + size_t max_size, + const char* ext, + const char* path); +# ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0501 -# endif -# include <windows.h> -# define mkdir(a,b) mkdir(a) -# define link(src,dst) (CreateHardLink(dst,src,NULL) ? 0 : -1) -# define lstat(a,b) stat(a,b) -# define execv(a,b) win32execute(a,b,0,-1,-1) -# define execute(a,b,c,d) win32execute(*(a),a,1,b,c) -# define DIR_DELIM_CH '\\' -# define PATH_DELIM ";" -# define F_RDLCK 0 -# define F_WRLCK 0 +# endif +# include <windows.h> +# define mkdir(a, b) mkdir(a) +# define link(src, dst) (CreateHardLink(dst, src, NULL) ? 0 : -1) +# define lstat(a, b) stat(a, b) +# define execv(a, b) win32execute(a, b, 0, -1, -1) +# define execute(a, b, c, d) win32execute(*(a), a, 1, b, c) +# define DIR_DELIM_CH '\\' +# define PATH_DELIM ";" +# define F_RDLCK 0 +# define F_WRLCK 0 #else -# define DIR_DELIM_CH '/' -# define PATH_DELIM ":" +# define DIR_DELIM_CH '/' +# define PATH_DELIM ":" #endif #ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +# define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif diff --git a/src/cleanup.cpp b/src/cleanup.cpp index 8116aa60..4ef6665a 100644 --- a/src/cleanup.cpp +++ b/src/cleanup.cpp @@ -21,11 +21,14 @@ #include <math.h> -static struct files { - char *fname; - time_t mtime; - uint64_t size; -} **files; +struct files +{ + char* fname; + time_t mtime; + uint64_t size; +}; + +static struct files** files; static unsigned allocated; // Size of the files array. static unsigned num_files; // Number of used entries in the files array. @@ -36,76 +39,76 @@ static size_t files_in_cache_threshold; // File comparison function that orders files in mtime order, oldest first. static int -files_compare(struct files **f1, struct files **f2) +files_compare(struct files** f1, struct files** f2) { - if ((*f2)->mtime == (*f1)->mtime) { - return strcmp((*f1)->fname, (*f2)->fname); - } - if ((*f2)->mtime > (*f1)->mtime) { - return -1; - } - return 1; + if ((*f2)->mtime == (*f1)->mtime) { + return strcmp((*f1)->fname, (*f2)->fname); + } + if ((*f2)->mtime > (*f1)->mtime) { + return -1; + } + return 1; } // This builds the list of files in the cache. static void -traverse_fn(const char *fname, struct stat *st) +traverse_fn(const char* fname, struct stat* st) { - if (!S_ISREG(st->st_mode)) { - return; - } - - char *p = x_basename(fname); - if (str_eq(p, "stats")) { - goto out; - } - - if (str_startswith(p, ".nfs")) { - // Ignore temporary NFS files that may be left for open but deleted files. - goto out; - } - - // Delete any tmp files older than 1 hour. - if (strstr(p, ".tmp.") && st->st_mtime + 3600 < time(NULL)) { - x_unlink(fname); - goto out; - } - - if (strstr(p, "CACHEDIR.TAG")) { - goto out; - } - - if (num_files == allocated) { - allocated = 10000 + num_files*2; - files = (struct files **)x_realloc(files, sizeof(struct files *)*allocated); - } - - files[num_files] = (struct files *)x_malloc(sizeof(struct files)); - files[num_files]->fname = x_strdup(fname); - files[num_files]->mtime = st->st_mtime; - files[num_files]->size = file_size(st); - cache_size += files[num_files]->size; - files_in_cache++; - num_files++; + if (!S_ISREG(st->st_mode)) { + return; + } + + char* p = x_basename(fname); + if (str_eq(p, "stats")) { + goto out; + } + + if (str_startswith(p, ".nfs")) { + // Ignore temporary NFS files that may be left for open but deleted files. + goto out; + } + + // Delete any tmp files older than 1 hour. + if (strstr(p, ".tmp.") && st->st_mtime + 3600 < time(NULL)) { + x_unlink(fname); + goto out; + } + + if (strstr(p, "CACHEDIR.TAG")) { + goto out; + } + + if (num_files == allocated) { + allocated = 10000 + num_files * 2; + files = (struct files**)x_realloc(files, sizeof(struct files*) * allocated); + } + + files[num_files] = (struct files*)x_malloc(sizeof(struct files)); + files[num_files]->fname = x_strdup(fname); + files[num_files]->mtime = st->st_mtime; + files[num_files]->size = file_size(st); + cache_size += files[num_files]->size; + files_in_cache++; + num_files++; out: - free(p); + free(p); } static void -delete_file(const char *path, size_t size, bool update_counters) +delete_file(const char* path, size_t size, bool update_counters) { - bool deleted = x_try_unlink(path) == 0; - if (!deleted && errno != ENOENT && errno != ESTALE) { - cc_log("Failed to unlink %s (%s)", path, strerror(errno)); - } else if (update_counters) { - // The counters are intentionally subtracted even if there was no file to - // delete since the final cache size calculation will be incorrect if they - // aren't. (This can happen when there are several parallel ongoing - // cleanups of the same directory.) - cache_size -= size; - files_in_cache--; - } + bool deleted = x_try_unlink(path) == 0; + if (!deleted && errno != ENOENT && errno != ESTALE) { + cc_log("Failed to unlink %s (%s)", path, strerror(errno)); + } else if (update_counters) { + // The counters are intentionally subtracted even if there was no file to + // delete since the final cache size calculation will be incorrect if they + // aren't. (This can happen when there are several parallel ongoing + // cleanups of the same directory.) + cache_size -= size; + files_in_cache--; + } } // Sort the files we've found and delete the oldest ones until we are below the @@ -113,160 +116,162 @@ delete_file(const char *path, size_t size, bool update_counters) static bool sort_and_clean(void) { - if (num_files > 1) { - // Sort in ascending mtime order. - qsort(files, num_files, sizeof(struct files *), (COMPAR_FN_T)files_compare); - } - - // Delete enough files to bring us below the threshold. - bool cleaned = false; - for (unsigned i = 0; i < num_files; i++) { - const char *ext; - - if ((cache_size_threshold == 0 - || cache_size <= cache_size_threshold) - && (files_in_cache_threshold == 0 - || files_in_cache <= files_in_cache_threshold)) { - break; - } - - ext = get_extension(files[i]->fname); - if (str_eq(ext, ".stderr")) { - // Make sure that the .o file is deleted before .stderr, because if the - // ccache process gets killed after deleting the .stderr but before - // deleting the .o, the cached result will be inconsistent. (.stderr is - // the only file that is optional; any other file missing from the cache - // will be detected by get_file_from_cache.) - char *base = remove_extension(files[i]->fname); - char *o_file = format("%s.o", base); - - // Don't subtract this extra deletion from the cache size; that - // bookkeeping will be done when the loop reaches the .o file. If the - // loop doesn't reach the .o file since the target limits have been - // reached, the bookkeeping won't happen, but that small counter - // discrepancy won't do much harm and it will correct itself in the next - // cleanup. - delete_file(o_file, 0, false); - - free(o_file); - free(base); - } - delete_file(files[i]->fname, files[i]->size, true); - cleaned = true; - } - return cleaned; + if (num_files > 1) { + // Sort in ascending mtime order. + qsort(files, num_files, sizeof(struct files*), (COMPAR_FN_T)files_compare); + } + + // Delete enough files to bring us below the threshold. + bool cleaned = false; + for (unsigned i = 0; i < num_files; i++) { + const char* ext; + + if ((cache_size_threshold == 0 || cache_size <= cache_size_threshold) + && (files_in_cache_threshold == 0 + || files_in_cache <= files_in_cache_threshold)) { + break; + } + + ext = get_extension(files[i]->fname); + if (str_eq(ext, ".stderr")) { + // Make sure that the .o file is deleted before .stderr, because if the + // ccache process gets killed after deleting the .stderr but before + // deleting the .o, the cached result will be inconsistent. (.stderr is + // the only file that is optional; any other file missing from the cache + // will be detected by get_file_from_cache.) + char* base = remove_extension(files[i]->fname); + char* o_file = format("%s.o", base); + + // Don't subtract this extra deletion from the cache size; that + // bookkeeping will be done when the loop reaches the .o file. If the + // loop doesn't reach the .o file since the target limits have been + // reached, the bookkeeping won't happen, but that small counter + // discrepancy won't do much harm and it will correct itself in the next + // cleanup. + delete_file(o_file, 0, false); + + free(o_file); + free(base); + } + delete_file(files[i]->fname, files[i]->size, true); + cleaned = true; + } + return cleaned; } // Clean up one cache subdirectory. void -clean_up_dir(struct conf *conf, const char *dir, double limit_multiple) +clean_up_dir(struct conf* conf, const char* dir, double limit_multiple) { - cc_log("Cleaning up cache directory %s", dir); - - // When "max files" or "max cache size" is reached, one of the 16 cache - // subdirectories is cleaned up. When doing so, files are deleted (in LRU - // order) until the levels are below limit_multiple. - double cache_size_float = round(conf->max_size * limit_multiple / 16); - cache_size_threshold = (uint64_t)cache_size_float; - double files_in_cache_float = round(conf->max_files * limit_multiple / 16); - files_in_cache_threshold = (size_t)files_in_cache_float; - - num_files = 0; - cache_size = 0; - files_in_cache = 0; - - // Build a list of files. - traverse(dir, traverse_fn); - - // Clean the cache. - cc_log("Before cleanup: %.0f KiB, %.0f files", - (double)cache_size / 1024, - (double)files_in_cache); - bool cleaned = sort_and_clean(); - cc_log("After cleanup: %.0f KiB, %.0f files", - (double)cache_size / 1024, - (double)files_in_cache); - - if (cleaned) { - cc_log("Cleaned up cache directory %s", dir); - stats_add_cleanup(dir, 1); - } - - stats_set_sizes(dir, files_in_cache, cache_size); - - // Free it up. - for (unsigned i = 0; i < num_files; i++) { - free(files[i]->fname); - free(files[i]); - files[i] = NULL; - } - if (files) { - free(files); - } - allocated = 0; - files = NULL; - - num_files = 0; - cache_size = 0; - files_in_cache = 0; + cc_log("Cleaning up cache directory %s", dir); + + // When "max files" or "max cache size" is reached, one of the 16 cache + // subdirectories is cleaned up. When doing so, files are deleted (in LRU + // order) until the levels are below limit_multiple. + double cache_size_float = round(conf->max_size * limit_multiple / 16); + cache_size_threshold = (uint64_t)cache_size_float; + double files_in_cache_float = round(conf->max_files * limit_multiple / 16); + files_in_cache_threshold = (size_t)files_in_cache_float; + + num_files = 0; + cache_size = 0; + files_in_cache = 0; + + // Build a list of files. + traverse(dir, traverse_fn); + + // Clean the cache. + cc_log("Before cleanup: %.0f KiB, %.0f files", + (double)cache_size / 1024, + (double)files_in_cache); + bool cleaned = sort_and_clean(); + cc_log("After cleanup: %.0f KiB, %.0f files", + (double)cache_size / 1024, + (double)files_in_cache); + + if (cleaned) { + cc_log("Cleaned up cache directory %s", dir); + stats_add_cleanup(dir, 1); + } + + stats_set_sizes(dir, files_in_cache, cache_size); + + // Free it up. + for (unsigned i = 0; i < num_files; i++) { + free(files[i]->fname); + free(files[i]); + files[i] = NULL; + } + if (files) { + free(files); + } + allocated = 0; + files = NULL; + + num_files = 0; + cache_size = 0; + files_in_cache = 0; } // Clean up all cache subdirectories. -void clean_up_all(struct conf *conf) +void +clean_up_all(struct conf* conf) { - for (int i = 0; i <= 0xF; i++) { - char *dname = format("%s/%1x", conf->cache_dir, i); - clean_up_dir(conf, dname, 1.0); - free(dname); - } + for (int i = 0; i <= 0xF; i++) { + char* dname = format("%s/%1x", conf->cache_dir, i); + clean_up_dir(conf, dname, 1.0); + free(dname); + } } // Traverse function for wiping files. -static void wipe_fn(const char *fname, struct stat *st) +static void +wipe_fn(const char* fname, struct stat* st) { - if (!S_ISREG(st->st_mode)) { - return; - } + if (!S_ISREG(st->st_mode)) { + return; + } - char *p = x_basename(fname); - if (str_eq(p, "stats")) { - free(p); - return; - } - free(p); + char* p = x_basename(fname); + if (str_eq(p, "stats")) { + free(p); + return; + } + free(p); - files_in_cache++; + files_in_cache++; - x_unlink(fname); + x_unlink(fname); } // Wipe one cache subdirectory. static void -wipe_dir(const char *dir) +wipe_dir(const char* dir) { - cc_log("Clearing out cache directory %s", dir); + cc_log("Clearing out cache directory %s", dir); - files_in_cache = 0; + files_in_cache = 0; - traverse(dir, wipe_fn); + traverse(dir, wipe_fn); - if (files_in_cache > 0) { - cc_log("Cleared out cache directory %s", dir); - stats_add_cleanup(dir, 1); - } + if (files_in_cache > 0) { + cc_log("Cleared out cache directory %s", dir); + stats_add_cleanup(dir, 1); + } - files_in_cache = 0; + files_in_cache = 0; } // Wipe all cached files in all subdirectories. -void wipe_all(struct conf *conf) +void +wipe_all(struct conf* conf) { - for (int i = 0; i <= 0xF; i++) { - char *dname = format("%s/%1x", conf->cache_dir, i); - wipe_dir(dname); - free(dname); - } - - // Fix the counters. - clean_up_all(conf); + for (int i = 0; i <= 0xF; i++) { + char* dname = format("%s/%1x", conf->cache_dir, i); + wipe_dir(dname); + free(dname); + } + + // Fix the counters. + clean_up_all(conf); } diff --git a/src/common_header.cpp b/src/common_header.cpp index f7f88d04..83f3f09c 100644 --- a/src/common_header.cpp +++ b/src/common_header.cpp @@ -16,146 +16,148 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +#include "common_header.hpp" + #include "ccache.hpp" #include "int_bytes_conversion.hpp" -#include "common_header.hpp" bool -common_header_initialize_for_writing( - struct common_header *header, - FILE *output, - const char magic[4], - uint8_t version, - uint8_t compression_type, - int8_t compression_level, - uint64_t content_size, - XXH64_state_t *checksum, - struct compressor **compressor, - struct compr_state **compr_state) +common_header_initialize_for_writing(struct common_header* header, + FILE* output, + const char magic[4], + uint8_t version, + uint8_t compression_type, + int8_t compression_level, + uint64_t content_size, + XXH64_state_t* checksum, + struct compressor** compressor, + struct compr_state** compr_state) { - memcpy(header->magic, magic, 4); - header->version = version; - header->compression_type = compression_type; - header->compression_level = compression_level; - header->content_size = content_size; - - XXH64_reset(checksum, 0); - - *compressor = compressor_from_type(header->compression_type); - assert(*compressor); - *compr_state = - (*compressor)->init(output, header->compression_level, checksum); - if (!*compr_state) { - cc_log("Failed to initialize compressor"); - return false; - } - header->compression_level = - (*compressor)->get_actual_compression_level(*compr_state); - - uint8_t header_bytes[COMMON_HEADER_SIZE]; - memcpy(header_bytes, header->magic, 4); - header_bytes[4] = header->version; - header_bytes[5] = header->compression_type; - header_bytes[6] = header->compression_level; - BYTES_FROM_UINT64(header_bytes + 7, header->content_size); - if (fwrite(header_bytes, sizeof(header_bytes), 1, output) != 1) { - cc_log("Failed to write common file header"); - return false; - } - XXH64_update(checksum, header_bytes, sizeof(header_bytes)); - return true; + memcpy(header->magic, magic, 4); + header->version = version; + header->compression_type = compression_type; + header->compression_level = compression_level; + header->content_size = content_size; + + XXH64_reset(checksum, 0); + + *compressor = compressor_from_type(header->compression_type); + assert(*compressor); + *compr_state = + (*compressor)->init(output, header->compression_level, checksum); + if (!*compr_state) { + cc_log("Failed to initialize compressor"); + return false; + } + header->compression_level = + (*compressor)->get_actual_compression_level(*compr_state); + + uint8_t header_bytes[COMMON_HEADER_SIZE]; + memcpy(header_bytes, header->magic, 4); + header_bytes[4] = header->version; + header_bytes[5] = header->compression_type; + header_bytes[6] = header->compression_level; + BYTES_FROM_UINT64(header_bytes + 7, header->content_size); + if (fwrite(header_bytes, sizeof(header_bytes), 1, output) != 1) { + cc_log("Failed to write common file header"); + return false; + } + XXH64_update(checksum, header_bytes, sizeof(header_bytes)); + return true; } -bool common_header_initialize_for_reading( - struct common_header *header, - FILE *input, - const char expected_magic[4], - uint8_t expected_version, - struct decompressor **decompressor, - struct decompr_state **decompr_state, - XXH64_state_t *checksum, - char **errmsg) +bool +common_header_initialize_for_reading(struct common_header* header, + FILE* input, + const char expected_magic[4], + uint8_t expected_version, + struct decompressor** decompressor, + struct decompr_state** decompr_state, + XXH64_state_t* checksum, + char** errmsg) { - uint8_t header_bytes[COMMON_HEADER_SIZE]; - if (fread(header_bytes, sizeof(header_bytes), 1, input) != 1) { - *errmsg = format("Failed to read common header"); - return false; - } - - memcpy(header->magic, header_bytes, 4); - header->version = header_bytes[4]; - header->compression_type = header_bytes[5]; - header->compression_level = header_bytes[6]; - header->content_size = UINT64_FROM_BYTES(header_bytes + 7); - - if (memcmp(header->magic, expected_magic, sizeof(header->magic)) != 0) { - *errmsg = format( - "Bad magic value 0x%x%x%x%x", - header->magic[0], - header->magic[1], - header->magic[2], - header->magic[3]); - return false; - } - - if (header->version != expected_version) { - *errmsg = format( - "Unknown version (actual %u, expected %u)", - header->version, - expected_version); - return false; - } - - if (header->compression_type == COMPR_TYPE_NONE) { - // Since we have the size available, let's use it as a super primitive - // consistency check for the non-compressed case. (A real checksum is used - // for compressed data.) - struct stat st; - if (x_fstat(fileno(input), &st) != 0) { - return false; - } - if ((uint64_t)st.st_size != header->content_size) { - *errmsg = format( - "Bad uncompressed file size (actual %llu bytes, expected %llu bytes)", - (unsigned long long)st.st_size, - (unsigned long long)header->content_size); - return false; - } - } - - if (!decompressor) { - return true; - } - - *decompressor = decompressor_from_type(header->compression_type); - if (!*decompressor) { - *errmsg = format( - "Unknown compression type: %u", header->compression_type); - return false; - } - - if (checksum) { - XXH64_reset(checksum, 0); - XXH64_update(checksum, header_bytes, sizeof(header_bytes)); - } - - *decompr_state = (*decompressor)->init(input, checksum); - if (!*decompr_state) { - *errmsg = x_strdup("Failed to initialize decompressor"); - return false; - } - - return true; + uint8_t header_bytes[COMMON_HEADER_SIZE]; + if (fread(header_bytes, sizeof(header_bytes), 1, input) != 1) { + *errmsg = format("Failed to read common header"); + return false; + } + + memcpy(header->magic, header_bytes, 4); + header->version = header_bytes[4]; + header->compression_type = header_bytes[5]; + header->compression_level = header_bytes[6]; + header->content_size = UINT64_FROM_BYTES(header_bytes + 7); + + if (memcmp(header->magic, expected_magic, sizeof(header->magic)) != 0) { + *errmsg = format("Bad magic value 0x%x%x%x%x", + header->magic[0], + header->magic[1], + header->magic[2], + header->magic[3]); + return false; + } + + if (header->version != expected_version) { + *errmsg = format("Unknown version (actual %u, expected %u)", + header->version, + expected_version); + return false; + } + + if (header->compression_type == COMPR_TYPE_NONE) { + // Since we have the size available, let's use it as a super primitive + // consistency check for the non-compressed case. (A real checksum is used + // for compressed data.) + struct stat st; + if (x_fstat(fileno(input), &st) != 0) { + return false; + } + if ((uint64_t)st.st_size != header->content_size) { + *errmsg = format( + "Bad uncompressed file size (actual %llu bytes, expected %llu bytes)", + (unsigned long long)st.st_size, + (unsigned long long)header->content_size); + return false; + } + } + + if (!decompressor) { + return true; + } + + *decompressor = decompressor_from_type(header->compression_type); + if (!*decompressor) { + *errmsg = format("Unknown compression type: %u", header->compression_type); + return false; + } + + if (checksum) { + XXH64_reset(checksum, 0); + XXH64_update(checksum, header_bytes, sizeof(header_bytes)); + } + + *decompr_state = (*decompressor)->init(input, checksum); + if (!*decompr_state) { + *errmsg = x_strdup("Failed to initialize decompressor"); + return false; + } + + return true; } -void common_header_dump(const struct common_header *header, FILE *f) +void +common_header_dump(const struct common_header* header, FILE* f) { - fprintf( - f, "Magic: %c%c%c%c\n", - header->magic[0], header->magic[1], header->magic[2], header->magic[3]); - fprintf(f, "Version: %u\n", header->version); - fprintf(f, "Compression type: %s\n", - compression_type_to_string(header->compression_type)); - fprintf(f, "Compression level: %d\n", header->compression_level); - fprintf(f, "Content size: %" PRIu64 "\n", header->content_size); + fprintf(f, + "Magic: %c%c%c%c\n", + header->magic[0], + header->magic[1], + header->magic[2], + header->magic[3]); + fprintf(f, "Version: %u\n", header->version); + fprintf(f, + "Compression type: %s\n", + compression_type_to_string(header->compression_type)); + fprintf(f, "Compression level: %d\n", header->compression_level); + fprintf(f, "Content size: %" PRIu64 "\n", header->content_size); } diff --git a/src/common_header.hpp b/src/common_header.hpp index 05bcda40..ae9af69e 100644 --- a/src/common_header.hpp +++ b/src/common_header.hpp @@ -19,16 +19,18 @@ #pragma once #include "compression.hpp" + #include "third_party/xxhash.h" #define COMMON_HEADER_SIZE 15 -struct common_header { - char magic[4]; - uint8_t version; - uint8_t compression_type; - int8_t compression_level; - uint64_t content_size; +struct common_header +{ + char magic[4]; + uint8_t version; + uint8_t compression_type; + int8_t compression_level; + uint64_t content_size; }; // Initialize a common_header and write the header data to a file. @@ -44,17 +46,16 @@ struct common_header { // compressor_state: State for the compressor. // checksum: Checksum state that will be updated with the written // bytes. -bool common_header_initialize_for_writing( - struct common_header *header, - FILE *output, - const char magic[4], - uint8_t version, - uint8_t compression_type, - int8_t compression_level, - uint64_t content_size, - XXH64_state_t *checksum, - struct compressor **compressor, - struct compr_state **compr_state); +bool common_header_initialize_for_writing(struct common_header* header, + FILE* output, + const char magic[4], + uint8_t version, + uint8_t compression_type, + int8_t compression_level, + uint64_t content_size, + XXH64_state_t* checksum, + struct compressor** compressor, + struct compr_state** compr_state); // Initialize a common_header by reading header data from a file. // @@ -68,14 +69,13 @@ bool common_header_initialize_for_writing( // decompressor is NULL. // checksum: Checksum state that will be updated with the read bytes. // May be NULL for no checksumming. -bool common_header_initialize_for_reading( - struct common_header *header, - FILE *input, - const char expected_magic[4], - uint8_t expected_version, - struct decompressor **decompressor, - struct decompr_state **decompr_state, - XXH64_state_t *checksum, - char **errmsg); +bool common_header_initialize_for_reading(struct common_header* header, + FILE* input, + const char expected_magic[4], + uint8_t expected_version, + struct decompressor** decompressor, + struct decompr_state** decompr_state, + XXH64_state_t* checksum, + char** errmsg); -void common_header_dump(const struct common_header *header, FILE *f); +void common_header_dump(const struct common_header* header, FILE* f); diff --git a/src/compopt.cpp b/src/compopt.cpp index afc46753..0bb03645 100644 --- a/src/compopt.cpp +++ b/src/compopt.cpp @@ -16,165 +16,170 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "ccache.hpp" #include "compopt.hpp" +#include "ccache.hpp" + // The option it too hard to handle at all. -#define TOO_HARD (1 << 0) +#define TOO_HARD (1 << 0) // The option it too hard for the direct mode. -#define TOO_HARD_DIRECT (1 << 1) +#define TOO_HARD_DIRECT (1 << 1) // The option takes a separate argument, e.g. "-D FOO=1". -#define TAKES_ARG (1 << 2) +#define TAKES_ARG (1 << 2) // The option takes a concatenated argument, e.g. "-DFOO=1". #define TAKES_CONCAT_ARG (1 << 3) // The argument to the option is a path that may be rewritten if base_dir is // used. -#define TAKES_PATH (1 << 4) +#define TAKES_PATH (1 << 4) // The option only affects preprocessing; not passed to the compiler if // run_second_cpp is false. -#define AFFECTS_CPP (1 << 5) +#define AFFECTS_CPP (1 << 5) // The option only affects compilation; not passed to the preprocesor. #define AFFECTS_COMP (1 << 6) -struct compopt { - const char *name; - int type; +struct compopt +{ + const char* name; + int type; }; static const struct compopt compopts[] = { - {"--analyze", TOO_HARD}, // clang - {"--compiler-bindir", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"--libdevice-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"--output-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"--param", TAKES_ARG}, - {"--save-temps", TOO_HARD}, - {"--save-temps=cwd", TOO_HARD}, - {"--save-temps=obj", TOO_HARD}, - {"--serialize-diagnostics", TAKES_ARG | TAKES_PATH}, - {"-A", TAKES_ARG}, - {"-B", TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-D", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, - {"-E", TOO_HARD}, - {"-F", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-G", TAKES_ARG}, - {"-I", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-L", TAKES_ARG}, - {"-M", TOO_HARD}, - {"-MF", TAKES_ARG}, - {"-MJ", TAKES_ARG | TOO_HARD}, - {"-MM", TOO_HARD}, - {"-MQ", TAKES_ARG}, - {"-MT", TAKES_ARG}, - {"-P", TOO_HARD}, - {"-U", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, - {"-V", TAKES_ARG}, - {"-Wa,", TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Werror", AFFECTS_COMP}, // don't exit with error when preprocessing - {"-Wl,", TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Xassembler", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Xclang", TAKES_ARG}, - {"-Xlinker", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, - {"-Xpreprocessor", AFFECTS_CPP | TOO_HARD_DIRECT | TAKES_ARG}, - {"-all_load", AFFECTS_COMP}, - {"-analyze", TOO_HARD}, // clang - {"-arch", TAKES_ARG}, - {"-aux-info", TAKES_ARG}, - {"-b", TAKES_ARG}, - {"-bind_at_load", AFFECTS_COMP}, - {"-bundle", AFFECTS_COMP}, - {"-ccbin", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"-fmodules", TOO_HARD}, - {"-fno-working-directory", AFFECTS_CPP}, - {"-fplugin=libcc1plugin", TOO_HARD}, // interaction with GDB - {"-frepo", TOO_HARD}, - {"-ftime-trace", TOO_HARD}, // clang - {"-fworking-directory", AFFECTS_CPP}, - {"-gtoggle", TOO_HARD}, - {"-idirafter", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iframework", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-imacros", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-imultilib", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-include", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-include-pch", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-install_name", TAKES_ARG}, // Darwin linker option - {"-iprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iquote", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-isysroot", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-isystem", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iwithprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-iwithprefixbefore", - AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, - {"-ldir", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"-nolibc", AFFECTS_COMP}, - {"-nostdinc", AFFECTS_CPP}, - {"-nostdinc++", AFFECTS_CPP}, - {"-odir", AFFECTS_CPP | TAKES_ARG}, // nvcc - {"-pie", AFFECTS_COMP}, - {"-prebind", AFFECTS_COMP}, - {"-preload", AFFECTS_COMP}, - {"-rdynamic", AFFECTS_COMP}, - {"-remap", AFFECTS_CPP}, - {"-save-temps", TOO_HARD}, - {"-save-temps=cwd", TOO_HARD}, - {"-save-temps=obj", TOO_HARD}, - {"-stdlib=", AFFECTS_CPP | TAKES_CONCAT_ARG}, - {"-trigraphs", AFFECTS_CPP}, - {"-u", TAKES_ARG | TAKES_CONCAT_ARG}, + {"--analyze", TOO_HARD}, // clang + {"--compiler-bindir", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"--libdevice-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"--output-directory", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"--param", TAKES_ARG}, + {"--save-temps", TOO_HARD}, + {"--save-temps=cwd", TOO_HARD}, + {"--save-temps=obj", TOO_HARD}, + {"--serialize-diagnostics", TAKES_ARG | TAKES_PATH}, + {"-A", TAKES_ARG}, + {"-B", TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-D", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, + {"-E", TOO_HARD}, + {"-F", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-G", TAKES_ARG}, + {"-I", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-L", TAKES_ARG}, + {"-M", TOO_HARD}, + {"-MF", TAKES_ARG}, + {"-MJ", TAKES_ARG | TOO_HARD}, + {"-MM", TOO_HARD}, + {"-MQ", TAKES_ARG}, + {"-MT", TAKES_ARG}, + {"-P", TOO_HARD}, + {"-U", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG}, + {"-V", TAKES_ARG}, + {"-Wa,", TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Werror", AFFECTS_COMP}, // don't exit with error when preprocessing + {"-Wl,", TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Xassembler", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Xclang", TAKES_ARG}, + {"-Xlinker", TAKES_ARG | TAKES_CONCAT_ARG | AFFECTS_COMP}, + {"-Xpreprocessor", AFFECTS_CPP | TOO_HARD_DIRECT | TAKES_ARG}, + {"-all_load", AFFECTS_COMP}, + {"-analyze", TOO_HARD}, // clang + {"-arch", TAKES_ARG}, + {"-aux-info", TAKES_ARG}, + {"-b", TAKES_ARG}, + {"-bind_at_load", AFFECTS_COMP}, + {"-bundle", AFFECTS_COMP}, + {"-ccbin", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"-fmodules", TOO_HARD}, + {"-fno-working-directory", AFFECTS_CPP}, + {"-fplugin=libcc1plugin", TOO_HARD}, // interaction with GDB + {"-frepo", TOO_HARD}, + {"-ftime-trace", TOO_HARD}, // clang + {"-fworking-directory", AFFECTS_CPP}, + {"-gtoggle", TOO_HARD}, + {"-idirafter", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iframework", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-imacros", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-imultilib", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-include", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-include-pch", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-install_name", TAKES_ARG}, // Darwin linker option + {"-iprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iquote", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-isysroot", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-isystem", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iwithprefix", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-iwithprefixbefore", + AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, + {"-ldir", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"-nolibc", AFFECTS_COMP}, + {"-nostdinc", AFFECTS_CPP}, + {"-nostdinc++", AFFECTS_CPP}, + {"-odir", AFFECTS_CPP | TAKES_ARG}, // nvcc + {"-pie", AFFECTS_COMP}, + {"-prebind", AFFECTS_COMP}, + {"-preload", AFFECTS_COMP}, + {"-rdynamic", AFFECTS_COMP}, + {"-remap", AFFECTS_CPP}, + {"-save-temps", TOO_HARD}, + {"-save-temps=cwd", TOO_HARD}, + {"-save-temps=obj", TOO_HARD}, + {"-stdlib=", AFFECTS_CPP | TAKES_CONCAT_ARG}, + {"-trigraphs", AFFECTS_CPP}, + {"-u", TAKES_ARG | TAKES_CONCAT_ARG}, }; - static int -compare_compopts(const void *key1, const void *key2) +compare_compopts(const void* key1, const void* key2) { - const struct compopt *opt1 = (const struct compopt *)key1; - const struct compopt *opt2 = (const struct compopt *)key2; - return strcmp(opt1->name, opt2->name); + const struct compopt* opt1 = (const struct compopt*)key1; + const struct compopt* opt2 = (const struct compopt*)key2; + return strcmp(opt1->name, opt2->name); } static int -compare_prefix_compopts(const void *key1, const void *key2) +compare_prefix_compopts(const void* key1, const void* key2) { - const struct compopt *opt1 = (const struct compopt *)key1; - const struct compopt *opt2 = (const struct compopt *)key2; - return strncmp(opt1->name, opt2->name, strlen(opt2->name)); + const struct compopt* opt1 = (const struct compopt*)key1; + const struct compopt* opt2 = (const struct compopt*)key2; + return strncmp(opt1->name, opt2->name, strlen(opt2->name)); } -static const struct compopt * -find(const char *option) +static const struct compopt* +find(const char* option) { - struct compopt key; - key.name = option; - void* result = bsearch( - &key, compopts, ARRAY_SIZE(compopts), sizeof(compopts[0]), - compare_compopts); - return static_cast<compopt*>(result); + struct compopt key; + key.name = option; + void* result = bsearch(&key, + compopts, + ARRAY_SIZE(compopts), + sizeof(compopts[0]), + compare_compopts); + return static_cast<compopt*>(result); } -static const struct compopt * -find_prefix(const char *option) +static const struct compopt* +find_prefix(const char* option) { - struct compopt key; - key.name = option; - void* result = bsearch( - &key, compopts, ARRAY_SIZE(compopts), sizeof(compopts[0]), - compare_prefix_compopts); - return static_cast<compopt*>(result); + struct compopt key; + key.name = option; + void* result = bsearch(&key, + compopts, + ARRAY_SIZE(compopts), + sizeof(compopts[0]), + compare_prefix_compopts); + return static_cast<compopt*>(result); } // Runs fn on the first two characters of option. bool -compopt_short(bool (*fn)(const char *), const char *option) +compopt_short(bool (*fn)(const char*), const char* option) { - char *short_opt = x_strndup(option, 2); - bool retval = fn(short_opt); - free(short_opt); - return retval; + char* short_opt = x_strndup(option, 2); + bool retval = fn(short_opt); + free(short_opt); + return retval; } // Used by unittest/test_compopt.c. @@ -184,83 +189,83 @@ bool compopt_verify_sortedness(void); bool compopt_verify_sortedness(void) { - for (size_t i = 1; i < ARRAY_SIZE(compopts); i++) { - if (strcmp(compopts[i-1].name, compopts[i].name) >= 0) { - fprintf(stderr, - "compopt_verify_sortedness: %s >= %s\n", - compopts[i-1].name, - compopts[i].name); - return false; - } - } - return true; + for (size_t i = 1; i < ARRAY_SIZE(compopts); i++) { + if (strcmp(compopts[i - 1].name, compopts[i].name) >= 0) { + fprintf(stderr, + "compopt_verify_sortedness: %s >= %s\n", + compopts[i - 1].name, + compopts[i].name); + return false; + } + } + return true; } bool -compopt_affects_cpp(const char *option) +compopt_affects_cpp(const char* option) { - const struct compopt *co = find(option); - return co && (co->type & AFFECTS_CPP); + const struct compopt* co = find(option); + return co && (co->type & AFFECTS_CPP); } bool -compopt_affects_comp(const char *option) +compopt_affects_comp(const char* option) { - const struct compopt *co = find(option); - return co && (co->type & AFFECTS_COMP); + const struct compopt* co = find(option); + return co && (co->type & AFFECTS_COMP); } bool -compopt_too_hard(const char *option) +compopt_too_hard(const char* option) { - const struct compopt *co = find(option); - return co && (co->type & TOO_HARD); + const struct compopt* co = find(option); + return co && (co->type & TOO_HARD); } bool -compopt_too_hard_for_direct_mode(const char *option) +compopt_too_hard_for_direct_mode(const char* option) { - const struct compopt *co = find(option); - return co && (co->type & TOO_HARD_DIRECT); + const struct compopt* co = find(option); + return co && (co->type & TOO_HARD_DIRECT); } bool -compopt_takes_path(const char *option) +compopt_takes_path(const char* option) { - const struct compopt *co = find(option); - return co && (co->type & TAKES_PATH); + const struct compopt* co = find(option); + return co && (co->type & TAKES_PATH); } bool -compopt_takes_arg(const char *option) +compopt_takes_arg(const char* option) { - const struct compopt *co = find(option); - return co && (co->type & TAKES_ARG); + const struct compopt* co = find(option); + return co && (co->type & TAKES_ARG); } bool -compopt_takes_concat_arg(const char *option) +compopt_takes_concat_arg(const char* option) { - const struct compopt *co = find(option); - return co && (co->type & TAKES_CONCAT_ARG); + const struct compopt* co = find(option); + return co && (co->type & TAKES_CONCAT_ARG); } // Determines if the prefix of the option matches any option and affects the // preprocessor. bool -compopt_prefix_affects_cpp(const char *option) +compopt_prefix_affects_cpp(const char* option) { - // Prefix options have to take concatenated args. - const struct compopt *co = find_prefix(option); - return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_CPP); + // Prefix options have to take concatenated args. + const struct compopt* co = find_prefix(option); + return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_CPP); } // Determines if the prefix of the option matches any option and affects the // preprocessor. bool -compopt_prefix_affects_comp(const char *option) +compopt_prefix_affects_comp(const char* option) { - // Prefix options have to take concatenated args. - const struct compopt *co = find_prefix(option); - return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_COMP); + // Prefix options have to take concatenated args. + const struct compopt* co = find_prefix(option); + return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_COMP); } diff --git a/src/compopt.hpp b/src/compopt.hpp index 97157c97..bb653694 100644 --- a/src/compopt.hpp +++ b/src/compopt.hpp @@ -20,13 +20,13 @@ #include "system.hpp" -bool compopt_short(bool (*fn)(const char *option), const char *option); -bool compopt_affects_cpp(const char *option); -bool compopt_affects_comp(const char *option); -bool compopt_too_hard(const char *option); -bool compopt_too_hard_for_direct_mode(const char *option); -bool compopt_takes_path(const char *option); -bool compopt_takes_arg(const char *option); -bool compopt_takes_concat_arg(const char *option); -bool compopt_prefix_affects_cpp(const char *option); -bool compopt_prefix_affects_comp(const char *option); +bool compopt_short(bool (*fn)(const char* option), const char* option); +bool compopt_affects_cpp(const char* option); +bool compopt_affects_comp(const char* option); +bool compopt_too_hard(const char* option); +bool compopt_too_hard_for_direct_mode(const char* option); +bool compopt_takes_path(const char* option); +bool compopt_takes_arg(const char* option); +bool compopt_takes_concat_arg(const char* option); +bool compopt_prefix_affects_cpp(const char* option); +bool compopt_prefix_affects_comp(const char* option); diff --git a/src/compr_none.cpp b/src/compr_none.cpp index 7d5cfa91..7093d3aa 100644 --- a/src/compr_none.cpp +++ b/src/compr_none.cpp @@ -18,51 +18,51 @@ #include "compression.hpp" -struct state { - FILE *output; - XXH64_state_t *checksum; +struct state +{ + FILE* output; + XXH64_state_t* checksum; }; -static struct compr_state * -compr_none_init(FILE *output, int8_t level, XXH64_state_t *checksum) +static struct compr_state* +compr_none_init(FILE* output, int8_t level, XXH64_state_t* checksum) { - auto state = static_cast<struct state*>(malloc(sizeof(struct state))); - state->output = output; - state->checksum = checksum; - (void)level; - return (struct compr_state *)state; + auto state = static_cast<struct state*>(malloc(sizeof(struct state))); + state->output = output; + state->checksum = checksum; + (void)level; + return (struct compr_state*)state; } static int8_t -compr_none_get_actual_compression_level(struct compr_state *handle) +compr_none_get_actual_compression_level(struct compr_state* handle) { - (void)handle; - return 0; + (void)handle; + return 0; } static bool -compr_none_write(struct compr_state *handle, const void *data, size_t size) +compr_none_write(struct compr_state* handle, const void* data, size_t size) { - struct state *state = (struct state *)handle; - size_t ret = fwrite(data, size, 1, state->output); - if (state->checksum) { - XXH64_update(state->checksum, data, size); - } - return ret == 1; + struct state* state = (struct state*)handle; + size_t ret = fwrite(data, size, 1, state->output); + if (state->checksum) { + XXH64_update(state->checksum, data, size); + } + return ret == 1; } static bool -compr_none_free(struct compr_state *handle) +compr_none_free(struct compr_state* handle) { - struct state *state = (struct state *)handle; - bool result = ferror(state->output) == 0; - free(state); - return result; + struct state* state = (struct state*)handle; + bool result = ferror(state->output) == 0; + free(state); + return result; } struct compressor compressor_none_impl = { - compr_none_init, - compr_none_get_actual_compression_level, - compr_none_write, - compr_none_free -}; + compr_none_init, + compr_none_get_actual_compression_level, + compr_none_write, + compr_none_free}; diff --git a/src/compr_zstd.cpp b/src/compr_zstd.cpp index 873b9e7f..005c3f0e 100644 --- a/src/compr_zstd.cpp +++ b/src/compr_zstd.cpp @@ -23,135 +23,134 @@ #define DEFAULT_ZSTD_COMPRESSION_LEVEL -1 -struct state { - FILE *output; - XXH64_state_t *checksum; - ZSTD_CStream *stream; - ZSTD_inBuffer in; - ZSTD_outBuffer out; - bool failed; - int8_t compression_level; +struct state +{ + FILE* output; + XXH64_state_t* checksum; + ZSTD_CStream* stream; + ZSTD_inBuffer in; + ZSTD_outBuffer out; + bool failed; + int8_t compression_level; }; -static struct compr_state * -compr_zstd_init(FILE *output, int8_t level, XXH64_state_t *checksum) +static struct compr_state* +compr_zstd_init(FILE* output, int8_t level, XXH64_state_t* checksum) { - auto state = static_cast<struct state*>(malloc(sizeof(struct state))); - state->output = output; - state->checksum = checksum; - state->stream = ZSTD_createCStream(); - state->failed = false; - - if (level == 0) { - level = DEFAULT_ZSTD_COMPRESSION_LEVEL; - cc_log("Using default compression level %d", level); - } - - // libzstd 1.3.4 and newer support negative levels. However, the query - // function ZSTD_minCLevel did not appear until 1.3.6, so perform detection - // based on version instead. - if (ZSTD_versionNumber() < 10304 && level < 1) { - cc_log( - "Using compression level 1 (minimum level supported by libzstd) instead" - " of %d", - level); - level = 1; - } - - state->compression_level = MIN(level, ZSTD_maxCLevel()); - if (state->compression_level != level) { - cc_log( - "Using compression level %d (max libzstd level) instead of %d", - state->compression_level, - level); - } - - size_t ret = ZSTD_initCStream(state->stream, state->compression_level); - if (ZSTD_isError(ret)) { - ZSTD_freeCStream(state->stream); - free(state); - return NULL; - } - return (struct compr_state *)state; + auto state = static_cast<struct state*>(malloc(sizeof(struct state))); + state->output = output; + state->checksum = checksum; + state->stream = ZSTD_createCStream(); + state->failed = false; + + if (level == 0) { + level = DEFAULT_ZSTD_COMPRESSION_LEVEL; + cc_log("Using default compression level %d", level); + } + + // libzstd 1.3.4 and newer support negative levels. However, the query + // function ZSTD_minCLevel did not appear until 1.3.6, so perform detection + // based on version instead. + if (ZSTD_versionNumber() < 10304 && level < 1) { + cc_log( + "Using compression level 1 (minimum level supported by libzstd) instead" + " of %d", + level); + level = 1; + } + + state->compression_level = MIN(level, ZSTD_maxCLevel()); + if (state->compression_level != level) { + cc_log("Using compression level %d (max libzstd level) instead of %d", + state->compression_level, + level); + } + + size_t ret = ZSTD_initCStream(state->stream, state->compression_level); + if (ZSTD_isError(ret)) { + ZSTD_freeCStream(state->stream); + free(state); + return NULL; + } + return (struct compr_state*)state; } static int8_t -compr_zstd_get_actual_compression_level(struct compr_state *handle) +compr_zstd_get_actual_compression_level(struct compr_state* handle) { - struct state *state = (struct state *)handle; - return state->compression_level; + struct state* state = (struct state*)handle; + return state->compression_level; } static bool -compr_zstd_write(struct compr_state *handle, const void *data, size_t size) +compr_zstd_write(struct compr_state* handle, const void* data, size_t size) { - if (!handle) { - return false; - } - struct state *state = (struct state *)handle; - - if (state->checksum) { - XXH64_update(state->checksum, data, size); - } - - state->in.src = data; - state->in.size = size; - state->in.pos = 0; - - int flush = data ? 0 : 1; - - size_t ret; - while (state->in.pos < state->in.size) { - unsigned char buffer[READ_BUFFER_SIZE]; - state->out.dst = buffer; - state->out.size = sizeof(buffer); - state->out.pos = 0; - ret = ZSTD_compressStream(state->stream, &state->out, &state->in); - assert(!(ZSTD_isError(ret))); - size_t compressed_bytes = state->out.pos; - if (fwrite(buffer, 1, compressed_bytes, state->output) != compressed_bytes - || ferror(state->output)) { - state->failed = true; - return false; - } - } - ret = flush; - while (ret > 0) { - unsigned char buffer[READ_BUFFER_SIZE]; - state->out.dst = buffer; - state->out.size = sizeof(buffer); - state->out.pos = 0; - ret = ZSTD_endStream(state->stream, &state->out); - size_t compressed_bytes = state->out.pos; - if (fwrite(buffer, 1, compressed_bytes, state->output) != compressed_bytes - || ferror(state->output)) { - state->failed = true; - return false; - } - } - - return true; + if (!handle) { + return false; + } + struct state* state = (struct state*)handle; + + if (state->checksum) { + XXH64_update(state->checksum, data, size); + } + + state->in.src = data; + state->in.size = size; + state->in.pos = 0; + + int flush = data ? 0 : 1; + + size_t ret; + while (state->in.pos < state->in.size) { + unsigned char buffer[READ_BUFFER_SIZE]; + state->out.dst = buffer; + state->out.size = sizeof(buffer); + state->out.pos = 0; + ret = ZSTD_compressStream(state->stream, &state->out, &state->in); + assert(!(ZSTD_isError(ret))); + size_t compressed_bytes = state->out.pos; + if (fwrite(buffer, 1, compressed_bytes, state->output) != compressed_bytes + || ferror(state->output)) { + state->failed = true; + return false; + } + } + ret = flush; + while (ret > 0) { + unsigned char buffer[READ_BUFFER_SIZE]; + state->out.dst = buffer; + state->out.size = sizeof(buffer); + state->out.pos = 0; + ret = ZSTD_endStream(state->stream, &state->out); + size_t compressed_bytes = state->out.pos; + if (fwrite(buffer, 1, compressed_bytes, state->output) != compressed_bytes + || ferror(state->output)) { + state->failed = true; + return false; + } + } + + return true; } static bool -compr_zstd_free(struct compr_state *handle) +compr_zstd_free(struct compr_state* handle) { - if (!handle) { - return false; - } + if (!handle) { + return false; + } - struct state *state = (struct state *)handle; + struct state* state = (struct state*)handle; - compr_zstd_write(handle, NULL, 0); - ZSTD_freeCStream(state->stream); - bool success = !state->failed; - free(state); - return success; + compr_zstd_write(handle, NULL, 0); + ZSTD_freeCStream(state->stream); + bool success = !state->failed; + free(state); + return success; } struct compressor compressor_zstd_impl = { - compr_zstd_init, - compr_zstd_get_actual_compression_level, - compr_zstd_write, - compr_zstd_free -}; + compr_zstd_init, + compr_zstd_get_actual_compression_level, + compr_zstd_write, + compr_zstd_free}; diff --git a/src/compress.cpp b/src/compress.cpp index e9299058..d7656fc9 100644 --- a/src/compress.cpp +++ b/src/compress.cpp @@ -17,37 +17,31 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "ccache.hpp" - #include "common_header.hpp" #include "manifest.hpp" #include "result.hpp" static bool -get_content_size( - const char *path, const char *magic, uint8_t version, size_t *size) +get_content_size(const char* path, + const char* magic, + uint8_t version, + size_t* size) { - char *errmsg; - FILE *f = fopen(path, "rb"); - if (!f) { - cc_log("Failed to open %s for reading: %s", path, strerror(errno)); - return false; - } - struct common_header header; - bool success = common_header_initialize_for_reading( - &header, - f, - magic, - version, - NULL, - NULL, - NULL, - &errmsg); - fclose(f); - if (success) { - *size = header.content_size; - } - - return success; + char* errmsg; + FILE* f = fopen(path, "rb"); + if (!f) { + cc_log("Failed to open %s for reading: %s", path, strerror(errno)); + return false; + } + struct common_header header; + bool success = common_header_initialize_for_reading( + &header, f, magic, version, NULL, NULL, NULL, &errmsg); + fclose(f); + if (success) { + *size = header.content_size; + } + + return success; } static uint64_t on_disk_size; @@ -57,93 +51,96 @@ static uint64_t incompr_size; // This measures the size of files in the cache. static void -measure_fn(const char *fname, struct stat *st) +measure_fn(const char* fname, struct stat* st) { - if (!S_ISREG(st->st_mode)) { - return; - } - - char *p = x_basename(fname); - if (str_eq(p, "stats")) { - free(p); - return; - } - - if (str_startswith(p, ".nfs")) { - // Ignore temporary NFS files that may be left for open but deleted files. - free(p); - return; - } - - if (strstr(p, ".tmp.")) { - // Ignore tmp files since they are transient. - free(p); - return; - } - - if (strstr(p, "CACHEDIR.TAG")) { - free(p); - return; - } - - free(p); - - on_disk_size += file_size(st); - - size_t content_size = 0; - const char *file_ext = get_extension(p); - bool is_compressible = false; - if (str_eq(file_ext, ".manifest")) { - is_compressible = get_content_size( - fname, MANIFEST_MAGIC, MANIFEST_VERSION, &content_size); - } else if (str_eq(file_ext, ".result")) { - is_compressible = get_content_size( - fname, RESULT_MAGIC, RESULT_VERSION, &content_size); - } - - if (is_compressible) { - compr_size += st->st_size; - compr_orig_size += content_size; - } else { - incompr_size += st->st_size; - } + if (!S_ISREG(st->st_mode)) { + return; + } + + char* p = x_basename(fname); + if (str_eq(p, "stats")) { + free(p); + return; + } + + if (str_startswith(p, ".nfs")) { + // Ignore temporary NFS files that may be left for open but deleted files. + free(p); + return; + } + + if (strstr(p, ".tmp.")) { + // Ignore tmp files since they are transient. + free(p); + return; + } + + if (strstr(p, "CACHEDIR.TAG")) { + free(p); + return; + } + + free(p); + + on_disk_size += file_size(st); + + size_t content_size = 0; + const char* file_ext = get_extension(p); + bool is_compressible = false; + if (str_eq(file_ext, ".manifest")) { + is_compressible = + get_content_size(fname, MANIFEST_MAGIC, MANIFEST_VERSION, &content_size); + } else if (str_eq(file_ext, ".result")) { + is_compressible = + get_content_size(fname, RESULT_MAGIC, RESULT_VERSION, &content_size); + } + + if (is_compressible) { + compr_size += st->st_size; + compr_orig_size += content_size; + } else { + incompr_size += st->st_size; + } } // Process up all cache subdirectories. -void compress_stats(struct conf *conf) +void +compress_stats(struct conf* conf) { - on_disk_size = 0; - compr_size = 0; - compr_orig_size = 0; - incompr_size = 0; - - for (int i = 0; i <= 0xF; i++) { - char *dname = format("%s/%1x", conf->cache_dir, i); - traverse(dname, measure_fn); - free(dname); - } - - double ratio = compr_size > 0 ? ((double) compr_orig_size) / compr_size : 0.0; - double savings = ratio > 0.0 ? 100.0 - (100.0 / ratio) : 0.0; - - char *on_disk_size_str = format_human_readable_size(on_disk_size); - char *cache_size_str = format_human_readable_size(compr_size + incompr_size); - char *compr_size_str = format_human_readable_size(compr_size); - char *compr_orig_size_str = format_human_readable_size(compr_orig_size); - char *incompr_size_str = format_human_readable_size(incompr_size); - - printf("Total data: %8s (%s disk blocks)\n", - cache_size_str, on_disk_size_str); - printf("Compressible data: %8s (%.1f%% of original size)\n", - compr_size_str, 100.0 - savings); - printf(" - Original size: %8s\n", compr_orig_size_str); - printf(" - Compression ratio: %5.3f x (%.1f%% space savings)\n", - ratio, savings); - printf("Incompressible data: %8s\n", incompr_size_str); - - free(incompr_size_str); - free(compr_orig_size_str); - free(compr_size_str); - free(cache_size_str); - free(on_disk_size_str); + on_disk_size = 0; + compr_size = 0; + compr_orig_size = 0; + incompr_size = 0; + + for (int i = 0; i <= 0xF; i++) { + char* dname = format("%s/%1x", conf->cache_dir, i); + traverse(dname, measure_fn); + free(dname); + } + + double ratio = compr_size > 0 ? ((double)compr_orig_size) / compr_size : 0.0; + double savings = ratio > 0.0 ? 100.0 - (100.0 / ratio) : 0.0; + + char* on_disk_size_str = format_human_readable_size(on_disk_size); + char* cache_size_str = format_human_readable_size(compr_size + incompr_size); + char* compr_size_str = format_human_readable_size(compr_size); + char* compr_orig_size_str = format_human_readable_size(compr_orig_size); + char* incompr_size_str = format_human_readable_size(incompr_size); + + printf("Total data: %8s (%s disk blocks)\n", + cache_size_str, + on_disk_size_str); + printf("Compressible data: %8s (%.1f%% of original size)\n", + compr_size_str, + 100.0 - savings); + printf(" - Original size: %8s\n", compr_orig_size_str); + printf( + " - Compression ratio: %5.3f x (%.1f%% space savings)\n", ratio, savings); + printf("Incompressible data: %8s\n", incompr_size_str); + + free(incompr_size_str); + free(compr_orig_size_str); + free(compr_size_str); + free(cache_size_str); + free(on_disk_size_str); } diff --git a/src/compression.cpp b/src/compression.cpp index f210541f..766c45cf 100644 --- a/src/compression.cpp +++ b/src/compression.cpp @@ -17,55 +17,61 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "compression.hpp" + #include "conf.hpp" -extern struct conf *conf; +extern struct conf* conf; -int8_t compression_level_from_config(void) +int8_t +compression_level_from_config(void) { - return conf->compression ? conf->compression_level : 0; + return conf->compression ? conf->compression_level : 0; } -enum compression_type compression_type_from_config(void) +enum compression_type +compression_type_from_config(void) { - return conf->compression ? COMPR_TYPE_ZSTD : COMPR_TYPE_NONE; + return conf->compression ? COMPR_TYPE_ZSTD : COMPR_TYPE_NONE; } -const char *compression_type_to_string(uint8_t type) +const char* +compression_type_to_string(uint8_t type) { - switch (type) { - case COMPR_TYPE_NONE: - return "none"; + switch (type) { + case COMPR_TYPE_NONE: + return "none"; - case COMPR_TYPE_ZSTD: - return "zstd"; - } + case COMPR_TYPE_ZSTD: + return "zstd"; + } - return "unknown"; + return "unknown"; } -struct compressor *compressor_from_type(uint8_t type) +struct compressor* +compressor_from_type(uint8_t type) { - switch (type) { - case COMPR_TYPE_NONE: - return &compressor_none_impl; + switch (type) { + case COMPR_TYPE_NONE: + return &compressor_none_impl; - case COMPR_TYPE_ZSTD: - return &compressor_zstd_impl; - } + case COMPR_TYPE_ZSTD: + return &compressor_zstd_impl; + } - return NULL; + return NULL; } -struct decompressor *decompressor_from_type(uint8_t type) +struct decompressor* +decompressor_from_type(uint8_t type) { - switch (type) { - case COMPR_TYPE_NONE: - return &decompressor_none_impl; + switch (type) { + case COMPR_TYPE_NONE: + return &decompressor_none_impl; - case COMPR_TYPE_ZSTD: - return &decompressor_zstd_impl; - } + case COMPR_TYPE_ZSTD: + return &decompressor_zstd_impl; + } - return NULL; + return NULL; } diff --git a/src/compression.hpp b/src/compression.hpp index b48be164..93514a71 100644 --- a/src/compression.hpp +++ b/src/compression.hpp @@ -19,68 +19,67 @@ #pragma once #include "system.hpp" + #include "third_party/xxhash.h" struct compr_state; -struct compressor { - // Create and initialize a compressor. - // - // output: The file to write compressed data to. - // compression_level: Desired compression level. - // checksum: Checksum state to update (NULL for no checksum). - struct compr_state *(*init)( - FILE *output, - int8_t compression_level, - XXH64_state_t *checksum); - - // Get the actual compression level that will be used. - int8_t (*get_actual_compression_level)(struct compr_state *state); - - // Compress data. - // - // data: Buffer to read decompressed data from. - // size: How many bytes to read. - // - // Returns false on failure, otherwise true. - bool (*write)(struct compr_state *state, const void *data, size_t size); - - // Finalize compressor and free its state. - // - // Returns false if finalization failed or if any previous operation failed, - // otherwise true. - bool (*free)(struct compr_state *state); +struct compressor +{ + // Create and initialize a compressor. + // + // output: The file to write compressed data to. + // compression_level: Desired compression level. + // checksum: Checksum state to update (NULL for no checksum). + struct compr_state* (*init)(FILE* output, + int8_t compression_level, + XXH64_state_t* checksum); + + // Get the actual compression level that will be used. + int8_t (*get_actual_compression_level)(struct compr_state* state); + + // Compress data. + // + // data: Buffer to read decompressed data from. + // size: How many bytes to read. + // + // Returns false on failure, otherwise true. + bool (*write)(struct compr_state* state, const void* data, size_t size); + + // Finalize compressor and free its state. + // + // Returns false if finalization failed or if any previous operation failed, + // otherwise true. + bool (*free)(struct compr_state* state); }; struct decompr_state; -struct decompressor { - // Create and initialize a decompressor. - // - // input: The file to read compressed data from. - // checksum: Checksum state to update (NULL for no checksum). - struct decompr_state *(*init)(FILE *input, XXH64_state_t *checksum); - - // Decompress data. - // - // data: Buffer to write decompressed data to. - // size: How many bytes to write. - // - // Returns false on failure, otherwise true. - bool (*read)(struct decompr_state *state, void *data, size_t size); - - // Finalize the decompressor and free its state. - // - // Returns false if finalization failed or if any previous operation failed, - // otherwise true. - bool (*free)(struct decompr_state *state); -}; +struct decompressor +{ + // Create and initialize a decompressor. + // + // input: The file to read compressed data from. + // checksum: Checksum state to update (NULL for no checksum). + struct decompr_state* (*init)(FILE* input, XXH64_state_t* checksum); + + // Decompress data. + // + // data: Buffer to write decompressed data to. + // size: How many bytes to write. + // + // Returns false on failure, otherwise true. + bool (*read)(struct decompr_state* state, void* data, size_t size); -enum compression_type { - COMPR_TYPE_NONE = 0, - COMPR_TYPE_ZSTD = 1 + // Finalize the decompressor and free its state. + // + // Returns false if finalization failed or if any previous operation failed, + // otherwise true. + bool (*free)(struct decompr_state* state); }; +enum compression_type { COMPR_TYPE_NONE = 0, COMPR_TYPE_ZSTD = 1 }; + extern struct compressor compressor_none_impl; extern struct decompressor decompressor_none_impl; @@ -89,6 +88,6 @@ extern struct decompressor decompressor_zstd_impl; int8_t compression_level_from_config(void); enum compression_type compression_type_from_config(void); -const char *compression_type_to_string(uint8_t type); -struct compressor *compressor_from_type(uint8_t type); -struct decompressor *decompressor_from_type(uint8_t type); +const char* compression_type_to_string(uint8_t type); +struct compressor* compressor_from_type(uint8_t type); +struct decompressor* decompressor_from_type(uint8_t type); diff --git a/src/conf.cpp b/src/conf.cpp index 379a2315..bf0a454d 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -17,183 +17,193 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "conf.hpp" + +#include "ccache.hpp" #include "confitems.hpp" #include "envtoconfitems.hpp" -#include "ccache.hpp" enum handle_conf_result { - HANDLE_CONF_OK, - HANDLE_CONF_UNKNOWN, - HANDLE_CONF_FAIL + HANDLE_CONF_OK, + HANDLE_CONF_UNKNOWN, + HANDLE_CONF_FAIL }; -static const struct conf_item * -find_conf(const char *name) +static const struct conf_item* +find_conf(const char* name) { - return confitems_get(name, strlen(name)); + return confitems_get(name, strlen(name)); } -static const struct env_to_conf_item * -find_env_to_conf(const char *name) +static const struct env_to_conf_item* +find_env_to_conf(const char* name) { - return envtoconfitems_get(name, strlen(name)); + return envtoconfitems_get(name, strlen(name)); } static enum handle_conf_result -handle_conf_setting(struct conf *conf, const char *key, const char *value, - char **errmsg, bool from_env_variable, bool negate_boolean, - const char *origin) +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 = find_conf(key); - if (!item) { - return HANDLE_CONF_UNKNOWN; - } - - if (from_env_variable && item->parser == confitem_parse_bool) { - // Special rule for boolean settings from the environment: "0", "false", - // "disable" and "no" (case insensitive) are invalid, and all other values - // mean true. - // - // Previously any value meant true, but this was surprising to users, who - // might do something like CCACHE_DISABLE=0 and expect ccache to be - // enabled. - if (str_eq(value, "0") || strcasecmp(value, "false") == 0 - || strcasecmp(value, "disable") == 0 || strcasecmp(value, "no") == 0) { - fatal("invalid boolean environment variable value \"%s\"", value); - } - - bool *boolvalue = (bool *)((char *)conf + item->offset); - *boolvalue = !negate_boolean; - goto out; - } - - if (!item->parser(value, (char *)conf + item->offset, errmsg)) { - return HANDLE_CONF_FAIL; - } - if (item->verifier && !item->verifier((char *)conf + item->offset, errmsg)) { - return HANDLE_CONF_FAIL; - } + const struct conf_item* item = find_conf(key); + if (!item) { + return HANDLE_CONF_UNKNOWN; + } + + if (from_env_variable && item->parser == confitem_parse_bool) { + // Special rule for boolean settings from the environment: "0", "false", + // "disable" and "no" (case insensitive) are invalid, and all other values + // mean true. + // + // Previously any value meant true, but this was surprising to users, who + // might do something like CCACHE_DISABLE=0 and expect ccache to be + // enabled. + if (str_eq(value, "0") || strcasecmp(value, "false") == 0 + || strcasecmp(value, "disable") == 0 || strcasecmp(value, "no") == 0) { + fatal("invalid boolean environment variable value \"%s\"", value); + } + + bool* boolvalue = (bool*)((char*)conf + item->offset); + *boolvalue = !negate_boolean; + goto out; + } + + if (!item->parser(value, (char*)conf + item->offset, errmsg)) { + return HANDLE_CONF_FAIL; + } + if (item->verifier && !item->verifier((char*)conf + item->offset, errmsg)) { + return HANDLE_CONF_FAIL; + } out: - conf->item_origins[item->number] = origin; - return HANDLE_CONF_OK; + conf->item_origins[item->number] = origin; + return HANDLE_CONF_OK; } static bool -parse_line(const char *line, char **key, char **value, char **errmsg) +parse_line(const char* line, char** key, char** value, char** errmsg) { -#define SKIP_WS(x) do { while (isspace(*x)) { ++x; } } while (false) - - *key = NULL; - *value = NULL; - - const char *p = line; - SKIP_WS(p); - if (*p == '\0' || *p == '#') { - return true; - } - const char *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; +#define SKIP_WS(x) \ + do { \ + while (isspace(*x)) { \ + ++x; \ + } \ + } while (false) + + *key = NULL; + *value = NULL; + + const char* p = line; + SKIP_WS(p); + if (*p == '\0' || *p == '#') { + return true; + } + const char* 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 * +struct conf* conf_create(void) { - auto conf = static_cast<struct conf*>(x_malloc(sizeof(struct 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 = true; - conf->compression_level = 0; - conf->cpp_extension = x_strdup(""); - conf->debug = false; - conf->depend_mode = false; - conf->direct_mode = true; - conf->disable = false; - conf->extra_files_to_hash = x_strdup(""); - conf->file_clone = false; - conf->hard_link = false; - conf->hash_dir = true; - conf->ignore_headers_in_manifest = x_strdup(""); - conf->keep_comments_cpp = false; - conf->limit_multiple = 0.8; - conf->log_file = x_strdup(""); - conf->max_files = 0; - conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000; - conf->path = x_strdup(""); - conf->pch_external_checksum = false; - conf->prefix_command = x_strdup(""); - conf->prefix_command_cpp = x_strdup(""); - conf->read_only = false; - conf->read_only_direct = false; - conf->recache = false; - conf->run_second_cpp = true; - 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 = static_cast<const char**>( - x_malloc(confitems_count() * sizeof(char *))); - for (size_t i = 0; i < confitems_count(); ++i) { - conf->item_origins[i] = "default"; - } - return conf; + auto conf = static_cast<struct conf*>(x_malloc(sizeof(struct 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 = true; + conf->compression_level = 0; + conf->cpp_extension = x_strdup(""); + conf->debug = false; + conf->depend_mode = false; + conf->direct_mode = true; + conf->disable = false; + conf->extra_files_to_hash = x_strdup(""); + conf->file_clone = false; + conf->hard_link = false; + conf->hash_dir = true; + conf->ignore_headers_in_manifest = x_strdup(""); + conf->keep_comments_cpp = false; + conf->limit_multiple = 0.8; + conf->log_file = x_strdup(""); + conf->max_files = 0; + conf->max_size = (uint64_t)5 * 1000 * 1000 * 1000; + conf->path = x_strdup(""); + conf->pch_external_checksum = false; + conf->prefix_command = x_strdup(""); + conf->prefix_command_cpp = x_strdup(""); + conf->read_only = false; + conf->read_only_direct = false; + conf->recache = false; + conf->run_second_cpp = true; + 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 = + static_cast<const char**>(x_malloc(confitems_count() * sizeof(char*))); + for (size_t i = 0; i < confitems_count(); ++i) { + conf->item_origins[i] = "default"; + } + return conf; } void -conf_free(struct conf *conf) +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->ignore_headers_in_manifest); - free(conf->log_file); - free(conf->path); - free(conf->prefix_command); - free(conf->prefix_command_cpp); - free(conf->temporary_dir); - free((void *)conf->item_origins); // Workaround for MSVC warning - free(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->ignore_headers_in_manifest); + free(conf->log_file); + free(conf->path); + free(conf->prefix_command); + free(conf->prefix_command_cpp); + free(conf->temporary_dir); + free((void*)conf->item_origins); // Workaround for MSVC warning + free(conf); } // Note: The path pointer is stored in conf, so path must outlive conf. @@ -201,241 +211,249 @@ conf_free(struct conf *conf) // On failure, if an I/O error occurred errno is set appropriately, otherwise // errno is set to zero indicating that config itself was invalid. bool -conf_read(struct conf *conf, const char *path, char **errmsg) +conf_read(struct conf* conf, const char* path, char** errmsg) { - assert(errmsg); - *errmsg = NULL; - - FILE *f = fopen(path, "r"); - if (!f) { - *errmsg = format("%s: %s", path, strerror(errno)); - return false; - } - - unsigned line_number = 0; - bool result = true; - char buf[10000]; - while (fgets(buf, sizeof(buf), f)) { - ++line_number; - - char *key; - char *value; - char *errmsg2; - enum handle_conf_result hcr = HANDLE_CONF_OK; - bool ok = parse_line(buf, &key, &value, &errmsg2); - if (ok && key) { // key == NULL if comment or blank line. - hcr = - handle_conf_setting(conf, key, value, &errmsg2, false, false, path); - ok = hcr != HANDLE_CONF_FAIL; // unknown is OK - } - free(key); - free(value); - if (!ok) { - *errmsg = format("%s:%u: %s", path, line_number, errmsg2); - free(errmsg2); - errno = 0; - result = false; - goto out; - } - } - if (ferror(f)) { - *errmsg = x_strdup(strerror(errno)); - result = false; - } + assert(errmsg); + *errmsg = NULL; + + FILE* f = fopen(path, "r"); + if (!f) { + *errmsg = format("%s: %s", path, strerror(errno)); + return false; + } + + unsigned line_number = 0; + bool result = true; + char buf[10000]; + while (fgets(buf, sizeof(buf), f)) { + ++line_number; + + char* key; + char* value; + char* errmsg2; + enum handle_conf_result hcr = HANDLE_CONF_OK; + bool ok = parse_line(buf, &key, &value, &errmsg2); + if (ok && key) { // key == NULL if comment or blank line. + hcr = handle_conf_setting(conf, key, value, &errmsg2, false, false, path); + ok = hcr != HANDLE_CONF_FAIL; // unknown is OK + } + free(key); + free(value); + if (!ok) { + *errmsg = format("%s:%u: %s", path, line_number, errmsg2); + free(errmsg2); + errno = 0; + result = false; + goto out; + } + } + if (ferror(f)) { + *errmsg = x_strdup(strerror(errno)); + result = false; + } out: - fclose(f); - return result; + fclose(f); + return result; } bool -conf_update_from_environment(struct conf *conf, char **errmsg) +conf_update_from_environment(struct conf* conf, char** errmsg) { - for (char **p = environ; *p; ++p) { - if (!str_startswith(*p, "CCACHE_")) { - continue; - } - char *q = strchr(*p, '='); - if (!q) { - continue; - } - - bool negate; - size_t key_start; - if (str_startswith(*p + 7, "NO")) { - negate = true; - key_start = 9; - } else { - negate = false; - key_start = 7; - } - char *key = x_strndup(*p + key_start, q - *p - key_start); - - ++q; // Now points to the value. - - const struct env_to_conf_item *env_to_conf_item = find_env_to_conf(key); - if (!env_to_conf_item) { - free(key); - continue; - } - - char *errmsg2 = NULL; - enum handle_conf_result hcr = handle_conf_setting( - conf, env_to_conf_item->conf_name, q, &errmsg2, true, negate, - "environment"); - if (hcr != HANDLE_CONF_OK) { - *errmsg = format("%s: %s", key, errmsg2); - free(errmsg2); - free(key); - return false; - } - - free(key); - } - - return true; + for (char** p = environ; *p; ++p) { + if (!str_startswith(*p, "CCACHE_")) { + continue; + } + char* q = strchr(*p, '='); + if (!q) { + continue; + } + + bool negate; + size_t key_start; + if (str_startswith(*p + 7, "NO")) { + negate = true; + key_start = 9; + } else { + negate = false; + key_start = 7; + } + char* key = x_strndup(*p + key_start, q - *p - key_start); + + ++q; // Now points to the value. + + const struct env_to_conf_item* env_to_conf_item = find_env_to_conf(key); + if (!env_to_conf_item) { + free(key); + continue; + } + + char* errmsg2 = NULL; + enum handle_conf_result hcr = + handle_conf_setting(conf, + env_to_conf_item->conf_name, + q, + &errmsg2, + true, + negate, + "environment"); + if (hcr != HANDLE_CONF_OK) { + *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) +conf_set_value_in_file(const char* path, + const char* key, + const char* value, + char** errmsg) { - const struct conf_item *item = find_conf(key); - if (!item) { - *errmsg = format("unknown configuration option \"%s\"", key); - return false; - } - - char dummy[8] = {0}; // The maximum entry size in struct conf. - if (!item->parser(value, (void *)dummy, errmsg) - || (item->verifier && !item->verifier(value, errmsg))) { - return false; - } - - FILE *infile = fopen(path, "r"); - if (!infile) { - *errmsg = format("%s: %s", path, strerror(errno)); - return false; - } - - char *outpath = format("%s.tmp", path); - FILE *outfile = create_tmp_file(&outpath, "w"); - if (!outfile) { - *errmsg = format("%s: %s", outpath, strerror(errno)); - free(outpath); - fclose(infile); - return false; - } - - bool found = false; - char buf[10000]; - while (fgets(buf, sizeof(buf), infile)) { - char *key2; - char *value2; - char *errmsg2; - bool 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; + const struct conf_item* item = find_conf(key); + if (!item) { + *errmsg = format("unknown configuration option \"%s\"", key); + return false; + } + + char dummy[8] = {0}; // The maximum entry size in struct conf. + if (!item->parser(value, (void*)dummy, errmsg) + || (item->verifier && !item->verifier(value, errmsg))) { + return false; + } + + FILE* infile = fopen(path, "r"); + if (!infile) { + *errmsg = format("%s: %s", path, strerror(errno)); + return false; + } + + char* outpath = format("%s.tmp", path); + FILE* outfile = create_tmp_file(&outpath, "w"); + if (!outfile) { + *errmsg = format("%s: %s", outpath, strerror(errno)); + free(outpath); + fclose(infile); + return false; + } + + bool found = false; + char buf[10000]; + while (fgets(buf, sizeof(buf), infile)) { + char* key2; + char* value2; + char* errmsg2; + bool 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_value(struct conf *conf, const char *key, - FILE *file, char **errmsg) +conf_print_value(struct conf* conf, const char* key, FILE* file, char** errmsg) { - const struct conf_item *item = find_conf(key); - if (!item) { - *errmsg = format("unknown configuration option \"%s\"", key); - return false; - } - void *value = (char *)conf + item->offset; - char *str = item->formatter(value); - fprintf(file, "%s\n", str); - free(str); - return true; + const struct conf_item* item = find_conf(key); + if (!item) { + *errmsg = format("unknown configuration option \"%s\"", key); + return false; + } + void* value = (char*)conf + item->offset; + char* str = item->formatter(value); + fprintf(file, "%s\n", str); + free(str); + return true; } static bool -print_item(struct conf *conf, const char *key, - void (*printer)(const char *descr, const char *origin, - void *context), - void *context) +print_item(struct conf* conf, + const char* key, + void (*printer)(const char* descr, + const char* origin, + void* context), + void* context) { - const struct conf_item *item = find_conf(key); - if (!item) { - return false; - } - void *value = (char *)conf + item->offset; - char *str = item->formatter(value); - char *buf = x_strdup(""); - reformat(&buf, "%s = %s", key, str); - printer(buf, conf->item_origins[item->number], context); - free(buf); - free(str); - return true; + const struct conf_item* item = find_conf(key); + if (!item) { + return false; + } + void* value = (char*)conf + item->offset; + char* str = item->formatter(value); + char* buf = x_strdup(""); + reformat(&buf, "%s = %s", key, str); + printer(buf, conf->item_origins[item->number], context); + free(buf); + free(str); + return true; } bool -conf_print_items(struct conf *conf, - void (*printer)(const char *descr, const char *origin, - void *context), - void *context) +conf_print_items(struct conf* conf, + void (*printer)(const char* descr, + const char* origin, + void* context), + void* context) { - bool ok = true; - ok &= print_item(conf, "base_dir", printer, context); - ok &= print_item(conf, "cache_dir", printer, context); - ok &= print_item(conf, "cache_dir_levels", printer, context); - ok &= print_item(conf, "compiler", printer, context); - ok &= print_item(conf, "compiler_check", printer, context); - ok &= print_item(conf, "compression", printer, context); - ok &= print_item(conf, "compression_level", printer, context); - ok &= print_item(conf, "cpp_extension", printer, context); - ok &= print_item(conf, "debug", printer, context); - ok &= print_item(conf, "depend_mode", printer, context); - ok &= print_item(conf, "direct_mode", printer, context); - ok &= print_item(conf, "disable", printer, context); - ok &= print_item(conf, "extra_files_to_hash", printer, context); - ok &= print_item(conf, "file_clone", printer, context); - ok &= print_item(conf, "hard_link", printer, context); - ok &= print_item(conf, "hash_dir", printer, context); - ok &= print_item(conf, "ignore_headers_in_manifest", printer, context); - ok &= print_item(conf, "keep_comments_cpp", printer, context); - ok &= print_item(conf, "limit_multiple", printer, context); - ok &= print_item(conf, "log_file", printer, context); - ok &= print_item(conf, "max_files", printer, context); - ok &= print_item(conf, "max_size", printer, context); - ok &= print_item(conf, "path", printer, context); - ok &= print_item(conf, "pch_external_checksum", printer, context); - ok &= print_item(conf, "prefix_command", printer, context); - ok &= print_item(conf, "prefix_command_cpp", printer, context); - ok &= print_item(conf, "read_only", printer, context); - ok &= print_item(conf, "read_only_direct", printer, context); - ok &= print_item(conf, "recache", printer, context); - ok &= print_item(conf, "run_second_cpp", printer, context); - ok &= print_item(conf, "sloppiness", printer, context); - ok &= print_item(conf, "stats", printer, context); - ok &= print_item(conf, "temporary_dir", printer, context); - ok &= print_item(conf, "umask", printer, context); - ok &= print_item(conf, "unify", printer, context); - return ok; + bool ok = true; + ok &= print_item(conf, "base_dir", printer, context); + ok &= print_item(conf, "cache_dir", printer, context); + ok &= print_item(conf, "cache_dir_levels", printer, context); + ok &= print_item(conf, "compiler", printer, context); + ok &= print_item(conf, "compiler_check", printer, context); + ok &= print_item(conf, "compression", printer, context); + ok &= print_item(conf, "compression_level", printer, context); + ok &= print_item(conf, "cpp_extension", printer, context); + ok &= print_item(conf, "debug", printer, context); + ok &= print_item(conf, "depend_mode", printer, context); + ok &= print_item(conf, "direct_mode", printer, context); + ok &= print_item(conf, "disable", printer, context); + ok &= print_item(conf, "extra_files_to_hash", printer, context); + ok &= print_item(conf, "file_clone", printer, context); + ok &= print_item(conf, "hard_link", printer, context); + ok &= print_item(conf, "hash_dir", printer, context); + ok &= print_item(conf, "ignore_headers_in_manifest", printer, context); + ok &= print_item(conf, "keep_comments_cpp", printer, context); + ok &= print_item(conf, "limit_multiple", printer, context); + ok &= print_item(conf, "log_file", printer, context); + ok &= print_item(conf, "max_files", printer, context); + ok &= print_item(conf, "max_size", printer, context); + ok &= print_item(conf, "path", printer, context); + ok &= print_item(conf, "pch_external_checksum", printer, context); + ok &= print_item(conf, "prefix_command", printer, context); + ok &= print_item(conf, "prefix_command_cpp", printer, context); + ok &= print_item(conf, "read_only", printer, context); + ok &= print_item(conf, "read_only_direct", printer, context); + ok &= print_item(conf, "recache", printer, context); + ok &= print_item(conf, "run_second_cpp", printer, context); + ok &= print_item(conf, "sloppiness", printer, context); + ok &= print_item(conf, "stats", printer, context); + ok &= print_item(conf, "temporary_dir", printer, context); + ok &= print_item(conf, "umask", printer, context); + ok &= print_item(conf, "unify", printer, context); + return ok; } diff --git a/src/conf.hpp b/src/conf.hpp index 792d48a9..54c27f03 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -20,55 +20,59 @@ #include "system.hpp" -struct conf { - char *base_dir; - char *cache_dir; - unsigned cache_dir_levels; - char *compiler; - char *compiler_check; - bool compression; - int compression_level; - char *cpp_extension; - bool debug; - bool depend_mode; - bool direct_mode; - bool disable; - char *extra_files_to_hash; - bool file_clone; - bool hard_link; - bool hash_dir; - char *ignore_headers_in_manifest; - bool keep_comments_cpp; - double limit_multiple; - char *log_file; - unsigned max_files; - uint64_t max_size; - char *path; - bool pch_external_checksum; - char *prefix_command; - char *prefix_command_cpp; - bool read_only; - bool read_only_direct; - bool recache; - bool run_second_cpp; - unsigned sloppiness; - bool stats; - char *temporary_dir; - unsigned umask; - bool unify; +struct conf +{ + char* base_dir; + char* cache_dir; + unsigned cache_dir_levels; + char* compiler; + char* compiler_check; + bool compression; + int compression_level; + char* cpp_extension; + bool debug; + bool depend_mode; + bool direct_mode; + bool disable; + char* extra_files_to_hash; + bool file_clone; + bool hard_link; + bool hash_dir; + char* ignore_headers_in_manifest; + bool keep_comments_cpp; + double limit_multiple; + char* log_file; + unsigned max_files; + uint64_t max_size; + char* path; + bool pch_external_checksum; + char* prefix_command; + char* prefix_command_cpp; + bool read_only; + bool read_only_direct; + bool recache; + bool run_second_cpp; + unsigned sloppiness; + bool stats; + char* temporary_dir; + unsigned umask; + bool unify; - const char **item_origins; + const char** item_origins; }; -struct conf *conf_create(void); -void conf_free(struct conf *conf); -bool conf_read(struct conf *conf, const char *path, char **errmsg); -bool conf_update_from_environment(struct conf *conf, char **errmsg); -bool conf_print_value(struct conf *conf, const char *key, - FILE *file, char **errmsg); -bool conf_set_value_in_file(const char *path, const char *key, - const char *value, char **errmsg); -bool conf_print_items(struct conf *conf, - void (*printer)(const char *descr, const char *origin, - void *context), - void *context); +struct conf* conf_create(void); +void conf_free(struct conf* conf); +bool conf_read(struct conf* conf, const char* path, char** errmsg); +bool conf_update_from_environment(struct conf* conf, char** errmsg); +bool +conf_print_value(struct conf* conf, const char* key, FILE* file, char** errmsg); +bool conf_set_value_in_file(const char* path, + const char* key, + const char* value, + char** errmsg); +bool conf_print_items(struct conf* conf, + void (*printer)(const char* descr, + const char* origin, + void* context), + void* context); diff --git a/src/confitems.cpp b/src/confitems.cpp index 1b4023f4..386a430f 100644 --- a/src/confitems.cpp +++ b/src/confitems.cpp @@ -17,317 +17,318 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "confitems.hpp" + #include "ccache.hpp" -static char * -format_string(const void *value) +static char* +format_string(const void* value) { - const char *const *str = (const char *const *)value; - return x_strdup(*str); + const char* const* str = (const char* const*)value; + return x_strdup(*str); } bool -confitem_parse_bool(const char *str, void *result, char **errmsg) +confitem_parse_bool(const char* str, void* result, char** errmsg) { - bool *value = (bool *)result; + 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; - } + 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; + } } -char * -confitem_format_bool(const void *value) +char* +confitem_format_bool(const void* value) { - const bool *b = (const bool *)value; - return x_strdup(*b ? "true" : "false"); + const bool* b = (const bool*)value; + return x_strdup(*b ? "true" : "false"); } bool -confitem_parse_env_string(const char *str, void *result, char **errmsg) +confitem_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 != NULL; + char** value = (char**)result; + free(*value); + *value = subst_env_in_string(str, errmsg); + return *value != NULL; } -char * -confitem_format_env_string(const void *value) +char* +confitem_format_env_string(const void* value) { - return format_string(value); + return format_string(value); } bool -confitem_parse_double(const char *str, void *result, char **errmsg) +confitem_parse_double(const char* str, void* result, char** errmsg) { - double *value = (double *)result; - errno = 0; - char *endptr; - double x = strtod(str, &endptr); - if (errno == 0 && *str != '\0' && *endptr == '\0') { - *value = x; - return true; - } else { - *errmsg = format("invalid floating point: \"%s\"", str); - return false; - } + double* value = (double*)result; + errno = 0; + char* endptr; + double x = strtod(str, &endptr); + if (errno == 0 && *str != '\0' && *endptr == '\0') { + *value = x; + return true; + } else { + *errmsg = format("invalid floating point: \"%s\"", str); + return false; + } } -char * -confitem_format_double(const void *value) +char* +confitem_format_double(const void* value) { - const double *x = (const double *)value; - return format("%.1f", *x); + const double* x = (const double*)value; + return format("%.1f", *x); } bool -confitem_parse_size(const char *str, void *result, char **errmsg) +confitem_parse_size(const char* str, void* result, char** errmsg) { - uint64_t *value = (uint64_t *)result; - uint64_t size; - if (parse_size_with_suffix(str, &size)) { - *value = size; - return true; - } else { - *errmsg = format("invalid size: \"%s\"", str); - return false; - } + uint64_t* value = (uint64_t*)result; + uint64_t size; + if (parse_size_with_suffix(str, &size)) { + *value = size; + return true; + } else { + *errmsg = format("invalid size: \"%s\"", str); + return false; + } } -char * -confitem_format_size(const void *value) +char* +confitem_format_size(const void* value) { - const uint64_t *size = (const uint64_t *)value; - return format_parsable_size_with_suffix(*size); + const uint64_t* size = (const uint64_t*)value; + return format_parsable_size_with_suffix(*size); } bool -confitem_parse_sloppiness(const char *str, void *result, char **errmsg) +confitem_parse_sloppiness(const char* str, void* result, char** errmsg) { - unsigned *value = (unsigned *)result; - if (!str) { - return *value; - } + unsigned* value = (unsigned*)result; + if (!str) { + return *value; + } - char *p = x_strdup(str); - char *q = p; - char *word; - char *saveptr = NULL; - while ((word = strtok_r(q, ", ", &saveptr))) { - if (str_eq(word, "file_macro")) { - *value |= SLOPPY_FILE_MACRO; - } else if (str_eq(word, "file_stat_matches")) { - *value |= SLOPPY_FILE_STAT_MATCHES; - } else if (str_eq(word, "file_stat_matches_ctime")) { - *value |= SLOPPY_FILE_STAT_MATCHES_CTIME; - } 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, "system_headers") - || str_eq(word, "no_system_headers")) { - *value |= SLOPPY_SYSTEM_HEADERS; - } 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, "clang_index_store")) { - *value |= SLOPPY_CLANG_INDEX_STORE; - } else if (str_eq(word, "locale")) { - *value |= SLOPPY_LOCALE; - } else { - *errmsg = format("unknown sloppiness: \"%s\"", word); - free(p); - return false; - } - q = NULL; - } - free(p); - return true; + char* p = x_strdup(str); + char* q = p; + char* word; + char* saveptr = NULL; + while ((word = strtok_r(q, ", ", &saveptr))) { + if (str_eq(word, "file_macro")) { + *value |= SLOPPY_FILE_MACRO; + } else if (str_eq(word, "file_stat_matches")) { + *value |= SLOPPY_FILE_STAT_MATCHES; + } else if (str_eq(word, "file_stat_matches_ctime")) { + *value |= SLOPPY_FILE_STAT_MATCHES_CTIME; + } 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, "system_headers") + || str_eq(word, "no_system_headers")) { + *value |= SLOPPY_SYSTEM_HEADERS; + } 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, "clang_index_store")) { + *value |= SLOPPY_CLANG_INDEX_STORE; + } else if (str_eq(word, "locale")) { + *value |= SLOPPY_LOCALE; + } else { + *errmsg = format("unknown sloppiness: \"%s\"", word); + free(p); + return false; + } + q = NULL; + } + free(p); + return true; } -char * -confitem_format_sloppiness(const void *value) +char* +confitem_format_sloppiness(const void* value) { - const unsigned *sloppiness = (const unsigned *)value; - char *s = x_strdup(""); - if (*sloppiness & SLOPPY_FILE_MACRO) { - reformat(&s, "%sfile_macro, ", s); - } - if (*sloppiness & SLOPPY_INCLUDE_FILE_MTIME) { - reformat(&s, "%sinclude_file_mtime, ", s); - } - if (*sloppiness & SLOPPY_INCLUDE_FILE_CTIME) { - reformat(&s, "%sinclude_file_ctime, ", s); - } - if (*sloppiness & SLOPPY_TIME_MACROS) { - reformat(&s, "%stime_macros, ", s); - } - if (*sloppiness & SLOPPY_PCH_DEFINES) { - reformat(&s, "%spch_defines, ", s); - } - if (*sloppiness & SLOPPY_FILE_STAT_MATCHES) { - reformat(&s, "%sfile_stat_matches, ", s); - } - if (*sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME) { - reformat(&s, "%sfile_stat_matches_ctime, ", s); - } - if (*sloppiness & SLOPPY_SYSTEM_HEADERS) { - reformat(&s, "%ssystem_headers, ", s); - } - if (*sloppiness & SLOPPY_CLANG_INDEX_STORE) { - reformat(&s, "%sclang_index_store, ", s); - } - if (*sloppiness & SLOPPY_LOCALE) { - reformat(&s, "%slocale, ", s); - } - if (*sloppiness) { - // Strip last ", ". - s[strlen(s) - 2] = '\0'; - } - return s; + const unsigned* sloppiness = (const unsigned*)value; + char* s = x_strdup(""); + if (*sloppiness & SLOPPY_FILE_MACRO) { + reformat(&s, "%sfile_macro, ", s); + } + if (*sloppiness & SLOPPY_INCLUDE_FILE_MTIME) { + reformat(&s, "%sinclude_file_mtime, ", s); + } + if (*sloppiness & SLOPPY_INCLUDE_FILE_CTIME) { + reformat(&s, "%sinclude_file_ctime, ", s); + } + if (*sloppiness & SLOPPY_TIME_MACROS) { + reformat(&s, "%stime_macros, ", s); + } + if (*sloppiness & SLOPPY_PCH_DEFINES) { + reformat(&s, "%spch_defines, ", s); + } + if (*sloppiness & SLOPPY_FILE_STAT_MATCHES) { + reformat(&s, "%sfile_stat_matches, ", s); + } + if (*sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME) { + reformat(&s, "%sfile_stat_matches_ctime, ", s); + } + if (*sloppiness & SLOPPY_SYSTEM_HEADERS) { + reformat(&s, "%ssystem_headers, ", s); + } + if (*sloppiness & SLOPPY_CLANG_INDEX_STORE) { + reformat(&s, "%sclang_index_store, ", s); + } + if (*sloppiness & SLOPPY_LOCALE) { + reformat(&s, "%slocale, ", s); + } + if (*sloppiness) { + // Strip last ", ". + s[strlen(s) - 2] = '\0'; + } + return s; } bool -confitem_parse_string(const char *str, void *result, char **errmsg) +confitem_parse_string(const char* str, void* result, char** errmsg) { - (void)errmsg; + (void)errmsg; - char **value = (char **)result; - free(*value); - *value = x_strdup(str); - return true; + char** value = (char**)result; + free(*value); + *value = x_strdup(str); + return true; } -char * -confitem_format_string(const void *value) +char* +confitem_format_string(const void* value) { - return format_string(value); + return format_string(value); } bool -confitem_parse_umask(const char *str, void *result, char **errmsg) +confitem_parse_umask(const char* str, void* result, char** errmsg) { - unsigned *value = (unsigned *)result; - if (str_eq(str, "")) { - *value = UINT_MAX; - return true; - } + unsigned* value = (unsigned*)result; + if (str_eq(str, "")) { + *value = UINT_MAX; + return true; + } - errno = 0; - char *endptr; - *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; - } + errno = 0; + char* endptr; + *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; + } } -char * -confitem_format_umask(const void *value) +char* +confitem_format_umask(const void* value) { - const unsigned *umask = (const unsigned *)value; - if (*umask == UINT_MAX) { - return x_strdup(""); - } else { - return format("%03o", *umask); - } + const unsigned* umask = (const unsigned*)value; + if (*umask == UINT_MAX) { + return x_strdup(""); + } else { + return format("%03o", *umask); + } } bool -confitem_parse_int(const char *str, void *result, char **errmsg) +confitem_parse_int(const char* str, void* result, char** errmsg) { - int *value = (int *)result; - errno = 0; - char *endptr; - long x = strtol(str, &endptr, 10); - if (errno == 0 && *str != '\0' && *endptr == '\0') { - *value = x; - return true; - } else { - *errmsg = format("invalid integer: \"%s\"", str); - return false; - } + int* value = (int*)result; + errno = 0; + char* endptr; + long x = strtol(str, &endptr, 10); + if (errno == 0 && *str != '\0' && *endptr == '\0') { + *value = x; + return true; + } else { + *errmsg = format("invalid integer: \"%s\"", str); + return false; + } } -char * -confitem_format_int(const void *value) +char* +confitem_format_int(const void* value) { - const int *i = (const int *)value; - return format("%d", *i); + const int* i = (const int*)value; + return format("%d", *i); } bool -confitem_parse_unsigned(const char *str, void *result, char **errmsg) +confitem_parse_unsigned(const char* str, void* result, char** errmsg) { - unsigned *value = (unsigned *)result; - errno = 0; - char *endptr; - long 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; - } + unsigned* value = (unsigned*)result; + errno = 0; + char* endptr; + long 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; + } } -char * -confitem_format_unsigned(const void *value) +char* +confitem_format_unsigned(const void* value) { - const unsigned *i = (const unsigned *)value; - return format("%u", *i); + const unsigned* i = (const unsigned*)value; + return format("%u", *i); } bool -confitem_verify_absolute_path(const void *value, char **errmsg) +confitem_verify_absolute_path(const void* value, char** errmsg) { - const char *const *path = (const char *const *)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; - } + const char* const* path = (const char* const*)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; + } } bool -confitem_verify_compression_level(const void *value, char **errmsg) +confitem_verify_compression_level(const void* value, char** errmsg) { - const int *level = (const int *)value; - assert(level); - if (*level >= -128 && *level <= 127) { - return true; - } else { - *errmsg = format("compression level must be between -128 and 127"); - return false; - } + const int* level = (const int*)value; + assert(level); + if (*level >= -128 && *level <= 127) { + return true; + } else { + *errmsg = format("compression level must be between -128 and 127"); + return false; + } } bool -confitem_verify_dir_levels(const void *value, char **errmsg) +confitem_verify_dir_levels(const void* value, char** errmsg) { - const unsigned *levels = (const 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; - } + const unsigned* levels = (const 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; + } } diff --git a/src/confitems.hpp b/src/confitems.hpp index ae2f2534..aba12d98 100644 --- a/src/confitems.hpp +++ b/src/confitems.hpp @@ -20,49 +20,50 @@ #include "system.hpp" -typedef bool (*conf_item_parser)(const char *str, void *result, char **errmsg); -typedef bool (*conf_item_verifier)(const void *value, char **errmsg); -typedef char *(*conf_item_formatter)(const void *value); +typedef bool (*conf_item_parser)(const char* str, void* result, char** errmsg); +typedef bool (*conf_item_verifier)(const void* value, char** errmsg); +typedef char* (*conf_item_formatter)(const void* value); -struct conf_item { - const char *name; - size_t number; - size_t offset; - conf_item_parser parser; - conf_item_formatter formatter; - conf_item_verifier verifier; +struct conf_item +{ + const char* name; + size_t number; + size_t offset; + conf_item_parser parser; + conf_item_formatter formatter; + conf_item_verifier verifier; }; -bool confitem_parse_bool(const char *str, void *result, char **errmsg); -char *confitem_format_bool(const void *value); +bool confitem_parse_bool(const char* str, void* result, char** errmsg); +char* confitem_format_bool(const void* value); -bool confitem_parse_env_string(const char *str, void *result, char **errmsg); -char *confitem_format_env_string(const void *value); +bool confitem_parse_env_string(const char* str, void* result, char** errmsg); +char* confitem_format_env_string(const void* value); -bool confitem_parse_double(const char *str, void *result, char **errmsg); -char *confitem_format_double(const void *value); +bool confitem_parse_double(const char* str, void* result, char** errmsg); +char* confitem_format_double(const void* value); -bool confitem_parse_size(const char *str, void *result, char **errmsg); -char *confitem_format_size(const void *value); +bool confitem_parse_size(const char* str, void* result, char** errmsg); +char* confitem_format_size(const void* value); -bool confitem_parse_sloppiness(const char *str, void *result, char **errmsg); -char *confitem_format_sloppiness(const void *value); +bool confitem_parse_sloppiness(const char* str, void* result, char** errmsg); +char* confitem_format_sloppiness(const void* value); -bool confitem_parse_string(const char *str, void *result, char **errmsg); -char *confitem_format_string(const void *value); +bool confitem_parse_string(const char* str, void* result, char** errmsg); +char* confitem_format_string(const void* value); -bool confitem_parse_umask(const char *str, void *result, char **errmsg); -char *confitem_format_umask(const void *value); +bool confitem_parse_umask(const char* str, void* result, char** errmsg); +char* confitem_format_umask(const void* value); -bool confitem_parse_int(const char *str, void *result, char **errmsg); -char *confitem_format_int(const void *value); +bool confitem_parse_int(const char* str, void* result, char** errmsg); +char* confitem_format_int(const void* value); -bool confitem_parse_unsigned(const char *str, void *result, char **errmsg); -char *confitem_format_unsigned(const void *value); +bool confitem_parse_unsigned(const char* str, void* result, char** errmsg); +char* confitem_format_unsigned(const void* value); -bool confitem_verify_absolute_path(const void *value, char **errmsg); -bool confitem_verify_compression_level(const void *value, char **errmsg); -bool confitem_verify_dir_levels(const void *value, char **errmsg); +bool confitem_verify_absolute_path(const void* value, char** errmsg); +bool confitem_verify_compression_level(const void* value, char** errmsg); +bool confitem_verify_dir_levels(const void* value, char** errmsg); -const struct conf_item *confitems_get(const char *str, size_t len); +const struct conf_item* confitems_get(const char* str, size_t len); size_t confitems_count(void); diff --git a/src/counters.cpp b/src/counters.cpp index d846f9d8..ade493c2 100644 --- a/src/counters.cpp +++ b/src/counters.cpp @@ -22,45 +22,45 @@ // Allocate and initialize a struct counters. Data entries up to the size are // set to 0. -struct counters * +struct counters* counters_init(size_t initial_size) { - auto c = static_cast<counters*>(x_malloc(sizeof(counters))); - c->data = NULL; - c->size = 0; - c->allocated = 0; - counters_resize(c, initial_size); - return c; + auto c = static_cast<counters*>(x_malloc(sizeof(counters))); + c->data = NULL; + c->size = 0; + c->allocated = 0; + counters_resize(c, initial_size); + return c; } // Free a counters struct. void -counters_free(struct counters *c) +counters_free(struct counters* c) { - if (c) { - free(c->data); - free(c); - } + if (c) { + free(c->data); + free(c); + } } // Set a new size. New data entries are set to 0. void -counters_resize(struct counters *c, size_t new_size) +counters_resize(struct counters* c, size_t new_size) { - if (new_size > c->size) { - bool realloc = false; - while (c->allocated < new_size) { - c->allocated += 32 + c->allocated; - realloc = true; - } - if (realloc) { - c->data = static_cast<unsigned*>( - x_realloc(c->data, c->allocated * sizeof(c->data[0]))); - } - for (size_t i = c->size; i < new_size; i++) { - c->data[i] = 0; - } - } + if (new_size > c->size) { + bool realloc = false; + while (c->allocated < new_size) { + c->allocated += 32 + c->allocated; + realloc = true; + } + if (realloc) { + c->data = static_cast<unsigned*>( + x_realloc(c->data, c->allocated * sizeof(c->data[0]))); + } + for (size_t i = c->size; i < new_size; i++) { + c->data[i] = 0; + } + } - c->size = new_size; + c->size = new_size; } diff --git a/src/counters.hpp b/src/counters.hpp index 43d334b6..c2560b73 100644 --- a/src/counters.hpp +++ b/src/counters.hpp @@ -20,12 +20,13 @@ #include <stddef.h> -struct counters { - unsigned *data; // counter value - size_t size; // logical array size - size_t allocated; // allocated size +struct counters +{ + unsigned* data; // counter value + size_t size; // logical array size + size_t allocated; // allocated size }; -struct counters *counters_init(size_t initial_size); -void counters_resize(struct counters *c, size_t new_size); -void counters_free(struct counters *c); +struct counters* counters_init(size_t initial_size); +void counters_resize(struct counters* c, size_t new_size); +void counters_free(struct counters* c); diff --git a/src/decompr_none.cpp b/src/decompr_none.cpp index e4c87c7e..4c40cd49 100644 --- a/src/decompr_none.cpp +++ b/src/decompr_none.cpp @@ -18,48 +18,46 @@ #include "compression.hpp" -struct state { - FILE *input; - XXH64_state_t *checksum; - bool failed; +struct state +{ + FILE* input; + XXH64_state_t* checksum; + bool failed; }; -static struct decompr_state * -decompr_none_init(FILE *input, XXH64_state_t *checksum) +static struct decompr_state* +decompr_none_init(FILE* input, XXH64_state_t* checksum) { - auto state = static_cast<struct state*>(malloc(sizeof(struct state))); - state->input = input; - state->checksum = checksum; - state->failed = false; - return (struct decompr_state *)state; + auto state = static_cast<struct state*>(malloc(sizeof(struct state))); + state->input = input; + state->checksum = checksum; + state->failed = false; + return (struct decompr_state*)state; } static bool -decompr_none_read(struct decompr_state *handle, void *data, size_t size) +decompr_none_read(struct decompr_state* handle, void* data, size_t size) { - struct state *state = (struct state *)handle; + struct state* state = (struct state*)handle; - bool result = fread(data, 1, size, state->input) == size; - if (result && state->checksum) { - XXH64_update(state->checksum, data, size); - } - if (!result) { - state->failed = true; - } - return result; + bool result = fread(data, 1, size, state->input) == size; + if (result && state->checksum) { + XXH64_update(state->checksum, data, size); + } + if (!result) { + state->failed = true; + } + return result; } static bool -decompr_none_free(struct decompr_state *handle) +decompr_none_free(struct decompr_state* handle) { - struct state *state = (struct state *)handle; - bool result = !state->failed; - free(state); - return result; + struct state* state = (struct state*)handle; + bool result = !state->failed; + free(state); + return result; } struct decompressor decompressor_none_impl = { - decompr_none_init, - decompr_none_read, - decompr_none_free -}; + decompr_none_init, decompr_none_read, decompr_none_free}; diff --git a/src/decompr_zstd.cpp b/src/decompr_zstd.cpp index b6691654..9fb895c3 100644 --- a/src/decompr_zstd.cpp +++ b/src/decompr_zstd.cpp @@ -22,105 +22,104 @@ #include <zstd.h> enum stream_state { - STREAM_STATE_READING, - STREAM_STATE_FAILED, - STREAM_STATE_END + STREAM_STATE_READING, + STREAM_STATE_FAILED, + STREAM_STATE_END }; -struct state { - FILE *input; - XXH64_state_t *checksum; - char input_buffer[READ_BUFFER_SIZE]; - size_t input_size; - size_t input_consumed; - ZSTD_DStream *stream; - ZSTD_inBuffer in; - ZSTD_outBuffer out; - enum stream_state stream_state; +struct state +{ + FILE* input; + XXH64_state_t* checksum; + char input_buffer[READ_BUFFER_SIZE]; + size_t input_size; + size_t input_consumed; + ZSTD_DStream* stream; + ZSTD_inBuffer in; + ZSTD_outBuffer out; + enum stream_state stream_state; }; -static struct decompr_state * -decompr_zstd_init(FILE *input, XXH64_state_t *checksum) +static struct decompr_state* +decompr_zstd_init(FILE* input, XXH64_state_t* checksum) { - auto state = static_cast<struct state*>(malloc(sizeof(struct state))); + auto state = static_cast<struct state*>(malloc(sizeof(struct state))); - state->input = input; - state->checksum = checksum; - state->input_size = 0; - state->input_consumed = 0; - state->stream = ZSTD_createDStream(); - state->stream_state = STREAM_STATE_READING; + state->input = input; + state->checksum = checksum; + state->input_size = 0; + state->input_consumed = 0; + state->stream = ZSTD_createDStream(); + state->stream_state = STREAM_STATE_READING; - size_t ret = ZSTD_initDStream(state->stream); - if (ZSTD_isError(ret)) { - ZSTD_freeDStream(state->stream); - free(state); - return NULL; - } - return (struct decompr_state *)state; + size_t ret = ZSTD_initDStream(state->stream); + if (ZSTD_isError(ret)) { + ZSTD_freeDStream(state->stream); + free(state); + return NULL; + } + return (struct decompr_state*)state; } static bool -decompr_zstd_read(struct decompr_state *handle, void *data, size_t size) +decompr_zstd_read(struct decompr_state* handle, void* data, size_t size) { - if (!handle) { - return false; - } - struct state *state = (struct state *)handle; + if (!handle) { + return false; + } + struct state* state = (struct state*)handle; - size_t bytes_read = 0; - while (bytes_read < size) { - assert(state->input_size >= state->input_consumed); - if (state->input_size == state->input_consumed) { - state->input_size = fread( - state->input_buffer, 1, sizeof(state->input_buffer), state->input); - if (state->input_size == 0) { - state->stream_state = STREAM_STATE_FAILED; - return false; - } - state->input_consumed = 0; - } + size_t bytes_read = 0; + while (bytes_read < size) { + assert(state->input_size >= state->input_consumed); + if (state->input_size == state->input_consumed) { + state->input_size = fread( + state->input_buffer, 1, sizeof(state->input_buffer), state->input); + if (state->input_size == 0) { + state->stream_state = STREAM_STATE_FAILED; + return false; + } + state->input_consumed = 0; + } - state->in.src = (state->input_buffer + state->input_consumed); - state->in.size = state->input_size - state->input_consumed; - state->in.pos = 0; + state->in.src = (state->input_buffer + state->input_consumed); + state->in.size = state->input_size - state->input_consumed; + state->in.pos = 0; - state->out.dst = ((char *)data + bytes_read); - state->out.size = size - bytes_read; - state->out.pos = 0; - size_t ret = ZSTD_decompressStream(state->stream, &state->out, &state->in); - if (ZSTD_isError(ret)) { - state->stream_state = STREAM_STATE_FAILED; - return false; - } - if (state->checksum) { - XXH64_update(state->checksum, state->out.dst, state->out.pos); - } - if (ret == 0) { - state->stream_state = STREAM_STATE_END; - break; - } - bytes_read += state->out.pos; - state->input_consumed += state->in.pos; - } + state->out.dst = ((char*)data + bytes_read); + state->out.size = size - bytes_read; + state->out.pos = 0; + size_t ret = ZSTD_decompressStream(state->stream, &state->out, &state->in); + if (ZSTD_isError(ret)) { + state->stream_state = STREAM_STATE_FAILED; + return false; + } + if (state->checksum) { + XXH64_update(state->checksum, state->out.dst, state->out.pos); + } + if (ret == 0) { + state->stream_state = STREAM_STATE_END; + break; + } + bytes_read += state->out.pos; + state->input_consumed += state->in.pos; + } - return true; + return true; } -static bool decompr_zstd_free(struct decompr_state *handle) +static bool +decompr_zstd_free(struct decompr_state* handle) { - if (!handle) { - return false; - } - struct state *state = (struct state *)handle; - ZSTD_freeDStream(state->stream); - bool success = state->stream_state == STREAM_STATE_END; - free(handle); - return success; + if (!handle) { + return false; + } + struct state* state = (struct state*)handle; + ZSTD_freeDStream(state->stream); + bool success = state->stream_state == STREAM_STATE_END; + free(handle); + return success; } struct decompressor decompressor_zstd_impl = { - decompr_zstd_init, - decompr_zstd_read, - decompr_zstd_free -}; + decompr_zstd_init, decompr_zstd_read, decompr_zstd_free}; diff --git a/src/envtoconfitems.hpp b/src/envtoconfitems.hpp index 425e38f8..7885b483 100644 --- a/src/envtoconfitems.hpp +++ b/src/envtoconfitems.hpp @@ -20,11 +20,12 @@ #include "system.hpp" -struct env_to_conf_item { - const char *env_name; - const char *conf_name; +struct env_to_conf_item +{ + const char* env_name; + const char* conf_name; }; -const struct env_to_conf_item *envtoconfitems_get(const char *str, size_t len); +const struct env_to_conf_item* envtoconfitems_get(const char* str, size_t len); size_t envtoconfitems_count(void); diff --git a/src/execute.cpp b/src/execute.cpp index 2a31bfae..fb5c638f 100644 --- a/src/execute.cpp +++ b/src/execute.cpp @@ -19,223 +19,228 @@ #include "ccache.hpp" -extern struct conf *conf; +extern struct conf* conf; -static char * -find_executable_in_path(const char *name, const char *exclude_name, char *path); +static char* +find_executable_in_path(const char* name, const char* exclude_name, char* path); #ifdef _WIN32 // Re-create a win32 command line string based on **argv. // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx -char * -win32argvtos(char *prefix, char **argv, int *length) +char* +win32argvtos(char* prefix, char** argv, int* length) { - int i = 0; - int k = 0; - char *arg = prefix ? prefix : argv[i++]; - do { - int bs = 0; - for (int j = 0; arg[j]; j++) { - switch (arg[j]) { - case '\\': - bs++; - break; - case '"': - bs = (bs << 1) + 1; - // Fallthrough. - default: - k += bs + 1; - bs = 0; - } - } - k += (bs << 1) + 3; - } while ((arg = argv[i++])); - - char *ptr = static_cast<char*>(malloc(k + 1)); - char *str = ptr; - if (!str) { - *length = 0; - return NULL; - } - - i = 0; - arg = prefix ? prefix : argv[i++]; - do { - int bs = 0; - *ptr++ = '"'; - for (int j = 0; arg[j]; j++) { - switch (arg[j]) { - case '\\': - bs++; - break; - // Fallthrough. - case '"': - bs = (bs << 1) + 1; - // Fallthrough. - default: - while (bs && bs--) { - *ptr++ = '\\'; - } - *ptr++ = arg[j]; - } - } - bs <<= 1; - while (bs && bs--) { - *ptr++ = '\\'; - } - *ptr++ = '"'; - *ptr++ = ' '; - } while ((arg = argv[i++])); - ptr[-1] = '\0'; - - *length = ptr - str - 1; - return str; + int i = 0; + int k = 0; + char* arg = prefix ? prefix : argv[i++]; + do { + int bs = 0; + for (int j = 0; arg[j]; j++) { + switch (arg[j]) { + case '\\': + bs++; + break; + case '"': + bs = (bs << 1) + 1; + // Fallthrough. + default: + k += bs + 1; + bs = 0; + } + } + k += (bs << 1) + 3; + } while ((arg = argv[i++])); + + char* ptr = static_cast<char*>(malloc(k + 1)); + char* str = ptr; + if (!str) { + *length = 0; + return NULL; + } + + i = 0; + arg = prefix ? prefix : argv[i++]; + do { + int bs = 0; + *ptr++ = '"'; + for (int j = 0; arg[j]; j++) { + switch (arg[j]) { + case '\\': + bs++; + break; + // Fallthrough. + case '"': + bs = (bs << 1) + 1; + // Fallthrough. + default: + while (bs && bs--) { + *ptr++ = '\\'; + } + *ptr++ = arg[j]; + } + } + bs <<= 1; + while (bs && bs--) { + *ptr++ = '\\'; + } + *ptr++ = '"'; + *ptr++ = ' '; + } while ((arg = argv[i++])); + ptr[-1] = '\0'; + + *length = ptr - str - 1; + return str; } -char * -win32getshell(char *path) +char* +win32getshell(char* path) { - char *path_env; - char *sh = NULL; - const char *ext = get_extension(path); - if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH"))) { - sh = find_executable_in_path("sh.exe", NULL, path_env); - } - if (!sh && getenv("CCACHE_DETECT_SHEBANG")) { - // Detect shebang. - FILE *fp = fopen(path, "r"); - if (fp) { - char buf[10]; - fgets(buf, sizeof(buf), fp); - buf[9] = 0; - if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH"))) { - sh = find_executable_in_path("sh.exe", NULL, path_env); - } - fclose(fp); - } - } - - return sh; + char* path_env; + char* sh = NULL; + const char* ext = get_extension(path); + if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH"))) { + sh = find_executable_in_path("sh.exe", NULL, path_env); + } + if (!sh && getenv("CCACHE_DETECT_SHEBANG")) { + // Detect shebang. + FILE* fp = fopen(path, "r"); + if (fp) { + char buf[10]; + fgets(buf, sizeof(buf), fp); + buf[9] = 0; + if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH"))) { + sh = find_executable_in_path("sh.exe", NULL, path_env); + } + fclose(fp); + } + } + + return sh; } -void add_exe_ext_if_no_to_fullpath(char *full_path_win_ext, size_t max_size, - const char *ext, const char *path) { - if (!ext || (!str_eq(".exe", ext) - && !str_eq(".sh", ext) - && !str_eq(".bat", ext) - && !str_eq(".EXE", ext) - && !str_eq(".BAT", ext))) { - snprintf(full_path_win_ext, max_size, "%s.exe", path); - } else { - snprintf(full_path_win_ext, max_size, "%s", path); - } +void +add_exe_ext_if_no_to_fullpath(char* full_path_win_ext, + size_t max_size, + const char* ext, + const char* path) +{ + if (!ext + || (!str_eq(".exe", ext) && !str_eq(".sh", ext) && !str_eq(".bat", ext) + && !str_eq(".EXE", ext) && !str_eq(".BAT", ext))) { + snprintf(full_path_win_ext, max_size, "%s.exe", path); + } else { + snprintf(full_path_win_ext, max_size, "%s", path); + } } int -win32execute(char *path, char **argv, int doreturn, - int fd_stdout, int fd_stderr) +win32execute( + char* path, char** argv, int doreturn, int fd_stdout, int fd_stderr) { - PROCESS_INFORMATION pi; - memset(&pi, 0x00, sizeof(pi)); - - STARTUPINFO si; - memset(&si, 0x00, sizeof(si)); - - char *sh = win32getshell(path); - if (sh) { - path = sh; - } - - si.cb = sizeof(STARTUPINFO); - if (fd_stdout != -1) { - si.hStdOutput = (HANDLE)_get_osfhandle(fd_stdout); - si.hStdError = (HANDLE)_get_osfhandle(fd_stderr); - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; - if (si.hStdOutput == INVALID_HANDLE_VALUE - || si.hStdError == INVALID_HANDLE_VALUE) { - return -1; - } - } else { - // Redirect subprocess stdout, stderr into current process. - si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; - if (si.hStdOutput == INVALID_HANDLE_VALUE - || si.hStdError == INVALID_HANDLE_VALUE) { - return -1; - } - } - - int length; - char *args = win32argvtos(sh, argv, &length); - const char *ext = strrchr(path, '.'); - char full_path_win_ext[MAX_PATH] = {0}; - add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path); - BOOL ret = FALSE; - if (length > 8192) { - char *tmp_file = format("%s.tmp", path); - FILE *fp = create_tmp_file(&tmp_file, "w"); - char atfile[MAX_PATH + 3]; - fwrite(args, 1, length, fp); - if (ferror(fp)) { - cc_log("Error writing @file; this command will probably fail: %s", args); - } - fclose(fp); - snprintf(atfile, sizeof(atfile), "\"@%s\"", tmp_file); - ret = CreateProcess(NULL, atfile, NULL, NULL, 1, 0, NULL, NULL, - &si, &pi); - tmp_unlink(tmp_file); - free(tmp_file); - } - if (!ret) { - ret = CreateProcess(full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL, - &si, &pi); - } - if (fd_stdout != -1) { - close(fd_stdout); - close(fd_stderr); - } - free(args); - if (ret == 0) { - LPVOID lpMsgBuf; - DWORD dw = GetLastError(); - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, - 0, NULL); - - LPVOID lpDisplayBuf = - (LPVOID) LocalAlloc(LMEM_ZEROINIT, - (lstrlen((LPCTSTR) lpMsgBuf) - + lstrlen((LPCTSTR) __FILE__) + 200) - * sizeof(TCHAR)); - _snprintf((LPTSTR) lpDisplayBuf, - LocalSize(lpDisplayBuf) / sizeof(TCHAR), - TEXT("%s failed with error %lu: %s"), __FILE__, dw, - (const char *)lpMsgBuf); - - cc_log("can't execute %s; OS returned error: %s", - full_path_win_ext, (char *)lpDisplayBuf); - - LocalFree(lpMsgBuf); - LocalFree(lpDisplayBuf); - - return -1; - } - WaitForSingleObject(pi.hProcess, INFINITE); - - DWORD exitcode; - GetExitCodeProcess(pi.hProcess, &exitcode); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - if (!doreturn) { - x_exit(exitcode); - } - return exitcode; + PROCESS_INFORMATION pi; + memset(&pi, 0x00, sizeof(pi)); + + STARTUPINFO si; + memset(&si, 0x00, sizeof(si)); + + char* sh = win32getshell(path); + if (sh) { + path = sh; + } + + si.cb = sizeof(STARTUPINFO); + if (fd_stdout != -1) { + si.hStdOutput = (HANDLE)_get_osfhandle(fd_stdout); + si.hStdError = (HANDLE)_get_osfhandle(fd_stderr); + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; + if (si.hStdOutput == INVALID_HANDLE_VALUE + || si.hStdError == INVALID_HANDLE_VALUE) { + return -1; + } + } else { + // Redirect subprocess stdout, stderr into current process. + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; + if (si.hStdOutput == INVALID_HANDLE_VALUE + || si.hStdError == INVALID_HANDLE_VALUE) { + return -1; + } + } + + int length; + char* args = win32argvtos(sh, argv, &length); + const char* ext = strrchr(path, '.'); + char full_path_win_ext[MAX_PATH] = {0}; + add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path); + BOOL ret = FALSE; + if (length > 8192) { + char* tmp_file = format("%s.tmp", path); + FILE* fp = create_tmp_file(&tmp_file, "w"); + char atfile[MAX_PATH + 3]; + fwrite(args, 1, length, fp); + if (ferror(fp)) { + cc_log("Error writing @file; this command will probably fail: %s", args); + } + fclose(fp); + snprintf(atfile, sizeof(atfile), "\"@%s\"", tmp_file); + ret = CreateProcess(NULL, atfile, NULL, NULL, 1, 0, NULL, NULL, &si, &pi); + tmp_unlink(tmp_file); + free(tmp_file); + } + if (!ret) { + ret = CreateProcess( + full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi); + } + if (fd_stdout != -1) { + close(fd_stdout); + close(fd_stderr); + } + free(args); + if (ret == 0) { + LPVOID lpMsgBuf; + DWORD dw = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, + NULL); + + LPVOID lpDisplayBuf = (LPVOID)LocalAlloc( + LMEM_ZEROINIT, + (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)__FILE__) + 200) + * sizeof(TCHAR)); + _snprintf((LPTSTR)lpDisplayBuf, + LocalSize(lpDisplayBuf) / sizeof(TCHAR), + TEXT("%s failed with error %lu: %s"), + __FILE__, + dw, + (const char*)lpMsgBuf); + + cc_log("can't execute %s; OS returned error: %s", + full_path_win_ext, + (char*)lpDisplayBuf); + + LocalFree(lpMsgBuf); + LocalFree(lpDisplayBuf); + + return -1; + } + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exitcode; + GetExitCodeProcess(pi.hProcess, &exitcode); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + if (!doreturn) { + x_exit(exitcode); + } + return exitcode; } #else @@ -243,156 +248,153 @@ win32execute(char *path, char **argv, int doreturn, // Execute a compiler backend, capturing all output to the given paths the full // path to the compiler to run is in argv[0]. int -execute(char **argv, int fd_out, int fd_err, pid_t *pid) +execute(char** argv, int fd_out, int fd_err, pid_t* pid) { - cc_log_argv("Executing ", argv); - - block_signals(); - *pid = fork(); - unblock_signals(); - - if (*pid == -1) { - fatal("Failed to fork: %s", strerror(errno)); - } - - if (*pid == 0) { - // Child. - dup2(fd_out, 1); - close(fd_out); - dup2(fd_err, 2); - close(fd_err); - x_exit(execv(argv[0], argv)); - } - - close(fd_out); - close(fd_err); - - int status; - if (waitpid(*pid, &status, 0) != *pid) { - fatal("waitpid failed: %s", strerror(errno)); - } - - block_signals(); - *pid = 0; - unblock_signals(); - - if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) { - return -1; - } - - return WEXITSTATUS(status); + cc_log_argv("Executing ", argv); + + block_signals(); + *pid = fork(); + unblock_signals(); + + if (*pid == -1) { + fatal("Failed to fork: %s", strerror(errno)); + } + + if (*pid == 0) { + // Child. + dup2(fd_out, 1); + close(fd_out); + dup2(fd_err, 2); + close(fd_err); + x_exit(execv(argv[0], argv)); + } + + close(fd_out); + close(fd_err); + + int status; + if (waitpid(*pid, &status, 0) != *pid) { + fatal("waitpid failed: %s", strerror(errno)); + } + + block_signals(); + *pid = 0; + unblock_signals(); + + if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) { + return -1; + } + + return WEXITSTATUS(status); } #endif // Find an executable by name in $PATH. Exclude any that are links to // exclude_name. -char * -find_executable(const char *name, const char *exclude_name) +char* +find_executable(const char* name, const char* exclude_name) { - if (is_absolute_path(name)) { - return x_strdup(name); - } - - char *path = conf->path; - if (str_eq(path, "")) { - path = getenv("PATH"); - } - if (!path) { - cc_log("No PATH variable"); - return NULL; - } - - return find_executable_in_path(name, exclude_name, path); + if (is_absolute_path(name)) { + return x_strdup(name); + } + + char* path = conf->path; + if (str_eq(path, "")) { + path = getenv("PATH"); + } + if (!path) { + cc_log("No PATH variable"); + return NULL; + } + + return find_executable_in_path(name, exclude_name, path); } -static char * -find_executable_in_path(const char *name, const char *exclude_name, char *path) +static char* +find_executable_in_path(const char* name, const char* exclude_name, char* path) { - path = x_strdup(path); - - // Search the path looking for the first compiler of the right name that - // isn't us. - char *saveptr = NULL; - for (char *tok = strtok_r(path, PATH_DELIM, &saveptr); - tok; - tok = strtok_r(NULL, PATH_DELIM, &saveptr)) { + path = x_strdup(path); + + // Search the path looking for the first compiler of the right name that + // isn't us. + char* saveptr = NULL; + for (char* tok = strtok_r(path, PATH_DELIM, &saveptr); tok; + tok = strtok_r(NULL, PATH_DELIM, &saveptr)) { #ifdef _WIN32 - char namebuf[MAX_PATH]; - int ret = SearchPath(tok, name, NULL, sizeof(namebuf), namebuf, NULL); - if (!ret) { - char *exename = format("%s.exe", name); - ret = SearchPath(tok, exename, NULL, sizeof(namebuf), namebuf, NULL); - free(exename); - } - (void) exclude_name; - if (ret) { - free(path); - return x_strdup(namebuf); - } + char namebuf[MAX_PATH]; + int ret = SearchPath(tok, name, NULL, sizeof(namebuf), namebuf, NULL); + if (!ret) { + char* exename = format("%s.exe", name); + ret = SearchPath(tok, exename, NULL, sizeof(namebuf), namebuf, NULL); + free(exename); + } + (void)exclude_name; + if (ret) { + free(path); + return x_strdup(namebuf); + } #else - struct stat st1, st2; - char *fname = format("%s/%s", tok, name); - // Look for a normal executable file. - if (access(fname, X_OK) == 0 && - lstat(fname, &st1) == 0 && - stat(fname, &st2) == 0 && - S_ISREG(st2.st_mode)) { - if (S_ISLNK(st1.st_mode)) { - char *buf = x_realpath(fname); - if (buf) { - char *p = x_basename(buf); - if (str_eq(p, exclude_name)) { - // It's a link to "ccache"! - free(p); - free(buf); - continue; - } - free(buf); - free(p); - } - } - - // Found it! - free(path); - return fname; - } - free(fname); + struct stat st1, st2; + char* fname = format("%s/%s", tok, name); + // Look for a normal executable file. + if (access(fname, X_OK) == 0 && lstat(fname, &st1) == 0 + && stat(fname, &st2) == 0 && S_ISREG(st2.st_mode)) { + if (S_ISLNK(st1.st_mode)) { + char* buf = x_realpath(fname); + if (buf) { + char* p = x_basename(buf); + if (str_eq(p, exclude_name)) { + // It's a link to "ccache"! + free(p); + free(buf); + continue; + } + free(buf); + free(p); + } + } + + // Found it! + free(path); + return fname; + } + free(fname); #endif - } + } - free(path); - return NULL; + free(path); + return NULL; } void -print_command(FILE *fp, char **argv) +print_command(FILE* fp, char** argv) { - for (int i = 0; argv[i]; i++) { - fprintf(fp, "%s%s", (i == 0) ? "" : " ", argv[i]); - } - fprintf(fp, "\n"); + for (int i = 0; argv[i]; i++) { + fprintf(fp, "%s%s", (i == 0) ? "" : " ", argv[i]); + } + fprintf(fp, "\n"); } -char * +char* format_command(const char* const* argv) { - size_t len = 0; - for (int i = 0; argv[i]; i++) { - len += (i == 0) ? 0 : 1; - len += strlen(argv[i]); - } - len += 1; - char *buf = static_cast<char*>(x_malloc(len + 1)); - char *p = buf; - for (int i = 0; argv[i]; i++) { - if (i != 0) { - *p++ = ' '; - } - for (const char *q = argv[i]; *q != '\0'; q++) { - *p++ = *q; - } - } - *p++ = '\n'; - *p++ = '\0'; - return buf; + size_t len = 0; + for (int i = 0; argv[i]; i++) { + len += (i == 0) ? 0 : 1; + len += strlen(argv[i]); + } + len += 1; + char* buf = static_cast<char*>(x_malloc(len + 1)); + char* p = buf; + for (int i = 0; argv[i]; i++) { + if (i != 0) { + *p++ = ' '; + } + for (const char* q = argv[i]; *q != '\0'; q++) { + *p++ = *q; + } + } + *p++ = '\n'; + *p++ = '\0'; + return buf; } diff --git a/src/exitfn.cpp b/src/exitfn.cpp index bc0562f7..32dfbdfa 100644 --- a/src/exitfn.cpp +++ b/src/exitfn.cpp @@ -18,33 +18,35 @@ #include "ccache.hpp" -struct exit_function { - void (*function)(void *); - void *context; - struct exit_function *next; +struct exit_function +{ + void (*function)(void*); + void* context; + struct exit_function* next; }; -struct nullary_exit_function { - void (*function)(void); +struct nullary_exit_function +{ + void (*function)(void); }; -static struct exit_function *exit_functions; +static struct exit_function* exit_functions; static void -call_nullary_exit_function(void *context) +call_nullary_exit_function(void* context) { - struct nullary_exit_function *p = (struct nullary_exit_function *)context; - p->function(); - free(p); + struct nullary_exit_function* p = (struct nullary_exit_function*)context; + p->function(); + free(p); } // Initialize exit functions. Must be called once before exitfn_add* are used. void exitfn_init(void) { - if (atexit(exitfn_call) != 0) { - fatal("atexit failed: %s", strerror(errno)); - } + if (atexit(exitfn_call) != 0) { + fatal("atexit failed: %s", strerror(errno)); + } } // Add a nullary function to be called when ccache exits. Functions are called @@ -52,51 +54,51 @@ exitfn_init(void) void exitfn_add_nullary(void (*function)(void)) { - auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function))); - p->function = reinterpret_cast<void (*)(void*)>(function); - exitfn_add(call_nullary_exit_function, p); + auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function))); + p->function = reinterpret_cast<void (*)(void*)>(function); + exitfn_add(call_nullary_exit_function, p); } // Add a function to be called with a context parameter when ccache exits. // Functions are called in LIFO order except when added via exitfn_add_last. void -exitfn_add(void (*function)(void *), void *context) +exitfn_add(void (*function)(void*), void* context) { - auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function))); - p->function = function; - p->context = context; - p->next = exit_functions; - exit_functions = p; + auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function))); + p->function = function; + p->context = context; + p->next = exit_functions; + exit_functions = p; } // Add a function to be called with a context parameter when ccache exits. In // contrast to exitfn_add, exitfn_add_last sets up the function to be called // last. void -exitfn_add_last(void (*function)(void *), void *context) +exitfn_add_last(void (*function)(void*), void* context) { - auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function))); - p->function = function; - p->context = context; - p->next = NULL; + auto p = static_cast<exit_function*>(x_malloc(sizeof(exit_function))); + p->function = function; + p->context = context; + p->next = NULL; - struct exit_function **q = &exit_functions; - while (*q) { - q = &(*q)->next; - } - *q = p; + struct exit_function** q = &exit_functions; + while (*q) { + q = &(*q)->next; + } + *q = p; } // Call added functions. void exitfn_call(void) { - struct exit_function *p = exit_functions; - exit_functions = NULL; - while (p) { - p->function(p->context); - struct exit_function *q = p; - p = p->next; - free(q); - } + struct exit_function* p = exit_functions; + exit_functions = NULL; + while (p) { + p->function(p->context); + struct exit_function* q = p; + p = p->next; + free(q); + } } diff --git a/src/hash.cpp b/src/hash.cpp index b3d6d468..83dc4703 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -17,172 +17,178 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "ccache.hpp" #include "hash.hpp" + +#include "ccache.hpp" + #include <blake2.h> #define HASH_DELIMITER "\000cCaChE" -struct hash { - blake2b_state state; - FILE *debug_binary; - FILE *debug_text; +struct hash +{ + blake2b_state state; + FILE* debug_binary; + FILE* debug_text; }; void -digest_as_string(const struct digest *d, char *buffer) +digest_as_string(const struct digest* d, char* buffer) { - format_hex(d->bytes, DIGEST_SIZE, buffer); + format_hex(d->bytes, DIGEST_SIZE, buffer); } bool -digests_equal(const struct digest *d1, const struct digest *d2) +digests_equal(const struct digest* d1, const struct digest* d2) { - return memcmp(d1->bytes, d2->bytes, DIGEST_SIZE) == 0; + return memcmp(d1->bytes, d2->bytes, DIGEST_SIZE) == 0; } static void -do_hash_buffer(struct hash *hash, const void *s, size_t len) +do_hash_buffer(struct hash* hash, const void* s, size_t len) { - assert(s); + assert(s); - blake2b_update(&hash->state, (const uint8_t *)s, len); - if (len > 0 && hash->debug_binary) { - (void) fwrite(s, 1, len, hash->debug_binary); - } + blake2b_update(&hash->state, (const uint8_t*)s, len); + if (len > 0 && hash->debug_binary) { + (void)fwrite(s, 1, len, hash->debug_binary); + } } static void -do_debug_text(struct hash *hash, const void *s, size_t len) +do_debug_text(struct hash* hash, const void* s, size_t len) { - if (len > 0 && hash->debug_text) { - (void) fwrite(s, 1, len, hash->debug_text); - } + if (len > 0 && hash->debug_text) { + (void)fwrite(s, 1, len, hash->debug_text); + } } -struct hash * +struct hash* hash_init(void) { - auto hash = static_cast<struct hash*>(malloc(sizeof(struct hash))); - blake2b_init(&hash->state, DIGEST_SIZE); - hash->debug_binary = NULL; - hash->debug_text = NULL; - return hash; + auto hash = static_cast<struct hash*>(malloc(sizeof(struct hash))); + blake2b_init(&hash->state, DIGEST_SIZE); + hash->debug_binary = NULL; + hash->debug_text = NULL; + return hash; } -struct hash * -hash_copy(struct hash *hash) +struct hash* +hash_copy(struct hash* hash) { - auto result = static_cast<struct hash*>(malloc(sizeof(struct hash))); - result->state = hash->state; - result->debug_binary = NULL; - result->debug_text = NULL; - return result; + auto result = static_cast<struct hash*>(malloc(sizeof(struct hash))); + result->state = hash->state; + result->debug_binary = NULL; + result->debug_text = NULL; + return result; } -void hash_free(struct hash *hash) +void +hash_free(struct hash* hash) { - free(hash); + free(hash); } -void hash_enable_debug( - struct hash *hash, const char *section_name, - FILE *debug_binary, FILE *debug_text) +void +hash_enable_debug(struct hash* hash, + const char* section_name, + FILE* debug_binary, + FILE* debug_text) { - hash->debug_binary = debug_binary; - hash->debug_text = debug_text; + hash->debug_binary = debug_binary; + hash->debug_text = debug_text; - do_debug_text(hash, "=== ", 4); - do_debug_text(hash, section_name, strlen(section_name)); - do_debug_text(hash, " ===\n", 5); + do_debug_text(hash, "=== ", 4); + do_debug_text(hash, section_name, strlen(section_name)); + do_debug_text(hash, " ===\n", 5); } void -hash_buffer(struct hash *hash, const void *s, size_t len) +hash_buffer(struct hash* hash, const void* s, size_t len) { - do_hash_buffer(hash, s, len); - do_debug_text(hash, s, len); + do_hash_buffer(hash, s, len); + do_debug_text(hash, s, len); } void -hash_result_as_bytes(struct hash *hash, struct digest *digest) +hash_result_as_bytes(struct hash* hash, struct digest* digest) { - // make a copy before altering state - struct hash *copy = hash_copy(hash); - blake2b_final(©->state, digest->bytes, DIGEST_SIZE); - hash_free(copy); + // make a copy before altering state + struct hash* copy = hash_copy(hash); + blake2b_final(©->state, digest->bytes, DIGEST_SIZE); + hash_free(copy); } void -hash_result_as_string(struct hash *hash, char *buffer) +hash_result_as_string(struct hash* hash, char* buffer) { - struct digest d; - hash_result_as_bytes(hash, &d); - digest_as_string(&d, buffer); + struct digest d; + hash_result_as_bytes(hash, &d); + digest_as_string(&d, buffer); } void -hash_delimiter(struct hash *hash, const char *type) +hash_delimiter(struct hash* hash, const char* type) { - do_hash_buffer(hash, HASH_DELIMITER, sizeof(HASH_DELIMITER)); - do_hash_buffer(hash, type, strlen(type) + 1); // Include NUL. - do_debug_text(hash, "### ", 4); - do_debug_text(hash, type, strlen(type)); - do_debug_text(hash, "\n", 1); + do_hash_buffer(hash, HASH_DELIMITER, sizeof(HASH_DELIMITER)); + do_hash_buffer(hash, type, strlen(type) + 1); // Include NUL. + do_debug_text(hash, "### ", 4); + do_debug_text(hash, type, strlen(type)); + do_debug_text(hash, "\n", 1); } void -hash_string(struct hash *hash, const char *s) +hash_string(struct hash* hash, const char* s) { - hash_string_buffer(hash, s, strlen(s)); + hash_string_buffer(hash, s, strlen(s)); } void -hash_string_buffer(struct hash *hash, const char *s, int length) +hash_string_buffer(struct hash* hash, const char* s, int length) { - hash_buffer(hash, s, length); - do_debug_text(hash, "\n", 1); + hash_buffer(hash, s, length); + do_debug_text(hash, "\n", 1); } void -hash_int(struct hash *hash, int x) +hash_int(struct hash* hash, int x) { - do_hash_buffer(hash, (char *)&x, sizeof(x)); + do_hash_buffer(hash, (char*)&x, sizeof(x)); - char buf[16]; - snprintf(buf, sizeof(buf), "%d", x); - do_debug_text(hash, buf, strlen(buf)); - do_debug_text(hash, "\n", 1); + char buf[16]; + snprintf(buf, sizeof(buf), "%d", x); + do_debug_text(hash, buf, strlen(buf)); + do_debug_text(hash, "\n", 1); } bool -hash_fd(struct hash *hash, int fd) +hash_fd(struct hash* hash, int fd) { - char buf[READ_BUFFER_SIZE]; - ssize_t n; + char buf[READ_BUFFER_SIZE]; + ssize_t n; - while ((n = read(fd, buf, sizeof(buf))) != 0) { - if (n == -1 && errno != EINTR) { - break; - } - if (n > 0) { - do_hash_buffer(hash, buf, n); - do_debug_text(hash, buf, n); - } - } - return n == 0; + while ((n = read(fd, buf, sizeof(buf))) != 0) { + if (n == -1 && errno != EINTR) { + break; + } + if (n > 0) { + do_hash_buffer(hash, buf, n); + do_debug_text(hash, buf, n); + } + } + return n == 0; } bool -hash_file(struct hash *hash, const char *fname) +hash_file(struct hash* hash, const char* fname) { - int fd = open(fname, O_RDONLY|O_BINARY); - if (fd == -1) { - cc_log("Failed to open %s: %s", fname, strerror(errno)); - return false; - } + int fd = open(fname, O_RDONLY | O_BINARY); + if (fd == -1) { + cc_log("Failed to open %s: %s", fname, strerror(errno)); + return false; + } - bool ret = hash_fd(hash, fd); - close(fd); - return ret; + bool ret = hash_fd(hash, fd); + close(fd); + return ret; } diff --git a/src/hash.hpp b/src/hash.hpp index eb341b2f..43b3a29a 100644 --- a/src/hash.hpp +++ b/src/hash.hpp @@ -27,39 +27,40 @@ // checksum) produced by the hash algorithm. struct digest { - uint8_t bytes[DIGEST_SIZE]; + uint8_t bytes[DIGEST_SIZE]; }; // Format the digest as a NUL-terminated hex string. The string buffer must // contain at least DIGEST_STRING_BUFFER_SIZE bytes. -void digest_as_string(const struct digest *d, char *buffer); +void digest_as_string(const struct digest* d, char* buffer); // Return true if d1 and d2 are equal, else false. -bool digests_equal(const struct digest *d1, const struct digest *d2); +bool digests_equal(const struct digest* d1, const struct digest* d2); // struct hash represents the hash algorithm's inner state. struct hash; // Create a new hash state. -struct hash *hash_init(void); +struct hash* hash_init(void); // Create a new hash state from an existing hash state. -struct hash *hash_copy(struct hash *hash); +struct hash* hash_copy(struct hash* hash); // Free a hash state created by hash_init or hash_copy. -void hash_free(struct hash *hash); +void hash_free(struct hash* hash); // Enable debug logging of hashed input to a binary and a text file. -void hash_enable_debug( - struct hash *hash, const char *section_name, FILE *debug_binary, - FILE *debug_text); +void hash_enable_debug(struct hash* hash, + const char* section_name, + FILE* debug_binary, + FILE* debug_text); // Retrieve the digest as bytes. -void hash_result_as_bytes(struct hash *hash, struct digest *digest); +void hash_result_as_bytes(struct hash* hash, struct digest* digest); // Retrieve the digest as a NUL-terminated hex string. The string buffer must // contain at least DIGEST_STRING_BUFFER_SIZE bytes. -void hash_result_as_string(struct hash *hash, char *buffer); +void hash_result_as_string(struct hash* hash, char* buffer); // Hash some data that is unlikely to occur in the input. The idea is twofold: // @@ -69,31 +70,31 @@ void hash_result_as_string(struct hash *hash, char *buffer); // conditional hashing of information in a safe way (e.g., if we want to hash // information X if CCACHE_A is set and information Y if CCACHE_B is set, // there should never be a hash collision risk). -void hash_delimiter(struct hash *hash, const char *type); +void hash_delimiter(struct hash* hash, const char* type); // Hash bytes in a buffer. // // If hash debugging is enabled, the bytes are written verbatim to the text // input file. -void hash_buffer(struct hash *hash, const void *s, size_t len); +void hash_buffer(struct hash* hash, const void* s, size_t len); // Hash a string. // // If hash debugging is enabled, the string is written to the text input file // followed by a newline. -void hash_string(struct hash *hash, const char *s); +void hash_string(struct hash* hash, const char* s); // Hash a string with a known size. // // If hash debugging is enabled, the string is written to the text input file // followed by a newline. -void hash_string_buffer(struct hash *hash, const char *s, int length); +void hash_string_buffer(struct hash* hash, const char* s, int length); // Hash an integer. // // If hash debugging is enabled, the integer is written in text form to the // text input file followed by a newline. -void hash_int(struct hash *hash, int x); +void hash_int(struct hash* hash, int x); // Add contents of an open file to the hash. // @@ -101,7 +102,7 @@ void hash_int(struct hash *hash, int x); // file. // // Returns true on success, otherwise false. -bool hash_fd(struct hash *hash, int fd); +bool hash_fd(struct hash* hash, int fd); // Add contents of a file to the hash. // @@ -109,4 +110,4 @@ bool hash_fd(struct hash *hash, int fd); // file. // // Returns true on success, otherwise false. -bool hash_file(struct hash *hash, const char *fname); +bool hash_file(struct hash* hash, const char* fname); diff --git a/src/hashutil.cpp b/src/hashutil.cpp index c9848a57..99888296 100644 --- a/src/hashutil.cpp +++ b/src/hashutil.cpp @@ -16,27 +16,29 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "ccache.hpp" #include "hashutil.hpp" + +#include "ccache.hpp" #include "macroskip.hpp" + #include "third_party/xxhash.h" unsigned -hash_from_string(void *str) +hash_from_string(void* str) { - return XXH64(str, strlen((const char *)str), 0); + return XXH64(str, strlen((const char*)str), 0); } unsigned hash_from_int(int i) { - return XXH64(&i, sizeof(int), 0); + return XXH64(&i, sizeof(int), 0); } int -strings_equal(void *str1, void *str2) +strings_equal(void* str1, void* str2) { - return str_eq((const char *)str1, (const char *)str2); + return str_eq((const char*)str1, (const char*)str2); } // Search for the strings "__DATE__" and "__TIME__" in str. @@ -44,265 +46,266 @@ strings_equal(void *str1, void *str2) // Returns a bitmask with HASH_SOURCE_CODE_FOUND_DATE and // HASH_SOURCE_CODE_FOUND_TIME set appropriately. int -check_for_temporal_macros(const char *str, size_t len) +check_for_temporal_macros(const char* str, size_t len) { - int result = 0; + int result = 0; - // We're using the Boyer-Moore-Horspool algorithm, which searches starting - // from the *end* of the needle. Our needles are 8 characters long, so i - // starts at 7. - size_t i = 7; + // We're using the Boyer-Moore-Horspool algorithm, which searches starting + // from the *end* of the needle. Our needles are 8 characters long, so i + // starts at 7. + size_t i = 7; - while (i < len) { - // Check whether the substring ending at str[i] has the form "__...E__". On - // the assumption that 'E' is less common in source than '_', we check - // str[i-2] first. - if (str[i - 2] == 'E' - && str[i - 0] == '_' - && str[i - 7] == '_' - && str[i - 1] == '_' - && str[i - 6] == '_' - && (i < 8 || (str[i - 8] != '_' && !isalnum(str[i - 8]))) - && (i + 1 >= len || (str[i + 1] != '_' && !isalnum(str[i + 1])))) { - // Check the remaining characters to see if the substring is "__DATE__" - // or "__TIME__". - if (str[i - 5] == 'D' && str[i - 4] == 'A' && str[i - 3] == 'T') { - result |= HASH_SOURCE_CODE_FOUND_DATE; - } else if (str[i - 5] == 'T' && str[i - 4] == 'I' && str[i - 3] == 'M') { - result |= HASH_SOURCE_CODE_FOUND_TIME; - } - } + while (i < len) { + // Check whether the substring ending at str[i] has the form "__...E__". On + // the assumption that 'E' is less common in source than '_', we check + // str[i-2] first. + if (str[i - 2] == 'E' && str[i - 0] == '_' && str[i - 7] == '_' + && str[i - 1] == '_' && str[i - 6] == '_' + && (i < 8 || (str[i - 8] != '_' && !isalnum(str[i - 8]))) + && (i + 1 >= len || (str[i + 1] != '_' && !isalnum(str[i + 1])))) { + // Check the remaining characters to see if the substring is "__DATE__" + // or "__TIME__". + if (str[i - 5] == 'D' && str[i - 4] == 'A' && str[i - 3] == 'T') { + result |= HASH_SOURCE_CODE_FOUND_DATE; + } else if (str[i - 5] == 'T' && str[i - 4] == 'I' && str[i - 3] == 'M') { + result |= HASH_SOURCE_CODE_FOUND_TIME; + } + } - // macro_skip tells us how far we can skip forward upon seeing str[i] at - // the end of a substring. - i += macro_skip[(uint8_t)str[i]]; - } + // macro_skip tells us how far we can skip forward upon seeing str[i] at + // the end of a substring. + i += macro_skip[(uint8_t)str[i]]; + } - return result; + return result; } // Hash a string. Returns a bitmask of HASH_SOURCE_CODE_* results. int -hash_source_code_string( - struct conf *conf, struct hash *hash, const char *str, size_t len, - const char *path) +hash_source_code_string(struct conf* conf, + struct hash* hash, + const char* str, + size_t len, + const char* path) { - int result = HASH_SOURCE_CODE_OK; + int result = HASH_SOURCE_CODE_OK; - // Check for __DATE__ and __TIME__ if the sloppiness configuration tells us - // we should. - if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) { - result |= check_for_temporal_macros(str, len); - } + // Check for __DATE__ and __TIME__ if the sloppiness configuration tells us + // we should. + if (!(conf->sloppiness & SLOPPY_TIME_MACROS)) { + result |= check_for_temporal_macros(str, len); + } - // Hash the source string. - hash_string_buffer(hash, str, len); + // Hash the source string. + hash_string_buffer(hash, str, len); - if (result & HASH_SOURCE_CODE_FOUND_DATE) { - // Make sure that the hash sum changes if the (potential) expansion of - // __DATE__ changes. - time_t t = time(NULL); - struct tm now; - localtime_r(&t, &now); - cc_log("Found __DATE__ in %s", path); - hash_delimiter(hash, "date"); - hash_int(hash, now.tm_year); - hash_int(hash, now.tm_mon); - hash_int(hash, now.tm_mday); - } - if (result & HASH_SOURCE_CODE_FOUND_TIME) { - // We don't know for sure that the program actually uses the __TIME__ - // macro, but we have to assume it anyway and hash the time stamp. However, - // that's not very useful since the chance that we get a cache hit later - // the same second should be quite slim... So, just signal back to the - // caller that __TIME__ has been found so that the direct mode can be - // disabled. - cc_log("Found __TIME__ in %s", path); - } + if (result & HASH_SOURCE_CODE_FOUND_DATE) { + // Make sure that the hash sum changes if the (potential) expansion of + // __DATE__ changes. + time_t t = time(NULL); + struct tm now; + localtime_r(&t, &now); + cc_log("Found __DATE__ in %s", path); + hash_delimiter(hash, "date"); + hash_int(hash, now.tm_year); + hash_int(hash, now.tm_mon); + hash_int(hash, now.tm_mday); + } + if (result & HASH_SOURCE_CODE_FOUND_TIME) { + // We don't know for sure that the program actually uses the __TIME__ + // macro, but we have to assume it anyway and hash the time stamp. However, + // that's not very useful since the chance that we get a cache hit later + // the same second should be quite slim... So, just signal back to the + // caller that __TIME__ has been found so that the direct mode can be + // disabled. + cc_log("Found __TIME__ in %s", path); + } - return result; + return result; } // Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_* // results. int -hash_source_code_file(struct conf *conf, struct hash *hash, const char *path) +hash_source_code_file(struct conf* conf, struct hash* hash, const char* path) { - if (is_precompiled_header(path)) { - if (hash_file(hash, path)) { - return HASH_SOURCE_CODE_OK; - } else { - return HASH_SOURCE_CODE_ERROR; - } - } else { - char *data; - size_t size; - if (!read_file(path, 0, &data, &size)) { - return HASH_SOURCE_CODE_ERROR; - } - int result = hash_source_code_string(conf, hash, data, size, path); - free(data); - return result; - } + if (is_precompiled_header(path)) { + if (hash_file(hash, path)) { + return HASH_SOURCE_CODE_OK; + } else { + return HASH_SOURCE_CODE_ERROR; + } + } else { + char* data; + size_t size; + if (!read_file(path, 0, &data, &size)) { + return HASH_SOURCE_CODE_ERROR; + } + int result = hash_source_code_string(conf, hash, data, size, path); + free(data); + return result; + } } bool -hash_command_output(struct hash *hash, const char *command, - const char *compiler) +hash_command_output(struct hash* hash, + const char* command, + const char* compiler) { #ifdef _WIN32 - // Trim leading space. - while (isspace(*command)) { - command++; - } + // Trim leading space. + while (isspace(*command)) { + command++; + } - // Add "echo" command. - bool cmd; - if (str_startswith(command, "echo")) { - command = format("cmd.exe /c \"%s\"", command); - cmd = true; - } else if (str_startswith(command, - "%compiler%") && str_eq(compiler, "echo")) { - command = format("cmd.exe /c \"%s%s\"", compiler, command + 10); - cmd = true; - } else { - command = x_strdup(command); - cmd = false; - } + // Add "echo" command. + bool cmd; + if (str_startswith(command, "echo")) { + command = format("cmd.exe /c \"%s\"", command); + cmd = true; + } else if (str_startswith(command, "%compiler%") + && str_eq(compiler, "echo")) { + command = format("cmd.exe /c \"%s%s\"", compiler, command + 10); + cmd = true; + } else { + command = x_strdup(command); + cmd = false; + } #endif - struct args *args = args_init_from_string(command); - for (int i = 0; i < args->argc; i++) { - if (str_eq(args->argv[i], "%compiler%")) { - args_set(args, i, compiler); - } - } - cc_log_argv("Executing compiler check command ", args->argv); + struct args* args = args_init_from_string(command); + for (int i = 0; i < args->argc; i++) { + if (str_eq(args->argv[i], "%compiler%")) { + args_set(args, i, compiler); + } + } + cc_log_argv("Executing compiler check command ", args->argv); #ifdef _WIN32 - PROCESS_INFORMATION pi; - memset(&pi, 0x00, sizeof(pi)); - STARTUPINFO si; - memset(&si, 0x00, sizeof(si)); + PROCESS_INFORMATION pi; + memset(&pi, 0x00, sizeof(pi)); + STARTUPINFO si; + memset(&si, 0x00, sizeof(si)); - char *path = find_executable(args->argv[0], NULL); - if (!path) { - path = args->argv[0]; - } - char *sh = win32getshell(path); - if (sh) { - path = sh; - } + char* path = find_executable(args->argv[0], NULL); + if (!path) { + path = args->argv[0]; + } + char* sh = win32getshell(path); + if (sh) { + path = sh; + } - si.cb = sizeof(STARTUPINFO); + si.cb = sizeof(STARTUPINFO); - HANDLE pipe_out[2]; - SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; - CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0); - SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0); - si.hStdOutput = pipe_out[1]; - si.hStdError = pipe_out[1]; - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; + HANDLE pipe_out[2]; + SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE}; + CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0); + SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0); + si.hStdOutput = pipe_out[1]; + si.hStdError = pipe_out[1]; + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; - char *win32args; - if (!cmd) { - int length; - win32args = win32argvtos(sh, args->argv, &length); - } else { - win32args = (char *)command; // quoted - } - BOOL ret = - CreateProcess(path, win32args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi); - CloseHandle(pipe_out[1]); - args_free(args); - free(win32args); - if (!cmd) { - free((char *)command); // Original argument was replaced above. - } - if (ret == 0) { - stats_update(STATS_COMPCHECK); - return false; - } - int fd = _open_osfhandle((intptr_t) pipe_out[0], O_BINARY); - bool ok = hash_fd(hash, fd); - if (!ok) { - cc_log("Error hashing compiler check command output: %s", strerror(errno)); - stats_update(STATS_COMPCHECK); - } - WaitForSingleObject(pi.hProcess, INFINITE); - DWORD exitcode; - GetExitCodeProcess(pi.hProcess, &exitcode); - CloseHandle(pipe_out[0]); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - if (exitcode != 0) { - cc_log("Compiler check command returned %d", (int) exitcode); - stats_update(STATS_COMPCHECK); - return false; - } - return ok; + char* win32args; + if (!cmd) { + int length; + win32args = win32argvtos(sh, args->argv, &length); + } else { + win32args = (char*)command; // quoted + } + BOOL ret = + CreateProcess(path, win32args, NULL, NULL, 1, 0, NULL, NULL, &si, &pi); + CloseHandle(pipe_out[1]); + args_free(args); + free(win32args); + if (!cmd) { + free((char*)command); // Original argument was replaced above. + } + if (ret == 0) { + stats_update(STATS_COMPCHECK); + return false; + } + int fd = _open_osfhandle((intptr_t)pipe_out[0], O_BINARY); + bool ok = hash_fd(hash, fd); + if (!ok) { + cc_log("Error hashing compiler check command output: %s", strerror(errno)); + stats_update(STATS_COMPCHECK); + } + WaitForSingleObject(pi.hProcess, INFINITE); + DWORD exitcode; + GetExitCodeProcess(pi.hProcess, &exitcode); + CloseHandle(pipe_out[0]); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + if (exitcode != 0) { + cc_log("Compiler check command returned %d", (int)exitcode); + stats_update(STATS_COMPCHECK); + return false; + } + return ok; #else - int pipefd[2]; - if (pipe(pipefd) == -1) { - fatal("pipe failed"); - } + int pipefd[2]; + if (pipe(pipefd) == -1) { + fatal("pipe failed"); + } - pid_t pid = fork(); - if (pid == -1) { - fatal("fork failed"); - } + pid_t pid = fork(); + if (pid == -1) { + fatal("fork failed"); + } - if (pid == 0) { - // Child. - close(pipefd[0]); - close(0); - dup2(pipefd[1], 1); - dup2(pipefd[1], 2); - _exit(execvp(args->argv[0], args->argv)); - // Never reached. - } else { - // Parent. - args_free(args); - close(pipefd[1]); - bool ok = hash_fd(hash, pipefd[0]); - if (!ok) { - cc_log("Error hashing compiler check command output: %s", - strerror(errno)); - stats_update(STATS_COMPCHECK); - } - close(pipefd[0]); + if (pid == 0) { + // Child. + close(pipefd[0]); + close(0); + dup2(pipefd[1], 1); + dup2(pipefd[1], 2); + _exit(execvp(args->argv[0], args->argv)); + // Never reached. + } else { + // Parent. + args_free(args); + close(pipefd[1]); + bool ok = hash_fd(hash, pipefd[0]); + if (!ok) { + cc_log("Error hashing compiler check command output: %s", + strerror(errno)); + stats_update(STATS_COMPCHECK); + } + close(pipefd[0]); - int status; - if (waitpid(pid, &status, 0) != pid) { - cc_log("waitpid failed"); - return false; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - cc_log("Compiler check command returned %d", WEXITSTATUS(status)); - stats_update(STATS_COMPCHECK); - return false; - } - return ok; - } + int status; + if (waitpid(pid, &status, 0) != pid) { + cc_log("waitpid failed"); + return false; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + cc_log("Compiler check command returned %d", WEXITSTATUS(status)); + stats_update(STATS_COMPCHECK); + return false; + } + return ok; + } #endif } bool -hash_multicommand_output(struct hash *hash, const char *commands, - const char *compiler) +hash_multicommand_output(struct hash* hash, + const char* commands, + const char* compiler) { - char *command_string = x_strdup(commands); - char *p = command_string; - char *command; - char *saveptr = NULL; - bool ok = true; - while ((command = strtok_r(p, ";", &saveptr))) { - if (!hash_command_output(hash, command, compiler)) { - ok = false; - } - p = NULL; - } - free(command_string); - return ok; + char* command_string = x_strdup(commands); + char* p = command_string; + char* command; + char* saveptr = NULL; + bool ok = true; + while ((command = strtok_r(p, ";", &saveptr))) { + if (!hash_command_output(hash, command, compiler)) { + ok = false; + } + p = NULL; + } + free(command_string); + return ok; } diff --git a/src/hashutil.hpp b/src/hashutil.hpp index 3a270ac4..09112d10 100644 --- a/src/hashutil.hpp +++ b/src/hashutil.hpp @@ -20,24 +20,29 @@ #include "conf.hpp" #include "hash.hpp" + #include <inttypes.h> -unsigned hash_from_string(void *str); +unsigned hash_from_string(void* str); unsigned hash_from_int(int i); -int strings_equal(void *str1, void *str2); +int strings_equal(void* str1, void* str2); #define HASH_SOURCE_CODE_OK 0 #define HASH_SOURCE_CODE_ERROR 1 -#define HASH_SOURCE_CODE_FOUND_DATE 2 -#define HASH_SOURCE_CODE_FOUND_TIME 4 +#define HASH_SOURCE_CODE_FOUND_DATE 2 +#define HASH_SOURCE_CODE_FOUND_TIME 4 -int check_for_temporal_macros(const char *str, size_t len); -int hash_source_code_string( - struct conf *conf, struct hash *hash, const char *str, size_t len, - const char *path); -int hash_source_code_file( - struct conf *conf, struct hash *hash, const char *path); -bool hash_command_output(struct hash *hash, const char *command, - const char *compiler); -bool hash_multicommand_output(struct hash *hash, const char *command, - const char *compiler); +int check_for_temporal_macros(const char* str, size_t len); +int hash_source_code_string(struct conf* conf, + struct hash* hash, + const char* str, + size_t len, + const char* path); +int +hash_source_code_file(struct conf* conf, struct hash* hash, const char* path); +bool hash_command_output(struct hash* hash, + const char* command, + const char* compiler); +bool hash_multicommand_output(struct hash* hash, + const char* command, + const char* compiler); diff --git a/src/int_bytes_conversion.hpp b/src/int_bytes_conversion.hpp index 24996fde..47540dae 100644 --- a/src/int_bytes_conversion.hpp +++ b/src/int_bytes_conversion.hpp @@ -16,70 +16,59 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#define BYTES_FROM_UINT16(bytes, uint16) \ - do { \ - (bytes)[0] = (uint16) >> 8 & 0xFF; \ - (bytes)[1] = (uint16) >> 0 & 0xFF; \ - } while (false) +#define BYTES_FROM_UINT16(bytes, uint16) \ + do { \ + (bytes)[0] = (uint16) >> 8 & 0xFF; \ + (bytes)[1] = (uint16) >> 0 & 0xFF; \ + } while (false) -#define UINT16_FROM_BYTES(bytes) \ - ((uint16_t)(uint8_t)(bytes)[0] << 8 | \ - (uint16_t)(uint8_t)(bytes)[1] << 0) +#define UINT16_FROM_BYTES(bytes) \ + ((uint16_t)(uint8_t)(bytes)[0] << 8 | (uint16_t)(uint8_t)(bytes)[1] << 0) -#define BYTES_FROM_UINT32(bytes, uint32) \ - do { \ - (bytes)[0] = (uint32) >> 24 & 0xFF; \ - (bytes)[1] = (uint32) >> 16 & 0xFF; \ - (bytes)[2] = (uint32) >> 8 & 0xFF; \ - (bytes)[3] = (uint32) >> 0 & 0xFF; \ - } while (false) +#define BYTES_FROM_UINT32(bytes, uint32) \ + do { \ + (bytes)[0] = (uint32) >> 24 & 0xFF; \ + (bytes)[1] = (uint32) >> 16 & 0xFF; \ + (bytes)[2] = (uint32) >> 8 & 0xFF; \ + (bytes)[3] = (uint32) >> 0 & 0xFF; \ + } while (false) -#define UINT32_FROM_BYTES(bytes) \ - ((uint32_t)(uint8_t)(bytes)[0] << 24 | \ - (uint32_t)(uint8_t)(bytes)[1] << 16 | \ - (uint32_t)(uint8_t)(bytes)[2] << 8 | \ - (uint32_t)(uint8_t)(bytes)[3] << 0) +#define UINT32_FROM_BYTES(bytes) \ + ((uint32_t)(uint8_t)(bytes)[0] << 24 | (uint32_t)(uint8_t)(bytes)[1] << 16 \ + | (uint32_t)(uint8_t)(bytes)[2] << 8 | (uint32_t)(uint8_t)(bytes)[3] << 0) -#define BYTES_FROM_INT64(bytes, int64) \ - do { \ - (bytes)[0] = (int64) >> 56 & 0xFF; \ - (bytes)[1] = (int64) >> 48 & 0xFF; \ - (bytes)[2] = (int64) >> 40 & 0xFF; \ - (bytes)[3] = (int64) >> 32 & 0xFF; \ - (bytes)[4] = (int64) >> 24 & 0xFF; \ - (bytes)[5] = (int64) >> 16 & 0xFF; \ - (bytes)[6] = (int64) >> 8 & 0xFF; \ - (bytes)[7] = (int64) >> 0 & 0xFF; \ - } while (false) +#define BYTES_FROM_INT64(bytes, int64) \ + do { \ + (bytes)[0] = (int64) >> 56 & 0xFF; \ + (bytes)[1] = (int64) >> 48 & 0xFF; \ + (bytes)[2] = (int64) >> 40 & 0xFF; \ + (bytes)[3] = (int64) >> 32 & 0xFF; \ + (bytes)[4] = (int64) >> 24 & 0xFF; \ + (bytes)[5] = (int64) >> 16 & 0xFF; \ + (bytes)[6] = (int64) >> 8 & 0xFF; \ + (bytes)[7] = (int64) >> 0 & 0xFF; \ + } while (false) -#define INT64_FROM_BYTES(bytes) \ - ((int64_t)(uint8_t)(bytes)[0] << 56 | \ - (int64_t)(uint8_t)(bytes)[1] << 48 | \ - (int64_t)(uint8_t)(bytes)[2] << 40 | \ - (int64_t)(uint8_t)(bytes)[3] << 32 | \ - (int64_t)(uint8_t)(bytes)[4] << 24 | \ - (int64_t)(uint8_t)(bytes)[5] << 16 | \ - (int64_t)(uint8_t)(bytes)[6] << 8 | \ - (int64_t)(uint8_t)(bytes)[7] << 0) +#define INT64_FROM_BYTES(bytes) \ + ((int64_t)(uint8_t)(bytes)[0] << 56 | (int64_t)(uint8_t)(bytes)[1] << 48 \ + | (int64_t)(uint8_t)(bytes)[2] << 40 | (int64_t)(uint8_t)(bytes)[3] << 32 \ + | (int64_t)(uint8_t)(bytes)[4] << 24 | (int64_t)(uint8_t)(bytes)[5] << 16 \ + | (int64_t)(uint8_t)(bytes)[6] << 8 | (int64_t)(uint8_t)(bytes)[7] << 0) -#define BYTES_FROM_UINT64(bytes, uint64) \ - do { \ - (bytes)[0] = (uint64) >> 56 & 0xFF; \ - (bytes)[1] = (uint64) >> 48 & 0xFF; \ - (bytes)[2] = (uint64) >> 40 & 0xFF; \ - (bytes)[3] = (uint64) >> 32 & 0xFF; \ - (bytes)[4] = (uint64) >> 24 & 0xFF; \ - (bytes)[5] = (uint64) >> 16 & 0xFF; \ - (bytes)[6] = (uint64) >> 8 & 0xFF; \ - (bytes)[7] = (uint64) >> 0 & 0xFF; \ - } while (false) +#define BYTES_FROM_UINT64(bytes, uint64) \ + do { \ + (bytes)[0] = (uint64) >> 56 & 0xFF; \ + (bytes)[1] = (uint64) >> 48 & 0xFF; \ + (bytes)[2] = (uint64) >> 40 & 0xFF; \ + (bytes)[3] = (uint64) >> 32 & 0xFF; \ + (bytes)[4] = (uint64) >> 24 & 0xFF; \ + (bytes)[5] = (uint64) >> 16 & 0xFF; \ + (bytes)[6] = (uint64) >> 8 & 0xFF; \ + (bytes)[7] = (uint64) >> 0 & 0xFF; \ + } while (false) -#define UINT64_FROM_BYTES(bytes) \ - ((uint64_t)(uint8_t)(bytes)[0] << 56 | \ - (uint64_t)(uint8_t)(bytes)[1] << 48 | \ - (uint64_t)(uint8_t)(bytes)[2] << 40 | \ - (uint64_t)(uint8_t)(bytes)[3] << 32 | \ - (uint64_t)(uint8_t)(bytes)[4] << 24 | \ - (uint64_t)(uint8_t)(bytes)[5] << 16 | \ - (uint64_t)(uint8_t)(bytes)[6] << 8 | \ - (uint64_t)(uint8_t)(bytes)[7] << 0) +#define UINT64_FROM_BYTES(bytes) \ + ((uint64_t)(uint8_t)(bytes)[0] << 56 | (uint64_t)(uint8_t)(bytes)[1] << 48 \ + | (uint64_t)(uint8_t)(bytes)[2] << 40 | (uint64_t)(uint8_t)(bytes)[3] << 32 \ + | (uint64_t)(uint8_t)(bytes)[4] << 24 | (uint64_t)(uint8_t)(bytes)[5] << 16 \ + | (uint64_t)(uint8_t)(bytes)[6] << 8 | (uint64_t)(uint8_t)(bytes)[7] << 0) diff --git a/src/language.cpp b/src/language.cpp index 24b99ff0..4b32f95f 100644 --- a/src/language.cpp +++ b/src/language.cpp @@ -16,138 +16,144 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "ccache.hpp" - #include "language.hpp" +#include "ccache.hpp" + // Supported file extensions and corresponding languages (as in parameter to // the -x option). -static const struct { - const char *extension; - const char *language; +// +// clang-format off +static const struct +{ + const char* extension; + const char* language; } extensions[] = { - {".c", "c"}, - {".C", "c++"}, - {".cc", "c++"}, - {".CC", "c++"}, - {".cp", "c++"}, - {".CP", "c++"}, - {".cpp", "c++"}, - {".CPP", "c++"}, - {".cxx", "c++"}, - {".CXX", "c++"}, - {".c++", "c++"}, - {".C++", "c++"}, - {".m", "objective-c"}, - {".M", "objective-c++"}, - {".mm", "objective-c++"}, - {".sx", "assembler-with-cpp"}, - {".S", "assembler-with-cpp"}, - // Preprocessed: - {".i", "cpp-output"}, - {".ii", "c++-cpp-output"}, - {".mi", "objective-c-cpp-output"}, - {".mii", "objective-c++-cpp-output"}, - {".s", "assembler"}, - // Header file (for precompilation): - {".h", "c-header"}, - {".H", "c++-header"}, - {".h++", "c++-header"}, - {".H++", "c++-header"}, - {".hh", "c++-header"}, - {".HH", "c++-header"}, - {".hp", "c++-header"}, - {".HP", "c++-header"}, - {".hpp", "c++-header"}, - {".HPP", "c++-header"}, - {".hxx", "c++-header"}, - {".HXX", "c++-header"}, - {".tcc", "c++-header"}, - {".TCC", "c++-header"}, - {".cu", "cu"}, - {NULL, NULL} -}; + {".c", "c"}, + {".C", "c++"}, + {".cc", "c++"}, + {".CC", "c++"}, + {".cp", "c++"}, + {".CP", "c++"}, + {".cpp", "c++"}, + {".CPP", "c++"}, + {".cxx", "c++"}, + {".CXX", "c++"}, + {".c++", "c++"}, + {".C++", "c++"}, + {".m", "objective-c"}, + {".M", "objective-c++"}, + {".mm", "objective-c++"}, + {".sx", "assembler-with-cpp"}, + {".S", "assembler-with-cpp"}, + // Preprocessed: + {".i", "cpp-output"}, + {".ii", "c++-cpp-output"}, + {".mi", "objective-c-cpp-output"}, + {".mii", "objective-c++-cpp-output"}, + {".s", "assembler"}, + // Header file (for precompilation): + {".h", "c-header"}, + {".H", "c++-header"}, + {".h++", "c++-header"}, + {".H++", "c++-header"}, + {".hh", "c++-header"}, + {".HH", "c++-header"}, + {".hp", "c++-header"}, + {".HP", "c++-header"}, + {".hpp", "c++-header"}, + {".HPP", "c++-header"}, + {".hxx", "c++-header"}, + {".HXX", "c++-header"}, + {".tcc", "c++-header"}, + {".TCC", "c++-header"}, + {".cu", "cu"}, + {NULL, NULL}}; +// clang-format on // Supported languages and corresponding preprocessed languages. -static const struct { - const char *language; - const char *p_language; +// +// clang-format off +static const struct +{ + const char* language; + const char* p_language; } languages[] = { - {"c", "cpp-output"}, - {"cpp-output", "cpp-output"}, - {"c-header", "cpp-output"}, - {"c++", "c++-cpp-output"}, - {"c++-cpp-output", "c++-cpp-output"}, - {"c++-header", "c++-cpp-output"}, - {"cu", "cpp-output"}, - {"objective-c", "objective-c-cpp-output"}, - {"objective-c-header", "objective-c-cpp-output"}, - {"objc-cpp-output", "objective-c-cpp-output"}, - {"objective-c-cpp-output", "objective-c-cpp-output"}, - {"objective-c++", "objective-c++-cpp-output"}, - {"objc++-cpp-output", "objective-c++-cpp-output"}, - {"objective-c++-header", "objective-c++-cpp-output"}, - {"objective-c++-cpp-output", "objective-c++-cpp-output"}, - {"assembler-with-cpp", "assembler"}, - {"assembler", "assembler"}, - {NULL, NULL} -}; + {"c", "cpp-output"}, + {"cpp-output", "cpp-output"}, + {"c-header", "cpp-output"}, + {"c++", "c++-cpp-output"}, + {"c++-cpp-output", "c++-cpp-output"}, + {"c++-header", "c++-cpp-output"}, + {"cu", "cpp-output"}, + {"objective-c", "objective-c-cpp-output"}, + {"objective-c-header", "objective-c-cpp-output"}, + {"objc-cpp-output", "objective-c-cpp-output"}, + {"objective-c-cpp-output", "objective-c-cpp-output"}, + {"objective-c++", "objective-c++-cpp-output"}, + {"objc++-cpp-output", "objective-c++-cpp-output"}, + {"objective-c++-header", "objective-c++-cpp-output"}, + {"objective-c++-cpp-output", "objective-c++-cpp-output"}, + {"assembler-with-cpp", "assembler"}, + {"assembler", "assembler"}, + {NULL, NULL}}; +// clang-format on // Guess the language of a file based on its extension. Returns NULL if the // extension is unknown. -const char * -language_for_file(const char *fname) +const char* +language_for_file(const char* fname) { - const char *p = get_extension(fname); - for (int i = 0; extensions[i].extension; i++) { - if (str_eq(p, extensions[i].extension)) { - return extensions[i].language; - } - } - return NULL; + const char* p = get_extension(fname); + for (int i = 0; extensions[i].extension; i++) { + if (str_eq(p, extensions[i].extension)) { + return extensions[i].language; + } + } + return NULL; } // Return the preprocessed language for a given language, or NULL if unknown. -const char * -p_language_for_language(const char *language) +const char* +p_language_for_language(const char* language) { - if (!language) { - return NULL; - } - for (int i = 0; languages[i].language; ++i) { - if (str_eq(language, languages[i].language)) { - return languages[i].p_language; - } - } - return NULL; + if (!language) { + return NULL; + } + for (int i = 0; languages[i].language; ++i) { + if (str_eq(language, languages[i].language)) { + return languages[i].p_language; + } + } + return NULL; } // Return the default file extension (including dot) for a language, or NULL if // unknown. -const char * -extension_for_language(const char *language) +const char* +extension_for_language(const char* language) { - if (!language) { - return NULL; - } - for (int i = 0; extensions[i].extension; i++) { - if (str_eq(language, extensions[i].language)) { - return extensions[i].extension; - } - } - return NULL; + if (!language) { + return NULL; + } + for (int i = 0; extensions[i].extension; i++) { + if (str_eq(language, extensions[i].language)) { + return extensions[i].extension; + } + } + return NULL; } bool -language_is_supported(const char *language) +language_is_supported(const char* language) { - return p_language_for_language(language) != NULL; + return p_language_for_language(language) != NULL; } bool -language_is_preprocessed(const char *language) +language_is_preprocessed(const char* language) { - const char *p_language = p_language_for_language(language); - assert(p_language); - return str_eq(language, p_language); + const char* p_language = p_language_for_language(language); + assert(p_language); + return str_eq(language, p_language); } diff --git a/src/language.hpp b/src/language.hpp index f119ece8..481d608d 100644 --- a/src/language.hpp +++ b/src/language.hpp @@ -20,8 +20,8 @@ #include <stdbool.h> -const char *language_for_file(const char *fname); -const char *p_language_for_language(const char *language); -const char *extension_for_language(const char *language); -bool language_is_supported(const char *language); -bool language_is_preprocessed(const char *language); +const char* language_for_file(const char* fname); +const char* p_language_for_language(const char* language); +const char* extension_for_language(const char* language); +bool language_is_supported(const char* language); +bool language_is_preprocessed(const char* language); diff --git a/src/lockfile.cpp b/src/lockfile.cpp index d904b190..c717d091 100644 --- a/src/lockfile.cpp +++ b/src/lockfile.cpp @@ -27,189 +27,190 @@ // probably be made with an atomic rename(2) to avoid corruption in the rare // case that the lock is broken by another process. bool -lockfile_acquire(const char *path, unsigned staleness_limit) +lockfile_acquire(const char* path, unsigned staleness_limit) { - char *lockfile = format("%s.lock", path); - char *my_content = NULL; - char *content = NULL; - char *initial_content = NULL; - const char *hostname = get_hostname(); - bool acquired = false; - unsigned to_sleep = 1000; // Microseconds. - unsigned slept = 0; // Microseconds. + char* lockfile = format("%s.lock", path); + char* my_content = NULL; + char* content = NULL; + char* initial_content = NULL; + const char* hostname = get_hostname(); + bool acquired = false; + unsigned to_sleep = 1000; // Microseconds. + unsigned slept = 0; // Microseconds. - while (true) { - free(my_content); - my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(NULL)); + while (true) { + free(my_content); + my_content = format("%s:%d:%d", hostname, (int)getpid(), (int)time(NULL)); #if defined(_WIN32) || defined(__CYGWIN__) - int fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0666); - if (fd == -1) { - int saved_errno = errno; - cc_log("lockfile_acquire: open WRONLY %s: %s", lockfile, strerror(errno)); - if (saved_errno == ENOENT) { - // Directory doesn't exist? - if (create_parent_dirs(lockfile) == 0) { - // OK. Retry. - continue; - } - } - if (saved_errno != EEXIST) { - // Directory doesn't exist or isn't writable? - goto out; - } - // Someone else has the lock. - fd = open(lockfile, O_RDONLY|O_BINARY); - if (fd == -1) { - if (errno == ENOENT) { - // The file was removed after the open() call above, so retry - // acquiring it. - continue; - } else { - cc_log("lockfile_acquire: open RDONLY %s: %s", - lockfile, strerror(errno)); - goto out; - } - } - free(content); - const size_t bufsize = 1024; - content = static_cast<char*>(x_malloc(bufsize)); - int len = read(fd, content, bufsize - 1); - if (len == -1) { - cc_log("lockfile_acquire: read %s: %s", lockfile, strerror(errno)); - close(fd); - goto out; - } - close(fd); - content[len] = '\0'; - } else { - // We got the lock. - if (write(fd, my_content, strlen(my_content)) == -1) { - cc_log("lockfile_acquire: write %s: %s", lockfile, strerror(errno)); - close(fd); - x_unlink(lockfile); - goto out; - } - close(fd); - acquired = true; - goto out; - } + int fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0666); + if (fd == -1) { + int saved_errno = errno; + cc_log("lockfile_acquire: open WRONLY %s: %s", lockfile, strerror(errno)); + if (saved_errno == ENOENT) { + // Directory doesn't exist? + if (create_parent_dirs(lockfile) == 0) { + // OK. Retry. + continue; + } + } + if (saved_errno != EEXIST) { + // Directory doesn't exist or isn't writable? + goto out; + } + // Someone else has the lock. + fd = open(lockfile, O_RDONLY | O_BINARY); + if (fd == -1) { + if (errno == ENOENT) { + // The file was removed after the open() call above, so retry + // acquiring it. + continue; + } else { + cc_log( + "lockfile_acquire: open RDONLY %s: %s", lockfile, strerror(errno)); + goto out; + } + } + free(content); + const size_t bufsize = 1024; + content = static_cast<char*>(x_malloc(bufsize)); + int len = read(fd, content, bufsize - 1); + if (len == -1) { + cc_log("lockfile_acquire: read %s: %s", lockfile, strerror(errno)); + close(fd); + goto out; + } + close(fd); + content[len] = '\0'; + } else { + // We got the lock. + if (write(fd, my_content, strlen(my_content)) == -1) { + cc_log("lockfile_acquire: write %s: %s", lockfile, strerror(errno)); + close(fd); + x_unlink(lockfile); + goto out; + } + close(fd); + acquired = true; + goto out; + } #else - if (symlink(my_content, lockfile) == 0) { - // We got the lock. - acquired = true; - goto out; - } - int saved_errno = errno; - cc_log("lockfile_acquire: symlink %s: %s", lockfile, strerror(saved_errno)); - if (saved_errno == ENOENT) { - // Directory doesn't exist? - if (create_parent_dirs(lockfile) == 0) { - // OK. Retry. - continue; - } - } - if (saved_errno == EPERM) { - // The file system does not support symbolic links. We have no choice but - // to grant the lock anyway. - acquired = true; - goto out; - } - if (saved_errno != EEXIST) { - // Directory doesn't exist or isn't writable? - goto out; - } - free(content); - content = x_readlink(lockfile); - if (!content) { - if (errno == ENOENT) { - // The symlink was removed after the symlink() call above, so retry - // acquiring it. - continue; - } else { - cc_log("lockfile_acquire: readlink %s: %s", lockfile, strerror(errno)); - goto out; - } - } + if (symlink(my_content, lockfile) == 0) { + // We got the lock. + acquired = true; + goto out; + } + int saved_errno = errno; + cc_log("lockfile_acquire: symlink %s: %s", lockfile, strerror(saved_errno)); + if (saved_errno == ENOENT) { + // Directory doesn't exist? + if (create_parent_dirs(lockfile) == 0) { + // OK. Retry. + continue; + } + } + if (saved_errno == EPERM) { + // The file system does not support symbolic links. We have no choice but + // to grant the lock anyway. + acquired = true; + goto out; + } + if (saved_errno != EEXIST) { + // Directory doesn't exist or isn't writable? + goto out; + } + free(content); + content = x_readlink(lockfile); + if (!content) { + if (errno == ENOENT) { + // The symlink was removed after the symlink() call above, so retry + // acquiring it. + continue; + } else { + cc_log("lockfile_acquire: readlink %s: %s", lockfile, strerror(errno)); + goto out; + } + } #endif - if (str_eq(content, my_content)) { - // Lost NFS reply? - cc_log("lockfile_acquire: symlink %s failed but we got the lock anyway", - lockfile); - acquired = true; - goto out; - } - // A possible improvement here would be to check if the process holding the - // lock is still alive and break the lock early if it isn't. - cc_log("lockfile_acquire: lock info for %s: %s", lockfile, content); - if (!initial_content) { - initial_content = x_strdup(content); - } - if (slept > staleness_limit) { - if (str_eq(content, initial_content)) { - // The lock seems to be stale -- break it. - cc_log("lockfile_acquire: breaking %s", lockfile); - // Try to acquire path.lock.lock: - if (lockfile_acquire(lockfile, staleness_limit)) { - lockfile_release(path); // Remove path.lock - lockfile_release(lockfile); // Remove path.lock.lock - to_sleep = 1000; - slept = 0; - continue; - } - } - cc_log("lockfile_acquire: gave up acquiring %s", lockfile); - goto out; - } - cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds", - lockfile, to_sleep); - usleep(to_sleep); - slept += to_sleep; - to_sleep *= 2; - } + if (str_eq(content, my_content)) { + // Lost NFS reply? + cc_log("lockfile_acquire: symlink %s failed but we got the lock anyway", + lockfile); + acquired = true; + goto out; + } + // A possible improvement here would be to check if the process holding the + // lock is still alive and break the lock early if it isn't. + cc_log("lockfile_acquire: lock info for %s: %s", lockfile, content); + if (!initial_content) { + initial_content = x_strdup(content); + } + if (slept > staleness_limit) { + if (str_eq(content, initial_content)) { + // The lock seems to be stale -- break it. + cc_log("lockfile_acquire: breaking %s", lockfile); + // Try to acquire path.lock.lock: + if (lockfile_acquire(lockfile, staleness_limit)) { + lockfile_release(path); // Remove path.lock + lockfile_release(lockfile); // Remove path.lock.lock + to_sleep = 1000; + slept = 0; + continue; + } + } + cc_log("lockfile_acquire: gave up acquiring %s", lockfile); + goto out; + } + cc_log("lockfile_acquire: failed to acquire %s; sleeping %u microseconds", + lockfile, + to_sleep); + usleep(to_sleep); + slept += to_sleep; + to_sleep *= 2; + } out: - if (acquired) { - cc_log("Acquired lock %s", lockfile); - } else { - cc_log("Failed to acquire lock %s", lockfile); - } - free(lockfile); - free(my_content); - free(initial_content); - free(content); - return acquired; + if (acquired) { + cc_log("Acquired lock %s", lockfile); + } else { + cc_log("Failed to acquire lock %s", lockfile); + } + free(lockfile); + free(my_content); + free(initial_content); + free(content); + return acquired; } // Release the lockfile for the given path. Assumes that we are the legitimate // owner. void -lockfile_release(const char *path) +lockfile_release(const char* path) { - char *lockfile = format("%s.lock", path); - cc_log("Releasing lock %s", lockfile); - tmp_unlink(lockfile); - free(lockfile); + char* lockfile = format("%s.lock", path); + cc_log("Releasing lock %s", lockfile); + tmp_unlink(lockfile); + free(lockfile); } #ifdef TEST_LOCKFILE int -main(int argc, char **argv) +main(int argc, char** argv) { - extern char *cache_logfile; - cache_logfile = "/dev/stdout"; - if (argc == 4) { - unsigned staleness_limit = atoi(argv[1]); - if (str_eq(argv[2], "acquire")) { - return lockfile_acquire(argv[3], staleness_limit) == 0; - } else if (str_eq(argv[2], "release")) { - lockfile_release(argv[3]); - return 0; - } - } - fprintf(stderr, - "Usage: testlockfile <staleness_limit> <acquire|release> <path>\n"); - return 1; + extern char* cache_logfile; + cache_logfile = "/dev/stdout"; + if (argc == 4) { + unsigned staleness_limit = atoi(argv[1]); + if (str_eq(argv[2], "acquire")) { + return lockfile_acquire(argv[3], staleness_limit) == 0; + } else if (str_eq(argv[2], "release")) { + lockfile_release(argv[3]); + return 0; + } + } + fprintf(stderr, + "Usage: testlockfile <staleness_limit> <acquire|release> <path>\n"); + return 1; } #endif diff --git a/src/macroskip.hpp b/src/macroskip.hpp index 52a06b14..d8dfc528 100644 --- a/src/macroskip.hpp +++ b/src/macroskip.hpp @@ -57,20 +57,14 @@ // print "" static const uint32_t macro_skip[] = { - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 4, 8, 8, 5, 2, 8, 8, 8, 4, 8, 8, 8, 3, 8, 8, - 8, 8, 8, 8, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 5, 2, 8, 8, 8, 4, 8, 8, 8, 3, + 8, 8, 8, 8, 8, 8, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, }; diff --git a/src/manifest.cpp b/src/manifest.cpp index 788f313f..623ade18 100644 --- a/src/manifest.cpp +++ b/src/manifest.cpp @@ -16,13 +16,15 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +#include "manifest.hpp" + #include "ccache.hpp" -#include "third_party/hashtable_itr.h" -#include "hashutil.hpp" #include "common_header.hpp" #include "compression.hpp" +#include "hashutil.hpp" #include "int_bytes_conversion.hpp" -#include "manifest.hpp" + +#include "third_party/hashtable_itr.h" #include "third_party/xxhash.h" // Manifest data format @@ -104,731 +106,737 @@ const char MANIFEST_MAGIC[4] = {'c', 'C', 'm', 'F'}; static const uint32_t MAX_MANIFEST_ENTRIES = 100; static const uint32_t MAX_MANIFEST_FILE_INFO_ENTRIES = 10000; -#define ccache_static_assert(e) \ - do { enum { ccache_static_assert__ = 1/(e) }; } while (false) +#define ccache_static_assert(e) \ + do { \ + enum { ccache_static_assert__ = 1 / (e) }; \ + } while (false) -struct file { - uint16_t path_len; // strlen(path) - char *path; // NUL-terminated +struct file +{ + uint16_t path_len; // strlen(path) + char* path; // NUL-terminated }; -struct file_info { - // Index to n_files. - uint32_t index; - // Digest of referenced file. - struct digest digest; - // Size of referenced file. - uint64_t fsize; - // mtime of referenced file. - int64_t mtime; - // ctime of referenced file. - int64_t ctime; +struct file_info +{ + // Index to n_files. + uint32_t index; + // Digest of referenced file. + struct digest digest; + // Size of referenced file. + uint64_t fsize; + // mtime of referenced file. + int64_t mtime; + // ctime of referenced file. + int64_t ctime; }; -struct result { - // Number of entries in file_info_indexes. - uint32_t n_file_info_indexes; - // Indexes to file_infos. - uint32_t *file_info_indexes; - // Name of the result. - struct digest name; +struct result +{ + // Number of entries in file_info_indexes. + uint32_t n_file_info_indexes; + // Indexes to file_infos. + uint32_t* file_info_indexes; + // Name of the result. + struct digest name; }; -struct manifest { - struct common_header header; +struct manifest +{ + struct common_header header; - // Referenced include files. - uint32_t n_files; - struct file *files; + // Referenced include files. + uint32_t n_files; + struct file* files; - // Information about referenced include files. - uint32_t n_file_infos; - struct file_info *file_infos; + // Information about referenced include files. + uint32_t n_file_infos; + struct file_info* file_infos; - // Result names plus references to include file infos. - uint32_t n_results; - struct result *results; + // Result names plus references to include file infos. + uint32_t n_results; + struct result* results; }; -struct file_stats { - uint64_t size; - int64_t mtime; - int64_t ctime; +struct file_stats +{ + uint64_t size; + int64_t mtime; + int64_t ctime; }; static unsigned int -hash_from_file_info(void *key) +hash_from_file_info(void* key) { - ccache_static_assert(sizeof(struct file_info) == 48); // No padding. - return XXH64(key, sizeof(struct file_info), 0); + ccache_static_assert(sizeof(struct file_info) == 48); // No padding. + return XXH64(key, sizeof(struct file_info), 0); } static int -file_infos_equal(void *key1, void *key2) +file_infos_equal(void* key1, void* key2) { - struct file_info *fi1 = (struct file_info *)key1; - struct file_info *fi2 = (struct file_info *)key2; - return fi1->index == fi2->index - && digests_equal(&fi1->digest, &fi2->digest) - && fi1->fsize == fi2->fsize - && fi1->mtime == fi2->mtime - && fi1->ctime == fi2->ctime; + struct file_info* fi1 = (struct file_info*)key1; + struct file_info* fi2 = (struct file_info*)key2; + return fi1->index == fi2->index && digests_equal(&fi1->digest, &fi2->digest) + && fi1->fsize == fi2->fsize && fi1->mtime == fi2->mtime + && fi1->ctime == fi2->ctime; } static void -free_manifest(struct manifest *mf) +free_manifest(struct manifest* mf) { - for (uint32_t i = 0; i < mf->n_files; i++) { - free(mf->files[i].path); - } - free(mf->files); - free(mf->file_infos); - for (uint32_t i = 0; i < mf->n_results; i++) { - free(mf->results[i].file_info_indexes); - } - free(mf->results); - free(mf); + for (uint32_t i = 0; i < mf->n_files; i++) { + free(mf->files[i].path); + } + free(mf->files); + free(mf->file_infos); + for (uint32_t i = 0; i < mf->n_results; i++) { + free(mf->results[i].file_info_indexes); + } + free(mf->results); + free(mf); } -#define READ_BYTES(buf, length) \ - do { \ - if (!decompressor->read(decompr_state, buf, length)) { \ - goto out; \ - } \ - } while (false) - -#define READ_UINT16(var) \ - do { \ - char buf_[2]; \ - READ_BYTES(buf_, sizeof(buf_)); \ - (var) = UINT16_FROM_BYTES(buf_); \ - } while (false) - -#define READ_UINT32(var) \ - do { \ - char buf_[4]; \ - READ_BYTES(buf_, sizeof(buf_)); \ - (var) = UINT32_FROM_BYTES(buf_); \ - } while (false) - -#define READ_INT64(var) \ - do { \ - char buf_[8]; \ - READ_BYTES(buf_, sizeof(buf_)); \ - (var) = INT64_FROM_BYTES(buf_); \ - } while (false) - -#define READ_UINT64(var) \ - do { \ - char buf_[8]; \ - READ_BYTES(buf_, sizeof(buf_)); \ - (var) = UINT64_FROM_BYTES(buf_); \ - } while (false) - -#define READ_STR(str_var, len_var) \ - do { \ - READ_UINT16(len_var); \ - (str_var) = static_cast<char*>(x_malloc(len_var + 1)); \ - READ_BYTES(str_var, len_var); \ - str_var[len_var] = '\0'; \ - } while (false) - -static struct manifest * +#define READ_BYTES(buf, length) \ + do { \ + if (!decompressor->read(decompr_state, buf, length)) { \ + goto out; \ + } \ + } while (false) + +#define READ_UINT16(var) \ + do { \ + char buf_[2]; \ + READ_BYTES(buf_, sizeof(buf_)); \ + (var) = UINT16_FROM_BYTES(buf_); \ + } while (false) + +#define READ_UINT32(var) \ + do { \ + char buf_[4]; \ + READ_BYTES(buf_, sizeof(buf_)); \ + (var) = UINT32_FROM_BYTES(buf_); \ + } while (false) + +#define READ_INT64(var) \ + do { \ + char buf_[8]; \ + READ_BYTES(buf_, sizeof(buf_)); \ + (var) = INT64_FROM_BYTES(buf_); \ + } while (false) + +#define READ_UINT64(var) \ + do { \ + char buf_[8]; \ + READ_BYTES(buf_, sizeof(buf_)); \ + (var) = UINT64_FROM_BYTES(buf_); \ + } while (false) + +#define READ_STR(str_var, len_var) \ + do { \ + READ_UINT16(len_var); \ + (str_var) = static_cast<char*>(x_malloc(len_var + 1)); \ + READ_BYTES(str_var, len_var); \ + str_var[len_var] = '\0'; \ + } while (false) + +static struct manifest* create_empty_manifest(void) { - auto mf = static_cast<manifest*>(x_malloc(sizeof(manifest))); - mf->n_files = 0; - mf->files = NULL; - mf->n_file_infos = 0; - mf->file_infos = NULL; - mf->n_results = 0; - mf->results = NULL; - - return mf; + auto mf = static_cast<manifest*>(x_malloc(sizeof(manifest))); + mf->n_files = 0; + mf->files = NULL; + mf->n_file_infos = 0; + mf->file_infos = NULL; + mf->n_results = 0; + mf->results = NULL; + + return mf; } -static struct manifest * -read_manifest(const char *path, char **errmsg) +static struct manifest* +read_manifest(const char* path, char** errmsg) { - bool success = false; - struct manifest *mf = create_empty_manifest(); - struct decompressor *decompressor = NULL; - struct decompr_state *decompr_state = NULL; - *errmsg = NULL; - XXH64_state_t *checksum = XXH64_createState(); - uint64_t actual_checksum; - uint64_t expected_checksum; - - FILE *f = fopen(path, "rb"); - if (!f) { - *errmsg = x_strdup("No such manifest file"); - goto out; - } - - if (!common_header_initialize_for_reading( - &mf->header, - f, - MANIFEST_MAGIC, - MANIFEST_VERSION, - &decompressor, - &decompr_state, - checksum, - errmsg)) { - goto out; - } - - READ_UINT32(mf->n_files); - mf->files = static_cast<file*>(x_calloc(mf->n_files, sizeof(file))); - for (uint32_t i = 0; i < mf->n_files; i++) { - READ_STR(mf->files[i].path, mf->files[i].path_len); - } - - READ_UINT32(mf->n_file_infos); - mf->file_infos = - static_cast<file_info*>(x_calloc(mf->n_file_infos, sizeof(file_info))); - for (uint32_t i = 0; i < mf->n_file_infos; i++) { - READ_UINT32(mf->file_infos[i].index); - READ_BYTES(mf->file_infos[i].digest.bytes, DIGEST_SIZE); - READ_UINT64(mf->file_infos[i].fsize); - READ_INT64(mf->file_infos[i].mtime); - READ_INT64(mf->file_infos[i].ctime); - } - - READ_UINT32(mf->n_results); - mf->results = static_cast<result*>(x_calloc(mf->n_results, sizeof(result))); - for (uint32_t i = 0; i < mf->n_results; i++) { - READ_UINT32(mf->results[i].n_file_info_indexes); - mf->results[i].file_info_indexes = static_cast<uint32_t*>( - x_calloc(mf->results[i].n_file_info_indexes, sizeof(uint32_t))); - for (uint32_t j = 0; j < mf->results[i].n_file_info_indexes; j++) { - READ_UINT32(mf->results[i].file_info_indexes[j]); - } - READ_BYTES(mf->results[i].name.bytes, DIGEST_SIZE); - } - - actual_checksum = XXH64_digest(checksum); - READ_UINT64(expected_checksum); - if (actual_checksum == expected_checksum) { - success = true; - } else { - *errmsg = format( - "Incorrect checksum (actual %016llx, expected %016llx)", - (unsigned long long)actual_checksum, - (unsigned long long)expected_checksum); - } + bool success = false; + struct manifest* mf = create_empty_manifest(); + struct decompressor* decompressor = NULL; + struct decompr_state* decompr_state = NULL; + *errmsg = NULL; + XXH64_state_t* checksum = XXH64_createState(); + uint64_t actual_checksum; + uint64_t expected_checksum; + + FILE* f = fopen(path, "rb"); + if (!f) { + *errmsg = x_strdup("No such manifest file"); + goto out; + } + + if (!common_header_initialize_for_reading(&mf->header, + f, + MANIFEST_MAGIC, + MANIFEST_VERSION, + &decompressor, + &decompr_state, + checksum, + errmsg)) { + goto out; + } + + READ_UINT32(mf->n_files); + mf->files = static_cast<file*>(x_calloc(mf->n_files, sizeof(file))); + for (uint32_t i = 0; i < mf->n_files; i++) { + READ_STR(mf->files[i].path, mf->files[i].path_len); + } + + READ_UINT32(mf->n_file_infos); + mf->file_infos = + static_cast<file_info*>(x_calloc(mf->n_file_infos, sizeof(file_info))); + for (uint32_t i = 0; i < mf->n_file_infos; i++) { + READ_UINT32(mf->file_infos[i].index); + READ_BYTES(mf->file_infos[i].digest.bytes, DIGEST_SIZE); + READ_UINT64(mf->file_infos[i].fsize); + READ_INT64(mf->file_infos[i].mtime); + READ_INT64(mf->file_infos[i].ctime); + } + + READ_UINT32(mf->n_results); + mf->results = static_cast<result*>(x_calloc(mf->n_results, sizeof(result))); + for (uint32_t i = 0; i < mf->n_results; i++) { + READ_UINT32(mf->results[i].n_file_info_indexes); + mf->results[i].file_info_indexes = static_cast<uint32_t*>( + x_calloc(mf->results[i].n_file_info_indexes, sizeof(uint32_t))); + for (uint32_t j = 0; j < mf->results[i].n_file_info_indexes; j++) { + READ_UINT32(mf->results[i].file_info_indexes[j]); + } + READ_BYTES(mf->results[i].name.bytes, DIGEST_SIZE); + } + + actual_checksum = XXH64_digest(checksum); + READ_UINT64(expected_checksum); + if (actual_checksum == expected_checksum) { + success = true; + } else { + *errmsg = format("Incorrect checksum (actual %016llx, expected %016llx)", + (unsigned long long)actual_checksum, + (unsigned long long)expected_checksum); + } out: - if (decompressor && !decompressor->free(decompr_state)) { - success = false; - } - if (f) { - fclose(f); - } - if (checksum) { - XXH64_freeState(checksum); - } - if (!success) { - if (!*errmsg) { - *errmsg = x_strdup("Corrupt manifest file"); - } - free_manifest(mf); - mf = NULL; - } - return mf; + if (decompressor && !decompressor->free(decompr_state)) { + success = false; + } + if (f) { + fclose(f); + } + if (checksum) { + XXH64_freeState(checksum); + } + if (!success) { + if (!*errmsg) { + *errmsg = x_strdup("Corrupt manifest file"); + } + free_manifest(mf); + mf = NULL; + } + return mf; } -#define WRITE_BYTES(buf, length) \ - do { \ - if (!compressor->write(compr_state, buf, length)) { \ - goto out; \ - } \ - } while (false) - -#define WRITE_UINT16(var) \ - do { \ - char buf_[2]; \ - BYTES_FROM_UINT16(buf_, (var)); \ - WRITE_BYTES(buf_, sizeof(buf_)); \ - } while (false) - -#define WRITE_UINT32(var) \ - do { \ - char buf_[4]; \ - BYTES_FROM_UINT32(buf_, (var)); \ - WRITE_BYTES(buf_, sizeof(buf_)); \ - } while (false) - -#define WRITE_INT64(var) \ - do { \ - char buf_[8]; \ - BYTES_FROM_INT64(buf_, (var)); \ - WRITE_BYTES(buf_, sizeof(buf_)); \ - } while (false) - -#define WRITE_UINT64(var) \ - do { \ - char buf_[8]; \ - BYTES_FROM_UINT64(buf_, (var)); \ - WRITE_BYTES(buf_, sizeof(buf_)); \ - } while (false) +#define WRITE_BYTES(buf, length) \ + do { \ + if (!compressor->write(compr_state, buf, length)) { \ + goto out; \ + } \ + } while (false) + +#define WRITE_UINT16(var) \ + do { \ + char buf_[2]; \ + BYTES_FROM_UINT16(buf_, (var)); \ + WRITE_BYTES(buf_, sizeof(buf_)); \ + } while (false) + +#define WRITE_UINT32(var) \ + do { \ + char buf_[4]; \ + BYTES_FROM_UINT32(buf_, (var)); \ + WRITE_BYTES(buf_, sizeof(buf_)); \ + } while (false) + +#define WRITE_INT64(var) \ + do { \ + char buf_[8]; \ + BYTES_FROM_INT64(buf_, (var)); \ + WRITE_BYTES(buf_, sizeof(buf_)); \ + } while (false) + +#define WRITE_UINT64(var) \ + do { \ + char buf_[8]; \ + BYTES_FROM_UINT64(buf_, (var)); \ + WRITE_BYTES(buf_, sizeof(buf_)); \ + } while (false) static bool -write_manifest(FILE *f, const struct manifest *mf) +write_manifest(FILE* f, const struct manifest* mf) { - int ret = false; - XXH64_state_t *checksum = XXH64_createState(); - - uint64_t content_size = COMMON_HEADER_SIZE; - content_size += 4; // n_files - for (size_t i = 0; i < mf->n_files; i++) { - content_size += 2 + mf->files[i].path_len; - } - content_size += 4; // n_file_infos - content_size += mf->n_file_infos * (4 + DIGEST_SIZE + 8 + 8 + 8); - content_size += 4; // n_results - for (size_t i = 0; i < mf->n_results; i++) { - content_size += 4; // n_file_info_indexes - content_size += mf->results[i].n_file_info_indexes * 4; - content_size += DIGEST_SIZE; - } - content_size += 8; // checksum - - struct common_header header; - struct compressor *compressor; - struct compr_state *compr_state; - if (!common_header_initialize_for_writing( - &header, - f, - MANIFEST_MAGIC, - MANIFEST_VERSION, - compression_type_from_config(), - compression_level_from_config(), - content_size, - checksum, - &compressor, - &compr_state)) { - goto out; - } - - WRITE_UINT32(mf->n_files); - for (uint32_t i = 0; i < mf->n_files; i++) { - WRITE_UINT16(mf->files[i].path_len); - WRITE_BYTES(mf->files[i].path, mf->files[i].path_len); - } - - WRITE_UINT32(mf->n_file_infos); - for (uint32_t i = 0; i < mf->n_file_infos; i++) { - WRITE_UINT32(mf->file_infos[i].index); - WRITE_BYTES(mf->file_infos[i].digest.bytes, DIGEST_SIZE); - WRITE_UINT64(mf->file_infos[i].fsize); - WRITE_INT64(mf->file_infos[i].mtime); - WRITE_INT64(mf->file_infos[i].ctime); - } - - WRITE_UINT32(mf->n_results); - for (uint32_t i = 0; i < mf->n_results; i++) { - WRITE_UINT32(mf->results[i].n_file_info_indexes); - for (uint32_t j = 0; j < mf->results[i].n_file_info_indexes; j++) { - WRITE_UINT32(mf->results[i].file_info_indexes[j]); - } - WRITE_BYTES(mf->results[i].name.bytes, DIGEST_SIZE); - } - - WRITE_UINT64(XXH64_digest(checksum)); - - ret = compressor->free(compr_state); + int ret = false; + XXH64_state_t* checksum = XXH64_createState(); + + uint64_t content_size = COMMON_HEADER_SIZE; + content_size += 4; // n_files + for (size_t i = 0; i < mf->n_files; i++) { + content_size += 2 + mf->files[i].path_len; + } + content_size += 4; // n_file_infos + content_size += mf->n_file_infos * (4 + DIGEST_SIZE + 8 + 8 + 8); + content_size += 4; // n_results + for (size_t i = 0; i < mf->n_results; i++) { + content_size += 4; // n_file_info_indexes + content_size += mf->results[i].n_file_info_indexes * 4; + content_size += DIGEST_SIZE; + } + content_size += 8; // checksum + + struct common_header header; + struct compressor* compressor; + struct compr_state* compr_state; + if (!common_header_initialize_for_writing(&header, + f, + MANIFEST_MAGIC, + MANIFEST_VERSION, + compression_type_from_config(), + compression_level_from_config(), + content_size, + checksum, + &compressor, + &compr_state)) { + goto out; + } + + WRITE_UINT32(mf->n_files); + for (uint32_t i = 0; i < mf->n_files; i++) { + WRITE_UINT16(mf->files[i].path_len); + WRITE_BYTES(mf->files[i].path, mf->files[i].path_len); + } + + WRITE_UINT32(mf->n_file_infos); + for (uint32_t i = 0; i < mf->n_file_infos; i++) { + WRITE_UINT32(mf->file_infos[i].index); + WRITE_BYTES(mf->file_infos[i].digest.bytes, DIGEST_SIZE); + WRITE_UINT64(mf->file_infos[i].fsize); + WRITE_INT64(mf->file_infos[i].mtime); + WRITE_INT64(mf->file_infos[i].ctime); + } + + WRITE_UINT32(mf->n_results); + for (uint32_t i = 0; i < mf->n_results; i++) { + WRITE_UINT32(mf->results[i].n_file_info_indexes); + for (uint32_t j = 0; j < mf->results[i].n_file_info_indexes; j++) { + WRITE_UINT32(mf->results[i].file_info_indexes[j]); + } + WRITE_BYTES(mf->results[i].name.bytes, DIGEST_SIZE); + } + + WRITE_UINT64(XXH64_digest(checksum)); + + ret = compressor->free(compr_state); out: - XXH64_freeState(checksum); - if (!ret) { - cc_log("Error writing to manifest file"); - } - return ret; + XXH64_freeState(checksum); + if (!ret) { + cc_log("Error writing to manifest file"); + } + return ret; } static bool -verify_result(struct conf *conf, struct manifest *mf, struct result *result, - struct hashtable *stated_files, struct hashtable *hashed_files) +verify_result(struct conf* conf, + struct manifest* mf, + struct result* result, + struct hashtable* stated_files, + struct hashtable* hashed_files) { - for (uint32_t i = 0; i < result->n_file_info_indexes; i++) { - struct file_info *fi = &mf->file_infos[result->file_info_indexes[i]]; - char *path = mf->files[fi->index].path; - auto st = static_cast<file_stats*>(hashtable_search(stated_files, path)); - if (!st) { - struct stat file_stat; - if (x_stat(path, &file_stat) != 0) { - return false; - } - st = static_cast<file_stats*>(x_malloc(sizeof(file_stats))); - st->size = file_stat.st_size; - st->mtime = file_stat.st_mtime; - st->ctime = file_stat.st_ctime; - hashtable_insert(stated_files, x_strdup(path), st); - } - - if (fi->fsize != st->size) { - return false; - } - - // Clang stores the mtime of the included files in the precompiled header, - // and will error out if that header is later used without rebuilding. - if ((guessed_compiler == GUESSED_CLANG - || guessed_compiler == GUESSED_UNKNOWN) - && output_is_precompiled_header - && fi->mtime != st->mtime) { - cc_log("Precompiled header includes %s, which has a new mtime", path); - return false; - } - - if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) { - if (!(conf->sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME)) { - if (fi->mtime == st->mtime && fi->ctime == st->ctime) { - cc_log("mtime/ctime hit for %s", path); - continue; - } else { - cc_log("mtime/ctime miss for %s", path); - } - } else { - if (fi->mtime == st->mtime) { - cc_log("mtime hit for %s", path); - continue; - } else { - cc_log("mtime miss for %s", path); - } - } - } - - auto actual = static_cast<digest*>(hashtable_search(hashed_files, path)); - if (!actual) { - struct hash *hash = hash_init(); - int ret = hash_source_code_file(conf, hash, path); - if (ret & HASH_SOURCE_CODE_ERROR) { - cc_log("Failed hashing %s", path); - hash_free(hash); - return false; - } - if (ret & HASH_SOURCE_CODE_FOUND_TIME) { - hash_free(hash); - return false; - } - - actual = static_cast<digest*>(malloc(sizeof(digest))); - hash_result_as_bytes(hash, actual); - hashtable_insert(hashed_files, x_strdup(path), actual); - hash_free(hash); - } - if (!digests_equal(&fi->digest, actual)) { - return false; - } - } - - return true; + for (uint32_t i = 0; i < result->n_file_info_indexes; i++) { + struct file_info* fi = &mf->file_infos[result->file_info_indexes[i]]; + char* path = mf->files[fi->index].path; + auto st = static_cast<file_stats*>(hashtable_search(stated_files, path)); + if (!st) { + struct stat file_stat; + if (x_stat(path, &file_stat) != 0) { + return false; + } + st = static_cast<file_stats*>(x_malloc(sizeof(file_stats))); + st->size = file_stat.st_size; + st->mtime = file_stat.st_mtime; + st->ctime = file_stat.st_ctime; + hashtable_insert(stated_files, x_strdup(path), st); + } + + if (fi->fsize != st->size) { + return false; + } + + // Clang stores the mtime of the included files in the precompiled header, + // and will error out if that header is later used without rebuilding. + if ((guessed_compiler == GUESSED_CLANG + || guessed_compiler == GUESSED_UNKNOWN) + && output_is_precompiled_header && fi->mtime != st->mtime) { + cc_log("Precompiled header includes %s, which has a new mtime", path); + return false; + } + + if (conf->sloppiness & SLOPPY_FILE_STAT_MATCHES) { + if (!(conf->sloppiness & SLOPPY_FILE_STAT_MATCHES_CTIME)) { + if (fi->mtime == st->mtime && fi->ctime == st->ctime) { + cc_log("mtime/ctime hit for %s", path); + continue; + } else { + cc_log("mtime/ctime miss for %s", path); + } + } else { + if (fi->mtime == st->mtime) { + cc_log("mtime hit for %s", path); + continue; + } else { + cc_log("mtime miss for %s", path); + } + } + } + + auto actual = static_cast<digest*>(hashtable_search(hashed_files, path)); + if (!actual) { + struct hash* hash = hash_init(); + int ret = hash_source_code_file(conf, hash, path); + if (ret & HASH_SOURCE_CODE_ERROR) { + cc_log("Failed hashing %s", path); + hash_free(hash); + return false; + } + if (ret & HASH_SOURCE_CODE_FOUND_TIME) { + hash_free(hash); + return false; + } + + actual = static_cast<digest*>(malloc(sizeof(digest))); + hash_result_as_bytes(hash, actual); + hashtable_insert(hashed_files, x_strdup(path), actual); + hash_free(hash); + } + if (!digests_equal(&fi->digest, actual)) { + return false; + } + } + + return true; } -static struct hashtable * -create_file_index_map(struct file *files, uint32_t len) +static struct hashtable* +create_file_index_map(struct file* files, uint32_t len) { - struct hashtable *h = - create_hashtable(1000, hash_from_string, strings_equal); - for (uint32_t i = 0; i < len; i++) { - auto index = static_cast<uint32_t*>(x_malloc(sizeof(uint32_t))); - *index = i; - hashtable_insert(h, x_strdup(files[i].path), index); - } - return h; + struct hashtable* h = create_hashtable(1000, hash_from_string, strings_equal); + for (uint32_t i = 0; i < len; i++) { + auto index = static_cast<uint32_t*>(x_malloc(sizeof(uint32_t))); + *index = i; + hashtable_insert(h, x_strdup(files[i].path), index); + } + return h; } -static struct hashtable * -create_file_info_index_map(struct file_info *infos, uint32_t len) +static struct hashtable* +create_file_info_index_map(struct file_info* infos, uint32_t len) { - struct hashtable *h = - create_hashtable(1000, hash_from_file_info, file_infos_equal); - for (uint32_t i = 0; i < len; i++) { - auto fi = static_cast<file_info*>(x_malloc(sizeof(file_info))); - *fi = infos[i]; - uint32_t *index = static_cast<uint32_t*>(x_malloc(sizeof(uint32_t))); - *index = i; - hashtable_insert(h, fi, index); - } - return h; + struct hashtable* h = + create_hashtable(1000, hash_from_file_info, file_infos_equal); + for (uint32_t i = 0; i < len; i++) { + auto fi = static_cast<file_info*>(x_malloc(sizeof(file_info))); + *fi = infos[i]; + uint32_t* index = static_cast<uint32_t*>(x_malloc(sizeof(uint32_t))); + *index = i; + hashtable_insert(h, fi, index); + } + return h; } static uint32_t -get_include_file_index(struct manifest *mf, char *path, - struct hashtable *mf_files) +get_include_file_index(struct manifest* mf, + char* path, + struct hashtable* mf_files) { - uint32_t *index = static_cast<uint32_t*>(hashtable_search(mf_files, path)); - if (index) { - return *index; - } - - uint32_t n = mf->n_files; - mf->files = static_cast<file*>(x_realloc(mf->files, (n + 1) * sizeof(file))); - mf->n_files++; - mf->files[n].path_len = strlen(path); - mf->files[n].path = x_strdup(path); - return n; + uint32_t* index = static_cast<uint32_t*>(hashtable_search(mf_files, path)); + if (index) { + return *index; + } + + uint32_t n = mf->n_files; + mf->files = static_cast<file*>(x_realloc(mf->files, (n + 1) * sizeof(file))); + mf->n_files++; + mf->files[n].path_len = strlen(path); + mf->files[n].path = x_strdup(path); + return n; } static uint32_t -get_file_info_index(struct manifest *mf, - char *path, - struct digest *digest, - struct hashtable *mf_files, - struct hashtable *mf_file_infos) +get_file_info_index(struct manifest* mf, + char* path, + struct digest* digest, + struct hashtable* mf_files, + struct hashtable* mf_file_infos) { - struct file_info fi; - fi.index = get_include_file_index(mf, path, mf_files); - fi.digest = *digest; - - // file_stat.st_{m,c}time has a resolution of 1 second, so we can cache the - // file's mtime and ctime only if they're at least one second older than - // time_of_compilation. - // - // st->ctime may be 0, so we have to check time_of_compilation against - // MAX(mtime, ctime). - - struct stat file_stat; - if (stat(path, &file_stat) != -1) { - if (time_of_compilation > MAX(file_stat.st_mtime, file_stat.st_ctime)) { - fi.mtime = file_stat.st_mtime; - fi.ctime = file_stat.st_ctime; - } else { - fi.mtime = -1; - fi.ctime = -1; - } - fi.fsize = file_stat.st_size; - } else { - fi.mtime = -1; - fi.ctime = -1; - fi.fsize = 0; - } - - auto fi_index = static_cast<uint32_t*>(hashtable_search(mf_file_infos, &fi)); - if (fi_index) { - return *fi_index; - } - - uint32_t n = mf->n_file_infos; - mf->file_infos = static_cast<file_info*>( - x_realloc(mf->file_infos, (n + 1) * sizeof(file_info))); - mf->n_file_infos++; - mf->file_infos[n] = fi; - return n; + struct file_info fi; + fi.index = get_include_file_index(mf, path, mf_files); + fi.digest = *digest; + + // file_stat.st_{m,c}time has a resolution of 1 second, so we can cache the + // file's mtime and ctime only if they're at least one second older than + // time_of_compilation. + // + // st->ctime may be 0, so we have to check time_of_compilation against + // MAX(mtime, ctime). + + struct stat file_stat; + if (stat(path, &file_stat) != -1) { + if (time_of_compilation > MAX(file_stat.st_mtime, file_stat.st_ctime)) { + fi.mtime = file_stat.st_mtime; + fi.ctime = file_stat.st_ctime; + } else { + fi.mtime = -1; + fi.ctime = -1; + } + fi.fsize = file_stat.st_size; + } else { + fi.mtime = -1; + fi.ctime = -1; + fi.fsize = 0; + } + + auto fi_index = static_cast<uint32_t*>(hashtable_search(mf_file_infos, &fi)); + if (fi_index) { + return *fi_index; + } + + uint32_t n = mf->n_file_infos; + mf->file_infos = static_cast<file_info*>( + x_realloc(mf->file_infos, (n + 1) * sizeof(file_info))); + mf->n_file_infos++; + mf->file_infos[n] = fi; + return n; } static void -add_file_info_indexes(uint32_t *indexes, uint32_t size, - struct manifest *mf, struct hashtable *included_files) +add_file_info_indexes(uint32_t* indexes, + uint32_t size, + struct manifest* mf, + struct hashtable* included_files) { - if (size == 0) { - return; - } - - // path --> index - struct hashtable *mf_files = create_file_index_map(mf->files, mf->n_files); - // struct file_info --> index - struct hashtable *mf_file_infos = - create_file_info_index_map(mf->file_infos, mf->n_file_infos); - struct hashtable_itr *iter = hashtable_iterator(included_files); - uint32_t i = 0; - do { - auto path = static_cast<char*>(hashtable_iterator_key(iter)); - auto digest = static_cast<struct digest*>(hashtable_iterator_value(iter)); - indexes[i] = get_file_info_index(mf, path, digest, mf_files, - mf_file_infos); - i++; - } while (hashtable_iterator_advance(iter)); - assert(i == size); - - hashtable_destroy(mf_file_infos, 1); - hashtable_destroy(mf_files, 1); + if (size == 0) { + return; + } + + // path --> index + struct hashtable* mf_files = create_file_index_map(mf->files, mf->n_files); + // struct file_info --> index + struct hashtable* mf_file_infos = + create_file_info_index_map(mf->file_infos, mf->n_file_infos); + struct hashtable_itr* iter = hashtable_iterator(included_files); + uint32_t i = 0; + do { + auto path = static_cast<char*>(hashtable_iterator_key(iter)); + auto digest = static_cast<struct digest*>(hashtable_iterator_value(iter)); + indexes[i] = get_file_info_index(mf, path, digest, mf_files, mf_file_infos); + i++; + } while (hashtable_iterator_advance(iter)); + assert(i == size); + + hashtable_destroy(mf_file_infos, 1); + hashtable_destroy(mf_files, 1); } static void -add_result_entry(struct manifest *mf, - struct digest *result_digest, - struct hashtable *included_files) +add_result_entry(struct manifest* mf, + struct digest* result_digest, + struct hashtable* included_files) { - uint32_t n_results = mf->n_results; - mf->results = static_cast<result*>( - x_realloc(mf->results, (n_results + 1) * sizeof(result))); - mf->n_results++; - struct result *result = &mf->results[n_results]; - - uint32_t n_fii = hashtable_count(included_files); - result->n_file_info_indexes = n_fii; - result->file_info_indexes = static_cast<uint32_t*>( - x_malloc(n_fii * sizeof(uint32_t))); - add_file_info_indexes(result->file_info_indexes, n_fii, mf, included_files); - result->name = *result_digest; + uint32_t n_results = mf->n_results; + mf->results = static_cast<result*>( + x_realloc(mf->results, (n_results + 1) * sizeof(result))); + mf->n_results++; + struct result* result = &mf->results[n_results]; + + uint32_t n_fii = hashtable_count(included_files); + result->n_file_info_indexes = n_fii; + result->file_info_indexes = + static_cast<uint32_t*>(x_malloc(n_fii * sizeof(uint32_t))); + add_file_info_indexes(result->file_info_indexes, n_fii, mf, included_files); + result->name = *result_digest; } // Try to get the result name from a manifest file. Caller frees. Returns NULL // on failure. -struct digest * -manifest_get(struct conf *conf, const char *manifest_path) +struct digest* +manifest_get(struct conf* conf, const char* manifest_path) { - char *errmsg; - struct manifest *mf = read_manifest(manifest_path, &errmsg); - if (!mf) { - cc_log("%s", errmsg); - return NULL; - } - - // path --> struct digest - struct hashtable *hashed_files = - create_hashtable(1000, hash_from_string, strings_equal); - // path --> struct file_stats - struct hashtable *stated_files = - create_hashtable(1000, hash_from_string, strings_equal); - - // Check newest result first since it's a bit more likely to match. - struct digest *name = NULL; - for (uint32_t i = mf->n_results; i > 0; i--) { - if (verify_result(conf, mf, &mf->results[i - 1], - stated_files, hashed_files)) { - name = static_cast<digest*>(x_malloc(sizeof(digest))); - *name = mf->results[i - 1].name; - goto out; - } - } + char* errmsg; + struct manifest* mf = read_manifest(manifest_path, &errmsg); + if (!mf) { + cc_log("%s", errmsg); + return NULL; + } + + // path --> struct digest + struct hashtable* hashed_files = + create_hashtable(1000, hash_from_string, strings_equal); + // path --> struct file_stats + struct hashtable* stated_files = + create_hashtable(1000, hash_from_string, strings_equal); + + // Check newest result first since it's a bit more likely to match. + struct digest* name = NULL; + for (uint32_t i = mf->n_results; i > 0; i--) { + if (verify_result( + conf, mf, &mf->results[i - 1], stated_files, hashed_files)) { + name = static_cast<digest*>(x_malloc(sizeof(digest))); + *name = mf->results[i - 1].name; + goto out; + } + } out: - if (hashed_files) { - hashtable_destroy(hashed_files, 1); - } - if (stated_files) { - hashtable_destroy(stated_files, 1); - } - free_manifest(mf); - if (name) { - // Update modification timestamp to save files from LRU cleanup. - update_mtime(manifest_path); - } - return name; + if (hashed_files) { + hashtable_destroy(hashed_files, 1); + } + if (stated_files) { + hashtable_destroy(stated_files, 1); + } + free_manifest(mf); + if (name) { + // Update modification timestamp to save files from LRU cleanup. + update_mtime(manifest_path); + } + return name; } // Put the result name into a manifest file given a set of included files. // Returns true on success, otherwise false. bool -manifest_put(const char *manifest_path, struct digest *result_name, - struct hashtable *included_files) +manifest_put(const char* manifest_path, + struct digest* result_name, + struct hashtable* included_files) { - // We don't bother to acquire a lock when writing the manifest to disk. A - // race between two processes will only result in one lost entry, which is - // not a big deal, and it's also very unlikely. - - char *errmsg; - struct manifest *mf = read_manifest(manifest_path, &errmsg); - if (!mf) { - // New or corrupt file. - mf = create_empty_manifest(); - free(errmsg); // Ignore. - } - - if (mf->n_results > MAX_MANIFEST_ENTRIES) { - // Normally, there shouldn't be many result entries in the manifest since - // new entries are added only if an include file has changed but not the - // source file, and you typically change source files more often than - // header files. However, it's certainly possible to imagine cases where - // the manifest will grow large (for instance, a generated header file that - // changes for every build), and this must be taken care of since - // processing an ever growing manifest eventually will take too much time. - // A good way of solving this would be to maintain the result entries in - // LRU order and discarding the old ones. An easy way is to throw away all - // entries when there are too many. Let's do that for now. - cc_log("More than %u entries in manifest file; discarding", - MAX_MANIFEST_ENTRIES); - free_manifest(mf); - mf = create_empty_manifest(); - } else if (mf->n_file_infos > MAX_MANIFEST_FILE_INFO_ENTRIES) { - // Rarely, file_info entries can grow large in pathological cases where - // many included files change, but the main file does not. This also puts - // an upper bound on the number of file_info entries. - cc_log("More than %u file_info entries in manifest file; discarding", - MAX_MANIFEST_FILE_INFO_ENTRIES); - free_manifest(mf); - mf = create_empty_manifest(); - } - - add_result_entry(mf, result_name, included_files); - - int ret = false; - char *tmp_file = format("%s.tmp", manifest_path); - int fd = create_tmp_fd(&tmp_file); - FILE *f = fdopen(fd, "wb"); - if (!f) { - cc_log("Failed to fdopen %s", tmp_file); - goto out; - } - if (write_manifest(f, mf)) { - fclose(f); - f = NULL; - if (x_rename(tmp_file, manifest_path) == 0) { - ret = true; - } else { - cc_log("Failed to rename %s to %s", tmp_file, manifest_path); - } - } else { - cc_log("Failed to write manifest file %s", tmp_file); - } + // We don't bother to acquire a lock when writing the manifest to disk. A + // race between two processes will only result in one lost entry, which is + // not a big deal, and it's also very unlikely. + + char* errmsg; + struct manifest* mf = read_manifest(manifest_path, &errmsg); + if (!mf) { + // New or corrupt file. + mf = create_empty_manifest(); + free(errmsg); // Ignore. + } + + if (mf->n_results > MAX_MANIFEST_ENTRIES) { + // Normally, there shouldn't be many result entries in the manifest since + // new entries are added only if an include file has changed but not the + // source file, and you typically change source files more often than + // header files. However, it's certainly possible to imagine cases where + // the manifest will grow large (for instance, a generated header file that + // changes for every build), and this must be taken care of since + // processing an ever growing manifest eventually will take too much time. + // A good way of solving this would be to maintain the result entries in + // LRU order and discarding the old ones. An easy way is to throw away all + // entries when there are too many. Let's do that for now. + cc_log("More than %u entries in manifest file; discarding", + MAX_MANIFEST_ENTRIES); + free_manifest(mf); + mf = create_empty_manifest(); + } else if (mf->n_file_infos > MAX_MANIFEST_FILE_INFO_ENTRIES) { + // Rarely, file_info entries can grow large in pathological cases where + // many included files change, but the main file does not. This also puts + // an upper bound on the number of file_info entries. + cc_log("More than %u file_info entries in manifest file; discarding", + MAX_MANIFEST_FILE_INFO_ENTRIES); + free_manifest(mf); + mf = create_empty_manifest(); + } + + add_result_entry(mf, result_name, included_files); + + int ret = false; + char* tmp_file = format("%s.tmp", manifest_path); + int fd = create_tmp_fd(&tmp_file); + FILE* f = fdopen(fd, "wb"); + if (!f) { + cc_log("Failed to fdopen %s", tmp_file); + goto out; + } + if (write_manifest(f, mf)) { + fclose(f); + f = NULL; + if (x_rename(tmp_file, manifest_path) == 0) { + ret = true; + } else { + cc_log("Failed to rename %s to %s", tmp_file, manifest_path); + } + } else { + cc_log("Failed to write manifest file %s", tmp_file); + } out: - if (f) { - fclose(f); - } - if (mf) { - free_manifest(mf); - } - if (tmp_file) { - free(tmp_file); - } - return ret; + if (f) { + fclose(f); + } + if (mf) { + free_manifest(mf); + } + if (tmp_file) { + free(tmp_file); + } + return ret; } bool -manifest_dump(const char *manifest_path, FILE *stream) +manifest_dump(const char* manifest_path, FILE* stream) { - char *errmsg; - struct manifest *mf = read_manifest(manifest_path, &errmsg); - if (!mf) { - assert(errmsg); - fprintf(stream, "Error: %s\n", errmsg); - free(errmsg); - return false; - } - - common_header_dump(&mf->header, stream); - - fprintf(stream, "File paths (%u):\n", (unsigned)mf->n_files); - for (unsigned i = 0; i < mf->n_files; ++i) { - fprintf(stream, " %u: %s\n", i, mf->files[i].path); - } - fprintf(stream, "File infos (%u):\n", (unsigned)mf->n_file_infos); - for (unsigned i = 0; i < mf->n_file_infos; ++i) { - char digest[DIGEST_STRING_BUFFER_SIZE]; - fprintf(stream, " %u:\n", i); - fprintf(stream, " Path index: %u\n", mf->file_infos[i].index); - digest_as_string(&mf->file_infos[i].digest, digest); - fprintf(stream, " Hash: %s\n", digest); - fprintf(stream, " File size: %" PRIu64 "\n", mf->file_infos[i].fsize); - fprintf(stream, " Mtime: %lld\n", (long long)mf->file_infos[i].mtime); - fprintf(stream, " Ctime: %lld\n", (long long)mf->file_infos[i].ctime); - } - fprintf(stream, "Results (%u):\n", (unsigned)mf->n_results); - for (unsigned i = 0; i < mf->n_results; ++i) { - char name[DIGEST_STRING_BUFFER_SIZE]; - fprintf(stream, " %u:\n", i); - fprintf(stream, " File info indexes:"); - for (unsigned j = 0; j < mf->results[i].n_file_info_indexes; ++j) { - fprintf(stream, " %u", mf->results[i].file_info_indexes[j]); - } - fprintf(stream, "\n"); - digest_as_string(&mf->results[i].name, name); - fprintf(stream, " Name: %s\n", name); - } - - free_manifest(mf); - return true; + char* errmsg; + struct manifest* mf = read_manifest(manifest_path, &errmsg); + if (!mf) { + assert(errmsg); + fprintf(stream, "Error: %s\n", errmsg); + free(errmsg); + return false; + } + + common_header_dump(&mf->header, stream); + + fprintf(stream, "File paths (%u):\n", (unsigned)mf->n_files); + for (unsigned i = 0; i < mf->n_files; ++i) { + fprintf(stream, " %u: %s\n", i, mf->files[i].path); + } + fprintf(stream, "File infos (%u):\n", (unsigned)mf->n_file_infos); + for (unsigned i = 0; i < mf->n_file_infos; ++i) { + char digest[DIGEST_STRING_BUFFER_SIZE]; + fprintf(stream, " %u:\n", i); + fprintf(stream, " Path index: %u\n", mf->file_infos[i].index); + digest_as_string(&mf->file_infos[i].digest, digest); + fprintf(stream, " Hash: %s\n", digest); + fprintf(stream, " File size: %" PRIu64 "\n", mf->file_infos[i].fsize); + fprintf(stream, " Mtime: %lld\n", (long long)mf->file_infos[i].mtime); + fprintf(stream, " Ctime: %lld\n", (long long)mf->file_infos[i].ctime); + } + fprintf(stream, "Results (%u):\n", (unsigned)mf->n_results); + for (unsigned i = 0; i < mf->n_results; ++i) { + char name[DIGEST_STRING_BUFFER_SIZE]; + fprintf(stream, " %u:\n", i); + fprintf(stream, " File info indexes:"); + for (unsigned j = 0; j < mf->results[i].n_file_info_indexes; ++j) { + fprintf(stream, " %u", mf->results[i].file_info_indexes[j]); + } + fprintf(stream, "\n"); + digest_as_string(&mf->results[i].name, name); + fprintf(stream, " Name: %s\n", name); + } + + free_manifest(mf); + return true; } diff --git a/src/manifest.hpp b/src/manifest.hpp index 4fe9e0c7..05dae1e8 100644 --- a/src/manifest.hpp +++ b/src/manifest.hpp @@ -20,12 +20,14 @@ #include "conf.hpp" #include "hashutil.hpp" + #include "third_party/hashtable.h" extern const char MANIFEST_MAGIC[4]; #define MANIFEST_VERSION 2 -struct digest *manifest_get(struct conf *conf, const char *manifest_path); -bool manifest_put(const char *manifest_path, struct digest *result_digest, - struct hashtable *included_files); -bool manifest_dump(const char *manifest_path, FILE *stream); +struct digest* manifest_get(struct conf* conf, const char* manifest_path); +bool manifest_put(const char* manifest_path, + struct digest* result_digest, + struct hashtable* included_files); +bool manifest_dump(const char* manifest_path, FILE* stream); diff --git a/src/result.cpp b/src/result.cpp index 45eac639..c1374e31 100644 --- a/src/result.cpp +++ b/src/result.cpp @@ -16,12 +16,13 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +#include "result.hpp" + #include "ccache.hpp" #include "common_header.hpp" -#include "int_bytes_conversion.hpp" #include "compression.hpp" #include "hash.hpp" -#include "result.hpp" +#include "int_bytes_conversion.hpp" // Result data format // ================== @@ -81,672 +82,655 @@ // // 1: Introduced in ccache 3.8. -extern const struct conf *conf; -extern char *stats_file; +extern const struct conf* conf; +extern char* stats_file; const char RESULT_MAGIC[4] = {'c', 'C', 'r', 'S'}; enum { - // File data stored inside the result file. - EMBEDDED_FILE_MARKER = 0, + // File data stored inside the result file. + EMBEDDED_FILE_MARKER = 0, - // File stored as-is in the file system. - RAW_FILE_MARKER = 1 + // File stored as-is in the file system. + RAW_FILE_MARKER = 1 }; -struct result_file { - char *suffix; - char *path; - uint64_t size; +struct result_file +{ + char* suffix; + char* path; + uint64_t size; }; -struct result_files { - uint32_t n_files; - struct result_file *files; - uint64_t *sizes; +struct result_files +{ + uint32_t n_files; + struct result_file* files; + uint64_t* sizes; }; -typedef bool (*read_entry_fn)( - struct decompressor *decompressor, - struct decompr_state *decompr_state, - const char *result_path_in_cache, - uint32_t entry_number, - const struct result_files *list, - FILE *dump_stream); - -typedef bool (*write_entry_fn)( - struct compressor *compressor, - struct compr_state *compr_state, - const char *result_path_in_cache, - uint32_t entry_number, - const struct result_file *file); - -struct result_files * +typedef bool (*read_entry_fn)(struct decompressor* decompressor, + struct decompr_state* decompr_state, + const char* result_path_in_cache, + uint32_t entry_number, + const struct result_files* list, + FILE* dump_stream); + +typedef bool (*write_entry_fn)(struct compressor* compressor, + struct compr_state* compr_state, + const char* result_path_in_cache, + uint32_t entry_number, + const struct result_file* file); + +struct result_files* result_files_init(void) { - auto list = static_cast<result_files*>(x_malloc(sizeof(result_files))); - list->n_files = 0; - list->files = NULL; - list->sizes = NULL; + auto list = static_cast<result_files*>(x_malloc(sizeof(result_files))); + list->n_files = 0; + list->files = NULL; + list->sizes = NULL; - return list; + return list; } void -result_files_add(struct result_files *list, const char *path, - const char *suffix) +result_files_add(struct result_files* list, + const char* path, + const char* suffix) { - uint32_t n = list->n_files; - list->files = static_cast<result_file*>( - x_realloc(list->files, (n + 1) * sizeof(result_file))); - list->sizes = static_cast<uint64_t*>( - x_realloc(list->sizes, (n + 1) * sizeof(uint64_t))); - struct result_file *f = &list->files[list->n_files]; - list->n_files++; - - struct stat st; - x_stat(path, &st); - - f->suffix = x_strdup(suffix); - f->path = x_strdup(path); - f->size = st.st_size; + uint32_t n = list->n_files; + list->files = static_cast<result_file*>( + x_realloc(list->files, (n + 1) * sizeof(result_file))); + list->sizes = + static_cast<uint64_t*>(x_realloc(list->sizes, (n + 1) * sizeof(uint64_t))); + struct result_file* f = &list->files[list->n_files]; + list->n_files++; + + struct stat st; + x_stat(path, &st); + + f->suffix = x_strdup(suffix); + f->path = x_strdup(path); + f->size = st.st_size; } void -result_files_free(struct result_files *list) +result_files_free(struct result_files* list) { - for (uint32_t i = 0; i < list->n_files; i++) { - free(list->files[i].suffix); - free(list->files[i].path); - } - free(list->files); - list->files = NULL; - free(list->sizes); - list->sizes = NULL; - - free(list); + for (uint32_t i = 0; i < list->n_files; i++) { + free(list->files[i].suffix); + free(list->files[i].path); + } + free(list->files); + list->files = NULL; + free(list->sizes); + list->sizes = NULL; + + free(list); } -#define READ_BYTES(buf, length) \ - do { \ - if (!decompressor->read(decompr_state, buf, length)) { \ - goto out; \ - } \ - } while (false) +#define READ_BYTES(buf, length) \ + do { \ + if (!decompressor->read(decompr_state, buf, length)) { \ + goto out; \ + } \ + } while (false) -#define READ_BYTE(var) \ - READ_BYTES(&var, 1) +#define READ_BYTE(var) READ_BYTES(&var, 1) -#define READ_UINT64(var) \ - do { \ - char buf_[8]; \ - READ_BYTES(buf_, sizeof(buf_)); \ - (var) = UINT64_FROM_BYTES(buf_); \ - } while (false) +#define READ_UINT64(var) \ + do { \ + char buf_[8]; \ + READ_BYTES(buf_, sizeof(buf_)); \ + (var) = UINT64_FROM_BYTES(buf_); \ + } while (false) static bool -read_embedded_file_entry( - struct decompressor *decompressor, - struct decompr_state *decompr_state, - const char *result_path_in_cache, - uint32_t entry_number, - const struct result_files *list, - FILE *dump_stream) +read_embedded_file_entry(struct decompressor* decompressor, + struct decompr_state* decompr_state, + const char* result_path_in_cache, + uint32_t entry_number, + const struct result_files* list, + FILE* dump_stream) { - (void)result_path_in_cache; - bool found = false; - bool success = false; - FILE *subfile = NULL; - - uint8_t suffix_len; - READ_BYTE(suffix_len); - - char suffix[256 + 1]; - READ_BYTES(suffix, suffix_len); - suffix[suffix_len] = '\0'; - - uint64_t file_len; - READ_UINT64(file_len); - - if (dump_stream) { - fprintf( - dump_stream, - "Embedded file #%u: %s (%" PRIu64 " bytes)\n", - entry_number, - suffix, - file_len); - } else { - cc_log( - "Retrieving embedded file #%u %s (%llu bytes)", - entry_number, - suffix, - (unsigned long long)file_len); - - for (uint32_t i = 0; i < list->n_files; i++) { - if (str_eq(suffix, list->files[i].suffix)) { - found = true; - - cc_log("Copying to %s", list->files[i].path); - - subfile = fopen(list->files[i].path, "wb"); - if (!subfile) { - cc_log("Failed to open %s for writing", list->files[i].path); - goto out; - } - char buf[READ_BUFFER_SIZE]; - size_t remain = file_len; - while (remain > 0) { - size_t n = MIN(remain, sizeof(buf)); - READ_BYTES(buf, n); - if (fwrite(buf, 1, n, subfile) != n) { - goto out; - } - remain -= n; - } - fclose(subfile); - subfile = NULL; - - break; - } - } - } - - if (!found) { - // Discard the file data. - char buf[READ_BUFFER_SIZE]; - size_t remain = file_len; - while (remain > 0) { - size_t n = MIN(remain, sizeof(buf)); - READ_BYTES(buf, n); - remain -= n; - } - } - - success = true; + (void)result_path_in_cache; + bool found = false; + bool success = false; + FILE* subfile = NULL; + + uint8_t suffix_len; + READ_BYTE(suffix_len); + + char suffix[256 + 1]; + READ_BYTES(suffix, suffix_len); + suffix[suffix_len] = '\0'; + + uint64_t file_len; + READ_UINT64(file_len); + + if (dump_stream) { + fprintf(dump_stream, + "Embedded file #%u: %s (%" PRIu64 " bytes)\n", + entry_number, + suffix, + file_len); + } else { + cc_log("Retrieving embedded file #%u %s (%llu bytes)", + entry_number, + suffix, + (unsigned long long)file_len); + + for (uint32_t i = 0; i < list->n_files; i++) { + if (str_eq(suffix, list->files[i].suffix)) { + found = true; + + cc_log("Copying to %s", list->files[i].path); + + subfile = fopen(list->files[i].path, "wb"); + if (!subfile) { + cc_log("Failed to open %s for writing", list->files[i].path); + goto out; + } + char buf[READ_BUFFER_SIZE]; + size_t remain = file_len; + while (remain > 0) { + size_t n = MIN(remain, sizeof(buf)); + READ_BYTES(buf, n); + if (fwrite(buf, 1, n, subfile) != n) { + goto out; + } + remain -= n; + } + fclose(subfile); + subfile = NULL; + + break; + } + } + } + + if (!found) { + // Discard the file data. + char buf[READ_BUFFER_SIZE]; + size_t remain = file_len; + while (remain > 0) { + size_t n = MIN(remain, sizeof(buf)); + READ_BYTES(buf, n); + remain -= n; + } + } + + success = true; out: - if (subfile) { - fclose(subfile); - } + if (subfile) { + fclose(subfile); + } - return success; + return success; } -static char * -get_raw_file_path(const char *result_path_in_cache, uint32_t entry_number) +static char* +get_raw_file_path(const char* result_path_in_cache, uint32_t entry_number) { - return format( - "%.*s_%u.raw", - (int)strlen(result_path_in_cache) - 7, // .result - result_path_in_cache, - entry_number); + return format("%.*s_%u.raw", + (int)strlen(result_path_in_cache) - 7, // .result + result_path_in_cache, + entry_number); } static bool -copy_raw_file(const char *source, const char *dest, bool to_cache) +copy_raw_file(const char* source, const char* dest, bool to_cache) { - if (conf->file_clone) { - cc_log("Cloning %s to %s", source, dest); - if (clone_file(source, dest, to_cache)) { - return true; - } - cc_log("Failed to clone: %s", strerror(errno)); - } - if (conf->hard_link) { - x_try_unlink(dest); - cc_log("Hard linking %s to %s", source, dest); - int ret = link(source, dest); - if (ret == 0) { - return true; - } - cc_log("Failed to hard link: %s", strerror(errno)); - } - - cc_log("Copying %s to %s", source, dest); - return copy_file(source, dest, to_cache); + if (conf->file_clone) { + cc_log("Cloning %s to %s", source, dest); + if (clone_file(source, dest, to_cache)) { + return true; + } + cc_log("Failed to clone: %s", strerror(errno)); + } + if (conf->hard_link) { + x_try_unlink(dest); + cc_log("Hard linking %s to %s", source, dest); + int ret = link(source, dest); + if (ret == 0) { + return true; + } + cc_log("Failed to hard link: %s", strerror(errno)); + } + + cc_log("Copying %s to %s", source, dest); + return copy_file(source, dest, to_cache); } static bool -read_raw_file_entry( - struct decompressor *decompressor, - struct decompr_state *decompr_state, - const char *result_path_in_cache, - uint32_t entry_number, - const struct result_files *list, - FILE *dump_stream) +read_raw_file_entry(struct decompressor* decompressor, + struct decompr_state* decompr_state, + const char* result_path_in_cache, + uint32_t entry_number, + const struct result_files* list, + FILE* dump_stream) { - bool success = false; - char *raw_path = get_raw_file_path(result_path_in_cache, entry_number); - - uint8_t suffix_len; - READ_BYTE(suffix_len); - - char suffix[256 + 1]; - READ_BYTES(suffix, suffix_len); - suffix[suffix_len] = '\0'; - - uint64_t file_len; - READ_UINT64(file_len); - - if (dump_stream) { - fprintf( - dump_stream, - "Raw file #%u: %s (%" PRIu64 " bytes)\n", - entry_number, - suffix, - file_len); - } else { - cc_log( - "Retrieving raw file #%u %s (%llu bytes)", - entry_number, - suffix, - (unsigned long long)file_len); - - struct stat st; - if (x_stat(raw_path, &st) != 0) { - goto out; - } - if ((uint64_t)st.st_size != file_len) { - cc_log( - "Bad file size of %s (actual %llu bytes, expected %llu bytes)", - raw_path, - (unsigned long long)st.st_size, - (unsigned long long)file_len); - goto out; - } - - for (uint32_t i = 0; i < list->n_files; i++) { - if (str_eq(suffix, list->files[i].suffix)) { - if (!copy_raw_file(raw_path, list->files[i].path, false)) { - goto out; - } - // Update modification timestamp to save the file from LRU cleanup - // (and, if hard-linked, to make the object file newer than the source - // file). - update_mtime(raw_path); - break; - } - } - } - - success = true; + bool success = false; + char* raw_path = get_raw_file_path(result_path_in_cache, entry_number); + + uint8_t suffix_len; + READ_BYTE(suffix_len); + + char suffix[256 + 1]; + READ_BYTES(suffix, suffix_len); + suffix[suffix_len] = '\0'; + + uint64_t file_len; + READ_UINT64(file_len); + + if (dump_stream) { + fprintf(dump_stream, + "Raw file #%u: %s (%" PRIu64 " bytes)\n", + entry_number, + suffix, + file_len); + } else { + cc_log("Retrieving raw file #%u %s (%llu bytes)", + entry_number, + suffix, + (unsigned long long)file_len); + + struct stat st; + if (x_stat(raw_path, &st) != 0) { + goto out; + } + if ((uint64_t)st.st_size != file_len) { + cc_log("Bad file size of %s (actual %llu bytes, expected %llu bytes)", + raw_path, + (unsigned long long)st.st_size, + (unsigned long long)file_len); + goto out; + } + + for (uint32_t i = 0; i < list->n_files; i++) { + if (str_eq(suffix, list->files[i].suffix)) { + if (!copy_raw_file(raw_path, list->files[i].path, false)) { + goto out; + } + // Update modification timestamp to save the file from LRU cleanup + // (and, if hard-linked, to make the object file newer than the source + // file). + update_mtime(raw_path); + break; + } + } + } + + success = true; out: - free(raw_path); - return success; + free(raw_path); + return success; } static bool -read_result( - const char *path, - struct result_files *list, - FILE *dump_stream, - char **errmsg) +read_result(const char* path, + struct result_files* list, + FILE* dump_stream, + char** errmsg) { - *errmsg = NULL; - bool cache_miss = false; - bool success = false; - struct decompressor *decompressor = NULL; - struct decompr_state *decompr_state = NULL; - XXH64_state_t *checksum = XXH64_createState(); - - FILE *f = fopen(path, "rb"); - if (!f) { - cache_miss = true; - goto out; - } - - struct common_header header; - if (!common_header_initialize_for_reading( - &header, - f, - RESULT_MAGIC, - RESULT_VERSION, - &decompressor, - &decompr_state, - checksum, - errmsg)) { - goto out; - } - - if (dump_stream) { - common_header_dump(&header, dump_stream); - } - - uint8_t n_entries; - READ_BYTE(n_entries); - - uint32_t i; - for (i = 0; i < n_entries; i++) { - uint8_t marker; - READ_BYTE(marker); - - read_entry_fn read_entry; - - switch (marker) { - case EMBEDDED_FILE_MARKER: - read_entry = read_embedded_file_entry; - break; - - case RAW_FILE_MARKER: - read_entry = read_raw_file_entry; - break; - - default: - *errmsg = format("Unknown entry type: %u", marker); - goto out; - } - - if (!read_entry(decompressor, decompr_state, path, i, list, dump_stream)) { - goto out; - } - } - - if (i != n_entries) { - *errmsg = format("Too few entries (read %u, expected %u)", i, n_entries); - goto out; - } - - { - uint64_t actual_checksum = XXH64_digest(checksum); - uint64_t expected_checksum; - READ_UINT64(expected_checksum); - - if (actual_checksum == expected_checksum) { - success = true; - } else { - *errmsg = format( - "Incorrect checksum (actual %016llx, expected %016llx)", - (unsigned long long)actual_checksum, - (unsigned long long)expected_checksum); - } - } + *errmsg = NULL; + bool cache_miss = false; + bool success = false; + struct decompressor* decompressor = NULL; + struct decompr_state* decompr_state = NULL; + XXH64_state_t* checksum = XXH64_createState(); + + FILE* f = fopen(path, "rb"); + if (!f) { + cache_miss = true; + goto out; + } + + struct common_header header; + if (!common_header_initialize_for_reading(&header, + f, + RESULT_MAGIC, + RESULT_VERSION, + &decompressor, + &decompr_state, + checksum, + errmsg)) { + goto out; + } + + if (dump_stream) { + common_header_dump(&header, dump_stream); + } + + uint8_t n_entries; + READ_BYTE(n_entries); + + uint32_t i; + for (i = 0; i < n_entries; i++) { + uint8_t marker; + READ_BYTE(marker); + + read_entry_fn read_entry; + + switch (marker) { + case EMBEDDED_FILE_MARKER: + read_entry = read_embedded_file_entry; + break; + + case RAW_FILE_MARKER: + read_entry = read_raw_file_entry; + break; + + default: + *errmsg = format("Unknown entry type: %u", marker); + goto out; + } + + if (!read_entry(decompressor, decompr_state, path, i, list, dump_stream)) { + goto out; + } + } + + if (i != n_entries) { + *errmsg = format("Too few entries (read %u, expected %u)", i, n_entries); + goto out; + } + + { + uint64_t actual_checksum = XXH64_digest(checksum); + uint64_t expected_checksum; + READ_UINT64(expected_checksum); + + if (actual_checksum == expected_checksum) { + success = true; + } else { + *errmsg = format("Incorrect checksum (actual %016llx, expected %016llx)", + (unsigned long long)actual_checksum, + (unsigned long long)expected_checksum); + } + } out: - if (decompressor && !decompressor->free(decompr_state)) { - success = false; - } - if (f) { - fclose(f); - } - if (checksum) { - XXH64_freeState(checksum); - } - if (!success && !cache_miss && !*errmsg) { - *errmsg = x_strdup("Corrupt result"); - } - return success; + if (decompressor && !decompressor->free(decompr_state)) { + success = false; + } + if (f) { + fclose(f); + } + if (checksum) { + XXH64_freeState(checksum); + } + if (!success && !cache_miss && !*errmsg) { + *errmsg = x_strdup("Corrupt result"); + } + return success; } -#define WRITE_BYTES(buf, length) \ - do { \ - if (!compressor->write(compr_state, buf, length)) { \ - goto error; \ - } \ - } while (false) - -#define WRITE_BYTE(var) \ - do { \ - char ch_ = var; \ - WRITE_BYTES(&ch_, 1); \ - } while (false) - -#define WRITE_UINT64(var) \ - do { \ - char buf_[8]; \ - BYTES_FROM_UINT64(buf_, (var)); \ - WRITE_BYTES(buf_, sizeof(buf_)); \ - } while (false) +#define WRITE_BYTES(buf, length) \ + do { \ + if (!compressor->write(compr_state, buf, length)) { \ + goto error; \ + } \ + } while (false) + +#define WRITE_BYTE(var) \ + do { \ + char ch_ = var; \ + WRITE_BYTES(&ch_, 1); \ + } while (false) + +#define WRITE_UINT64(var) \ + do { \ + char buf_[8]; \ + BYTES_FROM_UINT64(buf_, (var)); \ + WRITE_BYTES(buf_, sizeof(buf_)); \ + } while (false) static bool -write_embedded_file_entry( - struct compressor *compressor, - struct compr_state *compr_state, - const char *result_path_in_cache, - uint32_t entry_number, - const struct result_file *file) +write_embedded_file_entry(struct compressor* compressor, + struct compr_state* compr_state, + const char* result_path_in_cache, + uint32_t entry_number, + const struct result_file* file) { - (void)result_path_in_cache; - bool success = false; - size_t suffix_len; - FILE* f; - char buf[READ_BUFFER_SIZE]; - size_t remain; - - cc_log( - "Storing embedded file #%u %s (%llu bytes) from %s", - entry_number, - file->suffix, - (unsigned long long)file->size, - file->path); - - WRITE_BYTE(EMBEDDED_FILE_MARKER); - suffix_len = strlen(file->suffix); - WRITE_BYTE(suffix_len); - WRITE_BYTES(file->suffix, suffix_len); - WRITE_UINT64(file->size); - - f = fopen(file->path, "rb"); - if (!f) { - cc_log("Failed to open %s for reading", file->path); - goto error; - } - remain = file->size; - while (remain > 0) { - size_t n = MIN(remain, sizeof(buf)); - if (fread(buf, 1, n, f) != n) { - goto error; - } - WRITE_BYTES(buf, n); - remain -= n; - } - fclose(f); - - success = true; + (void)result_path_in_cache; + bool success = false; + size_t suffix_len; + FILE* f; + char buf[READ_BUFFER_SIZE]; + size_t remain; + + cc_log("Storing embedded file #%u %s (%llu bytes) from %s", + entry_number, + file->suffix, + (unsigned long long)file->size, + file->path); + + WRITE_BYTE(EMBEDDED_FILE_MARKER); + suffix_len = strlen(file->suffix); + WRITE_BYTE(suffix_len); + WRITE_BYTES(file->suffix, suffix_len); + WRITE_UINT64(file->size); + + f = fopen(file->path, "rb"); + if (!f) { + cc_log("Failed to open %s for reading", file->path); + goto error; + } + remain = file->size; + while (remain > 0) { + size_t n = MIN(remain, sizeof(buf)); + if (fread(buf, 1, n, f) != n) { + goto error; + } + WRITE_BYTES(buf, n); + remain -= n; + } + fclose(f); + + success = true; error: - return success; + return success; } static bool -write_raw_file_entry( - struct compressor *compressor, - struct compr_state *compr_state, - const char *result_path_in_cache, - uint32_t entry_number, - const struct result_file *file) +write_raw_file_entry(struct compressor* compressor, + struct compr_state* compr_state, + const char* result_path_in_cache, + uint32_t entry_number, + const struct result_file* file) { - bool success = false; - size_t suffix_len; - char* raw_file; - struct stat old_stat; - bool old_existed; - struct stat new_stat; - bool new_exists; - size_t old_size; - size_t new_size; - - cc_log( - "Storing raw file #%u %s (%llu bytes) from %s", - entry_number, - file->suffix, - (unsigned long long)file->size, - file->path); - - WRITE_BYTE(RAW_FILE_MARKER); - suffix_len = strlen(file->suffix); - WRITE_BYTE(suffix_len); - WRITE_BYTES(file->suffix, suffix_len); - WRITE_UINT64(file->size); - - raw_file = get_raw_file_path(result_path_in_cache, entry_number); - old_existed = stat(raw_file, &old_stat) == 0; - success = copy_raw_file(file->path, raw_file, true); - new_exists = stat(raw_file, &new_stat) == 0; - free(raw_file); - - old_size = old_existed ? file_size(&old_stat) : 0; - new_size = new_exists ? file_size(&new_stat) : 0; - stats_update_size( - stats_file, - new_size - old_size, - (new_exists ? 1 : 0) - (old_existed ? 1 : 0)); + bool success = false; + size_t suffix_len; + char* raw_file; + struct stat old_stat; + bool old_existed; + struct stat new_stat; + bool new_exists; + size_t old_size; + size_t new_size; + + cc_log("Storing raw file #%u %s (%llu bytes) from %s", + entry_number, + file->suffix, + (unsigned long long)file->size, + file->path); + + WRITE_BYTE(RAW_FILE_MARKER); + suffix_len = strlen(file->suffix); + WRITE_BYTE(suffix_len); + WRITE_BYTES(file->suffix, suffix_len); + WRITE_UINT64(file->size); + + raw_file = get_raw_file_path(result_path_in_cache, entry_number); + old_existed = stat(raw_file, &old_stat) == 0; + success = copy_raw_file(file->path, raw_file, true); + new_exists = stat(raw_file, &new_stat) == 0; + free(raw_file); + + old_size = old_existed ? file_size(&old_stat) : 0; + new_size = new_exists ? file_size(&new_stat) : 0; + stats_update_size(stats_file, + new_size - old_size, + (new_exists ? 1 : 0) - (old_existed ? 1 : 0)); error: - return success; + return success; } static bool -should_store_raw_file(const char *suffix) +should_store_raw_file(const char* suffix) { - if (!conf->file_clone && !conf->hard_link) { - return false; - } - - // - Don't store stderr outputs as raw files since they: - // 1. Never are large. - // 2. Will end up in a temporary file anyway. - // - // - Don't store .d files since they: - // 1. Never are large. - // 2. Compress well. - // 3. Cause trouble for automake if hard-linked (see ccache issue 378). - // - // Note that .d files can't be stored as raw files for the file_clone case - // since the hard link mode happily will try to use them if they exist. This - // could be fixed by letting read_raw_file_entry refuse to hard link .d - // files, but it's easier to simply always store them embedded. This will - // also save i-nodes in the cache. - return !str_eq(suffix, RESULT_STDERR_NAME) && !str_eq(suffix, ".d"); + if (!conf->file_clone && !conf->hard_link) { + return false; + } + + // - Don't store stderr outputs as raw files since they: + // 1. Never are large. + // 2. Will end up in a temporary file anyway. + // + // - Don't store .d files since they: + // 1. Never are large. + // 2. Compress well. + // 3. Cause trouble for automake if hard-linked (see ccache issue 378). + // + // Note that .d files can't be stored as raw files for the file_clone case + // since the hard link mode happily will try to use them if they exist. This + // could be fixed by letting read_raw_file_entry refuse to hard link .d + // files, but it's easier to simply always store them embedded. This will + // also save i-nodes in the cache. + return !str_eq(suffix, RESULT_STDERR_NAME) && !str_eq(suffix, ".d"); } static bool -write_result( - const struct result_files *list, - struct compressor *compressor, - struct compr_state *compr_state, - XXH64_state_t *checksum, - const char *result_path_in_cache) +write_result(const struct result_files* list, + struct compressor* compressor, + struct compr_state* compr_state, + XXH64_state_t* checksum, + const char* result_path_in_cache) { - WRITE_BYTE(list->n_files); + WRITE_BYTE(list->n_files); - for (uint32_t i = 0; i < list->n_files; i++) { - write_entry_fn write_entry = - should_store_raw_file(list->files[i].suffix) - ? write_raw_file_entry - : write_embedded_file_entry; - if (!write_entry( - compressor, compr_state, result_path_in_cache, i, &list->files[i])) { - goto error; - } - } + for (uint32_t i = 0; i < list->n_files; i++) { + write_entry_fn write_entry = should_store_raw_file(list->files[i].suffix) + ? write_raw_file_entry + : write_embedded_file_entry; + if (!write_entry( + compressor, compr_state, result_path_in_cache, i, &list->files[i])) { + goto error; + } + } - WRITE_UINT64(XXH64_digest(checksum)); + WRITE_UINT64(XXH64_digest(checksum)); - return true; + return true; error: - cc_log("Error writing to result file"); - return false; + cc_log("Error writing to result file"); + return false; } -bool result_get(const char *path, struct result_files *list) +bool +result_get(const char* path, struct result_files* list) { - cc_log("Getting result %s", path); - - char *errmsg; - bool success = read_result(path, list, NULL, &errmsg); - if (success) { - // Update modification timestamp to save files from LRU cleanup. - update_mtime(path); - } else if (errmsg) { - cc_log("Error: %s", errmsg); - free(errmsg); - } else { - cc_log("No such result file"); - } - return success; + cc_log("Getting result %s", path); + + char* errmsg; + bool success = read_result(path, list, NULL, &errmsg); + if (success) { + // Update modification timestamp to save files from LRU cleanup. + update_mtime(path); + } else if (errmsg) { + cc_log("Error: %s", errmsg); + free(errmsg); + } else { + cc_log("No such result file"); + } + return success; } -bool result_put(const char *path, struct result_files *list) +bool +result_put(const char* path, struct result_files* list) { - bool ret = false; - XXH64_state_t *checksum = XXH64_createState(); - bool ok; - uint64_t content_size; - - char *tmp_file = format("%s.tmp", path); - int fd = create_tmp_fd(&tmp_file); - FILE *f = fdopen(fd, "wb"); - if (!f) { - cc_log("Failed to fdopen %s", tmp_file); - goto out; - } - - content_size = COMMON_HEADER_SIZE; - content_size += 1; // n_entries - for (uint32_t i = 0; i < list->n_files; i++) { - content_size += 1; // embedded_file_marker - content_size += 1; // suffix_len - content_size += strlen(list->files[i].suffix); // suffix - content_size += 8; // data_len - content_size += list->files[i].size; // data - } - content_size += 8; // checksum - - struct common_header header; - struct compressor *compressor; - struct compr_state *compr_state; - if (!common_header_initialize_for_writing( - &header, - f, - RESULT_MAGIC, - RESULT_VERSION, - compression_type_from_config(), - compression_level_from_config(), - content_size, - checksum, - &compressor, - &compr_state)) { - goto out; - } - - ok = write_result(list, compressor, compr_state, checksum, path) - && compressor->free(compr_state); - if (!ok) { - cc_log("Failed to write result file"); - goto out; - } - - fclose(f); - f = NULL; - if (x_rename(tmp_file, path) == 0) { - ret = true; - } else { - cc_log("Failed to rename %s to %s", tmp_file, path); - } + bool ret = false; + XXH64_state_t* checksum = XXH64_createState(); + bool ok; + uint64_t content_size; + + char* tmp_file = format("%s.tmp", path); + int fd = create_tmp_fd(&tmp_file); + FILE* f = fdopen(fd, "wb"); + if (!f) { + cc_log("Failed to fdopen %s", tmp_file); + goto out; + } + + content_size = COMMON_HEADER_SIZE; + content_size += 1; // n_entries + for (uint32_t i = 0; i < list->n_files; i++) { + content_size += 1; // embedded_file_marker + content_size += 1; // suffix_len + content_size += strlen(list->files[i].suffix); // suffix + content_size += 8; // data_len + content_size += list->files[i].size; // data + } + content_size += 8; // checksum + + struct common_header header; + struct compressor* compressor; + struct compr_state* compr_state; + if (!common_header_initialize_for_writing(&header, + f, + RESULT_MAGIC, + RESULT_VERSION, + compression_type_from_config(), + compression_level_from_config(), + content_size, + checksum, + &compressor, + &compr_state)) { + goto out; + } + + ok = write_result(list, compressor, compr_state, checksum, path) + && compressor->free(compr_state); + if (!ok) { + cc_log("Failed to write result file"); + goto out; + } + + fclose(f); + f = NULL; + if (x_rename(tmp_file, path) == 0) { + ret = true; + } else { + cc_log("Failed to rename %s to %s", tmp_file, path); + } out: - free(tmp_file); - if (f) { - fclose(f); - } - if (checksum) { - XXH64_freeState(checksum); - } - return ret; + free(tmp_file); + if (f) { + fclose(f); + } + if (checksum) { + XXH64_freeState(checksum); + } + return ret; } bool -result_dump(const char *path, FILE *stream) +result_dump(const char* path, FILE* stream) { - assert(stream); - - char *errmsg; - bool success = read_result(path, NULL, stream, &errmsg); - if (errmsg) { - fprintf(stream, "Error: %s\n", errmsg); - free(errmsg); - } - return success; + assert(stream); + + char* errmsg; + bool success = read_result(path, NULL, stream, &errmsg); + if (errmsg) { + fprintf(stream, "Error: %s\n", errmsg); + free(errmsg); + } + return success; } diff --git a/src/result.hpp b/src/result.hpp index 20212b60..e9b0700d 100644 --- a/src/result.hpp +++ b/src/result.hpp @@ -26,11 +26,11 @@ extern const char RESULT_MAGIC[4]; struct result_files; -struct result_files *result_files_init(void); -void result_files_add( - struct result_files *c, const char *path, const char *suffix); -void result_files_free(struct result_files *c); +struct result_files* result_files_init(void); +void +result_files_add(struct result_files* c, const char* path, const char* suffix); +void result_files_free(struct result_files* c); -bool result_get(const char *path, struct result_files *list); -bool result_put(const char *path, struct result_files *list); -bool result_dump(const char *path, FILE *stream); +bool result_get(const char* path, struct result_files* list); +bool result_put(const char* path, struct result_files* list); +bool result_dump(const char* path, FILE* stream); diff --git a/src/stats.cpp b/src/stats.cpp index 033b458e..0a6b9c60 100644 --- a/src/stats.cpp +++ b/src/stats.cpp @@ -23,687 +23,572 @@ #include "ccache.hpp" #include "hashutil.hpp" -#include <sys/types.h> -#include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> -extern char *stats_file; -extern struct conf *conf; +extern char* stats_file; +extern struct conf* conf; extern unsigned lock_staleness_limit; -extern char *primary_config_path; -extern char *secondary_config_path; +extern char* primary_config_path; +extern char* secondary_config_path; -static struct counters *counter_updates; +static struct counters* counter_updates; #define FLAG_NOZERO 1 // don't zero with the -z option #define FLAG_ALWAYS 2 // always show, even if zero -#define FLAG_NEVER 4 // never show +#define FLAG_NEVER 4 // never show // Returns a formatted version of a statistics value, or NULL if the statistics // line shouldn't be printed. Caller frees. -typedef char *(*format_fn)(uint64_t value); +typedef char* (*format_fn)(uint64_t value); -static char *format_size_times_1024(uint64_t size); -static char *format_timestamp(uint64_t timestamp); -static void stats_flush_to_file(const char *sfile, struct counters *updates); +static char* format_size_times_1024(uint64_t size); +static char* format_timestamp(uint64_t timestamp); +static void stats_flush_to_file(const char* sfile, struct counters* updates); // Statistics fields in display order. -static struct { - enum stats stat; - const char *id; // for --print-stats - const char *message; // for --show-stats - format_fn format; // NULL -> use plain integer format - unsigned flags; +static struct +{ + enum stats stat; + const char* id; // for --print-stats + const char* message; // for --show-stats + format_fn format; // NULL -> use plain integer format + unsigned flags; } stats_info[] = { - { - STATS_ZEROTIMESTAMP, - "stats_zeroed_timestamp", - "stats zeroed", - format_timestamp, - FLAG_ALWAYS - }, - { - STATS_CACHEHIT_DIR, - "direct_cache_hit", - "cache hit (direct)", - NULL, - FLAG_ALWAYS - }, - { - STATS_CACHEHIT_CPP, - "preprocessed_cache_hit", - "cache hit (preprocessed)", - NULL, - FLAG_ALWAYS - }, - { - STATS_TOCACHE, - "cache_miss", - "cache miss", - NULL, - FLAG_ALWAYS - }, - { - STATS_LINK, - "called_for_link", - "called for link", - NULL, - 0 - }, - { - STATS_PREPROCESSING, - "called_for_preprocessing", - "called for preprocessing", - NULL, - 0 - }, - { - STATS_MULTIPLE, - "multiple_source_files", - "multiple source files", - NULL, - 0 - }, - { - STATS_STDOUT, - "compiler_produced_stdout", - "compiler produced stdout", - NULL, - 0 - }, - { - STATS_NOOUTPUT, - "compiler_produced_no_output", - "compiler produced no output", - NULL, - 0 - }, - { - STATS_EMPTYOUTPUT, - "compiler_produced_empty_output", - "compiler produced empty output", - NULL, - 0 - }, - { - STATS_STATUS, - "compile_failed", - "compile failed", - NULL, - 0 - }, - { - STATS_ERROR, - "internal_error", - "ccache internal error", - NULL, - 0 - }, - { - STATS_PREPROCESSOR, - "preprocessor_error", - "preprocessor error", - NULL, - 0 - }, - { - STATS_CANTUSEPCH, - "could_not_use_precompiled_header", - "can't use precompiled header", - NULL, - 0 - }, - { - STATS_COMPILER, - "could_not_find_compiler", - "couldn't find the compiler", - NULL, - 0 - }, - { - STATS_MISSING, - "missing_cache_file", - "cache file missing", - NULL, - 0 - }, - { - STATS_ARGS, - "bad_compiler_arguments", - "bad compiler arguments", - NULL, - 0 - }, - { - STATS_SOURCELANG, - "unsupported_source_language", - "unsupported source language", - NULL, - 0 - }, - { - STATS_COMPCHECK, - "compiler_check_failed", - "compiler check failed", - NULL, - 0 - }, - { - STATS_CONFTEST, - "autoconf_test", - "autoconf compile/link", - NULL, - 0 - }, - { - STATS_UNSUPPORTED_OPTION, - "unsupported_compiler_option", - "unsupported compiler option", - NULL, - 0 - }, - { - STATS_UNSUPPORTED_DIRECTIVE, - "unsupported_code_directive", - "unsupported code directive", - NULL, - 0 - }, - { - STATS_OUTSTDOUT, - "output_to_stdout", - "output to stdout", - NULL, - 0 - }, - { - STATS_BADOUTPUTFILE, - "bad_output_file", - "could not write to output file", - NULL, - 0 - }, - { - STATS_NOINPUT, - "no_input_file", - "no input file", - NULL, - 0 - }, - { - STATS_BADEXTRAFILE, - "error_hashing_extra_file", - "error hashing extra file", - NULL, - 0 - }, - { - STATS_NUMCLEANUPS, - "cleanups_performed", - "cleanups performed", - NULL, - FLAG_ALWAYS - }, - { - STATS_NUMFILES, - "files_in_cache", - "files in cache", - NULL, - FLAG_NOZERO|FLAG_ALWAYS - }, - { - STATS_TOTALSIZE, - "cache_size_kibibyte", - "cache size", - format_size_times_1024, - FLAG_NOZERO|FLAG_ALWAYS - }, - { - STATS_OBSOLETE_MAXFILES, - "OBSOLETE", - "OBSOLETE", - NULL, - FLAG_NOZERO|FLAG_NEVER - }, - { - STATS_OBSOLETE_MAXSIZE, - "OBSOLETE", - "OBSOLETE", - NULL, - FLAG_NOZERO|FLAG_NEVER - }, - { - STATS_NONE, - NULL, - NULL, - NULL, - 0 - } -}; - -static char * + {STATS_ZEROTIMESTAMP, + "stats_zeroed_timestamp", + "stats zeroed", + format_timestamp, + FLAG_ALWAYS}, + {STATS_CACHEHIT_DIR, + "direct_cache_hit", + "cache hit (direct)", + NULL, + FLAG_ALWAYS}, + {STATS_CACHEHIT_CPP, + "preprocessed_cache_hit", + "cache hit (preprocessed)", + NULL, + FLAG_ALWAYS}, + {STATS_TOCACHE, "cache_miss", "cache miss", NULL, FLAG_ALWAYS}, + {STATS_LINK, "called_for_link", "called for link", NULL, 0}, + {STATS_PREPROCESSING, + "called_for_preprocessing", + "called for preprocessing", + NULL, + 0}, + {STATS_MULTIPLE, "multiple_source_files", "multiple source files", NULL, 0}, + {STATS_STDOUT, + "compiler_produced_stdout", + "compiler produced stdout", + NULL, + 0}, + {STATS_NOOUTPUT, + "compiler_produced_no_output", + "compiler produced no output", + NULL, + 0}, + {STATS_EMPTYOUTPUT, + "compiler_produced_empty_output", + "compiler produced empty output", + NULL, + 0}, + {STATS_STATUS, "compile_failed", "compile failed", NULL, 0}, + {STATS_ERROR, "internal_error", "ccache internal error", NULL, 0}, + {STATS_PREPROCESSOR, "preprocessor_error", "preprocessor error", NULL, 0}, + {STATS_CANTUSEPCH, + "could_not_use_precompiled_header", + "can't use precompiled header", + NULL, + 0}, + {STATS_COMPILER, + "could_not_find_compiler", + "couldn't find the compiler", + NULL, + 0}, + {STATS_MISSING, "missing_cache_file", "cache file missing", NULL, 0}, + {STATS_ARGS, "bad_compiler_arguments", "bad compiler arguments", NULL, 0}, + {STATS_SOURCELANG, + "unsupported_source_language", + "unsupported source language", + NULL, + 0}, + {STATS_COMPCHECK, "compiler_check_failed", "compiler check failed", NULL, 0}, + {STATS_CONFTEST, "autoconf_test", "autoconf compile/link", NULL, 0}, + {STATS_UNSUPPORTED_OPTION, + "unsupported_compiler_option", + "unsupported compiler option", + NULL, + 0}, + {STATS_UNSUPPORTED_DIRECTIVE, + "unsupported_code_directive", + "unsupported code directive", + NULL, + 0}, + {STATS_OUTSTDOUT, "output_to_stdout", "output to stdout", NULL, 0}, + {STATS_BADOUTPUTFILE, + "bad_output_file", + "could not write to output file", + NULL, + 0}, + {STATS_NOINPUT, "no_input_file", "no input file", NULL, 0}, + {STATS_BADEXTRAFILE, + "error_hashing_extra_file", + "error hashing extra file", + NULL, + 0}, + {STATS_NUMCLEANUPS, + "cleanups_performed", + "cleanups performed", + NULL, + FLAG_ALWAYS}, + {STATS_NUMFILES, + "files_in_cache", + "files in cache", + NULL, + FLAG_NOZERO | FLAG_ALWAYS}, + {STATS_TOTALSIZE, + "cache_size_kibibyte", + "cache size", + format_size_times_1024, + FLAG_NOZERO | FLAG_ALWAYS}, + {STATS_OBSOLETE_MAXFILES, + "OBSOLETE", + "OBSOLETE", + NULL, + FLAG_NOZERO | FLAG_NEVER}, + {STATS_OBSOLETE_MAXSIZE, + "OBSOLETE", + "OBSOLETE", + NULL, + FLAG_NOZERO | FLAG_NEVER}, + {STATS_NONE, NULL, NULL, NULL, 0}}; + +static char* format_size(uint64_t size) { - char *s = format_human_readable_size(size); - reformat(&s, "%11s", s); - return s; + char* s = format_human_readable_size(size); + reformat(&s, "%11s", s); + return s; } -static char * +static char* format_size_times_1024(uint64_t size) { - return format_size(size * 1024); + return format_size(size * 1024); } -static char * +static char* format_timestamp(uint64_t timestamp) { - if (timestamp > 0) { - struct tm tm; - localtime_r((time_t *)×tamp, &tm); - char buffer[100]; - strftime(buffer, sizeof(buffer), "%c", &tm); - return format(" %s", buffer); - } else { - return NULL; - } + if (timestamp > 0) { + struct tm tm; + localtime_r((time_t*)×tamp, &tm); + char buffer[100]; + strftime(buffer, sizeof(buffer), "%c", &tm); + return format(" %s", buffer); + } else { + return NULL; + } } // Parse a stats file from a buffer, adding to the counters. static void -parse_stats(struct counters *counters, const char *buf) +parse_stats(struct counters* counters, const char* buf) { - size_t i = 0; - const char *p = buf; - while (true) { - char *p2; - long val = strtol(p, &p2, 10); - if (p2 == p) { - break; - } - if (counters->size < i + 1) { - counters_resize(counters, i + 1); - } - counters->data[i] += val; - i++; - p = p2; - } + size_t i = 0; + const char* p = buf; + while (true) { + char* p2; + long val = strtol(p, &p2, 10); + if (p2 == p) { + break; + } + if (counters->size < i + 1) { + counters_resize(counters, i + 1); + } + counters->data[i] += val; + i++; + p = p2; + } } // Write out a stats file. void -stats_write(const char *path, struct counters *counters) +stats_write(const char* path, struct counters* counters) { - char *tmp_file = format("%s.tmp", path); - FILE *f = create_tmp_file(&tmp_file, "wb"); - for (size_t i = 0; i < counters->size; i++) { - if (fprintf(f, "%u\n", counters->data[i]) < 0) { - fatal("Failed to write to %s", tmp_file); - } - } - fclose(f); - x_rename(tmp_file, path); - free(tmp_file); + char* tmp_file = format("%s.tmp", path); + FILE* f = create_tmp_file(&tmp_file, "wb"); + for (size_t i = 0; i < counters->size; i++) { + if (fprintf(f, "%u\n", counters->data[i]) < 0) { + fatal("Failed to write to %s", tmp_file); + } + } + fclose(f); + x_rename(tmp_file, path); + free(tmp_file); } static void init_counter_updates(void) { - if (!counter_updates) { - counter_updates = counters_init(STATS_END); - } + if (!counter_updates) { + counter_updates = counters_init(STATS_END); + } } static double -stats_hit_rate(struct counters *counters) +stats_hit_rate(struct counters* counters) { - unsigned direct = counters->data[STATS_CACHEHIT_DIR]; - unsigned preprocessed = counters->data[STATS_CACHEHIT_CPP]; - unsigned hit = direct + preprocessed; - unsigned miss = counters->data[STATS_TOCACHE]; - unsigned total = hit + miss; - return total > 0 ? (100.0 * hit) / total : 0.0; + unsigned direct = counters->data[STATS_CACHEHIT_DIR]; + unsigned preprocessed = counters->data[STATS_CACHEHIT_CPP]; + unsigned hit = direct + preprocessed; + unsigned miss = counters->data[STATS_TOCACHE]; + unsigned total = hit + miss; + return total > 0 ? (100.0 * hit) / total : 0.0; } static void -stats_collect(struct counters *counters, time_t *last_updated) +stats_collect(struct counters* counters, time_t* last_updated) { - struct stat st; - unsigned zero_timestamp = 0; - - *last_updated = 0; - - // Add up the stats in each directory. - for (int dir = -1; dir <= 0xF; dir++) { - char *fname; - - if (dir == -1) { - fname = format("%s/stats", conf->cache_dir); - } else { - fname = format("%s/%1x/stats", conf->cache_dir, dir); - } - - counters->data[STATS_ZEROTIMESTAMP] = 0; // Don't add - stats_read(fname, counters); - zero_timestamp = MAX(counters->data[STATS_ZEROTIMESTAMP], zero_timestamp); - if (stat(fname, &st) == 0 && st.st_mtime > *last_updated) { - *last_updated = st.st_mtime; - } - free(fname); - } - - counters->data[STATS_ZEROTIMESTAMP] = zero_timestamp; + struct stat st; + unsigned zero_timestamp = 0; + + *last_updated = 0; + + // Add up the stats in each directory. + for (int dir = -1; dir <= 0xF; dir++) { + char* fname; + + if (dir == -1) { + fname = format("%s/stats", conf->cache_dir); + } else { + fname = format("%s/%1x/stats", conf->cache_dir, dir); + } + + counters->data[STATS_ZEROTIMESTAMP] = 0; // Don't add + stats_read(fname, counters); + zero_timestamp = MAX(counters->data[STATS_ZEROTIMESTAMP], zero_timestamp); + if (stat(fname, &st) == 0 && st.st_mtime > *last_updated) { + *last_updated = st.st_mtime; + } + free(fname); + } + + counters->data[STATS_ZEROTIMESTAMP] = zero_timestamp; } // Record that a number of bytes and files have been added to the cache. Size // is in bytes. void -stats_update_size(const char *sfile, int64_t size, int files) +stats_update_size(const char* sfile, int64_t size, int files) { - struct counters *updates; - if (sfile == stats_file) { - init_counter_updates(); - updates = counter_updates; - } else { - updates = counters_init(STATS_END); - } - updates->data[STATS_NUMFILES] += files; - updates->data[STATS_TOTALSIZE] += size / 1024; - if (sfile != stats_file) { - stats_flush_to_file(sfile, updates); - counters_free(updates); - } + struct counters* updates; + if (sfile == stats_file) { + init_counter_updates(); + updates = counter_updates; + } else { + updates = counters_init(STATS_END); + } + updates->data[STATS_NUMFILES] += files; + updates->data[STATS_TOTALSIZE] += size / 1024; + if (sfile != stats_file) { + stats_flush_to_file(sfile, updates); + counters_free(updates); + } } // Read in the stats from one directory and add to the counters. void -stats_read(const char *sfile, struct counters *counters) +stats_read(const char* sfile, struct counters* counters) { - char *data = read_text_file(sfile, 1024); - if (data) { - parse_stats(counters, data); - } - free(data); + char* data = read_text_file(sfile, 1024); + if (data) { + parse_stats(counters, data); + } + free(data); } // Write counter updates in updates to sfile. static void -stats_flush_to_file(const char *sfile, struct counters *updates) +stats_flush_to_file(const char* sfile, struct counters* updates) { - assert(conf); - - if (!conf->stats) { - return; - } - - if (!updates) { - return; - } - - bool should_flush = false; - for (int i = 0; i < STATS_END; ++i) { - if (updates->data[i] > 0) { - should_flush = true; - break; - } - } - if (!should_flush) { - return; - } - - if (!sfile) { - char *stats_dir; - - // A NULL sfile means that we didn't get past calculate_object_hash(), so - // we just choose one of stats files in the 16 subdirectories. - stats_dir = format("%s/%x", conf->cache_dir, hash_from_int(getpid()) % 16); - sfile = format("%s/stats", stats_dir); - free(stats_dir); - } - - if (!lockfile_acquire(sfile, lock_staleness_limit)) { - return; - } - - struct counters *counters = counters_init(STATS_END); - stats_read(sfile, counters); - for (int i = 0; i < STATS_END; ++i) { - counters->data[i] += updates->data[i]; - } - stats_write(sfile, counters); - lockfile_release(sfile); - - if (!str_eq(conf->log_file, "") || conf->debug) { - for (int i = 0; i < STATS_END; ++i) { - if (updates->data[stats_info[i].stat] != 0 - && !(stats_info[i].flags & FLAG_NOZERO)) { - cc_log("Result: %s", stats_info[i].message); - } - } - } - - char *subdir = x_dirname(sfile); - bool need_cleanup = false; - - if (conf->max_files != 0 - && counters->data[STATS_NUMFILES] > conf->max_files / 16) { - cc_log("Need to clean up %s since it holds %u files (limit: %u files)", - subdir, - counters->data[STATS_NUMFILES], - conf->max_files / 16); - need_cleanup = true; - } - if (conf->max_size != 0 - && counters->data[STATS_TOTALSIZE] > conf->max_size / 1024 / 16) { - cc_log("Need to clean up %s since it holds %u KiB (limit: %lu KiB)", - subdir, - counters->data[STATS_TOTALSIZE], - (unsigned long)conf->max_size / 1024 / 16); - need_cleanup = true; - } - - if (need_cleanup) { - clean_up_dir(conf, subdir, conf->limit_multiple); - } - - free(subdir); - counters_free(counters); + assert(conf); + + if (!conf->stats) { + return; + } + + if (!updates) { + return; + } + + bool should_flush = false; + for (int i = 0; i < STATS_END; ++i) { + if (updates->data[i] > 0) { + should_flush = true; + break; + } + } + if (!should_flush) { + return; + } + + if (!sfile) { + char* stats_dir; + + // A NULL sfile means that we didn't get past calculate_object_hash(), so + // we just choose one of stats files in the 16 subdirectories. + stats_dir = format("%s/%x", conf->cache_dir, hash_from_int(getpid()) % 16); + sfile = format("%s/stats", stats_dir); + free(stats_dir); + } + + if (!lockfile_acquire(sfile, lock_staleness_limit)) { + return; + } + + struct counters* counters = counters_init(STATS_END); + stats_read(sfile, counters); + for (int i = 0; i < STATS_END; ++i) { + counters->data[i] += updates->data[i]; + } + stats_write(sfile, counters); + lockfile_release(sfile); + + if (!str_eq(conf->log_file, "") || conf->debug) { + for (int i = 0; i < STATS_END; ++i) { + if (updates->data[stats_info[i].stat] != 0 + && !(stats_info[i].flags & FLAG_NOZERO)) { + cc_log("Result: %s", stats_info[i].message); + } + } + } + + char* subdir = x_dirname(sfile); + bool need_cleanup = false; + + if (conf->max_files != 0 + && counters->data[STATS_NUMFILES] > conf->max_files / 16) { + cc_log("Need to clean up %s since it holds %u files (limit: %u files)", + subdir, + counters->data[STATS_NUMFILES], + conf->max_files / 16); + need_cleanup = true; + } + if (conf->max_size != 0 + && counters->data[STATS_TOTALSIZE] > conf->max_size / 1024 / 16) { + cc_log("Need to clean up %s since it holds %u KiB (limit: %lu KiB)", + subdir, + counters->data[STATS_TOTALSIZE], + (unsigned long)conf->max_size / 1024 / 16); + need_cleanup = true; + } + + if (need_cleanup) { + clean_up_dir(conf, subdir, conf->limit_multiple); + } + + free(subdir); + counters_free(counters); } // Write counter updates in counter_updates to disk. void stats_flush(void) { - stats_flush_to_file(stats_file, counter_updates); - counters_free(counter_updates); - counter_updates = NULL; + stats_flush_to_file(stats_file, counter_updates); + counters_free(counter_updates); + counter_updates = NULL; } // Update a normal stat. void stats_update(enum stats stat) { - assert(stat > STATS_NONE && stat < STATS_END); - init_counter_updates(); - counter_updates->data[stat]++; + assert(stat > STATS_NONE && stat < STATS_END); + init_counter_updates(); + counter_updates->data[stat]++; } // Get the pending update of a counter value. unsigned stats_get_pending(enum stats stat) { - init_counter_updates(); - return counter_updates->data[stat]; + init_counter_updates(); + return counter_updates->data[stat]; } // Sum and display the total stats for all cache dirs. void stats_summary(void) { - assert(conf); - - struct counters *counters = counters_init(STATS_END); - time_t last_updated; - stats_collect(counters, &last_updated); - - printf("cache directory %s\n", conf->cache_dir); - printf("primary config %s\n", - primary_config_path ? primary_config_path : ""); - printf("secondary config (readonly) %s\n", - secondary_config_path ? secondary_config_path : ""); - if (last_updated > 0) { - struct tm tm; - localtime_r(&last_updated, &tm); - char timestamp[100]; - strftime(timestamp, sizeof(timestamp), "%c", &tm); - printf("stats updated %s\n", timestamp); - } - - // ...and display them. - for (int i = 0; stats_info[i].message; i++) { - enum stats stat = stats_info[i].stat; - - if (stats_info[i].flags & FLAG_NEVER) { - continue; - } - if (counters->data[stat] == 0 && !(stats_info[i].flags & FLAG_ALWAYS)) { - continue; - } - - char *value; - if (stats_info[i].format) { - value = stats_info[i].format(counters->data[stat]); - } else { - value = format("%8u", counters->data[stat]); - } - if (value) { - printf("%-31s %s\n", stats_info[i].message, value); - free(value); - } - - if (stat == STATS_TOCACHE) { - double percent = stats_hit_rate(counters); - printf("cache hit rate %6.2f %%\n", percent); - } - } - - if (conf->max_files != 0) { - printf("max files %8u\n", conf->max_files); - } - if (conf->max_size != 0) { - char *value = format_size(conf->max_size); - printf("max cache size %s\n", value); - free(value); - } - - counters_free(counters); + assert(conf); + + struct counters* counters = counters_init(STATS_END); + time_t last_updated; + stats_collect(counters, &last_updated); + + printf("cache directory %s\n", conf->cache_dir); + printf("primary config %s\n", + primary_config_path ? primary_config_path : ""); + printf("secondary config (readonly) %s\n", + secondary_config_path ? secondary_config_path : ""); + if (last_updated > 0) { + struct tm tm; + localtime_r(&last_updated, &tm); + char timestamp[100]; + strftime(timestamp, sizeof(timestamp), "%c", &tm); + printf("stats updated %s\n", timestamp); + } + + // ...and display them. + for (int i = 0; stats_info[i].message; i++) { + enum stats stat = stats_info[i].stat; + + if (stats_info[i].flags & FLAG_NEVER) { + continue; + } + if (counters->data[stat] == 0 && !(stats_info[i].flags & FLAG_ALWAYS)) { + continue; + } + + char* value; + if (stats_info[i].format) { + value = stats_info[i].format(counters->data[stat]); + } else { + value = format("%8u", counters->data[stat]); + } + if (value) { + printf("%-31s %s\n", stats_info[i].message, value); + free(value); + } + + if (stat == STATS_TOCACHE) { + double percent = stats_hit_rate(counters); + printf("cache hit rate %6.2f %%\n", percent); + } + } + + if (conf->max_files != 0) { + printf("max files %8u\n", conf->max_files); + } + if (conf->max_size != 0) { + char* value = format_size(conf->max_size); + printf("max cache size %s\n", value); + free(value); + } + + counters_free(counters); } // Print machine-parsable (tab-separated) statistics counters. void stats_print(void) { - assert(conf); + assert(conf); - struct counters *counters = counters_init(STATS_END); - time_t last_updated; - stats_collect(counters, &last_updated); + struct counters* counters = counters_init(STATS_END); + time_t last_updated; + stats_collect(counters, &last_updated); - printf("stats_updated_timestamp\t%llu\n", (unsigned long long)last_updated); + printf("stats_updated_timestamp\t%llu\n", (unsigned long long)last_updated); - for (int i = 0; stats_info[i].message; i++) { - if (!(stats_info[i].flags & FLAG_NEVER)) { - printf("%s\t%u\n", stats_info[i].id, counters->data[stats_info[i].stat]); - } - } + for (int i = 0; stats_info[i].message; i++) { + if (!(stats_info[i].flags & FLAG_NEVER)) { + printf("%s\t%u\n", stats_info[i].id, counters->data[stats_info[i].stat]); + } + } - counters_free(counters); + counters_free(counters); } // Zero all the stats structures. void stats_zero(void) { - assert(conf); - - char *fname = format("%s/stats", conf->cache_dir); - x_unlink(fname); - free(fname); - - time_t timestamp = time(NULL); - - for (int dir = 0; dir <= 0xF; dir++) { - struct counters *counters = counters_init(STATS_END); - struct stat st; - fname = format("%s/%1x/stats", conf->cache_dir, dir); - if (stat(fname, &st) != 0) { - // No point in trying to reset the stats file if it doesn't exist. - free(fname); - continue; - } - if (lockfile_acquire(fname, lock_staleness_limit)) { - stats_read(fname, counters); - for (unsigned i = 0; stats_info[i].message; i++) { - if (!(stats_info[i].flags & FLAG_NOZERO)) { - counters->data[stats_info[i].stat] = 0; - } - } - counters->data[STATS_ZEROTIMESTAMP] = timestamp; - stats_write(fname, counters); - lockfile_release(fname); - } - counters_free(counters); - free(fname); - } + assert(conf); + + char* fname = format("%s/stats", conf->cache_dir); + x_unlink(fname); + free(fname); + + time_t timestamp = time(NULL); + + for (int dir = 0; dir <= 0xF; dir++) { + struct counters* counters = counters_init(STATS_END); + struct stat st; + fname = format("%s/%1x/stats", conf->cache_dir, dir); + if (stat(fname, &st) != 0) { + // No point in trying to reset the stats file if it doesn't exist. + free(fname); + continue; + } + if (lockfile_acquire(fname, lock_staleness_limit)) { + stats_read(fname, counters); + for (unsigned i = 0; stats_info[i].message; i++) { + if (!(stats_info[i].flags & FLAG_NOZERO)) { + counters->data[stats_info[i].stat] = 0; + } + } + counters->data[STATS_ZEROTIMESTAMP] = timestamp; + stats_write(fname, counters); + lockfile_release(fname); + } + counters_free(counters); + free(fname); + } } // Get the per-directory limits. void -stats_get_obsolete_limits(const char *dir, unsigned *maxfiles, - uint64_t *maxsize) +stats_get_obsolete_limits(const char* dir, + unsigned* maxfiles, + uint64_t* maxsize) { - struct counters *counters = counters_init(STATS_END); - char *sname = format("%s/stats", dir); - stats_read(sname, counters); - *maxfiles = counters->data[STATS_OBSOLETE_MAXFILES]; - *maxsize = (uint64_t)counters->data[STATS_OBSOLETE_MAXSIZE] * 1024; - free(sname); - counters_free(counters); + struct counters* counters = counters_init(STATS_END); + char* sname = format("%s/stats", dir); + stats_read(sname, counters); + *maxfiles = counters->data[STATS_OBSOLETE_MAXFILES]; + *maxsize = (uint64_t)counters->data[STATS_OBSOLETE_MAXSIZE] * 1024; + free(sname); + counters_free(counters); } // Set the per-directory sizes. void -stats_set_sizes(const char *dir, unsigned num_files, uint64_t total_size) +stats_set_sizes(const char* dir, unsigned num_files, uint64_t total_size) { - struct counters *counters = counters_init(STATS_END); - char *statsfile = format("%s/stats", dir); - if (lockfile_acquire(statsfile, lock_staleness_limit)) { - stats_read(statsfile, counters); - counters->data[STATS_NUMFILES] = num_files; - counters->data[STATS_TOTALSIZE] = total_size / 1024; - stats_write(statsfile, counters); - lockfile_release(statsfile); - } - free(statsfile); - counters_free(counters); + struct counters* counters = counters_init(STATS_END); + char* statsfile = format("%s/stats", dir); + if (lockfile_acquire(statsfile, lock_staleness_limit)) { + stats_read(statsfile, counters); + counters->data[STATS_NUMFILES] = num_files; + counters->data[STATS_TOTALSIZE] = total_size / 1024; + stats_write(statsfile, counters); + lockfile_release(statsfile); + } + free(statsfile); + counters_free(counters); } // Count directory cleanup run. void -stats_add_cleanup(const char *dir, unsigned count) +stats_add_cleanup(const char* dir, unsigned count) { - struct counters *counters = counters_init(STATS_END); - char *statsfile = format("%s/stats", dir); - if (lockfile_acquire(statsfile, lock_staleness_limit)) { - stats_read(statsfile, counters); - counters->data[STATS_NUMCLEANUPS] += count; - stats_write(statsfile, counters); - lockfile_release(statsfile); - } - free(statsfile); - counters_free(counters); + struct counters* counters = counters_init(STATS_END); + char* statsfile = format("%s/stats", dir); + if (lockfile_acquire(statsfile, lock_staleness_limit)) { + stats_read(statsfile, counters); + counters->data[STATS_NUMCLEANUPS] += count; + stats_write(statsfile, counters); + lockfile_release(statsfile); + } + free(statsfile); + counters_free(counters); } diff --git a/src/system.hpp b/src/system.hpp index 72a98dbf..e95f3e72 100644 --- a/src/system.hpp +++ b/src/system.hpp @@ -30,12 +30,12 @@ #include <sys/file.h> #ifdef HAVE_SYS_MMAN_H -#include <sys/mman.h> +# include <sys/mman.h> #endif #include <sys/stat.h> #include <sys/types.h> #ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> +# include <sys/wait.h> #endif #include <assert.h> @@ -64,29 +64,29 @@ extern int usleep(useconds_t); #endif -extern char **environ; +extern char** environ; #ifndef ESTALE -#define ESTALE -1 +# define ESTALE -1 #endif extern "C" { #if !HAVE_VSNPRINTF - int rpl_vsnprintf(char *, size_t, const char *, va_list); - #define vsnprintf rpl_vsnprintf +int rpl_vsnprintf(char*, size_t, const char*, va_list); +# define vsnprintf rpl_vsnprintf #endif #if !HAVE_SNPRINTF - int rpl_snprintf(char *, size_t, const char *, ...); - #define snprintf rpl_snprintf +int rpl_snprintf(char*, size_t, const char*, ...); +# define snprintf rpl_snprintf #endif #if !HAVE_VASPRINTF - int rpl_vasprintf(char **, const char *, va_list); - #define vasprintf rpl_vasprintf +int rpl_vasprintf(char**, const char*, va_list); +# define vasprintf rpl_vasprintf #endif #if !HAVE_ASPRINTF - int rpl_asprintf(char **, const char *, ...); - #define asprintf rpl_asprintf +int rpl_asprintf(char**, const char*, ...); +# define asprintf rpl_asprintf #endif } // extern "C" diff --git a/src/unify.cpp b/src/unify.cpp index e2f700e2..7a7744ed 100644 --- a/src/unify.cpp +++ b/src/unify.cpp @@ -29,236 +29,235 @@ // syntactic errors is important to cope with C/C++ extensions in the local // compiler (for example, inline assembly systems). +#include "unify.hpp" + #include "ccache.hpp" #include "hash.hpp" -#include "unify.hpp" static bool print_unified = true; -static const char *const s_tokens[] = { - "...", ">>=", "<<=", "+=", "-=", "*=", "/=", "%=", "&=", "^=", - "|=", ">>", "<<", "++", "--", "->", "&&", "||", "<=", ">=", - "==", "!=", ";", "{", "<%", "}", "%>", ",", ":", "=", - "(", ")", "[", "<:", "]", ":>", ".", "&", "!", "~", - "-", "+", "*", "/", "%", "<", ">", "^", "|", "?", - 0 -}; +static const char* const s_tokens[] = { + "...", ">>=", "<<=", "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", + ">>", "<<", "++", "--", "->", "&&", "||", "<=", ">=", "==", "!=", + ";", "{", "<%", "}", "%>", ",", ":", "=", "(", ")", "[", + "<:", "]", ":>", ".", "&", "!", "~", "-", "+", "*", "/", + "%", "<", ">", "^", "|", "?", 0}; #define C_ALPHA 1 #define C_SPACE 2 #define C_TOKEN 4 #define C_QUOTE 8 #define C_DIGIT 16 -#define C_HEX 32 +#define C_HEX 32 #define C_FLOAT 64 -#define C_SIGN 128 +#define C_SIGN 128 -static struct { - unsigned char type; - unsigned char num_toks; - const char *toks[7]; +static struct +{ + unsigned char type; + unsigned char num_toks; + const char* toks[7]; } tokens[256]; // Build up the table used by the unifier. static void build_table(void) { - static bool done; - if (done) { - return; - } - done = true; + static bool done; + if (done) { + return; + } + done = true; - memset(tokens, 0, sizeof(tokens)); - for (unsigned char c = 0; c < 128; c++) { - if (isalpha(c) || c == '_') { - tokens[c].type |= C_ALPHA; - } - if (isdigit(c)) { - tokens[c].type |= C_DIGIT; - } - if (isspace(c)) { - tokens[c].type |= C_SPACE; - } - if (isxdigit(c)) { - tokens[c].type |= C_HEX; - } - } - tokens[static_cast<unsigned char>('\'')].type |= C_QUOTE; - tokens[static_cast<unsigned char>('"')].type |= C_QUOTE; - tokens[static_cast<unsigned char>('l')].type |= C_FLOAT; - tokens[static_cast<unsigned char>('L')].type |= C_FLOAT; - tokens[static_cast<unsigned char>('f')].type |= C_FLOAT; - tokens[static_cast<unsigned char>('F')].type |= C_FLOAT; - tokens[static_cast<unsigned char>('U')].type |= C_FLOAT; - tokens[static_cast<unsigned char>('u')].type |= C_FLOAT; + memset(tokens, 0, sizeof(tokens)); + for (unsigned char c = 0; c < 128; c++) { + if (isalpha(c) || c == '_') { + tokens[c].type |= C_ALPHA; + } + if (isdigit(c)) { + tokens[c].type |= C_DIGIT; + } + if (isspace(c)) { + tokens[c].type |= C_SPACE; + } + if (isxdigit(c)) { + tokens[c].type |= C_HEX; + } + } + tokens[static_cast<unsigned char>('\'')].type |= C_QUOTE; + tokens[static_cast<unsigned char>('"')].type |= C_QUOTE; + tokens[static_cast<unsigned char>('l')].type |= C_FLOAT; + tokens[static_cast<unsigned char>('L')].type |= C_FLOAT; + tokens[static_cast<unsigned char>('f')].type |= C_FLOAT; + tokens[static_cast<unsigned char>('F')].type |= C_FLOAT; + tokens[static_cast<unsigned char>('U')].type |= C_FLOAT; + tokens[static_cast<unsigned char>('u')].type |= C_FLOAT; - tokens[static_cast<unsigned char>('-')].type |= C_SIGN; - tokens[static_cast<unsigned char>('+')].type |= C_SIGN; + tokens[static_cast<unsigned char>('-')].type |= C_SIGN; + tokens[static_cast<unsigned char>('+')].type |= C_SIGN; - for (int i = 0; s_tokens[i]; i++) { - unsigned char c = s_tokens[i][0]; - tokens[c].type |= C_TOKEN; - tokens[c].toks[tokens[c].num_toks] = s_tokens[i]; - tokens[c].num_toks++; - } + for (int i = 0; s_tokens[i]; i++) { + unsigned char c = s_tokens[i][0]; + tokens[c].type |= C_TOKEN; + tokens[c].toks[tokens[c].num_toks] = s_tokens[i]; + tokens[c].num_toks++; + } } // Buffer up characters before hashing them. static void -pushchar(struct hash *hash, unsigned char c) +pushchar(struct hash* hash, unsigned char c) { - static unsigned char buf[64]; - static size_t len; + static unsigned char buf[64]; + static size_t len; - if (c == 0) { - if (len > 0) { - hash_buffer(hash, (char *)buf, len); - if (print_unified) { - printf("%.*s", (int) len, buf); - } - len = 0; - } - return; - } + if (c == 0) { + if (len > 0) { + hash_buffer(hash, (char*)buf, len); + if (print_unified) { + printf("%.*s", (int)len, buf); + } + len = 0; + } + return; + } - buf[len++] = c; - if (len == 64) { - hash_buffer(hash, (char *)buf, len); - if (print_unified) { - printf("%.*s", (int) len, buf); - } - len = 0; - } + buf[len++] = c; + if (len == 64) { + hash_buffer(hash, (char*)buf, len); + if (print_unified) { + printf("%.*s", (int)len, buf); + } + len = 0; + } } // Hash some C/C++ code after unifying. static void -unify(struct hash *hash, unsigned char *p, size_t size) +unify(struct hash* hash, unsigned char* p, size_t size) { - build_table(); + build_table(); - for (size_t ofs = 0; ofs < size;) { - if (p[ofs] == '#') { - if ((size-ofs) > 2 && p[ofs+1] == ' ' && isdigit(p[ofs+2])) { - do { - ofs++; - } while (ofs < size && p[ofs] != '\n'); - ofs++; - } else { - do { - pushchar(hash, p[ofs]); - ofs++; - } while (ofs < size && p[ofs] != '\n'); - pushchar(hash, '\n'); - ofs++; - } - continue; - } + for (size_t ofs = 0; ofs < size;) { + if (p[ofs] == '#') { + if ((size - ofs) > 2 && p[ofs + 1] == ' ' && isdigit(p[ofs + 2])) { + do { + ofs++; + } while (ofs < size && p[ofs] != '\n'); + ofs++; + } else { + do { + pushchar(hash, p[ofs]); + ofs++; + } while (ofs < size && p[ofs] != '\n'); + pushchar(hash, '\n'); + ofs++; + } + continue; + } - if (tokens[p[ofs]].type & C_ALPHA) { - do { - pushchar(hash, p[ofs]); - ofs++; - } while (ofs < size && (tokens[p[ofs]].type & (C_ALPHA|C_DIGIT))); - pushchar(hash, '\n'); - continue; - } + if (tokens[p[ofs]].type & C_ALPHA) { + do { + pushchar(hash, p[ofs]); + ofs++; + } while (ofs < size && (tokens[p[ofs]].type & (C_ALPHA | C_DIGIT))); + pushchar(hash, '\n'); + continue; + } - if (tokens[p[ofs]].type & C_DIGIT) { - do { - pushchar(hash, p[ofs]); - ofs++; - } while (ofs < size && - ((tokens[p[ofs]].type & C_DIGIT) || p[ofs] == '.')); - if (ofs < size && (p[ofs] == 'x' || p[ofs] == 'X')) { - do { - pushchar(hash, p[ofs]); - ofs++; - } while (ofs < size && (tokens[p[ofs]].type & C_HEX)); - } - if (ofs < size && (p[ofs] == 'E' || p[ofs] == 'e')) { - pushchar(hash, p[ofs]); - ofs++; - while (ofs < size && (tokens[p[ofs]].type & (C_DIGIT|C_SIGN))) { - pushchar(hash, p[ofs]); - ofs++; - } - } - while (ofs < size && (tokens[p[ofs]].type & C_FLOAT)) { - pushchar(hash, p[ofs]); - ofs++; - } - pushchar(hash, '\n'); - continue; - } + if (tokens[p[ofs]].type & C_DIGIT) { + do { + pushchar(hash, p[ofs]); + ofs++; + } while (ofs < size + && ((tokens[p[ofs]].type & C_DIGIT) || p[ofs] == '.')); + if (ofs < size && (p[ofs] == 'x' || p[ofs] == 'X')) { + do { + pushchar(hash, p[ofs]); + ofs++; + } while (ofs < size && (tokens[p[ofs]].type & C_HEX)); + } + if (ofs < size && (p[ofs] == 'E' || p[ofs] == 'e')) { + pushchar(hash, p[ofs]); + ofs++; + while (ofs < size && (tokens[p[ofs]].type & (C_DIGIT | C_SIGN))) { + pushchar(hash, p[ofs]); + ofs++; + } + } + while (ofs < size && (tokens[p[ofs]].type & C_FLOAT)) { + pushchar(hash, p[ofs]); + ofs++; + } + pushchar(hash, '\n'); + continue; + } - if (tokens[p[ofs]].type & C_SPACE) { - do { - ofs++; - } while (ofs < size && (tokens[p[ofs]].type & C_SPACE)); - continue; - } + if (tokens[p[ofs]].type & C_SPACE) { + do { + ofs++; + } while (ofs < size && (tokens[p[ofs]].type & C_SPACE)); + continue; + } - if (tokens[p[ofs]].type & C_QUOTE) { - unsigned char q = p[ofs]; - pushchar(hash, p[ofs]); - do { - ofs++; - while (ofs < size-1 && p[ofs] == '\\') { - pushchar(hash, p[ofs]); - pushchar(hash, p[ofs+1]); - ofs += 2; - } - pushchar(hash, p[ofs]); - } while (ofs < size && p[ofs] != q); - pushchar(hash, '\n'); - ofs++; - continue; - } + if (tokens[p[ofs]].type & C_QUOTE) { + unsigned char q = p[ofs]; + pushchar(hash, p[ofs]); + do { + ofs++; + while (ofs < size - 1 && p[ofs] == '\\') { + pushchar(hash, p[ofs]); + pushchar(hash, p[ofs + 1]); + ofs += 2; + } + pushchar(hash, p[ofs]); + } while (ofs < size && p[ofs] != q); + pushchar(hash, '\n'); + ofs++; + continue; + } - if (tokens[p[ofs]].type & C_TOKEN) { - unsigned char q = p[ofs]; - int i; - for (i = 0; i < tokens[q].num_toks; i++) { - const unsigned char *s = (const unsigned char *)tokens[q].toks[i]; - int len = strlen((const char *)s); - if (size >= ofs+len && memcmp(&p[ofs], s, len) == 0) { - int j; - for (j = 0; s[j]; j++) { - pushchar(hash, s[j]); - ofs++; - } - pushchar(hash, '\n'); - break; - } - } - if (i < tokens[q].num_toks) { - continue; - } - } + if (tokens[p[ofs]].type & C_TOKEN) { + unsigned char q = p[ofs]; + int i; + for (i = 0; i < tokens[q].num_toks; i++) { + const unsigned char* s = (const unsigned char*)tokens[q].toks[i]; + int len = strlen((const char*)s); + if (size >= ofs + len && memcmp(&p[ofs], s, len) == 0) { + int j; + for (j = 0; s[j]; j++) { + pushchar(hash, s[j]); + ofs++; + } + pushchar(hash, '\n'); + break; + } + } + if (i < tokens[q].num_toks) { + continue; + } + } - pushchar(hash, p[ofs]); - pushchar(hash, '\n'); - ofs++; - } - pushchar(hash, 0); + pushchar(hash, p[ofs]); + pushchar(hash, '\n'); + ofs++; + } + pushchar(hash, 0); } - // Hash a file that consists of preprocessor output, but remove any line number // information from the hash. int -unify_hash(struct hash *hash, const char *fname, bool debug) +unify_hash(struct hash* hash, const char* fname, bool debug) { - char *data; - size_t size; - if (!read_file(fname, 0, &data, &size)) { - stats_update(STATS_PREPROCESSOR); - return -1; - } - print_unified = debug; - unify(hash, (unsigned char *)data, size); - free(data); - return 0; + char* data; + size_t size; + if (!read_file(fname, 0, &data, &size)) { + stats_update(STATS_PREPROCESSOR); + return -1; + } + print_unified = debug; + unify(hash, (unsigned char*)data, size); + free(data); + return 0; } diff --git a/src/unify.hpp b/src/unify.hpp index eb6e6c4b..3629f693 100644 --- a/src/unify.hpp +++ b/src/unify.hpp @@ -20,4 +20,4 @@ #include "hash.hpp" -int unify_hash(struct hash *hash, const char *fname, bool print); +int unify_hash(struct hash* hash, const char* fname, bool print); diff --git a/src/util.cpp b/src/util.cpp index ca0ec00b..26c7e499 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -20,18 +20,18 @@ #include "ccache.hpp" #ifdef HAVE_PWD_H -#include <pwd.h> +# include <pwd.h> #endif #ifdef HAVE_SYSLOG_H -#include <syslog.h> +# include <syslog.h> #endif #ifdef HAVE_SYS_TIME_H -#include <sys/time.h> +# include <sys/time.h> #endif #ifdef __linux__ # ifdef HAVE_SYS_IOCTL_H -# include <sys/ioctl.h> +# include <sys/ioctl.h> # endif # ifdef HAVE_LINUX_FS_H # include <linux/fs.h> @@ -50,22 +50,22 @@ #endif #ifdef _WIN32 -#include <windows.h> -#include <sys/locking.h> -#include <psapi.h> -#include <tchar.h> +# include <psapi.h> +# include <sys/locking.h> +# include <tchar.h> +# include <windows.h> #endif -extern const struct conf *conf; +extern const struct conf* conf; // Destination for conf->log_file. -static FILE *logfile; +static FILE* logfile; // Whether to use syslog() instead. static bool use_syslog; // Buffer used for logs in conf->debug mode. -static char *debug_log_buffer; +static char* debug_log_buffer; // Allocated debug_log_buffer size. static size_t debug_log_buffer_capacity; @@ -78,96 +78,100 @@ static size_t debug_log_size; static bool init_log(void) { - if (debug_log_buffer || logfile || use_syslog) { - return true; - } - assert(conf); - if (conf->debug) { - debug_log_buffer_capacity = DEBUG_LOG_BUFFER_MARGIN; - debug_log_buffer = static_cast<char*>(x_malloc(debug_log_buffer_capacity)); - debug_log_size = 0; - } - if (str_eq(conf->log_file, "")) { - return conf->debug; - } + if (debug_log_buffer || logfile || use_syslog) { + return true; + } + assert(conf); + if (conf->debug) { + debug_log_buffer_capacity = DEBUG_LOG_BUFFER_MARGIN; + debug_log_buffer = static_cast<char*>(x_malloc(debug_log_buffer_capacity)); + debug_log_size = 0; + } + if (str_eq(conf->log_file, "")) { + return conf->debug; + } #ifdef HAVE_SYSLOG - if (str_eq(conf->log_file, "syslog")) { - use_syslog = true; - openlog("ccache", LOG_PID, LOG_USER); - return true; - } + if (str_eq(conf->log_file, "syslog")) { + use_syslog = true; + openlog("ccache", LOG_PID, LOG_USER); + return true; + } #endif - logfile = fopen(conf->log_file, "a"); - if (logfile) { + logfile = fopen(conf->log_file, "a"); + if (logfile) { #ifndef _WIN32 - set_cloexec_flag(fileno(logfile)); + set_cloexec_flag(fileno(logfile)); #endif - return true; - } else { - return false; - } + return true; + } else { + return false; + } } static void -append_to_debug_log(const char *s, size_t len) +append_to_debug_log(const char* s, size_t len) { - assert(debug_log_buffer); - if (debug_log_size + len + 1 > debug_log_buffer_capacity) { - debug_log_buffer_capacity += len + 1 + DEBUG_LOG_BUFFER_MARGIN; - debug_log_buffer = static_cast<char*>( - x_realloc(debug_log_buffer, debug_log_buffer_capacity)); - } - memcpy(debug_log_buffer + debug_log_size, s, len); - debug_log_size += len; + assert(debug_log_buffer); + if (debug_log_size + len + 1 > debug_log_buffer_capacity) { + debug_log_buffer_capacity += len + 1 + DEBUG_LOG_BUFFER_MARGIN; + debug_log_buffer = static_cast<char*>( + x_realloc(debug_log_buffer, debug_log_buffer_capacity)); + } + memcpy(debug_log_buffer + debug_log_size, s, len); + debug_log_size += len; } static void log_prefix(bool log_updated_time) { - static char prefix[200]; + static char prefix[200]; #ifdef HAVE_GETTIMEOFDAY - if (log_updated_time) { - char timestamp[100]; - struct tm tm; - struct timeval tv; - gettimeofday(&tv, NULL); -#ifdef __MINGW64_VERSION_MAJOR - localtime_r((time_t *)&tv.tv_sec, &tm); -#else - localtime_r(&tv.tv_sec, &tm); -#endif - strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", &tm); - snprintf(prefix, sizeof(prefix), - "[%s.%06d %-5d] ", timestamp, (int)tv.tv_usec, (int)getpid()); - } + if (log_updated_time) { + char timestamp[100]; + struct tm tm; + struct timeval tv; + gettimeofday(&tv, NULL); +# ifdef __MINGW64_VERSION_MAJOR + localtime_r((time_t*)&tv.tv_sec, &tm); +# else + localtime_r(&tv.tv_sec, &tm); +# endif + strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", &tm); + snprintf(prefix, + sizeof(prefix), + "[%s.%06d %-5d] ", + timestamp, + (int)tv.tv_usec, + (int)getpid()); + } #else - snprintf(prefix, sizeof(prefix), "[%-5d] ", (int)getpid()); + snprintf(prefix, sizeof(prefix), "[%-5d] ", (int)getpid()); #endif - if (logfile) { - fputs(prefix, logfile); - } + if (logfile) { + fputs(prefix, logfile); + } #ifdef HAVE_SYSLOG - if (use_syslog) { - // prefix information will be added by syslog - } + if (use_syslog) { + // prefix information will be added by syslog + } #endif - if (debug_log_buffer) { - append_to_debug_log(prefix, strlen(prefix)); - } + if (debug_log_buffer) { + append_to_debug_log(prefix, strlen(prefix)); + } } static long -path_max(const char *path) +path_max(const char* path) { #ifdef PATH_MAX - (void)path; - return PATH_MAX; + (void)path; + return PATH_MAX; #elif defined(MAXPATHLEN) - (void)path; - return MAXPATHLEN; + (void)path; + return MAXPATHLEN; #elif defined(_PC_PATH_MAX) - long maxlen = pathconf(path, _PC_PATH_MAX); - return maxlen >= 4096 ? maxlen : 4096; + long maxlen = pathconf(path, _PC_PATH_MAX); + return maxlen >= 4096 ? maxlen : 4096; #endif } @@ -177,185 +181,186 @@ static void warn_log_fail(void) ATTR_NORETURN; static void warn_log_fail(void) { - // Note: Can't call fatal() since that would lead to recursion. - fprintf(stderr, "ccache: error: Failed to write to %s: %s\n", - conf->log_file, strerror(errno)); - x_exit(EXIT_FAILURE); + // Note: Can't call fatal() since that would lead to recursion. + fprintf(stderr, + "ccache: error: Failed to write to %s: %s\n", + conf->log_file, + strerror(errno)); + x_exit(EXIT_FAILURE); } static void -vlog(const char *format, va_list ap, bool log_updated_time) -{ - if (!init_log()) { - return; - } - - va_list aq; - va_copy(aq, ap); - log_prefix(log_updated_time); - if (logfile) { - int rc1 = vfprintf(logfile, format, ap); - int rc2 = fprintf(logfile, "\n"); - if (rc1 < 0 || rc2 < 0) { - warn_log_fail(); - } - } +vlog(const char* format, va_list ap, bool log_updated_time) +{ + if (!init_log()) { + return; + } + + va_list aq; + va_copy(aq, ap); + log_prefix(log_updated_time); + if (logfile) { + int rc1 = vfprintf(logfile, format, ap); + int rc2 = fprintf(logfile, "\n"); + if (rc1 < 0 || rc2 < 0) { + warn_log_fail(); + } + } #ifdef HAVE_SYSLOG - if (use_syslog) { - vsyslog(LOG_DEBUG, format, ap); - } + if (use_syslog) { + vsyslog(LOG_DEBUG, format, ap); + } #endif - if (debug_log_buffer) { - char buf[8192]; - int len = vsnprintf(buf, sizeof(buf), format, aq); - if (len >= 0) { - append_to_debug_log(buf, MIN((size_t)len, sizeof(buf) - 1)); - append_to_debug_log("\n", 1); - } - } - va_end(aq); + if (debug_log_buffer) { + char buf[8192]; + int len = vsnprintf(buf, sizeof(buf), format, aq); + if (len >= 0) { + append_to_debug_log(buf, MIN((size_t)len, sizeof(buf) - 1)); + append_to_debug_log("\n", 1); + } + } + va_end(aq); } // Write a message to the log file (adding a newline) and flush. void -cc_log(const char *format, ...) +cc_log(const char* format, ...) { - va_list ap; - va_start(ap, format); - vlog(format, ap, true); - va_end(ap); - if (logfile) { - fflush(logfile); - } + va_list ap; + va_start(ap, format); + vlog(format, ap, true); + va_end(ap); + if (logfile) { + fflush(logfile); + } } // Write a message to the log file (adding a newline) without flushing and with // a reused timestamp. void -cc_bulklog(const char *format, ...) +cc_bulklog(const char* format, ...) { - va_list ap; - va_start(ap, format); - vlog(format, ap, false); - va_end(ap); + va_list ap; + va_start(ap, format); + vlog(format, ap, false); + va_end(ap); } // Log an executed command to the CCACHE_LOGFILE location. void -cc_log_argv(const char *prefix, char **argv) -{ - if (!init_log()) { - return; - } - - log_prefix(true); - if (logfile) { - fputs(prefix, logfile); - print_command(logfile, argv); - int rc = fflush(logfile); - if (rc) { - warn_log_fail(); - } - } +cc_log_argv(const char* prefix, char** argv) +{ + if (!init_log()) { + return; + } + + log_prefix(true); + if (logfile) { + fputs(prefix, logfile); + print_command(logfile, argv); + int rc = fflush(logfile); + if (rc) { + warn_log_fail(); + } + } #ifdef HAVE_SYSLOG - if (use_syslog) { - char *s = format_command(argv); - syslog(LOG_DEBUG, "%s", s); - free(s); - } + if (use_syslog) { + char* s = format_command(argv); + syslog(LOG_DEBUG, "%s", s); + free(s); + } #endif - if (debug_log_buffer) { - append_to_debug_log(prefix, strlen(prefix)); - char *s = format_command(argv); - append_to_debug_log(s, strlen(s)); - free(s); - } + if (debug_log_buffer) { + append_to_debug_log(prefix, strlen(prefix)); + char* s = format_command(argv); + append_to_debug_log(s, strlen(s)); + free(s); + } } // Copy the current log memory buffer to an output file. void -cc_dump_debug_log_buffer(const char *path) +cc_dump_debug_log_buffer(const char* path) { - FILE *file = fopen(path, "w"); - if (file) { - (void) fwrite(debug_log_buffer, 1, debug_log_size, file); - fclose(file); - } else { - cc_log("Failed to open %s: %s", path, strerror(errno)); - } + FILE* file = fopen(path, "w"); + if (file) { + (void)fwrite(debug_log_buffer, 1, debug_log_size, file); + fclose(file); + } else { + cc_log("Failed to open %s: %s", path, strerror(errno)); + } } // Something went badly wrong! void -fatal(const char *format, ...) +fatal(const char* format, ...) { - va_list ap; - va_start(ap, format); - char msg[8192]; - vsnprintf(msg, sizeof(msg), format, ap); - va_end(ap); + va_list ap; + va_start(ap, format); + char msg[8192]; + vsnprintf(msg, sizeof(msg), format, ap); + va_end(ap); - cc_log("FATAL: %s", msg); - fprintf(stderr, "ccache: error: %s\n", msg); + cc_log("FATAL: %s", msg); + fprintf(stderr, "ccache: error: %s\n", msg); - x_exit(1); + x_exit(1); } // Transform a name to a full path into the cache directory, creating needed // sublevels if needed. Caller frees. -char * -get_path_in_cache(const char *name, const char *suffix) +char* +get_path_in_cache(const char* name, const char* suffix) { - char *path = x_strdup(conf->cache_dir); - for (unsigned i = 0; i < conf->cache_dir_levels; ++i) { - char *p = format("%s/%c", path, name[i]); - free(path); - path = p; - } + char* path = x_strdup(conf->cache_dir); + for (unsigned i = 0; i < conf->cache_dir_levels; ++i) { + char* p = format("%s/%c", path, name[i]); + free(path); + path = p; + } - char *result = - format("%s/%s%s", path, name + conf->cache_dir_levels, suffix); - free(path); - return result; + char* result = format("%s/%s%s", path, name + conf->cache_dir_levels, suffix); + free(path); + return result; } // Copy all data from fd_in to fd_out. bool copy_fd(int fd_in, int fd_out) { - int n; - char buf[READ_BUFFER_SIZE]; - while ((n = read(fd_in, buf, sizeof(buf))) > 0) { - ssize_t written = 0; - do { - ssize_t count = write(fd_out, buf + written, n - written); - if (count == -1) { - if (errno != EAGAIN && errno != EINTR) { - return false; - } - } else { - written += count; - } - } while (written < n); - } - - return true; + int n; + char buf[READ_BUFFER_SIZE]; + while ((n = read(fd_in, buf, sizeof(buf))) > 0) { + ssize_t written = 0; + do { + ssize_t count = write(fd_out, buf + written, n - written); + if (count == -1) { + if (errno != EAGAIN && errno != EINTR) { + return false; + } + } else { + written += count; + } + } while (written < n); + } + + return true; } #ifndef HAVE_MKSTEMP // Cheap and nasty mkstemp replacement. int -mkstemp(char *name_template) +mkstemp(char* name_template) { -#ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - mktemp(name_template); -#ifdef __GNUC__ - #pragma GCC diagnostic pop -#endif - return open(name_template, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); +# ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# endif + mktemp(name_template); +# ifdef __GNUC__ +# pragma GCC diagnostic pop +# endif + return open(name_template, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); } #endif @@ -363,1121 +368,1127 @@ mkstemp(char *name_template) static mode_t get_umask(void) { - static bool mask_retrieved = false; - static mode_t mask; - if (!mask_retrieved) { - mask = umask(0); - umask(mask); - mask_retrieved = true; - } - return mask; + static bool mask_retrieved = false; + static mode_t mask; + if (!mask_retrieved) { + mask = umask(0); + umask(mask); + mask_retrieved = true; + } + return mask; } #endif // Clone a file from src to dest. If via_tmp_file is true, the file is cloned // to a temporary file and then renamed to dest. bool -clone_file(const char *src, const char *dest, bool via_tmp_file) +clone_file(const char* src, const char* dest, bool via_tmp_file) { - bool result; + bool result; #ifdef FILE_CLONING_SUPPORTED -#if defined(__linux__) - int src_fd = open(src, O_RDONLY); - if (src_fd == -1) { - return false; - } - - int dest_fd; - char *tmp_file = NULL; - if (via_tmp_file) { - tmp_file = x_strdup(dest); - dest_fd = create_tmp_fd(&tmp_file); - } else { - dest_fd = open(dest, O_WRONLY | O_CREAT | O_BINARY, 0666); - if (dest_fd == -1) { - close(dest_fd); - close(src_fd); - return false; - } - } - - int saved_errno = 0; - if (ioctl(dest_fd, FICLONE, src_fd) == 0) { - result = true; - } else { - result = false; - saved_errno = errno; - } - - close(dest_fd); - close(src_fd); - - if (via_tmp_file) { - x_try_unlink(dest); - if (x_rename(tmp_file, dest) != 0) { - result = false; - } - free(tmp_file); - } - - errno = saved_errno; -#elif defined(__APPLE__) - (void)via_tmp_file; - result = clonefile(src, dest, CLONE_NOOWNERCOPY) == 0; -#endif +# if defined(__linux__) + int src_fd = open(src, O_RDONLY); + if (src_fd == -1) { + return false; + } + + int dest_fd; + char* tmp_file = NULL; + if (via_tmp_file) { + tmp_file = x_strdup(dest); + dest_fd = create_tmp_fd(&tmp_file); + } else { + dest_fd = open(dest, O_WRONLY | O_CREAT | O_BINARY, 0666); + if (dest_fd == -1) { + close(dest_fd); + close(src_fd); + return false; + } + } + + int saved_errno = 0; + if (ioctl(dest_fd, FICLONE, src_fd) == 0) { + result = true; + } else { + result = false; + saved_errno = errno; + } + + close(dest_fd); + close(src_fd); + + if (via_tmp_file) { + x_try_unlink(dest); + if (x_rename(tmp_file, dest) != 0) { + result = false; + } + free(tmp_file); + } + + errno = saved_errno; +# elif defined(__APPLE__) + (void)via_tmp_file; + result = clonefile(src, dest, CLONE_NOOWNERCOPY) == 0; +# endif #else // FILE_CLONING_SUPPORTED - (void)src; - (void)dest; - (void)via_tmp_file; - errno = EOPNOTSUPP; - result = false; + (void)src; + (void)dest; + (void)via_tmp_file; + errno = EOPNOTSUPP; + result = false; #endif // FILE_CLONING_SUPPORTED - return result; + return result; } // Copy a file from src to dest. If via_tmp_file is true, the file is copied to // a temporary file and then renamed to dest. bool -copy_file(const char *src, const char *dest, bool via_tmp_file) -{ - bool result = false; - - int src_fd = open(src, O_RDONLY); - if (src_fd == -1) { - return false; - } - - int dest_fd; - char *tmp_file = NULL; - if (via_tmp_file) { - tmp_file = x_strdup(dest); - dest_fd = create_tmp_fd(&tmp_file); - } else { - dest_fd = open(dest, O_WRONLY | O_CREAT | O_BINARY, 0666); - if (dest_fd == -1) { - close(dest_fd); - close(src_fd); - return false; - } - } - - if (copy_fd(src_fd, dest_fd)) { - result = true; - } - - close(dest_fd); - close(src_fd); - - if (via_tmp_file) { - x_try_unlink(dest); - if (x_rename(tmp_file, dest) != 0) { - result = false; - } - free(tmp_file); - } - - return result; +copy_file(const char* src, const char* dest, bool via_tmp_file) +{ + bool result = false; + + int src_fd = open(src, O_RDONLY); + if (src_fd == -1) { + return false; + } + + int dest_fd; + char* tmp_file = NULL; + if (via_tmp_file) { + tmp_file = x_strdup(dest); + dest_fd = create_tmp_fd(&tmp_file); + } else { + dest_fd = open(dest, O_WRONLY | O_CREAT | O_BINARY, 0666); + if (dest_fd == -1) { + close(dest_fd); + close(src_fd); + return false; + } + } + + if (copy_fd(src_fd, dest_fd)) { + result = true; + } + + close(dest_fd); + close(src_fd); + + if (via_tmp_file) { + x_try_unlink(dest); + if (x_rename(tmp_file, dest) != 0) { + result = false; + } + free(tmp_file); + } + + return result; } // Run copy_file() and, if successful, delete the source file. bool -move_file(const char *src, const char *dest) +move_file(const char* src, const char* dest) { - bool ok = copy_file(src, dest, false); - if (ok) { - x_unlink(src); - } - return ok; + bool ok = copy_file(src, dest, false); + if (ok) { + x_unlink(src); + } + return ok; } // Make sure a directory exists. int -create_dir(const char *dir) -{ - struct stat st; - if (stat(dir, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - return 0; - } - errno = ENOTDIR; - return 1; - } - if (mkdir(dir, 0777) != 0 && errno != EEXIST) { - return 1; - } - return 0; +create_dir(const char* dir) +{ + struct stat st; + if (stat(dir, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + return 0; + } + errno = ENOTDIR; + return 1; + } + if (mkdir(dir, 0777) != 0 && errno != EEXIST) { + return 1; + } + return 0; } // Create directories leading to path. Returns 0 on success, otherwise -1. int -create_parent_dirs(const char *path) -{ - int res; - char *parent = x_dirname(path); - struct stat st; - if (stat(parent, &st) == 0) { - if (S_ISDIR(st.st_mode)) { - res = 0; - } else { - res = -1; - errno = ENOTDIR; - } - } else { - res = create_parent_dirs(parent); - if (res == 0) { - res = mkdir(parent, 0777); - // Have to handle the condition of the directory already existing because - // the file system could have changed in between calling stat and - // actually creating the directory. This can happen when there are - // multiple instances of ccache running and trying to create the same - // directory chain, which usually is the case when the cache root does - // not initially exist. As long as one of the processes creates the - // directories then our condition is satisfied and we avoid a race - // condition. - if (res != 0 && errno == EEXIST) { - res = 0; - } - } else { - res = -1; - } - } - free(parent); - return res; +create_parent_dirs(const char* path) +{ + int res; + char* parent = x_dirname(path); + struct stat st; + if (stat(parent, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + res = 0; + } else { + res = -1; + errno = ENOTDIR; + } + } else { + res = create_parent_dirs(parent); + if (res == 0) { + res = mkdir(parent, 0777); + // Have to handle the condition of the directory already existing because + // the file system could have changed in between calling stat and + // actually creating the directory. This can happen when there are + // multiple instances of ccache running and trying to create the same + // directory chain, which usually is the case when the cache root does + // not initially exist. As long as one of the processes creates the + // directories then our condition is satisfied and we avoid a race + // condition. + if (res != 0 && errno == EEXIST) { + res = 0; + } + } else { + res = -1; + } + } + free(parent); + return res; } // Return a static string with the current hostname. -const char * +const char* get_hostname(void) { - static char hostname[260] = ""; + static char hostname[260] = ""; - if (hostname[0]) { - return hostname; - } + if (hostname[0]) { + return hostname; + } - strcpy(hostname, "unknown"); + strcpy(hostname, "unknown"); #if HAVE_GETHOSTNAME - gethostname(hostname, sizeof(hostname) - 1); + gethostname(hostname, sizeof(hostname) - 1); #elif defined(_WIN32) - const char *computer_name = getenv("COMPUTERNAME"); - if (computer_name) { - snprintf(hostname, sizeof(hostname), "%s", computer_name); - return hostname; - } - - WORD w_version_requested = MAKEWORD(2, 2); - WSADATA wsa_data; - int err = WSAStartup(w_version_requested, &wsa_data); - if (err != 0) { - // Tell the user that we could not find a usable Winsock DLL. - cc_log("WSAStartup failed with error: %d", err); - return hostname; - } - - if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) { - // Tell the user that we could not find a usable WinSock DLL. - cc_log("Could not find a usable version of Winsock.dll"); - WSACleanup(); - return hostname; - } - - int result = gethostname(hostname, sizeof(hostname) - 1); - if (result != 0) { - LPVOID lp_msg_buf; - DWORD dw = WSAGetLastError(); - - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lp_msg_buf, 0, NULL); - - LPVOID lp_display_buf = (LPVOID) LocalAlloc( - LMEM_ZEROINIT, - (lstrlen((LPCTSTR) lp_msg_buf) + lstrlen((LPCTSTR) __FILE__) + 200) - * sizeof(TCHAR)); - _snprintf((LPTSTR) lp_display_buf, - LocalSize(lp_display_buf) / sizeof(TCHAR), - TEXT("%s failed with error %lu: %s"), __FILE__, dw, - (const char *)lp_msg_buf); - - cc_log("can't get hostname OS returned error: %s", (char *)lp_display_buf); - - LocalFree(lp_msg_buf); - LocalFree(lp_display_buf); - } - WSACleanup(); + const char* computer_name = getenv("COMPUTERNAME"); + if (computer_name) { + snprintf(hostname, sizeof(hostname), "%s", computer_name); + return hostname; + } + + WORD w_version_requested = MAKEWORD(2, 2); + WSADATA wsa_data; + int err = WSAStartup(w_version_requested, &wsa_data); + if (err != 0) { + // Tell the user that we could not find a usable Winsock DLL. + cc_log("WSAStartup failed with error: %d", err); + return hostname; + } + + if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2) { + // Tell the user that we could not find a usable WinSock DLL. + cc_log("Could not find a usable version of Winsock.dll"); + WSACleanup(); + return hostname; + } + + int result = gethostname(hostname, sizeof(hostname) - 1); + if (result != 0) { + LPVOID lp_msg_buf; + DWORD dw = WSAGetLastError(); + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lp_msg_buf, + 0, + NULL); + + LPVOID lp_display_buf = (LPVOID)LocalAlloc( + LMEM_ZEROINIT, + (lstrlen((LPCTSTR)lp_msg_buf) + lstrlen((LPCTSTR)__FILE__) + 200) + * sizeof(TCHAR)); + _snprintf((LPTSTR)lp_display_buf, + LocalSize(lp_display_buf) / sizeof(TCHAR), + TEXT("%s failed with error %lu: %s"), + __FILE__, + dw, + (const char*)lp_msg_buf); + + cc_log("can't get hostname OS returned error: %s", (char*)lp_display_buf); + + LocalFree(lp_msg_buf); + LocalFree(lp_display_buf); + } + WSACleanup(); #endif - hostname[sizeof(hostname) - 1] = 0; - return hostname; + hostname[sizeof(hostname) - 1] = 0; + return hostname; } // Return a string to be passed to mkstemp to create a temporary file. Also // tries to cope with NFS by adding the local hostname. -const char * +const char* tmp_string(void) { - static char *ret; - if (!ret) { - ret = format("%s.%u.XXXXXX", get_hostname(), (unsigned)getpid()); - } - return ret; + static char* ret; + if (!ret) { + ret = format("%s.%u.XXXXXX", get_hostname(), (unsigned)getpid()); + } + return ret; } static char const CACHEDIR_TAG[] = - "Signature: 8a477f597d28d172789f06886806bc55\n" - "# This file is a cache directory tag created by ccache.\n" - "# For information about cache directory tags, see:\n" - "#\thttp://www.brynosaurus.com/cachedir/\n"; + "Signature: 8a477f597d28d172789f06886806bc55\n" + "# This file is a cache directory tag created by ccache.\n" + "# For information about cache directory tags, see:\n" + "#\thttp://www.brynosaurus.com/cachedir/\n"; int -create_cachedirtag(const char *dir) -{ - char *filename = format("%s/CACHEDIR.TAG", dir); - FILE* f; - struct stat st; - - if (stat(filename, &st) == 0) { - if (S_ISREG(st.st_mode)) { - goto success; - } - errno = EEXIST; - goto error; - } - - f = fopen(filename, "w"); - if (!f) { - goto error; - } - if (fwrite(CACHEDIR_TAG, sizeof(CACHEDIR_TAG)-1, 1, f) != 1) { - fclose(f); - goto error; - } - if (fclose(f)) { - goto error; - } +create_cachedirtag(const char* dir) +{ + char* filename = format("%s/CACHEDIR.TAG", dir); + FILE* f; + struct stat st; + + if (stat(filename, &st) == 0) { + if (S_ISREG(st.st_mode)) { + goto success; + } + errno = EEXIST; + goto error; + } + + f = fopen(filename, "w"); + if (!f) { + goto error; + } + if (fwrite(CACHEDIR_TAG, sizeof(CACHEDIR_TAG) - 1, 1, f) != 1) { + fclose(f); + goto error; + } + if (fclose(f)) { + goto error; + } success: - free(filename); - return 0; + free(filename); + return 0; error: - free(filename); - return 1; + free(filename); + return 1; } // Construct a string according to a format. Caller frees. -char * -format(const char *format, ...) +char* +format(const char* format, ...) { - va_list ap; - va_start(ap, format); + va_list ap; + va_start(ap, format); - char *ptr = NULL; - if (vasprintf(&ptr, format, ap) == -1) { - fatal("Out of memory in format"); - } - va_end(ap); + char* ptr = NULL; + if (vasprintf(&ptr, format, ap) == -1) { + fatal("Out of memory in format"); + } + va_end(ap); - if (!*ptr) { - fatal("Internal error in format"); - } - return ptr; + if (!*ptr) { + fatal("Internal error in format"); + } + return ptr; } // Construct a hexadecimal string representing binary data. The buffer must // hold at least 2 * size + 1 bytes. void -format_hex(const uint8_t *data, size_t size, char *buffer) +format_hex(const uint8_t* data, size_t size, char* buffer) { - for (size_t i = 0; i < size; i++) { - sprintf(&buffer[i*2], "%02x", (unsigned)data[i]); - } - buffer[2 * size] = '\0'; + for (size_t i = 0; i < size; i++) { + sprintf(&buffer[i * 2], "%02x", (unsigned)data[i]); + } + buffer[2 * size] = '\0'; } // This is like strdup() but dies if the malloc fails. -char * -x_strdup(const char *s) +char* +x_strdup(const char* s) { - char *ret = strdup(s); - if (!ret) { - fatal("Out of memory in x_strdup"); - } - return ret; + char* ret = strdup(s); + if (!ret) { + fatal("Out of memory in x_strdup"); + } + return ret; } // This is like strndup() but dies if the malloc fails. -char * -x_strndup(const char *s, size_t n) +char* +x_strndup(const char* s, size_t n) { #ifndef HAVE_STRNDUP - if (!s) { - return NULL; - } - size_t m = 0; - while (m < n && s[m]) { - m++; - } - char *ret = static_cast<char*>(malloc(m + 1)); - if (ret) { - memcpy(ret, s, m); - ret[m] = '\0'; - } + if (!s) { + return NULL; + } + size_t m = 0; + while (m < n && s[m]) { + m++; + } + char* ret = static_cast<char*>(malloc(m + 1)); + if (ret) { + memcpy(ret, s, m); + ret[m] = '\0'; + } #else - char *ret = strndup(s, n); + char* ret = strndup(s, n); #endif - if (!ret) { - fatal("x_strndup: Could not allocate %lu bytes", (unsigned long)n); - } - return ret; + if (!ret) { + fatal("x_strndup: Could not allocate %lu bytes", (unsigned long)n); + } + return ret; } // This is like malloc() but dies if the malloc fails. -void * +void* x_malloc(size_t size) { - if (size == 0) { - // malloc() may return NULL if size is zero, so always do this to make sure - // that the code handles it regardless of platform. - return NULL; - } - void *ret = malloc(size); - if (!ret) { - fatal("x_malloc: Could not allocate %lu bytes", (unsigned long)size); - } - return ret; + if (size == 0) { + // malloc() may return NULL if size is zero, so always do this to make sure + // that the code handles it regardless of platform. + return NULL; + } + void* ret = malloc(size); + if (!ret) { + fatal("x_malloc: Could not allocate %lu bytes", (unsigned long)size); + } + return ret; } // This is like calloc() but dies if the allocation fails. -void * +void* x_calloc(size_t nmemb, size_t size) { - if (nmemb * size == 0) { - // calloc() may return NULL if nmemb or size is 0, so always do this to - // make sure that the code handles it regardless of platform. - return NULL; - } - void *ret = calloc(nmemb, size); - if (!ret) { - fatal("x_calloc: Could not allocate %lu bytes", (unsigned long)size); - } - return ret; + if (nmemb * size == 0) { + // calloc() may return NULL if nmemb or size is 0, so always do this to + // make sure that the code handles it regardless of platform. + return NULL; + } + void* ret = calloc(nmemb, size); + if (!ret) { + fatal("x_calloc: Could not allocate %lu bytes", (unsigned long)size); + } + return ret; } // This is like realloc() but dies if the malloc fails. -void * -x_realloc(void *ptr, size_t size) +void* +x_realloc(void* ptr, size_t size) { - if (!ptr) { - return x_malloc(size); - } - void *p2 = realloc(ptr, size); - if (!p2) { - fatal("x_realloc: Could not allocate %lu bytes", (unsigned long)size); - } - return p2; + if (!ptr) { + return x_malloc(size); + } + void* p2 = realloc(ptr, size); + if (!p2) { + fatal("x_realloc: Could not allocate %lu bytes", (unsigned long)size); + } + return p2; } // This is like setenv. -void x_setenv(const char *name, const char *value) +void +x_setenv(const char* name, const char* value) { #ifdef HAVE_SETENV - setenv(name, value, true); + setenv(name, value, true); #else - putenv(format("%s=%s", name, value)); // Leak to environment. + putenv(format("%s=%s", name, value)); // Leak to environment. #endif } // This is like unsetenv. -void x_unsetenv(const char *name) +void +x_unsetenv(const char* name) { #ifdef HAVE_UNSETENV - unsetenv(name); + unsetenv(name); #else - putenv(x_strdup(name)); // Leak to environment. + putenv(x_strdup(name)); // Leak to environment. #endif } // Like fstat() but also call cc_log on failure. int -x_fstat(int fd, struct stat *buf) +x_fstat(int fd, struct stat* buf) { - int result = fstat(fd, buf); - if (result != 0) { - cc_log("Failed to fstat fd %d: %s", fd, strerror(errno)); - } - return result; + int result = fstat(fd, buf); + if (result != 0) { + cc_log("Failed to fstat fd %d: %s", fd, strerror(errno)); + } + return result; } // Like lstat() but also call cc_log on failure. int -x_lstat(const char *pathname, struct stat *buf) +x_lstat(const char* pathname, struct stat* buf) { - int result = lstat(pathname, buf); - if (result != 0) { - cc_log("Failed to lstat %s: %s", pathname, strerror(errno)); - } - return result; + int result = lstat(pathname, buf); + if (result != 0) { + cc_log("Failed to lstat %s: %s", pathname, strerror(errno)); + } + return result; } // Like stat() but also call cc_log on failure. int -x_stat(const char *pathname, struct stat *buf) +x_stat(const char* pathname, struct stat* buf) { - int result = stat(pathname, buf); - if (result != 0) { - cc_log("Failed to stat %s: %s", pathname, strerror(errno)); - } - return result; + int result = stat(pathname, buf); + if (result != 0) { + cc_log("Failed to stat %s: %s", pathname, strerror(errno)); + } + return result; } // Construct a string according to the format and store it in *ptr. The // original *ptr is then freed. void -reformat(char **ptr, const char *format, ...) +reformat(char** ptr, const char* format, ...) { - char *saved = *ptr; - *ptr = NULL; + char* saved = *ptr; + *ptr = NULL; - va_list ap; - va_start(ap, format); - if (vasprintf(ptr, format, ap) == -1) { - fatal("Out of memory in reformat"); - } - va_end(ap); + va_list ap; + va_start(ap, format); + if (vasprintf(ptr, format, ap) == -1) { + fatal("Out of memory in reformat"); + } + va_end(ap); - if (saved) { - free(saved); - } + if (saved) { + free(saved); + } } // Recursive directory traversal. fn() is called on all entries in the tree. void -traverse(const char *dir, void (*fn)(const char *, struct stat *)) -{ - DIR *d = opendir(dir); - if (!d) { - return; - } - - struct dirent *de; - while ((de = readdir(d))) { - if (str_eq(de->d_name, ".")) { - continue; - } - if (str_eq(de->d_name, "..")) { - continue; - } - - if (strlen(de->d_name) == 0) { - continue; - } - - char *fname = format("%s/%s", dir, de->d_name); - struct stat st; - if (lstat(fname, &st)) { - if (errno != ENOENT && errno != ESTALE) { - fatal("lstat %s failed: %s", fname, strerror(errno)); - } - free(fname); - continue; - } - - if (S_ISDIR(st.st_mode)) { - traverse(fname, fn); - } - - fn(fname, &st); - free(fname); - } - - closedir(d); +traverse(const char* dir, void (*fn)(const char*, struct stat*)) +{ + DIR* d = opendir(dir); + if (!d) { + return; + } + + struct dirent* de; + while ((de = readdir(d))) { + if (str_eq(de->d_name, ".")) { + continue; + } + if (str_eq(de->d_name, "..")) { + continue; + } + + if (strlen(de->d_name) == 0) { + continue; + } + + char* fname = format("%s/%s", dir, de->d_name); + struct stat st; + if (lstat(fname, &st)) { + if (errno != ENOENT && errno != ESTALE) { + fatal("lstat %s failed: %s", fname, strerror(errno)); + } + free(fname); + continue; + } + + if (S_ISDIR(st.st_mode)) { + traverse(fname, fn); + } + + fn(fname, &st); + free(fname); + } + + closedir(d); } - // Return the base name of a file - caller frees. -char * -x_basename(const char *path) +char* +x_basename(const char* path) { - const char *p = strrchr(path, '/'); - if (p) { - path = p + 1; - } + const char* p = strrchr(path, '/'); + if (p) { + path = p + 1; + } #ifdef _WIN32 - p = strrchr(path, '\\'); - if (p) { - path = p + 1; - } + p = strrchr(path, '\\'); + if (p) { + path = p + 1; + } #endif - return x_strdup(path); + return x_strdup(path); } // Return the dir name of a file - caller frees. -char * -x_dirname(const char *path) +char* +x_dirname(const char* path) { - char *s = x_strdup(path); - char *p = strrchr(s, '/'); + char* s = x_strdup(path); + char* p = strrchr(s, '/'); #ifdef _WIN32 - char *p2 = strrchr(s, '\\'); - if (!p || (p2 && p < p2)) { - p = p2; - } + char* p2 = strrchr(s, '\\'); + if (!p || (p2 && p < p2)) { + p = p2; + } #endif - if (!p) { - free(s); - s = x_strdup("."); - } else if (p == s) { - *(p + 1) = 0; - } else { - *p = 0; - } - return s; + if (!p) { + free(s); + s = x_strdup("."); + } else if (p == s) { + *(p + 1) = 0; + } else { + *p = 0; + } + return s; } // Return the file extension (including the dot) of a path as a pointer into // path. If path has no file extension, the empty string and the end of path is // returned. -const char * -get_extension(const char *path) +const char* +get_extension(const char* path) { - size_t len = strlen(path); - for (const char *p = &path[len - 1]; p >= path; --p) { - if (*p == '.') { - return p; - } - if (*p == '/') { - break; - } - } - return &path[len]; + size_t len = strlen(path); + for (const char* p = &path[len - 1]; p >= path; --p) { + if (*p == '.') { + return p; + } + if (*p == '/') { + break; + } + } + return &path[len]; } // Return a string containing the given path without the filename extension. // Caller frees. -char * -remove_extension(const char *path) +char* +remove_extension(const char* path) { - return x_strndup(path, strlen(path) - strlen(get_extension(path))); + return x_strndup(path, strlen(path) - strlen(get_extension(path))); } // Return size on disk of a file. size_t -file_size(struct stat *st) +file_size(struct stat* st) { #ifdef _WIN32 - return (st->st_size + 1023) & ~1023; + return (st->st_size + 1023) & ~1023; #else - return st->st_blocks * 512; + return st->st_blocks * 512; #endif } // Format a size as a human-readable string. Caller frees. -char * +char* format_human_readable_size(uint64_t v) { - char *s; - if (v >= 1000*1000*1000) { - s = format("%.1f GB", v/((double)(1000*1000*1000))); - } else { - s = format("%.1f MB", v/((double)(1000*1000))); - } - return s; + char* s; + if (v >= 1000 * 1000 * 1000) { + s = format("%.1f GB", v / ((double)(1000 * 1000 * 1000))); + } else { + s = format("%.1f MB", v / ((double)(1000 * 1000))); + } + return s; } // Format a size as a parsable string. Caller frees. -char * +char* format_parsable_size_with_suffix(uint64_t size) { - char *s; - if (size >= 1000*1000*1000) { - s = format("%.1fG", size / ((double)(1000*1000*1000))); - } else if (size >= 1000*1000) { - s = format("%.1fM", size / ((double)(1000*1000))); - } else { - s = format("%u", (unsigned)size); - } - return s; + char* s; + if (size >= 1000 * 1000 * 1000) { + s = format("%.1fG", size / ((double)(1000 * 1000 * 1000))); + } else if (size >= 1000 * 1000) { + s = format("%.1fM", size / ((double)(1000 * 1000))); + } else { + s = format("%u", (unsigned)size); + } + return s; } // Parse a "size value", i.e. a string that can end in k, M, G, T (10-based // suffixes) or Ki, Mi, Gi, Ti (2-based suffixes). For backward compatibility, // K is also recognized as a synonym of k. bool -parse_size_with_suffix(const char *str, uint64_t *size) -{ - errno = 0; - - char *p; - double x = strtod(str, &p); - if (errno != 0 || x < 0 || p == str || *str == '\0') { - return false; - } - - while (isspace(*p)) { - ++p; - } - - if (*p != '\0') { - unsigned multiplier = *(p+1) == 'i' ? 1024 : 1000; - switch (*p) { - case 'T': - x *= multiplier; - // Fallthrough. - case 'G': - x *= multiplier; - // Fallthrough. - case 'M': - x *= multiplier; - // Fallthrough. - case 'K': - case 'k': - x *= multiplier; - break; - default: - return false; - } - } else { - // Default suffix: G. - x *= 1000 * 1000 * 1000; - } - *size = (uint64_t)x; - return true; -} - - -#if !defined(HAVE_REALPATH) && \ - defined(_WIN32) && \ - !defined(HAVE_GETFINALPATHNAMEBYHANDLEW) -static BOOL GetFileNameFromHandle(HANDLE file_handle, TCHAR *filename, - WORD cch_filename) -{ - BOOL success = FALSE; - - // Get the file size. - DWORD file_size_hi = 0; - DWORD file_size_lo = GetFileSize(file_handle, &file_size_hi); - if (file_size_lo == 0 && file_size_hi == 0) { - // Cannot map a file with a length of zero. - return FALSE; - } - - // Create a file mapping object. - HANDLE file_map = - CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 1, NULL); - if (!file_map) { - return FALSE; - } - - // Create a file mapping to get the file name. - void *mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1); - if (mem) { - if (GetMappedFileName(GetCurrentProcess(), - mem, - filename, - cch_filename)) { - // Translate path with device name to drive letters. - TCHAR temp[512]; - temp[0] = '\0'; - - if (GetLogicalDriveStrings(512-1, temp)) { - TCHAR name[MAX_PATH]; - TCHAR drive[3] = TEXT(" :"); - BOOL found = FALSE; - TCHAR *p = temp; - - do { - // Copy the drive letter to the template string. - *drive = *p; - - // Look up each device name. - if (QueryDosDevice(drive, name, MAX_PATH)) { - size_t name_len = _tcslen(name); - if (name_len < MAX_PATH) { - found = _tcsnicmp(filename, name, name_len) == 0 - && *(filename + name_len) == _T('\\'); - if (found) { - // Reconstruct filename using temp_file and replace device path - // with DOS path. - TCHAR temp_file[MAX_PATH]; - _sntprintf(temp_file, - MAX_PATH - 1, - TEXT("%s%s"), - drive, - filename+name_len); - strcpy(filename, temp_file); - } - } - } - - // Go to the next NULL character. - while (*p++) { - // Do nothing. - } - } while (!found && *p); // End of string. - } - } - success = TRUE; - UnmapViewOfFile(mem); - } - - CloseHandle(file_map); - return success; +parse_size_with_suffix(const char* str, uint64_t* size) +{ + errno = 0; + + char* p; + double x = strtod(str, &p); + if (errno != 0 || x < 0 || p == str || *str == '\0') { + return false; + } + + while (isspace(*p)) { + ++p; + } + + if (*p != '\0') { + unsigned multiplier = *(p + 1) == 'i' ? 1024 : 1000; + switch (*p) { + case 'T': + x *= multiplier; + // Fallthrough. + case 'G': + x *= multiplier; + // Fallthrough. + case 'M': + x *= multiplier; + // Fallthrough. + case 'K': + case 'k': + x *= multiplier; + break; + default: + return false; + } + } else { + // Default suffix: G. + x *= 1000 * 1000 * 1000; + } + *size = (uint64_t)x; + return true; +} + +#if !defined(HAVE_REALPATH) && defined(_WIN32) \ + && !defined(HAVE_GETFINALPATHNAMEBYHANDLEW) +static BOOL +GetFileNameFromHandle(HANDLE file_handle, TCHAR* filename, WORD cch_filename) +{ + BOOL success = FALSE; + + // Get the file size. + DWORD file_size_hi = 0; + DWORD file_size_lo = GetFileSize(file_handle, &file_size_hi); + if (file_size_lo == 0 && file_size_hi == 0) { + // Cannot map a file with a length of zero. + return FALSE; + } + + // Create a file mapping object. + HANDLE file_map = + CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 1, NULL); + if (!file_map) { + return FALSE; + } + + // Create a file mapping to get the file name. + void* mem = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 1); + if (mem) { + if (GetMappedFileName(GetCurrentProcess(), mem, filename, cch_filename)) { + // Translate path with device name to drive letters. + TCHAR temp[512]; + temp[0] = '\0'; + + if (GetLogicalDriveStrings(512 - 1, temp)) { + TCHAR name[MAX_PATH]; + TCHAR drive[3] = TEXT(" :"); + BOOL found = FALSE; + TCHAR* p = temp; + + do { + // Copy the drive letter to the template string. + *drive = *p; + + // Look up each device name. + if (QueryDosDevice(drive, name, MAX_PATH)) { + size_t name_len = _tcslen(name); + if (name_len < MAX_PATH) { + found = _tcsnicmp(filename, name, name_len) == 0 + && *(filename + name_len) == _T('\\'); + if (found) { + // Reconstruct filename using temp_file and replace device path + // with DOS path. + TCHAR temp_file[MAX_PATH]; + _sntprintf(temp_file, + MAX_PATH - 1, + TEXT("%s%s"), + drive, + filename + name_len); + strcpy(filename, temp_file); + } + } + } + + // Go to the next NULL character. + while (*p++) { + // Do nothing. + } + } while (!found && *p); // End of string. + } + } + success = TRUE; + UnmapViewOfFile(mem); + } + + CloseHandle(file_map); + return success; } #endif // A sane realpath() function, trying to cope with stupid path limits and a // broken API. Caller frees. -char * -x_realpath(const char *path) +char* +x_realpath(const char* path) { - long maxlen = path_max(path); - char *ret = static_cast<char*>(x_malloc(maxlen)); - char *p; + long maxlen = path_max(path); + char* ret = static_cast<char*>(x_malloc(maxlen)); + char* p; #if HAVE_REALPATH - p = realpath(path, ret); + p = realpath(path, ret); #elif defined(_WIN32) - if (path[0] == '/') { - path++; // Skip leading slash. - } - HANDLE path_handle = CreateFile( - path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE != path_handle) { -#ifdef HAVE_GETFINALPATHNAMEBYHANDLEW - GetFinalPathNameByHandle(path_handle, ret, maxlen, FILE_NAME_NORMALIZED); -#else - GetFileNameFromHandle(path_handle, ret, maxlen); -#endif - CloseHandle(path_handle); - p = ret + 4; // Strip \\?\ from the file name. - } else { - snprintf(ret, maxlen, "%s", path); - p = ret; - } + if (path[0] == '/') { + path++; // Skip leading slash. + } + HANDLE path_handle = CreateFile(path, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (INVALID_HANDLE_VALUE != path_handle) { +# ifdef HAVE_GETFINALPATHNAMEBYHANDLEW + GetFinalPathNameByHandle(path_handle, ret, maxlen, FILE_NAME_NORMALIZED); +# else + GetFileNameFromHandle(path_handle, ret, maxlen); +# endif + CloseHandle(path_handle); + p = ret + 4; // Strip \\?\ from the file name. + } else { + snprintf(ret, maxlen, "%s", path); + p = ret; + } #else - // Yes, there are such systems. This replacement relies on the fact that when - // we call x_realpath we only care about symlinks. - { - int len = readlink(path, ret, maxlen-1); - if (len == -1) { - free(ret); - return NULL; - } - ret[len] = 0; - p = ret; - } + // Yes, there are such systems. This replacement relies on the fact that when + // we call x_realpath we only care about symlinks. + { + int len = readlink(path, ret, maxlen - 1); + if (len == -1) { + free(ret); + return NULL; + } + ret[len] = 0; + p = ret; + } #endif - if (p) { - p = x_strdup(p); - free(ret); - return p; - } - free(ret); - return NULL; + if (p) { + p = x_strdup(p); + free(ret); + return p; + } + free(ret); + return NULL; } // A getcwd that will returns an allocated buffer. -char * +char* gnu_getcwd(void) { - unsigned size = 128; + unsigned size = 128; - while (true) { - char *buffer = (char *)x_malloc(size); - if (getcwd(buffer, size) == buffer) { - return buffer; - } - free(buffer); - if (errno != ERANGE) { - cc_log("getcwd error: %d (%s)", errno, strerror(errno)); - return NULL; - } - size *= 2; - } + while (true) { + char* buffer = (char*)x_malloc(size); + if (getcwd(buffer, size) == buffer) { + return buffer; + } + free(buffer); + if (errno != ERANGE) { + cc_log("getcwd error: %d (%s)", errno, strerror(errno)); + return NULL; + } + size *= 2; + } } #if !defined(_WIN32) && !defined(HAVE_LOCALTIME_R) // localtime_r replacement. (Mingw-w64 has an inline localtime_r which is not // detected by AC_CHECK_FUNCS.) -struct tm * -localtime_r(const time_t *timep, struct tm *result) +struct tm* +localtime_r(const time_t* timep, struct tm* result) { - struct tm *tm = localtime(timep); - *result = *tm; - return result; + struct tm* tm = localtime(timep); + *result = *tm; + return result; } #endif #ifndef HAVE_STRTOK_R // strtok_r replacement. -char * -strtok_r(char *str, const char *delim, char **saveptr) -{ - if (!str) { - str = *saveptr; - } - int len = strlen(str); - char *ret = strtok(str, delim); - if (ret) { - char *save = ret; - while (*save++) { - // Do nothing. - } - if ((len + 1) == (intptr_t) (save - str)) { - save--; - } - *saveptr = save; - } - return ret; +char* +strtok_r(char* str, const char* delim, char** saveptr) +{ + if (!str) { + str = *saveptr; + } + int len = strlen(str); + char* ret = strtok(str, delim); + if (ret) { + char* save = ret; + while (*save++) { + // Do nothing. + } + if ((len + 1) == (intptr_t)(save - str)) { + save--; + } + *saveptr = save; + } + return ret; } #endif // Create an empty temporary file. *fname will be reallocated and set to the // resulting filename. Returns an open file descriptor to the file. int -create_tmp_fd(char **fname) -{ - char *tmpl = format("%s.%s", *fname, tmp_string()); - int fd = mkstemp(tmpl); - if (fd == -1 && errno == ENOENT) { - if (create_parent_dirs(*fname) != 0) { - fatal("Failed to create directory %s: %s", - x_dirname(*fname), strerror(errno)); - } - reformat(&tmpl, "%s.%s", *fname, tmp_string()); - fd = mkstemp(tmpl); - } - if (fd == -1) { - fatal("Failed to create temporary file for %s: %s", - *fname, strerror(errno)); - } - set_cloexec_flag(fd); +create_tmp_fd(char** fname) +{ + char* tmpl = format("%s.%s", *fname, tmp_string()); + int fd = mkstemp(tmpl); + if (fd == -1 && errno == ENOENT) { + if (create_parent_dirs(*fname) != 0) { + fatal("Failed to create directory %s: %s", + x_dirname(*fname), + strerror(errno)); + } + reformat(&tmpl, "%s.%s", *fname, tmp_string()); + fd = mkstemp(tmpl); + } + if (fd == -1) { + fatal( + "Failed to create temporary file for %s: %s", *fname, strerror(errno)); + } + set_cloexec_flag(fd); #ifndef _WIN32 - fchmod(fd, 0666 & ~get_umask()); + fchmod(fd, 0666 & ~get_umask()); #endif - free(*fname); - *fname = tmpl; - return fd; + free(*fname); + *fname = tmpl; + return fd; } // Create an empty temporary file. *fname will be reallocated and set to the // resulting filename. Returns an open FILE*. -FILE * -create_tmp_file(char **fname, const char *mode) +FILE* +create_tmp_file(char** fname, const char* mode) { - FILE *file = fdopen(create_tmp_fd(fname), mode); - if (!file) { - fatal("Failed to create file %s: %s", *fname, strerror(errno)); - } - return file; + FILE* file = fdopen(create_tmp_fd(fname), mode); + if (!file) { + fatal("Failed to create file %s: %s", *fname, strerror(errno)); + } + return file; } // Return current user's home directory, or NULL if it can't be determined. -const char * +const char* get_home_directory(void) { - const char *p = getenv("HOME"); - if (p) { - return p; - } + const char* p = getenv("HOME"); + if (p) { + return p; + } #ifdef _WIN32 - p = getenv("APPDATA"); - if (p) { - return p; - } + p = getenv("APPDATA"); + if (p) { + return p; + } #endif #ifdef HAVE_GETPWUID - { - struct passwd *pwd = getpwuid(getuid()); - if (pwd) { - return pwd->pw_dir; - } - } + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd) { + return pwd->pw_dir; + } + } #endif - return NULL; + return NULL; } // Get the current directory by reading $PWD. If $PWD isn't sane, gnu_getcwd() // is used. Caller frees. -char * +char* get_cwd(void) { - struct stat st_pwd; - struct stat st_cwd; - - char *cwd = gnu_getcwd(); - if (!cwd) { - return NULL; - } - char *pwd = getenv("PWD"); - if (!pwd) { - return cwd; - } - if (stat(pwd, &st_pwd) != 0) { - return cwd; - } - if (stat(cwd, &st_cwd) != 0) { - return cwd; - } - if (st_pwd.st_dev == st_cwd.st_dev && st_pwd.st_ino == st_cwd.st_ino) { - free(cwd); - return x_strdup(pwd); - } else { - return cwd; - } + struct stat st_pwd; + struct stat st_cwd; + + char* cwd = gnu_getcwd(); + if (!cwd) { + return NULL; + } + char* pwd = getenv("PWD"); + if (!pwd) { + return cwd; + } + if (stat(pwd, &st_pwd) != 0) { + return cwd; + } + if (stat(cwd, &st_cwd) != 0) { + return cwd; + } + if (st_pwd.st_dev == st_cwd.st_dev && st_pwd.st_ino == st_cwd.st_ino) { + free(cwd); + return x_strdup(pwd); + } else { + return cwd; + } } // Check whether s1 and s2 have the same executable name. bool -same_executable_name(const char *s1, const char *s2) +same_executable_name(const char* s1, const char* s2) { #ifdef _WIN32 - bool eq = strcasecmp(s1, s2) == 0; - if (!eq) { - char *tmp = format("%s.exe", s2); - eq = strcasecmp(s1, tmp) == 0; - free(tmp); - } - return eq; + bool eq = strcasecmp(s1, s2) == 0; + if (!eq) { + char* tmp = format("%s.exe", s2); + eq = strcasecmp(s1, tmp) == 0; + free(tmp); + } + return eq; #else - return str_eq(s1, s2); + return str_eq(s1, s2); #endif } // Compute the length of the longest directory path that is common to two // paths. s1 is assumed to be the path to a directory. size_t -common_dir_prefix_length(const char *s1, const char *s2) -{ - const char *p1 = s1; - const char *p2 = s2; - - while (*p1 && *p2 && *p1 == *p2) { - ++p1; - ++p2; - } - while ((*p1 && *p1 != '/') || (*p2 && *p2 != '/')) { - p1--; - p2--; - } - if (!*p1 && !*p2 && p2 == s2 + 1) { - // Special case for s1 and s2 both being "/". - return 0; - } - return p1 - s1; +common_dir_prefix_length(const char* s1, const char* s2) +{ + const char* p1 = s1; + const char* p2 = s2; + + while (*p1 && *p2 && *p1 == *p2) { + ++p1; + ++p2; + } + while ((*p1 && *p1 != '/') || (*p2 && *p2 != '/')) { + p1--; + p2--; + } + if (!*p1 && !*p2 && p2 == s2 + 1) { + // Special case for s1 and s2 both being "/". + return 0; + } + return p1 - s1; } // Compute a relative path from from (an absolute path to a directory) to to (a // path). Assumes that both from and to are well-formed and canonical. Caller // frees. -char * -get_relative_path(const char *from, const char *to) +char* +get_relative_path(const char* from, const char* to) { - size_t common_prefix_len; - char *result; + size_t common_prefix_len; + char* result; - assert(from && is_absolute_path(from)); - assert(to); + assert(from && is_absolute_path(from)); + assert(to); - if (!*to || !is_absolute_path(to)) { - return x_strdup(to); - } + if (!*to || !is_absolute_path(to)) { + return x_strdup(to); + } #ifdef _WIN32 - // Paths can be escaped by a slash for use with -isystem. - if (from[0] == '/') { - from++; - } - if (to[0] == '/') { - to++; - } - // Both paths are absolute, drop the drive letters. - assert(from[0] == to[0]); // Assume the same drive letter. - from += 2; - to += 2; + // Paths can be escaped by a slash for use with -isystem. + if (from[0] == '/') { + from++; + } + if (to[0] == '/') { + to++; + } + // Both paths are absolute, drop the drive letters. + assert(from[0] == to[0]); // Assume the same drive letter. + from += 2; + to += 2; #endif - result = x_strdup(""); - common_prefix_len = common_dir_prefix_length(from, to); - if (common_prefix_len > 0 || !str_eq(from, "/")) { - const char *p; - for (p = from + common_prefix_len; *p; p++) { - if (*p == '/') { - reformat(&result, "../%s", result); - } - } - } - if (strlen(to) > common_prefix_len) { - reformat(&result, "%s%s", result, to + common_prefix_len + 1); - } - for (int i = strlen(result) - 1; i >= 0 && result[i] == '/'; i--) { - result[i] = '\0'; - } - if (str_eq(result, "")) { - free(result); - result = x_strdup("."); - } - return result; + result = x_strdup(""); + common_prefix_len = common_dir_prefix_length(from, to); + if (common_prefix_len > 0 || !str_eq(from, "/")) { + const char* p; + for (p = from + common_prefix_len; *p; p++) { + if (*p == '/') { + reformat(&result, "../%s", result); + } + } + } + if (strlen(to) > common_prefix_len) { + reformat(&result, "%s%s", result, to + common_prefix_len + 1); + } + for (int i = strlen(result) - 1; i >= 0 && result[i] == '/'; i--) { + result[i] = '\0'; + } + if (str_eq(result, "")) { + free(result); + result = x_strdup("."); + } + return result; } // Return whether path is absolute. bool -is_absolute_path(const char *path) +is_absolute_path(const char* path) { #ifdef _WIN32 - return path[0] && path[1] == ':'; + return path[0] && path[1] == ':'; #else - return path[0] == '/'; + return path[0] == '/'; #endif } // Return whether the argument is a full path. bool -is_full_path(const char *path) +is_full_path(const char* path) { - if (strchr(path, '/')) { - return true; - } + if (strchr(path, '/')) { + return true; + } #ifdef _WIN32 - if (strchr(path, '\\')) { - return true; - } + if (strchr(path, '\\')) { + return true; + } #endif - return false; + return false; } -bool is_symlink(const char *path) +bool +is_symlink(const char* path) { #ifdef _WIN32 - (void)path; - return false; + (void)path; + return false; #else - struct stat st; - return x_lstat(path, &st) == 0 && ((st.st_mode & S_IFMT) == S_IFLNK); + struct stat st; + return x_lstat(path, &st) == 0 && ((st.st_mode & S_IFMT) == S_IFLNK); #endif } // Update the modification time of a file in the cache to save it from LRU // cleanup. void -update_mtime(const char *path) +update_mtime(const char* path) { #ifdef HAVE_UTIMES - utimes(path, NULL); + utimes(path, NULL); #else - utime(path, NULL); + utime(path, NULL); #endif } @@ -1486,294 +1497,299 @@ update_mtime(const char *path) void x_exit(int status) { - static bool first_time = true; - if (first_time) { - first_time = false; - exit(status); - } else { - _exit(status); - } + static bool first_time = true; + if (first_time) { + first_time = false; + exit(status); + } else { + _exit(status); + } } // Rename oldpath to newpath (deleting newpath). int -x_rename(const char *oldpath, const char *newpath) +x_rename(const char* oldpath, const char* newpath) { #ifndef _WIN32 - return rename(oldpath, newpath); + return rename(oldpath, newpath); #else - // Windows' rename() refuses to overwrite an existing file. - unlink(newpath); // Not x_unlink, as x_unlink calls x_rename. - // If the function succeeds, the return value is nonzero. - if (MoveFileA(oldpath, newpath) == 0) { - LPVOID lp_msg_buf; - DWORD dw = GetLastError(); - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lp_msg_buf, - 0, - NULL); - - LPVOID lp_display_buf = (LPVOID) LocalAlloc( - LMEM_ZEROINIT, - (lstrlen((LPCTSTR) lp_msg_buf) + lstrlen((LPCTSTR) __FILE__) + 40) - * sizeof(TCHAR)); - _snprintf((LPTSTR) lp_display_buf, - LocalSize(lp_display_buf) / sizeof(TCHAR), - TEXT("%s failed with error %lu: %s"), __FILE__, dw, - (const char *)lp_msg_buf); - - cc_log("can't rename file %s to %s OS returned error: %s", - oldpath, newpath, (char *) lp_display_buf); - - LocalFree(lp_msg_buf); - LocalFree(lp_display_buf); - return -1; - } else { - return 0; - } + // Windows' rename() refuses to overwrite an existing file. + unlink(newpath); // Not x_unlink, as x_unlink calls x_rename. + // If the function succeeds, the return value is nonzero. + if (MoveFileA(oldpath, newpath) == 0) { + LPVOID lp_msg_buf; + DWORD dw = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lp_msg_buf, + 0, + NULL); + + LPVOID lp_display_buf = (LPVOID)LocalAlloc( + LMEM_ZEROINIT, + (lstrlen((LPCTSTR)lp_msg_buf) + lstrlen((LPCTSTR)__FILE__) + 40) + * sizeof(TCHAR)); + _snprintf((LPTSTR)lp_display_buf, + LocalSize(lp_display_buf) / sizeof(TCHAR), + TEXT("%s failed with error %lu: %s"), + __FILE__, + dw, + (const char*)lp_msg_buf); + + cc_log("can't rename file %s to %s OS returned error: %s", + oldpath, + newpath, + (char*)lp_display_buf); + + LocalFree(lp_msg_buf); + LocalFree(lp_display_buf); + return -1; + } else { + return 0; + } #endif } // Remove path, NFS hazardous. Use only for temporary files that will not exist // on other systems. That is, the path should include tmp_string(). int -tmp_unlink(const char *path) +tmp_unlink(const char* path) { - cc_log("Unlink %s", path); - int rc = unlink(path); - if (rc) { - cc_log("Unlink failed: %s", strerror(errno)); - } - return rc; + cc_log("Unlink %s", path); + int rc = unlink(path); + if (rc) { + cc_log("Unlink failed: %s", strerror(errno)); + } + return rc; } static int -do_x_unlink(const char *path, bool log_failure) -{ - int saved_errno = 0; - - // If path is on an NFS share, unlink isn't atomic, so we rename to a temp - // file. We don't care if the temp file is trashed, so it's always safe to - // unlink it first. - char *tmp_name = format("%s.rm.%s", path, tmp_string()); - - int result = 0; - if (x_rename(path, tmp_name) == -1) { - result = -1; - saved_errno = errno; - goto out; - } - if (unlink(tmp_name) == -1) { - // If it was released in a race, that's OK. - if (errno != ENOENT && errno != ESTALE) { - result = -1; - saved_errno = errno; - } - } +do_x_unlink(const char* path, bool log_failure) +{ + int saved_errno = 0; + + // If path is on an NFS share, unlink isn't atomic, so we rename to a temp + // file. We don't care if the temp file is trashed, so it's always safe to + // unlink it first. + char* tmp_name = format("%s.rm.%s", path, tmp_string()); + + int result = 0; + if (x_rename(path, tmp_name) == -1) { + result = -1; + saved_errno = errno; + goto out; + } + if (unlink(tmp_name) == -1) { + // If it was released in a race, that's OK. + if (errno != ENOENT && errno != ESTALE) { + result = -1; + saved_errno = errno; + } + } out: - if (result == 0 || log_failure) { - cc_log("Unlink %s via %s", path, tmp_name); - if (result != 0 && log_failure) { - cc_log("x_unlink failed: %s", strerror(saved_errno)); - } - } - free(tmp_name); - errno = saved_errno; - return result; + if (result == 0 || log_failure) { + cc_log("Unlink %s via %s", path, tmp_name); + if (result != 0 && log_failure) { + cc_log("x_unlink failed: %s", strerror(saved_errno)); + } + } + free(tmp_name); + errno = saved_errno; + return result; } // Remove path, NFS safe, log both successes and failures. int -x_unlink(const char *path) +x_unlink(const char* path) { - return do_x_unlink(path, true); + return do_x_unlink(path, true); } // Remove path, NFS safe, only log successes. int -x_try_unlink(const char *path) +x_try_unlink(const char* path) { - return do_x_unlink(path, false); + return do_x_unlink(path, false); } #ifndef _WIN32 // Like readlink() but returns the string or NULL on failure. Caller frees. -char * -x_readlink(const char *path) -{ - long maxlen = path_max(path); - char *buf = static_cast<char*>(x_malloc(maxlen)); - ssize_t len = readlink(path, buf, maxlen-1); - if (len == -1) { - free(buf); - return NULL; - } - buf[len] = 0; - return buf; +char* +x_readlink(const char* path) +{ + long maxlen = path_max(path); + char* buf = static_cast<char*>(x_malloc(maxlen)); + ssize_t len = readlink(path, buf, maxlen - 1); + if (len == -1) { + free(buf); + return NULL; + } + buf[len] = 0; + return buf; } #endif // Reads the content of a file. Size hint 0 means no hint. Returns true on // success, otherwise false. bool -read_file(const char *path, size_t size_hint, char **data, size_t *size) -{ - if (size_hint == 0) { - struct stat st; - if (x_stat(path, &st) == 0) { - size_hint = st.st_size; - } - } - size_hint = (size_hint < 1024) ? 1024 : size_hint; - - int fd = open(path, O_RDONLY | O_BINARY); - if (fd == -1) { - return false; - } - size_t allocated = size_hint; - *data = static_cast<char*>(x_malloc(allocated)); - int ret; - size_t pos = 0; - while (true) { - if (pos > allocated / 2) { - allocated *= 2; - *data = static_cast<char*>(x_realloc(*data, allocated)); - } - ret = read(fd, *data + pos, allocated - pos); - if (ret == 0 || (ret == -1 && errno != EINTR)) { - break; - } - if (ret > 0) { - pos += ret; - } - } - close(fd); - if (ret == -1) { - cc_log("Failed reading %s", path); - free(*data); - *data = NULL; - return false; - } - - *size = pos; - return true; +read_file(const char* path, size_t size_hint, char** data, size_t* size) +{ + if (size_hint == 0) { + struct stat st; + if (x_stat(path, &st) == 0) { + size_hint = st.st_size; + } + } + size_hint = (size_hint < 1024) ? 1024 : size_hint; + + int fd = open(path, O_RDONLY | O_BINARY); + if (fd == -1) { + return false; + } + size_t allocated = size_hint; + *data = static_cast<char*>(x_malloc(allocated)); + int ret; + size_t pos = 0; + while (true) { + if (pos > allocated / 2) { + allocated *= 2; + *data = static_cast<char*>(x_realloc(*data, allocated)); + } + ret = read(fd, *data + pos, allocated - pos); + if (ret == 0 || (ret == -1 && errno != EINTR)) { + break; + } + if (ret > 0) { + pos += ret; + } + } + close(fd); + if (ret == -1) { + cc_log("Failed reading %s", path); + free(*data); + *data = NULL; + return false; + } + + *size = pos; + return true; } // Return the content (with NUL termination) of a text file, or NULL on error. // Caller frees. Size hint 0 means no hint. -char * -read_text_file(const char *path, size_t size_hint) +char* +read_text_file(const char* path, size_t size_hint) { - size_t size; - char *data; - if (read_file(path, size_hint, &data, &size)) { - data = static_cast<char*>(x_realloc(data, size + 1)); - data[size] = '\0'; - return data; - } else { - return NULL; - } + size_t size; + char* data; + if (read_file(path, size_hint, &data, &size)) { + data = static_cast<char*>(x_realloc(data, size + 1)); + data[size] = '\0'; + return data; + } else { + return NULL; + } } static bool -expand_variable(const char **str, char **result, char **errmsg) -{ - assert(**str == '$'); - - bool curly; - const char *p = *str + 1; - if (*p == '{') { - curly = true; - ++p; - } else { - curly = false; - } - - const char *q = p; - while (isalnum(*q) || *q == '_') { - ++q; - } - if (curly) { - if (*q != '}') { - *errmsg = format("syntax error: missing '}' after \"%s\"", p); - return false; - } - } - - if (q == p) { - // Special case: don't consider a single $ the start of a variable. - reformat(result, "%s$", *result); - return true; - } - - char *name = x_strndup(p, q - p); - const char *value = getenv(name); - if (!value) { - *errmsg = format("environment variable \"%s\" not set", name); - free(name); - return false; - } - reformat(result, "%s%s", *result, value); - if (!curly) { - --q; - } - *str = q; - free(name); - return true; +expand_variable(const char** str, char** result, char** errmsg) +{ + assert(**str == '$'); + + bool curly; + const char* p = *str + 1; + if (*p == '{') { + curly = true; + ++p; + } else { + curly = false; + } + + const char* q = p; + while (isalnum(*q) || *q == '_') { + ++q; + } + if (curly) { + if (*q != '}') { + *errmsg = format("syntax error: missing '}' after \"%s\"", p); + return false; + } + } + + if (q == p) { + // Special case: don't consider a single $ the start of a variable. + reformat(result, "%s$", *result); + return true; + } + + char* name = x_strndup(p, q - p); + const char* value = getenv(name); + if (!value) { + *errmsg = format("environment variable \"%s\" not set", name); + free(name); + return false; + } + reformat(result, "%s%s", *result, value); + if (!curly) { + --q; + } + *str = q; + free(name); + return true; } // Substitute all instances of $VAR or ${VAR}, where VAR is an environment // variable, in a string. Caller frees. If one of the environment variables // doesn't exist, NULL will be returned and *errmsg will be an appropriate // error message (caller frees). -char * -subst_env_in_string(const char *str, char **errmsg) -{ - assert(errmsg); - *errmsg = NULL; - - char *result = x_strdup(""); - const char *p = str; // Interval start. - const char *q = str; // Interval end. - for (q = str; *q; ++q) { - if (*q == '$') { - reformat(&result, "%s%.*s", result, (int)(q - p), p); - if (!expand_variable(&q, &result, errmsg)) { - free(result); - return NULL; - } - p = q + 1; - } - } - reformat(&result, "%s%.*s", result, (int)(q - p), p); - return result; +char* +subst_env_in_string(const char* str, char** errmsg) +{ + assert(errmsg); + *errmsg = NULL; + + char* result = x_strdup(""); + const char* p = str; // Interval start. + const char* q = str; // Interval end. + for (q = str; *q; ++q) { + if (*q == '$') { + reformat(&result, "%s%.*s", result, (int)(q - p), p); + if (!expand_variable(&q, &result, errmsg)) { + free(result); + return NULL; + } + p = q + 1; + } + } + reformat(&result, "%s%.*s", result, (int)(q - p), p); + return result; } void set_cloexec_flag(int fd) { #ifndef _WIN32 - int flags = fcntl(fd, F_GETFD, 0); - if (flags >= 0) { - fcntl(fd, F_SETFD, flags | FD_CLOEXEC); - } + int flags = fcntl(fd, F_GETFD, 0); + if (flags >= 0) { + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } #else - (void)fd; + (void)fd; #endif } -double time_seconds(void) +double +time_seconds(void) { #ifdef HAVE_GETTIMEOFDAY - struct timeval tv; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; #else - return (double)time(NULL); + return (double)time(NULL); #endif } diff --git a/unittest/framework.cpp b/unittest/framework.cpp index ca4bbbe3..4cab44be 100644 --- a/unittest/framework.cpp +++ b/unittest/framework.cpp @@ -17,23 +17,24 @@ // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "framework.hpp" + #include "util.hpp" #include <float.h> #include <math.h> #if defined(HAVE_TERMIOS_H) -#define USE_COLOR -#include <termios.h> +# define USE_COLOR +# include <termios.h> #endif static unsigned total_asserts; static unsigned total_tests; static unsigned total_suites; static unsigned failed_tests; -static const char *current_suite; -static const char *current_test; -static char *dir_before_suite; -static char *dir_before_test; +static const char* current_suite; +static const char* current_test; +static char* dir_before_suite; +static char* dir_before_test; static int verbose; static const char COLOR_END[] = "\x1b[m"; @@ -41,282 +42,309 @@ static const char COLOR_GREEN[] = "\x1b[1;32m"; static const char COLOR_RED[] = "\x1b[1;31m"; static char CONFIG_PATH_ENV[] = "CCACHE_CONFIG_PATH=/dev/null"; -#define COLOR(tty, color) ((tty) ? COLOR_ ## color : "") +#define COLOR(tty, color) ((tty) ? COLOR_##color : "") static int is_tty(int fd) { #ifdef USE_COLOR - struct termios t; - return tcgetattr(fd, &t) == 0; + struct termios t; + return tcgetattr(fd, &t) == 0; #else - (void)fd; - return 0; + (void)fd; + return 0; #endif } -static const char * +static const char* plural_s(unsigned n) { - return n == 1 ? "" : "s"; + return n == 1 ? "" : "s"; } int -cct_run(suite_fn *suites, int verbose_output) +cct_run(suite_fn* suites, int verbose_output) { - suite_fn *suite; - int tty = is_tty(1); + suite_fn* suite; + int tty = is_tty(1); - x_unsetenv("GCC_COLORS"); // Avoid confusing argument processing tests. - verbose = verbose_output; + x_unsetenv("GCC_COLORS"); // Avoid confusing argument processing tests. + verbose = verbose_output; - for (suite = suites; *suite; suite++) { - unsigned test_index = 0; - while (true) { - test_index = (*suite)(test_index + 1); - if (test_index == 0) { - // We have reached the end of the suite. - break; - } - } - } + for (suite = suites; *suite; suite++) { + unsigned test_index = 0; + while (true) { + test_index = (*suite)(test_index + 1); + if (test_index == 0) { + // We have reached the end of the suite. + break; + } + } + } - if (failed_tests == 0) { - printf("%sPASSED%s: %u assertion%s, %u test%s, %u suite%s\n", - COLOR(tty, GREEN), COLOR(tty, END), - total_asserts, plural_s(total_asserts), - total_tests, plural_s(total_tests), - total_suites, plural_s(total_suites)); - } else { - printf("%sFAILED%s: %u test%s\n", - COLOR(tty, RED), COLOR(tty, END), - failed_tests, plural_s(failed_tests)); - } - return failed_tests > 0 ? 1 : 0; + if (failed_tests == 0) { + printf("%sPASSED%s: %u assertion%s, %u test%s, %u suite%s\n", + COLOR(tty, GREEN), + COLOR(tty, END), + total_asserts, + plural_s(total_asserts), + total_tests, + plural_s(total_tests), + total_suites, + plural_s(total_suites)); + } else { + printf("%sFAILED%s: %u test%s\n", + COLOR(tty, RED), + COLOR(tty, END), + failed_tests, + plural_s(failed_tests)); + } + return failed_tests > 0 ? 1 : 0; } void -cct_suite_begin(const char *name) +cct_suite_begin(const char* name) { - ++total_suites; - if (verbose) { - printf("=== SUITE: %s ===\n", name); - } - dir_before_suite = gnu_getcwd(); - create_dir(name); - cct_chdir(name); - current_suite = name; + ++total_suites; + if (verbose) { + printf("=== SUITE: %s ===\n", name); + } + dir_before_suite = gnu_getcwd(); + create_dir(name); + cct_chdir(name); + current_suite = name; } void cct_suite_end() { - cct_chdir(dir_before_suite); - free(dir_before_suite); - dir_before_suite = NULL; + cct_chdir(dir_before_suite); + free(dir_before_suite); + dir_before_suite = NULL; } void -cct_test_begin(const char *name) +cct_test_begin(const char* name) { - ++total_tests; - if (verbose) { - printf("--- TEST: %s ---\n", name); - } - dir_before_test = gnu_getcwd(); - create_dir(name); - cct_chdir(name); - current_test = name; + ++total_tests; + if (verbose) { + printf("--- TEST: %s ---\n", name); + } + dir_before_test = gnu_getcwd(); + create_dir(name); + cct_chdir(name); + current_test = name; - putenv(CONFIG_PATH_ENV); - cc_reset(); + putenv(CONFIG_PATH_ENV); + cc_reset(); } void cct_test_end() { - if (dir_before_test) { - cct_chdir(dir_before_test); - free(dir_before_test); - dir_before_test = NULL; - } + if (dir_before_test) { + cct_chdir(dir_before_test); + free(dir_before_test); + dir_before_test = NULL; + } } void -cct_check_passed(const char *file, int line, const char *what) +cct_check_passed(const char* file, int line, const char* what) { - ++total_asserts; - if (verbose) { - printf("%s:%d: Passed assertion: %s\n", file, line, what); - } + ++total_asserts; + if (verbose) { + printf("%s:%d: Passed assertion: %s\n", file, line, what); + } } void -cct_check_failed(const char *file, int line, const char *what, - const char *expected, const char *actual) +cct_check_failed(const char* file, + int line, + const char* what, + const char* expected, + const char* actual) { - ++total_asserts; - ++failed_tests; - fprintf(stderr, "%s:%d: Failed assertion:\n", file, line); - fprintf(stderr, " Suite: %s\n", current_suite); - fprintf(stderr, " Test: %s\n", current_test); - if (expected) { - fprintf(stderr, " Expression: %s\n", what); - if (actual) { - fprintf(stderr, " Expected: %s\n", expected); - fprintf(stderr, " Actual: %s\n", actual); - } else { - fprintf(stderr, " Message: %s\n", expected); - } - } else { - fprintf(stderr, " Assertion: %s\n", what); - } - fprintf(stderr, "\n"); + ++total_asserts; + ++failed_tests; + fprintf(stderr, "%s:%d: Failed assertion:\n", file, line); + fprintf(stderr, " Suite: %s\n", current_suite); + fprintf(stderr, " Test: %s\n", current_test); + if (expected) { + fprintf(stderr, " Expression: %s\n", what); + if (actual) { + fprintf(stderr, " Expected: %s\n", expected); + fprintf(stderr, " Actual: %s\n", actual); + } else { + fprintf(stderr, " Message: %s\n", expected); + } + } else { + fprintf(stderr, " Assertion: %s\n", what); + } + fprintf(stderr, "\n"); } bool -cct_check_double_eq(const char *file, int line, const char *expression, - double expected, double actual) +cct_check_double_eq(const char* file, + int line, + const char* expression, + double expected, + double actual) { - if (fabs(expected - actual) < DBL_EPSILON) { - cct_check_passed(file, line, expression); - return true; - } else { - char *exp_str = format("%.1f", expected); - char *act_str = format("%.1f", actual); - cct_check_failed(file, line, expression, exp_str, act_str); - free(exp_str); - free(act_str); - return false; - } + if (fabs(expected - actual) < DBL_EPSILON) { + cct_check_passed(file, line, expression); + return true; + } else { + char* exp_str = format("%.1f", expected); + char* act_str = format("%.1f", actual); + cct_check_failed(file, line, expression, exp_str, act_str); + free(exp_str); + free(act_str); + return false; + } } bool -cct_check_int_eq(const char *file, int line, const char *expression, - int64_t expected, int64_t actual) +cct_check_int_eq(const char* file, + int line, + const char* expression, + int64_t expected, + int64_t actual) { - if (expected == actual) { - cct_check_passed(file, line, expression); - return true; - } else { + if (expected == actual) { + cct_check_passed(file, line, expression); + return true; + } else { #if defined(HAVE_LONG_LONG) && !defined(__MINGW32__) - char *exp_str = format("%lld", (long long)expected); - char *act_str = format("%lld", (long long)actual); + char* exp_str = format("%lld", (long long)expected); + char* act_str = format("%lld", (long long)actual); #else - char *exp_str = format("%ld", (long)expected); - char *act_str = format("%ld", (long)actual); + char* exp_str = format("%ld", (long)expected); + char* act_str = format("%ld", (long)actual); #endif - cct_check_failed(file, line, expression, exp_str, act_str); - free(exp_str); - free(act_str); - return false; - } + cct_check_failed(file, line, expression, exp_str, act_str); + free(exp_str); + free(act_str); + return false; + } } -bool cct_check_data_eq(const char *file, int line, const char *expression, - const uint8_t *expected, const uint8_t *actual, - size_t size) +bool +cct_check_data_eq(const char* file, + int line, + const char* expression, + const uint8_t* expected, + const uint8_t* actual, + size_t size) { - if (memcmp(actual, expected, size) == 0) { - cct_check_passed(file, line, expression); - return true; - } else { - char *exp_str = static_cast<char*>(x_malloc(2 * size + 1)); - char *act_str = static_cast<char*>(x_malloc(2 * size + 1)); - format_hex(expected, size, exp_str); - format_hex(actual, size, act_str); - cct_check_failed(file, line, expression, exp_str, act_str); - free(exp_str); - free(act_str); - return false; - } + if (memcmp(actual, expected, size) == 0) { + cct_check_passed(file, line, expression); + return true; + } else { + char* exp_str = static_cast<char*>(x_malloc(2 * size + 1)); + char* act_str = static_cast<char*>(x_malloc(2 * size + 1)); + format_hex(expected, size, exp_str); + format_hex(actual, size, act_str); + cct_check_failed(file, line, expression, exp_str, act_str); + free(exp_str); + free(act_str); + return false; + } } bool -cct_check_str_eq(const char *file, int line, const char *expression, - const char *expected, const char *actual, - bool free1, bool free2) +cct_check_str_eq(const char* file, + int line, + const char* expression, + const char* expected, + const char* actual, + bool free1, + bool free2) { - bool result; + bool result; - if (expected && actual && str_eq(actual, expected)) { - cct_check_passed(file, line, expression); - result = true; - } else { - char *exp_str = expected ? format("\"%s\"", expected) : x_strdup("(null)"); - char *act_str = actual ? format("\"%s\"", actual) : x_strdup("(null)"); - cct_check_failed(file, line, expression, exp_str, act_str); - free(exp_str); - free(act_str); - result = false; - } + if (expected && actual && str_eq(actual, expected)) { + cct_check_passed(file, line, expression); + result = true; + } else { + char* exp_str = expected ? format("\"%s\"", expected) : x_strdup("(null)"); + char* act_str = actual ? format("\"%s\"", actual) : x_strdup("(null)"); + cct_check_failed(file, line, expression, exp_str, act_str); + free(exp_str); + free(act_str); + result = false; + } - if (free1) { - free(const_cast<char*>(expected)); - } - if (free2) { - free(const_cast<char*>(actual)); - } - return result; + if (free1) { + free(const_cast<char*>(expected)); + } + if (free2) { + free(const_cast<char*>(actual)); + } + return result; } bool -cct_check_args_eq(const char *file, int line, const char *expression, - const struct args *expected, const struct args *actual, - bool free1, bool free2) +cct_check_args_eq(const char* file, + int line, + const char* expression, + const struct args* expected, + const struct args* actual, + bool free1, + bool free2) { - bool result; + bool result; - if (expected && actual && args_equal(actual, expected)) { - cct_check_passed(file, line, expression); - result = true; - } else { - char *exp_str = expected ? args_to_string(expected) : x_strdup("(null)"); - char *act_str = actual ? args_to_string(actual) : x_strdup("(null)"); - cct_check_failed(file, line, expression, exp_str, act_str); - free(exp_str); - free(act_str); - result = false; - } + if (expected && actual && args_equal(actual, expected)) { + cct_check_passed(file, line, expression); + result = true; + } else { + char* exp_str = expected ? args_to_string(expected) : x_strdup("(null)"); + char* act_str = actual ? args_to_string(actual) : x_strdup("(null)"); + cct_check_failed(file, line, expression, exp_str, act_str); + free(exp_str); + free(act_str); + result = false; + } - if (free1) { - args_free(const_cast<struct args*>(expected)); - } - if (free2) { - args_free(const_cast<struct args*>(actual)); - } - return result; + if (free1) { + args_free(const_cast<struct args*>(expected)); + } + if (free2) { + args_free(const_cast<struct args*>(actual)); + } + return result; } void -cct_chdir(const char *path) +cct_chdir(const char* path) { - if (chdir(path) != 0) { - fprintf(stderr, "chdir: %s: %s", path, strerror(errno)); - abort(); - } + if (chdir(path) != 0) { + fprintf(stderr, "chdir: %s: %s", path, strerror(errno)); + abort(); + } } void -cct_wipe(const char *path) +cct_wipe(const char* path) { - // TODO: rewrite using traverse(). + // TODO: rewrite using traverse(). #ifndef __MINGW32__ - char *command = format("rm -rf %s", path); + char* command = format("rm -rf %s", path); #else - char *command = format("rd /s /q %s", path); + char* command = format("rd /s /q %s", path); #endif - if (system(command) != 0) { - perror(command); - } - free(command); + if (system(command) != 0) { + perror(command); + } + free(command); } void -cct_create_fresh_dir(const char *path) +cct_create_fresh_dir(const char* path) { - cct_wipe(path); - if (mkdir(path, 0777) != 0) { - fprintf(stderr, "mkdir: %s: %s", path, strerror(errno)); - abort(); - } + cct_wipe(path); + if (mkdir(path, 0777) != 0) { + fprintf(stderr, "mkdir: %s: %s", path, strerror(errno)); + abort(); + } } diff --git a/unittest/framework.hpp b/unittest/framework.hpp index 77034410..f97a71d8 100644 --- a/unittest/framework.hpp +++ b/unittest/framework.hpp @@ -22,144 +22,167 @@ // ============================================================================ -#define TEST_SUITE(name) \ - unsigned suite_##name(unsigned _start_point); \ - unsigned suite_##name(unsigned _start_point) \ - { \ - unsigned _test_counter = 0; \ - cct_suite_begin(#name); \ - { \ - // Empty due to macro trickery. - -#define TEST(name) \ - cct_test_end(); \ - } \ - ++_test_counter; \ - { static int name = 0; (void)name; /* Verify test name. */ } \ - if (_test_counter >= _start_point) { \ - cct_test_begin(#name); - -#define TEST_SUITE_END \ - cct_test_end(); \ - } \ - cct_suite_end(); \ - return 0; /* We have reached the end. */ \ - } +#define TEST_SUITE(name) \ + unsigned suite_##name(unsigned _start_point); \ + unsigned suite_##name(unsigned _start_point) \ + { \ + unsigned _test_counter = 0; \ + cct_suite_begin(#name); \ + { \ + // Empty due to macro trickery. + +#define TEST(name) \ + cct_test_end(); \ + } \ + ++_test_counter; \ + { \ + static int name = 0; \ + (void)name; /* Verify test name. */ \ + } \ + if (_test_counter >= _start_point) { \ + cct_test_begin(#name); + +#define TEST_SUITE_END \ + cct_test_end(); \ + } \ + cct_suite_end(); \ + return 0; /* We have reached the end. */ \ + } // ============================================================================ -#define CHECKM(assertion, message) \ - do { \ - if ((assertion)) { \ - cct_check_passed(__FILE__, __LINE__, #assertion); \ - } else { \ - cct_check_failed(__FILE__, __LINE__, #assertion, (message), NULL); \ - cct_test_end(); \ - cct_suite_end(); \ - return _test_counter; \ - } \ - } while (false) - -#define CHECK(assertion) \ - CHECKM(assertion, NULL) - -#define CHECK_POINTER_EQ_BASE(t, e, a, f1, f2) \ - do { \ - if (!cct_check_##t##_eq(__FILE__, __LINE__, #a, (e), (a), (f1), (f2))) { \ - cct_test_end(); \ - cct_suite_end(); \ - return _test_counter; \ - } \ - } while (false) +#define CHECKM(assertion, message) \ + do { \ + if ((assertion)) { \ + cct_check_passed(__FILE__, __LINE__, #assertion); \ + } else { \ + cct_check_failed(__FILE__, __LINE__, #assertion, (message), NULL); \ + cct_test_end(); \ + cct_suite_end(); \ + return _test_counter; \ + } \ + } while (false) + +#define CHECK(assertion) CHECKM(assertion, NULL) + +#define CHECK_POINTER_EQ_BASE(t, e, a, f1, f2) \ + do { \ + if (!cct_check_##t##_eq(__FILE__, __LINE__, #a, (e), (a), (f1), (f2))) { \ + cct_test_end(); \ + cct_suite_end(); \ + return _test_counter; \ + } \ + } while (false) // ============================================================================ -#define CHECK_INT_EQ(expected, actual) \ - do { \ - if (!cct_check_int_eq(__FILE__, __LINE__, #actual, (expected), \ - (actual))) { \ - cct_test_end(); \ - cct_suite_end(); \ - return _test_counter; \ - } \ - } while (false) +#define CHECK_INT_EQ(expected, actual) \ + do { \ + if (!cct_check_int_eq( \ + __FILE__, __LINE__, #actual, (expected), (actual))) { \ + cct_test_end(); \ + cct_suite_end(); \ + return _test_counter; \ + } \ + } while (false) // ============================================================================ -#define CHECK_DOUBLE_EQ(expected, actual) \ - do { \ - if (!cct_check_double_eq(__FILE__, __LINE__, #actual, (expected), \ - (actual))) { \ - cct_test_end(); \ - cct_suite_end(); \ - return _test_counter; \ - } \ - } while (false) +#define CHECK_DOUBLE_EQ(expected, actual) \ + do { \ + if (!cct_check_double_eq( \ + __FILE__, __LINE__, #actual, (expected), (actual))) { \ + cct_test_end(); \ + cct_suite_end(); \ + return _test_counter; \ + } \ + } while (false) // ============================================================================ -#define CHECK_DATA_EQ(expected, actual, size) \ - do { \ - if (!cct_check_data_eq(__FILE__, __LINE__, #actual, (expected), \ - (actual), size)) { \ - cct_test_end(); \ - cct_suite_end(); \ - return _test_counter; \ - } \ - } while (false) +#define CHECK_DATA_EQ(expected, actual, size) \ + do { \ + if (!cct_check_data_eq( \ + __FILE__, __LINE__, #actual, (expected), (actual), size)) { \ + cct_test_end(); \ + cct_suite_end(); \ + return _test_counter; \ + } \ + } while (false) // ============================================================================ -#define CHECK_STR_EQ(expected, actual) \ - CHECK_POINTER_EQ_BASE(str, expected, actual, false, false) +#define CHECK_STR_EQ(expected, actual) \ + CHECK_POINTER_EQ_BASE(str, expected, actual, false, false) -#define CHECK_STR_EQ_FREE1(expected, actual) \ - CHECK_POINTER_EQ_BASE(str, expected, actual, true, false) +#define CHECK_STR_EQ_FREE1(expected, actual) \ + CHECK_POINTER_EQ_BASE(str, expected, actual, true, false) -#define CHECK_STR_EQ_FREE2(expected, actual) \ - CHECK_POINTER_EQ_BASE(str, expected, actual, false, true) +#define CHECK_STR_EQ_FREE2(expected, actual) \ + CHECK_POINTER_EQ_BASE(str, expected, actual, false, true) -#define CHECK_STR_EQ_FREE12(expected, actual) \ - CHECK_POINTER_EQ_BASE(str, expected, actual, true, true) +#define CHECK_STR_EQ_FREE12(expected, actual) \ + CHECK_POINTER_EQ_BASE(str, expected, actual, true, true) // ============================================================================ -#define CHECK_ARGS_EQ(expected, actual) \ - CHECK_POINTER_EQ_BASE(args, expected, actual, false, false) +#define CHECK_ARGS_EQ(expected, actual) \ + CHECK_POINTER_EQ_BASE(args, expected, actual, false, false) -#define CHECK_ARGS_EQ_FREE1(expected, actual) \ - CHECK_POINTER_EQ_BASE(args, expected, actual, true, false) +#define CHECK_ARGS_EQ_FREE1(expected, actual) \ + CHECK_POINTER_EQ_BASE(args, expected, actual, true, false) -#define CHECK_ARGS_EQ_FREE2(expected, actual) \ - CHECK_POINTER_EQ_BASE(args, expected, actual, false, true) +#define CHECK_ARGS_EQ_FREE2(expected, actual) \ + CHECK_POINTER_EQ_BASE(args, expected, actual, false, true) -#define CHECK_ARGS_EQ_FREE12(expected, actual) \ - CHECK_POINTER_EQ_BASE(args, expected, actual, true, true) +#define CHECK_ARGS_EQ_FREE12(expected, actual) \ + CHECK_POINTER_EQ_BASE(args, expected, actual, true, true) // ============================================================================ typedef unsigned (*suite_fn)(unsigned); -int cct_run(suite_fn *suites, int verbose); +int cct_run(suite_fn* suites, int verbose); -void cct_suite_begin(const char *name); +void cct_suite_begin(const char* name); void cct_suite_end(void); -void cct_test_begin(const char *name); +void cct_test_begin(const char* name); void cct_test_end(void); -void cct_check_passed(const char *file, int line, const char *assertion); -void cct_check_failed(const char *file, int line, const char *assertion, - const char *expected, const char *actual); -bool cct_check_double_eq(const char *file, int line, const char *expression, - double expected, double actual); -bool cct_check_int_eq(const char *file, int line, const char *expression, - int64_t expected, int64_t actual); -bool cct_check_data_eq(const char *file, int line, const char *expression, - const uint8_t *expected, const uint8_t *actual, size_t size); -bool cct_check_str_eq(const char *file, int line, const char *expression, - const char *expected, const char *actual, - bool free1, bool free2); -bool cct_check_args_eq(const char *file, int line, const char *expression, - const struct args *expected, const struct args *actual, - bool free1, bool free2); -void cct_chdir(const char *path); -void cct_wipe(const char *path); -void cct_create_fresh_dir(const char *path); +void cct_check_passed(const char* file, int line, const char* assertion); +void cct_check_failed(const char* file, + int line, + const char* assertion, + const char* expected, + const char* actual); +bool cct_check_double_eq(const char* file, + int line, + const char* expression, + double expected, + double actual); +bool cct_check_int_eq(const char* file, + int line, + const char* expression, + int64_t expected, + int64_t actual); +bool cct_check_data_eq(const char* file, + int line, + const char* expression, + const uint8_t* expected, + const uint8_t* actual, + size_t size); +bool cct_check_str_eq(const char* file, + int line, + const char* expression, + const char* expected, + const char* actual, + bool free1, + bool free2); +bool cct_check_args_eq(const char* file, + int line, + const char* expression, + const struct args* expected, + const struct args* actual, + bool free1, + bool free2); +void cct_chdir(const char* path); +void cct_wipe(const char* path); +void cct_create_fresh_dir(const char* path); diff --git a/unittest/main.cpp b/unittest/main.cpp index 9dbc6648..615044d9 100644 --- a/unittest/main.cpp +++ b/unittest/main.cpp @@ -18,72 +18,69 @@ #include "framework.hpp" #ifdef HAVE_GETOPT_LONG -#include <getopt.h> +# include <getopt.h> #else -#include "../src/third_party/getopt_long.h" +# include "../src/third_party/getopt_long.h" #endif -#define SUITE(name) unsigned suite_ ## name(unsigned); +#define SUITE(name) unsigned suite_##name(unsigned); #include "suites.hpp" #undef SUITE static const char USAGE_TEXT[] = - "Usage:\n" - " test [options]\n" - "\n" - "Options:\n" - " -h, --help print this help text\n" - " -v, --verbose enable verbose logging of tests\n"; + "Usage:\n" + " test [options]\n" + "\n" + "Options:\n" + " -h, --help print this help text\n" + " -v, --verbose enable verbose logging of tests\n"; int -main(int argc, char **argv) +main(int argc, char** argv) { - suite_fn suites[] = { -#define SUITE(name) &suite_ ## name, + suite_fn suites[] = { +#define SUITE(name) &suite_##name, #include "suites.hpp" #undef SUITE - NULL - }; - static const struct option options[] = { - {"help", no_argument, NULL, 'h'}, - {"verbose", no_argument, NULL, 'v'}, - {NULL, 0, NULL, 0} - }; - int verbose = 0; - int c; - char *testdir, *dir_before; - int result; + NULL}; + static const struct option options[] = {{"help", no_argument, NULL, 'h'}, + {"verbose", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0}}; + int verbose = 0; + int c; + char *testdir, *dir_before; + int result; #ifdef _WIN32 - putenv("CCACHE_DETECT_SHEBANG=1"); + putenv("CCACHE_DETECT_SHEBANG=1"); #endif - while ((c = getopt_long(argc, argv, "hv", options, NULL)) != -1) { - switch (c) { - case 'h': - fprintf(stdout, USAGE_TEXT); - return 0; + while ((c = getopt_long(argc, argv, "hv", options, NULL)) != -1) { + switch (c) { + case 'h': + fprintf(stdout, USAGE_TEXT); + return 0; - case 'v': - verbose = 1; - break; + case 'v': + verbose = 1; + break; - default: - fprintf(stderr, USAGE_TEXT); - return 1; - } - } + default: + fprintf(stderr, USAGE_TEXT); + return 1; + } + } - testdir = format("testdir.%d", (int)getpid()); - cct_create_fresh_dir(testdir); - dir_before = gnu_getcwd(); - cct_chdir(testdir); - result = cct_run(suites, verbose); - if (result == 0) { - cct_chdir(dir_before); - cct_wipe(testdir); - } - free(testdir); - free(dir_before); - return result; + testdir = format("testdir.%d", (int)getpid()); + cct_create_fresh_dir(testdir); + dir_before = gnu_getcwd(); + cct_chdir(testdir); + result = cct_run(suites, verbose); + if (result == 0) { + cct_chdir(dir_before); + cct_wipe(testdir); + } + free(testdir); + free(dir_before); + return result; } diff --git a/unittest/test_args.cpp b/unittest/test_args.cpp index 55129095..d46bfa66 100644 --- a/unittest/test_args.cpp +++ b/unittest/test_args.cpp @@ -26,190 +26,190 @@ TEST_SUITE(args) TEST(args_init_empty) { - struct args *args = args_init(0, NULL); - CHECK(args); - CHECK_INT_EQ(0, args->argc); - CHECK(!args->argv[0]); - args_free(args); + struct args* args = args_init(0, NULL); + CHECK(args); + CHECK_INT_EQ(0, args->argc); + CHECK(!args->argv[0]); + args_free(args); } TEST(args_init_populated) { - const char *argv[] = {"first", "second"}; - struct args *args = args_init(2, argv); - CHECK(args); - CHECK_INT_EQ(2, args->argc); - CHECK_STR_EQ("first", args->argv[0]); - CHECK_STR_EQ("second", args->argv[1]); - CHECK(!args->argv[2]); - args_free(args); + const char* argv[] = {"first", "second"}; + struct args* args = args_init(2, argv); + CHECK(args); + CHECK_INT_EQ(2, args->argc); + CHECK_STR_EQ("first", args->argv[0]); + CHECK_STR_EQ("second", args->argv[1]); + CHECK(!args->argv[2]); + args_free(args); } TEST(args_init_from_string) { - struct args *args = args_init_from_string("first second\tthird\nfourth"); - CHECK(args); - CHECK_INT_EQ(4, args->argc); - CHECK_STR_EQ("first", args->argv[0]); - CHECK_STR_EQ("second", args->argv[1]); - CHECK_STR_EQ("third", args->argv[2]); - CHECK_STR_EQ("fourth", args->argv[3]); - CHECK(!args->argv[4]); - args_free(args); + struct args* args = args_init_from_string("first second\tthird\nfourth"); + CHECK(args); + CHECK_INT_EQ(4, args->argc); + CHECK_STR_EQ("first", args->argv[0]); + CHECK_STR_EQ("second", args->argv[1]); + CHECK_STR_EQ("third", args->argv[2]); + CHECK_STR_EQ("fourth", args->argv[3]); + CHECK(!args->argv[4]); + args_free(args); } TEST(args_init_from_gcc_atfile) { - struct args *args; - const char *argtext = - "first\rsec\\\tond\tthi\\\\rd\nfourth \tfif\\ th \"si'x\\\" th\"" - " 'seve\nth'\\"; - - create_file("gcc_atfile", argtext); - - args = args_init_from_gcc_atfile("gcc_atfile"); - CHECK(args); - CHECK_INT_EQ(7, args->argc); - CHECK_STR_EQ("first", args->argv[0]); - CHECK_STR_EQ("sec\tond", args->argv[1]); - CHECK_STR_EQ("thi\\rd", args->argv[2]); - CHECK_STR_EQ("fourth", args->argv[3]); - CHECK_STR_EQ("fif th", args->argv[4]); - CHECK_STR_EQ("si'x\" th", args->argv[5]); + struct args* args; + const char* argtext = + "first\rsec\\\tond\tthi\\\\rd\nfourth \tfif\\ th \"si'x\\\" th\"" + " 'seve\nth'\\"; + + create_file("gcc_atfile", argtext); + + args = args_init_from_gcc_atfile("gcc_atfile"); + CHECK(args); + CHECK_INT_EQ(7, args->argc); + CHECK_STR_EQ("first", args->argv[0]); + CHECK_STR_EQ("sec\tond", args->argv[1]); + CHECK_STR_EQ("thi\\rd", args->argv[2]); + CHECK_STR_EQ("fourth", args->argv[3]); + CHECK_STR_EQ("fif th", args->argv[4]); + CHECK_STR_EQ("si'x\" th", args->argv[5]); #ifndef _WIN32 - CHECK_STR_EQ("seve\nth", args->argv[6]); + CHECK_STR_EQ("seve\nth", args->argv[6]); #else - CHECK_STR_EQ("seve\r\nth", args->argv[6]); + CHECK_STR_EQ("seve\r\nth", args->argv[6]); #endif - CHECK(!args->argv[7]); - args_free(args); + CHECK(!args->argv[7]); + args_free(args); } TEST(args_copy) { - struct args *args1 = args_init_from_string("foo"); - struct args *args2 = args_copy(args1); - CHECK_ARGS_EQ_FREE12(args1, args2); + struct args* args1 = args_init_from_string("foo"); + struct args* args2 = args_copy(args1); + CHECK_ARGS_EQ_FREE12(args1, args2); } TEST(args_add) { - struct args *args = args_init_from_string("first"); - CHECK_INT_EQ(1, args->argc); - args_add(args, "second"); - CHECK_INT_EQ(2, args->argc); - CHECK_STR_EQ("second", args->argv[1]); - CHECK(!args->argv[2]); - args_free(args); + struct args* args = args_init_from_string("first"); + CHECK_INT_EQ(1, args->argc); + args_add(args, "second"); + CHECK_INT_EQ(2, args->argc); + CHECK_STR_EQ("second", args->argv[1]); + CHECK(!args->argv[2]); + args_free(args); } TEST(args_extend) { - struct args *args1 = args_init_from_string("first"); - struct args *args2 = args_init_from_string("second third"); - CHECK_INT_EQ(1, args1->argc); - args_extend(args1, args2); - CHECK_INT_EQ(3, args1->argc); - CHECK_STR_EQ("second", args1->argv[1]); - CHECK_STR_EQ("third", args1->argv[2]); - CHECK(!args1->argv[3]); - args_free(args1); - args_free(args2); + struct args* args1 = args_init_from_string("first"); + struct args* args2 = args_init_from_string("second third"); + CHECK_INT_EQ(1, args1->argc); + args_extend(args1, args2); + CHECK_INT_EQ(3, args1->argc); + CHECK_STR_EQ("second", args1->argv[1]); + CHECK_STR_EQ("third", args1->argv[2]); + CHECK(!args1->argv[3]); + args_free(args1); + args_free(args2); } TEST(args_pop) { - struct args *args = args_init_from_string("first second third"); - args_pop(args, 2); - CHECK_INT_EQ(1, args->argc); - CHECK_STR_EQ("first", args->argv[0]); - CHECK(!args->argv[1]); - args_free(args); + struct args* args = args_init_from_string("first second third"); + args_pop(args, 2); + CHECK_INT_EQ(1, args->argc); + CHECK_STR_EQ("first", args->argv[0]); + CHECK(!args->argv[1]); + args_free(args); } TEST(args_set) { - struct args *args = args_init_from_string("first second third"); - args_set(args, 1, "2nd"); - CHECK_INT_EQ(3, args->argc); - CHECK_STR_EQ("first", args->argv[0]); - CHECK_STR_EQ("2nd", args->argv[1]); - CHECK_STR_EQ("third", args->argv[2]); - CHECK(!args->argv[3]); - args_free(args); + struct args* args = args_init_from_string("first second third"); + args_set(args, 1, "2nd"); + CHECK_INT_EQ(3, args->argc); + CHECK_STR_EQ("first", args->argv[0]); + CHECK_STR_EQ("2nd", args->argv[1]); + CHECK_STR_EQ("third", args->argv[2]); + CHECK(!args->argv[3]); + args_free(args); } TEST(args_remove_first) { - struct args *args1 = args_init_from_string("first second third"); - struct args *args2 = args_init_from_string("second third"); - args_remove_first(args1); - CHECK_ARGS_EQ_FREE12(args1, args2); + struct args* args1 = args_init_from_string("first second third"); + struct args* args2 = args_init_from_string("second third"); + args_remove_first(args1); + CHECK_ARGS_EQ_FREE12(args1, args2); } TEST(args_add_prefix) { - struct args *args1 = args_init_from_string("second third"); - struct args *args2 = args_init_from_string("first second third"); - args_add_prefix(args1, "first"); - CHECK_ARGS_EQ_FREE12(args1, args2); + struct args* args1 = args_init_from_string("second third"); + struct args* args2 = args_init_from_string("first second third"); + args_add_prefix(args1, "first"); + CHECK_ARGS_EQ_FREE12(args1, args2); } TEST(args_strip) { - struct args *args1 = args_init_from_string("first xsecond third xfourth"); - struct args *args2 = args_init_from_string("first third"); - args_strip(args1, "x"); - CHECK_ARGS_EQ_FREE12(args1, args2); + struct args* args1 = args_init_from_string("first xsecond third xfourth"); + struct args* args2 = args_init_from_string("first third"); + args_strip(args1, "x"); + CHECK_ARGS_EQ_FREE12(args1, args2); } TEST(args_to_string) { - struct args *args = args_init_from_string("first second"); - CHECK_STR_EQ_FREE2("first second", args_to_string(args)); - args_free(args); + struct args* args = args_init_from_string("first second"); + CHECK_STR_EQ_FREE2("first second", args_to_string(args)); + args_free(args); } TEST(args_insert) { - struct args *args = args_init_from_string("first second third fourth fifth"); - - struct args *src1 = args_init_from_string("alpha beta gamma"); - struct args *src2 = args_init_from_string("one"); - struct args *src3 = args_init_from_string(""); - struct args *src4 = args_init_from_string("alpha beta gamma"); - struct args *src5 = args_init_from_string("one"); - struct args *src6 = args_init_from_string(""); - - args_insert(args, 2, src1, true); - CHECK_STR_EQ_FREE2("first second alpha beta gamma fourth fifth", - args_to_string(args)); - CHECK_INT_EQ(7, args->argc); - args_insert(args, 2, src2, true); - CHECK_STR_EQ_FREE2("first second one beta gamma fourth fifth", - args_to_string(args)); - CHECK_INT_EQ(7, args->argc); - args_insert(args, 2, src3, true); - CHECK_STR_EQ_FREE2("first second beta gamma fourth fifth", - args_to_string(args)); - CHECK_INT_EQ(6, args->argc); - - args_insert(args, 1, src4, false); - CHECK_STR_EQ_FREE2("first alpha beta gamma second beta gamma fourth fifth", - args_to_string(args)); - CHECK_INT_EQ(9, args->argc); - args_insert(args, 1, src5, false); - CHECK_STR_EQ_FREE2( - "first one alpha beta gamma second beta gamma fourth fifth", - args_to_string(args)); - CHECK_INT_EQ(10, args->argc); - args_insert(args, 1, src6, false); - CHECK_STR_EQ_FREE2( - "first one alpha beta gamma second beta gamma fourth fifth", - args_to_string(args)); - CHECK_INT_EQ(10, args->argc); - - args_free(args); + struct args* args = args_init_from_string("first second third fourth fifth"); + + struct args* src1 = args_init_from_string("alpha beta gamma"); + struct args* src2 = args_init_from_string("one"); + struct args* src3 = args_init_from_string(""); + struct args* src4 = args_init_from_string("alpha beta gamma"); + struct args* src5 = args_init_from_string("one"); + struct args* src6 = args_init_from_string(""); + + args_insert(args, 2, src1, true); + CHECK_STR_EQ_FREE2("first second alpha beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(7, args->argc); + args_insert(args, 2, src2, true); + CHECK_STR_EQ_FREE2("first second one beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(7, args->argc); + args_insert(args, 2, src3, true); + CHECK_STR_EQ_FREE2("first second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(6, args->argc); + + args_insert(args, 1, src4, false); + CHECK_STR_EQ_FREE2("first alpha beta gamma second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(9, args->argc); + args_insert(args, 1, src5, false); + CHECK_STR_EQ_FREE2( + "first one alpha beta gamma second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(10, args->argc); + args_insert(args, 1, src6, false); + CHECK_STR_EQ_FREE2( + "first one alpha beta gamma second beta gamma fourth fifth", + args_to_string(args)); + CHECK_INT_EQ(10, args->argc); + + args_free(args); } TEST_SUITE_END diff --git a/unittest/test_argument_processing.cpp b/unittest/test_argument_processing.cpp index c940ec61..6f46cc7c 100644 --- a/unittest/test_argument_processing.cpp +++ b/unittest/test_argument_processing.cpp @@ -23,42 +23,42 @@ #include "framework.hpp" #include "util.hpp" -extern struct conf *conf; +extern struct conf* conf; -static char * +static char* get_root(void) { #ifndef _WIN32 - return x_strdup("/"); + return x_strdup("/"); #else - char volume[4]; // "C:\" - GetVolumePathName(get_cwd(), volume, sizeof(volume)); - return x_strdup(volume); + char volume[4]; // "C:\" + GetVolumePathName(get_cwd(), volume, sizeof(volume)); + return x_strdup(volume); #endif } -static char * -get_posix_path(char *path) +static char* +get_posix_path(char* path) { #ifndef _WIN32 - return x_strdup(path); + return x_strdup(path); #else - char *posix; - char *p; - - // /-escape volume. - if (path[0] >= 'A' && path[0] <= 'Z' && path[1] == ':') { - posix = format("/%s", path); - } else { - posix = x_strdup(path); - } - // Convert slashes. - for (p = posix; *p; p++) { - if (*p == '\\') { - *p = '/'; - } - } - return posix; + char* posix; + char* p; + + // /-escape volume. + if (path[0] >= 'A' && path[0] <= 'Z' && path[1] == ':') { + posix = format("/%s", path); + } else { + posix = x_strdup(path); + } + // Convert slashes. + for (p = posix; *p; p++) { + if (*p == '\\') { + *p = '/'; + } + } + return posix; #endif } @@ -66,452 +66,449 @@ TEST_SUITE(argument_processing) TEST(dash_E_should_result_in_called_for_preprocessing) { - struct args *orig = args_init_from_string("cc -c foo.c -E"); - struct args *preprocessed, *compiler; + struct args* orig = args_init_from_string("cc -c foo.c -E"); + struct args *preprocessed, *compiler; - create_file("foo.c", ""); - CHECK(!cc_process_args(orig, &preprocessed, &compiler)); - CHECK_INT_EQ(1, stats_get_pending(STATS_PREPROCESSING)); + create_file("foo.c", ""); + CHECK(!cc_process_args(orig, &preprocessed, &compiler)); + CHECK_INT_EQ(1, stats_get_pending(STATS_PREPROCESSING)); - args_free(orig); + args_free(orig); } TEST(dash_M_should_be_unsupported) { - struct args *orig = args_init_from_string("cc -c foo.c -M"); - struct args *preprocessed, *compiler; + struct args* orig = args_init_from_string("cc -c foo.c -M"); + struct args *preprocessed, *compiler; - create_file("foo.c", ""); - CHECK(!cc_process_args(orig, &preprocessed, &compiler)); - CHECK_INT_EQ(1, stats_get_pending(STATS_UNSUPPORTED_OPTION)); + create_file("foo.c", ""); + CHECK(!cc_process_args(orig, &preprocessed, &compiler)); + CHECK_INT_EQ(1, stats_get_pending(STATS_UNSUPPORTED_OPTION)); - args_free(orig); + args_free(orig); } TEST(dependency_flags_should_only_be_sent_to_the_preprocessor) { -#define CMD \ - "cc -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2" \ - " -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf" - struct args *orig = args_init_from_string(CMD " -c foo.c -o foo.o"); - struct args *exp_cpp = args_init_from_string(CMD); +#define CMD \ + "cc -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 -MQ mq1 -MQ mq2" \ + " -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt -Wp,-MQ,wpmq -Wp,-MF,wpf" + struct args* orig = args_init_from_string(CMD " -c foo.c -o foo.o"); + struct args* exp_cpp = args_init_from_string(CMD); #undef CMD - struct args *exp_cc = args_init_from_string("cc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); + struct args* exp_cc = args_init_from_string("cc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - args_free(orig); + args_free(orig); } TEST(cpp_only_flags_to_preprocessor_if_run_second_cpp_is_false) { -#define CMD \ - "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \ - " -include test.h -include-pch test.pch -iprefix . -iquote ." \ - " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \ - " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \ - " -fno-working-directory -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2" \ - " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt" \ - " -Wp,-MQ,wpmq -Wp,-MF,wpf" - struct args *orig = args_init_from_string(CMD " -c foo.c -o foo.o"); - struct args *exp_cpp = args_init_from_string(CMD); +#define CMD \ + "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \ + " -include test.h -include-pch test.pch -iprefix . -iquote ." \ + " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \ + " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \ + " -fno-working-directory -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2" \ + " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd -Wp,-MP -Wp,-MT,wpmt" \ + " -Wp,-MQ,wpmq -Wp,-MF,wpf" + struct args* orig = args_init_from_string(CMD " -c foo.c -o foo.o"); + struct args* exp_cpp = args_init_from_string(CMD); #undef CMD - struct args *exp_cc = args_init_from_string("cc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); + struct args* exp_cc = args_init_from_string("cc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); - conf->run_second_cpp = false; - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + conf->run_second_cpp = false; + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - args_free(orig); + args_free(orig); } TEST(cpp_only_flags_to_preprocessor_and_compiler_if_run_second_cpp_is_true) { -#define CMD \ - "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \ - " -include test.h -include-pch test.pch -iprefix . -iquote ." \ - " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \ - " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \ - " -fno-working-directory" -#define DEP_OPTS \ - " -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 " \ - " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd" - struct args *orig = args_init_from_string(CMD DEP_OPTS " -c foo.c -o foo.o"); - struct args *exp_cpp = args_init_from_string(CMD DEP_OPTS); - struct args *exp_cc = args_init_from_string(CMD " -c"); +#define CMD \ + "cc -I. -idirafter . -iframework. -imacros . -imultilib ." \ + " -include test.h -include-pch test.pch -iprefix . -iquote ." \ + " -isysroot . -isystem . -iwithprefix . -iwithprefixbefore ." \ + " -DTEST_MACRO -DTEST_MACRO2=1 -F. -trigraphs -fworking-directory" \ + " -fno-working-directory" +#define DEP_OPTS \ + " -MD -MMD -MP -MF foo.d -MT mt1 -MT mt2 " \ + " -MQ mq1 -MQ mq2 -Wp,-MD,wpmd -Wp,-MMD,wpmmd" + struct args* orig = args_init_from_string(CMD DEP_OPTS " -c foo.c -o foo.o"); + struct args* exp_cpp = args_init_from_string(CMD DEP_OPTS); + struct args* exp_cc = args_init_from_string(CMD " -c"); #undef CMD - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); - conf->run_second_cpp = true; - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + conf->run_second_cpp = true; + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - args_free(orig); + args_free(orig); } TEST(dependency_flags_that_take_an_argument_should_not_require_space_delimiter) { - struct args *orig = args_init_from_string( - "cc -c -MMD -MFfoo.d -MT mt -MTmt -MQmq foo.c -o foo.o"); - struct args *exp_cpp = args_init_from_string( - "cc -MMD -MFfoo.d -MT mt -MTmt -MQmq"); - struct args *exp_cc = args_init_from_string("cc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = args_init_from_string( + "cc -c -MMD -MFfoo.d -MT mt -MTmt -MQmq foo.c -o foo.o"); + struct args* exp_cpp = + args_init_from_string("cc -MMD -MFfoo.d -MT mt -MTmt -MQmq"); + struct args* exp_cc = args_init_from_string("cc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(sysroot_should_be_rewritten_if_basedir_is_used) { - extern char *current_working_dir; - char *arg_string; - struct args *orig; - struct args *act_cpp = NULL, *act_cc = NULL; - - create_file("foo.c", ""); - free(conf->base_dir); - conf->base_dir = get_root(); - current_working_dir = get_cwd(); - arg_string = format("cc --sysroot=%s/foo/bar -c foo.c", current_working_dir); - orig = args_init_from_string(arg_string); - free(arg_string); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_STR_EQ(act_cpp->argv[1], "--sysroot=./foo/bar"); - - args_free(orig); - args_free(act_cpp); - args_free(act_cc); + extern char* current_working_dir; + char* arg_string; + struct args* orig; + struct args *act_cpp = NULL, *act_cc = NULL; + + create_file("foo.c", ""); + free(conf->base_dir); + conf->base_dir = get_root(); + current_working_dir = get_cwd(); + arg_string = format("cc --sysroot=%s/foo/bar -c foo.c", current_working_dir); + orig = args_init_from_string(arg_string); + free(arg_string); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_STR_EQ(act_cpp->argv[1], "--sysroot=./foo/bar"); + + args_free(orig); + args_free(act_cpp); + args_free(act_cc); } TEST(sysroot_with_separate_argument_should_be_rewritten_if_basedir_is_used) { - extern char *current_working_dir; - char *arg_string; - struct args *orig; - struct args *act_cpp = NULL, *act_cc = NULL; - - create_file("foo.c", ""); - free(conf->base_dir); - conf->base_dir = get_root(); - current_working_dir = get_cwd(); - arg_string = format("cc --sysroot %s/foo -c foo.c", current_working_dir); - orig = args_init_from_string(arg_string); - free(arg_string); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_STR_EQ(act_cpp->argv[1], "--sysroot"); - CHECK_STR_EQ(act_cpp->argv[2], "./foo"); - - args_free(orig); - args_free(act_cpp); - args_free(act_cc); + extern char* current_working_dir; + char* arg_string; + struct args* orig; + struct args *act_cpp = NULL, *act_cc = NULL; + + create_file("foo.c", ""); + free(conf->base_dir); + conf->base_dir = get_root(); + current_working_dir = get_cwd(); + arg_string = format("cc --sysroot %s/foo -c foo.c", current_working_dir); + orig = args_init_from_string(arg_string); + free(arg_string); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_STR_EQ(act_cpp->argv[1], "--sysroot"); + CHECK_STR_EQ(act_cpp->argv[2], "./foo"); + + args_free(orig); + args_free(act_cpp); + args_free(act_cc); } TEST(MF_flag_with_immediate_argument_should_work_as_last_argument) { - struct args *orig = args_init_from_string( - "cc -c foo.c -o foo.o -MMD -MT bar -MFfoo.d"); - struct args *exp_cpp = args_init_from_string( - "cc -MMD -MT bar -MFfoo.d"); - struct args *exp_cc = args_init_from_string("cc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("cc -c foo.c -o foo.o -MMD -MT bar -MFfoo.d"); + struct args* exp_cpp = args_init_from_string("cc -MMD -MT bar -MFfoo.d"); + struct args* exp_cc = args_init_from_string("cc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(MT_flag_with_immediate_argument_should_work_as_last_argument) { - struct args *orig = args_init_from_string( - "cc -c foo.c -o foo.o -MMD -MFfoo.d -MT foo -MTbar"); - struct args *exp_cpp = args_init_from_string( - "cc -MMD -MFfoo.d -MT foo -MTbar"); - struct args *exp_cc = args_init_from_string("cc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("cc -c foo.c -o foo.o -MMD -MFfoo.d -MT foo -MTbar"); + struct args* exp_cpp = + args_init_from_string("cc -MMD -MFfoo.d -MT foo -MTbar"); + struct args* exp_cc = args_init_from_string("cc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(MQ_flag_with_immediate_argument_should_work_as_last_argument) { - struct args *orig = args_init_from_string( - "cc -c foo.c -o foo.o -MMD -MFfoo.d -MQ foo -MQbar"); - struct args *exp_cpp = args_init_from_string( - "cc -MMD -MFfoo.d -MQ foo -MQbar"); - struct args *exp_cc = args_init_from_string("cc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("cc -c foo.c -o foo.o -MMD -MFfoo.d -MQ foo -MQbar"); + struct args* exp_cpp = + args_init_from_string("cc -MMD -MFfoo.d -MQ foo -MQbar"); + struct args* exp_cc = args_init_from_string("cc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(MQ_flag_without_immediate_argument_should_not_add_MQobj) { - struct args *orig = args_init_from_string( - "gcc -c -MD -MP -MFfoo.d -MQ foo.d foo.c"); - struct args *exp_cpp = args_init_from_string( - "gcc -MD -MP -MFfoo.d -MQ foo.d"); - struct args *exp_cc = args_init_from_string("gcc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("gcc -c -MD -MP -MFfoo.d -MQ foo.d foo.c"); + struct args* exp_cpp = + args_init_from_string("gcc -MD -MP -MFfoo.d -MQ foo.d"); + struct args* exp_cc = args_init_from_string("gcc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(MT_flag_without_immediate_argument_should_not_add_MTobj) { - struct args *orig = args_init_from_string( - "gcc -c -MD -MP -MFfoo.d -MT foo.d foo.c"); - struct args *exp_cpp = args_init_from_string( - "gcc -MD -MP -MFfoo.d -MT foo.d"); - struct args *exp_cc = args_init_from_string("gcc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("gcc -c -MD -MP -MFfoo.d -MT foo.d foo.c"); + struct args* exp_cpp = + args_init_from_string("gcc -MD -MP -MFfoo.d -MT foo.d"); + struct args* exp_cc = args_init_from_string("gcc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(MQ_flag_with_immediate_argument_should_not_add_MQobj) { - struct args *orig = args_init_from_string( - "gcc -c -MD -MP -MFfoo.d -MQfoo.d foo.c"); - struct args *exp_cpp = args_init_from_string( - "gcc -MD -MP -MFfoo.d -MQfoo.d"); - struct args *exp_cc = args_init_from_string("gcc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("gcc -c -MD -MP -MFfoo.d -MQfoo.d foo.c"); + struct args* exp_cpp = args_init_from_string("gcc -MD -MP -MFfoo.d -MQfoo.d"); + struct args* exp_cc = args_init_from_string("gcc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(MT_flag_with_immediate_argument_should_not_add_MQobj) { - struct args *orig = args_init_from_string( - "gcc -c -MD -MP -MFfoo.d -MTfoo.d foo.c"); - struct args *exp_cpp = args_init_from_string( - "gcc -MD -MP -MFfoo.d -MTfoo.d"); - struct args *exp_cc = args_init_from_string("gcc -c"); - struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("gcc -c -MD -MP -MFfoo.d -MTfoo.d foo.c"); + struct args* exp_cpp = args_init_from_string("gcc -MD -MP -MFfoo.d -MTfoo.d"); + struct args* exp_cc = args_init_from_string("gcc -c"); + struct args *act_cpp = NULL, *act_cc = NULL; + create_file("foo.c", ""); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(fprofile_flag_with_existing_dir_should_be_rewritten_to_real_path) { - struct args *orig = args_init_from_string( - "gcc -c -fprofile-generate=some/dir foo.c"); - struct args *exp_cpp = args_init_from_string("gcc"); - struct args *exp_cc = args_init_from_string("gcc"); - struct args *act_cpp = NULL, *act_cc = NULL; - char *s, *path; - - create_file("foo.c", ""); - mkdir("some", 0777); - mkdir("some/dir", 0777); - path = x_realpath("some/dir"); - s = format("-fprofile-generate=%s", path); - free(path); - args_add(exp_cpp, s); - args_add(exp_cc, s); - args_add(exp_cc, "-c"); - free(s); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = + args_init_from_string("gcc -c -fprofile-generate=some/dir foo.c"); + struct args* exp_cpp = args_init_from_string("gcc"); + struct args* exp_cc = args_init_from_string("gcc"); + struct args *act_cpp = NULL, *act_cc = NULL; + char *s, *path; + + create_file("foo.c", ""); + mkdir("some", 0777); + mkdir("some/dir", 0777); + path = x_realpath("some/dir"); + s = format("-fprofile-generate=%s", path); + free(path); + args_add(exp_cpp, s); + args_add(exp_cc, s); + args_add(exp_cc, "-c"); + free(s); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(fprofile_flag_with_nonexistent_dir_should_not_be_rewritten) { - struct args *orig = args_init_from_string( - "gcc -c -fprofile-generate=some/dir foo.c"); - struct args *exp_cpp = args_init_from_string( - "gcc -fprofile-generate=some/dir"); - struct args *exp_cc = args_init_from_string( - "gcc -fprofile-generate=some/dir -c"); - struct args *act_cpp = NULL, *act_cc = NULL; + struct args* orig = + args_init_from_string("gcc -c -fprofile-generate=some/dir foo.c"); + struct args* exp_cpp = + args_init_from_string("gcc -fprofile-generate=some/dir"); + struct args* exp_cc = + args_init_from_string("gcc -fprofile-generate=some/dir -c"); + struct args *act_cpp = NULL, *act_cc = NULL; - create_file("foo.c", ""); + create_file("foo.c", ""); - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - args_free(orig); + args_free(orig); } TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used) { - extern char *current_working_dir; - char *arg_string; - struct args *orig; - struct args *act_cpp = NULL, *act_cc = NULL; - - create_file("foo.c", ""); - free(conf->base_dir); - conf->base_dir = get_root(); - current_working_dir = get_cwd(); - arg_string = format("cc -isystem %s/foo -c foo.c", current_working_dir); - orig = args_init_from_string(arg_string); - free(arg_string); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_STR_EQ("./foo", act_cpp->argv[2]); - - args_free(orig); - args_free(act_cpp); - args_free(act_cc); + extern char* current_working_dir; + char* arg_string; + struct args* orig; + struct args *act_cpp = NULL, *act_cc = NULL; + + create_file("foo.c", ""); + free(conf->base_dir); + conf->base_dir = get_root(); + current_working_dir = get_cwd(); + arg_string = format("cc -isystem %s/foo -c foo.c", current_working_dir); + orig = args_init_from_string(arg_string); + free(arg_string); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_STR_EQ("./foo", act_cpp->argv[2]); + + args_free(orig); + args_free(act_cpp); + args_free(act_cc); } TEST(isystem_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used) { - extern char *current_working_dir; - char *cwd; - char *arg_string; - struct args *orig; - struct args *act_cpp = NULL, *act_cc = NULL; - - create_file("foo.c", ""); - free(conf->base_dir); - conf->base_dir = x_strdup("/"); // posix - current_working_dir = get_cwd(); - // Windows path doesn't work concatenated. - cwd = get_posix_path(current_working_dir); - arg_string = format("cc -isystem%s/foo -c foo.c", cwd); - orig = args_init_from_string(arg_string); - free(arg_string); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_STR_EQ("-isystem./foo", act_cpp->argv[1]); - - free(cwd); - args_free(orig); - args_free(act_cpp); - args_free(act_cc); + extern char* current_working_dir; + char* cwd; + char* arg_string; + struct args* orig; + struct args *act_cpp = NULL, *act_cc = NULL; + + create_file("foo.c", ""); + free(conf->base_dir); + conf->base_dir = x_strdup("/"); // posix + current_working_dir = get_cwd(); + // Windows path doesn't work concatenated. + cwd = get_posix_path(current_working_dir); + arg_string = format("cc -isystem%s/foo -c foo.c", cwd); + orig = args_init_from_string(arg_string); + free(arg_string); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_STR_EQ("-isystem./foo", act_cpp->argv[1]); + + free(cwd); + args_free(orig); + args_free(act_cpp); + args_free(act_cc); } TEST(I_flag_with_concat_arg_should_be_rewritten_if_basedir_is_used) { - extern char *current_working_dir; - char *cwd; - char *arg_string; - struct args *orig; - struct args *act_cpp = NULL, *act_cc = NULL; - - create_file("foo.c", ""); - free(conf->base_dir); - conf->base_dir = x_strdup("/"); // posix - current_working_dir = get_cwd(); - // Windows path doesn't work concatenated. - cwd = get_posix_path(current_working_dir); - arg_string = format("cc -I%s/foo -c foo.c", cwd); - orig = args_init_from_string(arg_string); - free(arg_string); - - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_STR_EQ("-I./foo", act_cpp->argv[1]); - - free(cwd); - args_free(orig); - args_free(act_cpp); - args_free(act_cc); + extern char* current_working_dir; + char* cwd; + char* arg_string; + struct args* orig; + struct args *act_cpp = NULL, *act_cc = NULL; + + create_file("foo.c", ""); + free(conf->base_dir); + conf->base_dir = x_strdup("/"); // posix + current_working_dir = get_cwd(); + // Windows path doesn't work concatenated. + cwd = get_posix_path(current_working_dir); + arg_string = format("cc -I%s/foo -c foo.c", cwd); + orig = args_init_from_string(arg_string); + free(arg_string); + + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_STR_EQ("-I./foo", act_cpp->argv[1]); + + free(cwd); + args_free(orig); + args_free(act_cpp); + args_free(act_cc); } TEST(debug_flag_order_with_known_option_first) { - struct args *orig = args_init_from_string("cc -g1 -gsplit-dwarf foo.c -c"); - struct args *exp_cpp = args_init_from_string("cc -g1 -gsplit-dwarf"); - struct args *exp_cc = args_init_from_string("cc -g1 -gsplit-dwarf -c"); - struct args *act_cpp = NULL; - struct args *act_cc = NULL; - - create_file("foo.c", ""); - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = args_init_from_string("cc -g1 -gsplit-dwarf foo.c -c"); + struct args* exp_cpp = args_init_from_string("cc -g1 -gsplit-dwarf"); + struct args* exp_cc = args_init_from_string("cc -g1 -gsplit-dwarf -c"); + struct args* act_cpp = NULL; + struct args* act_cc = NULL; + + create_file("foo.c", ""); + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(debug_flag_order_with_known_option_last) { - struct args *orig = args_init_from_string("cc -gsplit-dwarf -g1 foo.c -c"); - struct args *exp_cpp = args_init_from_string("cc -gsplit-dwarf -g1"); - struct args *exp_cc = args_init_from_string("cc -gsplit-dwarf -g1 -c"); - struct args *act_cpp = NULL; - struct args *act_cc = NULL; - - create_file("foo.c", ""); - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = args_init_from_string("cc -gsplit-dwarf -g1 foo.c -c"); + struct args* exp_cpp = args_init_from_string("cc -gsplit-dwarf -g1"); + struct args* exp_cc = args_init_from_string("cc -gsplit-dwarf -g1 -c"); + struct args* act_cpp = NULL; + struct args* act_cc = NULL; + + create_file("foo.c", ""); + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST(options_not_to_be_passed_to_the_preprocesor) { - struct args *orig = args_init_from_string( - "cc -Wa,foo foo.c -g -Xlinker fie -Xlinker,fum -c -Werror"); - struct args *exp_cpp = args_init_from_string("cc -g"); - struct args *exp_cc = args_init_from_string( - "cc -g -Wa,foo -Xlinker fie -Xlinker,fum -Werror -c"); - struct args *act_cpp = NULL; - struct args *act_cc = NULL; - - create_file("foo.c", ""); - CHECK(cc_process_args(orig, &act_cpp, &act_cc)); - CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); - CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); - - args_free(orig); + struct args* orig = args_init_from_string( + "cc -Wa,foo foo.c -g -Xlinker fie -Xlinker,fum -c -Werror"); + struct args* exp_cpp = args_init_from_string("cc -g"); + struct args* exp_cc = + args_init_from_string("cc -g -Wa,foo -Xlinker fie -Xlinker,fum -Werror -c"); + struct args* act_cpp = NULL; + struct args* act_cc = NULL; + + create_file("foo.c", ""); + CHECK(cc_process_args(orig, &act_cpp, &act_cc)); + CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp); + CHECK_ARGS_EQ_FREE12(exp_cc, act_cc); + + args_free(orig); } TEST_SUITE_END diff --git a/unittest/test_compopt.cpp b/unittest/test_compopt.cpp index 1b0c5bf5..99c3b2bd 100644 --- a/unittest/test_compopt.cpp +++ b/unittest/test_compopt.cpp @@ -26,115 +26,115 @@ TEST_SUITE(compopt) TEST(option_table_should_be_sorted) { - bool compopt_verify_sortedness(void); - CHECK(compopt_verify_sortedness()); + bool compopt_verify_sortedness(void); + CHECK(compopt_verify_sortedness()); } TEST(dash_I_affects_cpp) { - CHECK(compopt_affects_cpp("-I")); - CHECK(!compopt_affects_cpp("-Ifoo")); + CHECK(compopt_affects_cpp("-I")); + CHECK(!compopt_affects_cpp("-Ifoo")); } TEST(compopt_short) { - CHECK(compopt_short(compopt_affects_cpp, "-Ifoo")); - CHECK(!compopt_short(compopt_affects_cpp, "-include")); + CHECK(compopt_short(compopt_affects_cpp, "-Ifoo")); + CHECK(!compopt_short(compopt_affects_cpp, "-include")); } TEST(dash_V_doesnt_affect_cpp) { - CHECK(!compopt_affects_cpp("-V")); + CHECK(!compopt_affects_cpp("-V")); } TEST(dash_doesntexist_doesnt_affect_cpp) { - CHECK(!compopt_affects_cpp("-doesntexist")); + CHECK(!compopt_affects_cpp("-doesntexist")); } TEST(dash_MM_too_hard) { - CHECK(compopt_too_hard("-MM")); + CHECK(compopt_too_hard("-MM")); } TEST(dash_save_temps_too_hard) { - CHECK(compopt_too_hard("-save-temps")); + CHECK(compopt_too_hard("-save-temps")); } TEST(dash_save_temps_cwd_too_hard) { - CHECK(compopt_too_hard("-save-temps=cwd")); + CHECK(compopt_too_hard("-save-temps=cwd")); } TEST(dash_save_temps_obj_too_hard) { - CHECK(compopt_too_hard("-save-temps=obj")); + CHECK(compopt_too_hard("-save-temps=obj")); } TEST(dash_MD_not_too_hard) { - CHECK(!compopt_too_hard("-MD")); + CHECK(!compopt_too_hard("-MD")); } TEST(dash_fprofile_arcs_not_too_hard) { - CHECK(!compopt_too_hard("-fprofile-arcs")); + CHECK(!compopt_too_hard("-fprofile-arcs")); } TEST(dash_ftest_coverage_not_too_hard) { - CHECK(!compopt_too_hard("-ftest-coverage")); + CHECK(!compopt_too_hard("-ftest-coverage")); } TEST(dash_fstack_usage_not_too_hard) { - CHECK(!compopt_too_hard("-fstack-usage")); + CHECK(!compopt_too_hard("-fstack-usage")); } TEST(dash_doesntexist_not_too_hard) { - CHECK(!compopt_too_hard("-doesntexist")); + CHECK(!compopt_too_hard("-doesntexist")); } TEST(dash_Xpreprocessor_too_hard_for_direct_mode) { - CHECK(compopt_too_hard_for_direct_mode("-Xpreprocessor")); + CHECK(compopt_too_hard_for_direct_mode("-Xpreprocessor")); } TEST(dash_nostdinc_not_too_hard_for_direct_mode) { - CHECK(!compopt_too_hard_for_direct_mode("-nostdinc")); + CHECK(!compopt_too_hard_for_direct_mode("-nostdinc")); } TEST(dash_I_takes_path) { - CHECK(compopt_takes_path("-I")); + CHECK(compopt_takes_path("-I")); } TEST(dash_Xlinker_takes_arg) { - CHECK(compopt_takes_arg("-Xlinker")); + CHECK(compopt_takes_arg("-Xlinker")); } TEST(dash_xxx_doesnt_take_arg) { - CHECK(!compopt_takes_arg("-xxx")); + CHECK(!compopt_takes_arg("-xxx")); } TEST(dash_iframework_prefix_affects_cpp) { - CHECK(compopt_prefix_affects_cpp("-iframework")); + CHECK(compopt_prefix_affects_cpp("-iframework")); } TEST(dash_analyze_too_hard) { - CHECK(compopt_too_hard("-analyze")); + CHECK(compopt_too_hard("-analyze")); } TEST(dash_dash_analyze_too_hard) { - CHECK(compopt_too_hard("--analyze")); + CHECK(compopt_too_hard("--analyze")); } TEST_SUITE_END diff --git a/unittest/test_compr_none.cpp b/unittest/test_compr_none.cpp index 76411e7b..f19704c0 100644 --- a/unittest/test_compr_none.cpp +++ b/unittest/test_compr_none.cpp @@ -24,45 +24,45 @@ TEST_SUITE(compr_type_none) TEST(small_roundtrip) { - const uint64_t expected_foobar_checksum = 0xa2aa05ed9085aaf9ULL; + const uint64_t expected_foobar_checksum = 0xa2aa05ed9085aaf9ULL; - XXH64_state_t *checksum = XXH64_createState(); - XXH64_reset(checksum, 0); + XXH64_state_t* checksum = XXH64_createState(); + XXH64_reset(checksum, 0); - FILE *f = fopen("data.uncompressed", "w"); - struct compressor *compr_none = compressor_from_type(COMPR_TYPE_NONE); - struct compr_state *c_state = compr_none->init(f, -1, checksum); - CHECK(c_state); + FILE* f = fopen("data.uncompressed", "w"); + struct compressor* compr_none = compressor_from_type(COMPR_TYPE_NONE); + struct compr_state* c_state = compr_none->init(f, -1, checksum); + CHECK(c_state); - CHECK(compr_none->write(c_state, "foobar", 6)); + CHECK(compr_none->write(c_state, "foobar", 6)); - CHECK(compr_none->free(c_state)); - fclose(f); + CHECK(compr_none->free(c_state)); + fclose(f); - CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); + CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); - XXH64_reset(checksum, 0); - f = fopen("data.uncompressed", "r"); - struct decompressor *decompr_none = decompressor_from_type(COMPR_TYPE_NONE); - struct decompr_state *d_state = decompr_none->init(f, checksum); - CHECK(d_state); + XXH64_reset(checksum, 0); + f = fopen("data.uncompressed", "r"); + struct decompressor* decompr_none = decompressor_from_type(COMPR_TYPE_NONE); + struct decompr_state* d_state = decompr_none->init(f, checksum); + CHECK(d_state); - char buffer[4]; - CHECK(decompr_none->read(d_state, buffer, 4)); - CHECK(memcmp(buffer, "foob", 4) == 0); - CHECK(decompr_none->read(d_state, buffer, 2)); - CHECK(memcmp(buffer, "ar", 2) == 0); + char buffer[4]; + CHECK(decompr_none->read(d_state, buffer, 4)); + CHECK(memcmp(buffer, "foob", 4) == 0); + CHECK(decompr_none->read(d_state, buffer, 2)); + CHECK(memcmp(buffer, "ar", 2) == 0); - // Nothing left to read. - CHECK(!decompr_none->read(d_state, buffer, 1)); + // Nothing left to read. + CHECK(!decompr_none->read(d_state, buffer, 1)); - // Error state is remembered. - CHECK(!decompr_none->free(d_state)); - fclose(f); + // Error state is remembered. + CHECK(!decompr_none->free(d_state)); + fclose(f); - CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); + CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); - XXH64_freeState(checksum); + XXH64_freeState(checksum); } TEST_SUITE_END diff --git a/unittest/test_compr_zstd.cpp b/unittest/test_compr_zstd.cpp index 89d75b7f..393bb876 100644 --- a/unittest/test_compr_zstd.cpp +++ b/unittest/test_compr_zstd.cpp @@ -24,110 +24,110 @@ TEST_SUITE(compr_type_zstd) TEST(small_roundtrip) { - const uint64_t expected_foobar_checksum = 0xa2aa05ed9085aaf9ULL; + const uint64_t expected_foobar_checksum = 0xa2aa05ed9085aaf9ULL; - XXH64_state_t *checksum = XXH64_createState(); - XXH64_reset(checksum, 0); + XXH64_state_t* checksum = XXH64_createState(); + XXH64_reset(checksum, 0); - FILE *f = fopen("data.zstd", "w"); - struct compressor *compr_zstd = compressor_from_type(COMPR_TYPE_ZSTD); - struct compr_state *c_state = compr_zstd->init(f, -1, checksum); - CHECK(c_state); + FILE* f = fopen("data.zstd", "w"); + struct compressor* compr_zstd = compressor_from_type(COMPR_TYPE_ZSTD); + struct compr_state* c_state = compr_zstd->init(f, -1, checksum); + CHECK(c_state); - CHECK(compr_zstd->write(c_state, "foobar", 6)); + CHECK(compr_zstd->write(c_state, "foobar", 6)); - CHECK(compr_zstd->free(c_state)); - fclose(f); + CHECK(compr_zstd->free(c_state)); + fclose(f); - CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); + CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); - XXH64_reset(checksum, 0); - f = fopen("data.zstd", "r"); - struct decompressor *decompr_zstd = decompressor_from_type(COMPR_TYPE_ZSTD); - struct decompr_state *d_state = decompr_zstd->init(f, checksum); - CHECK(d_state); + XXH64_reset(checksum, 0); + f = fopen("data.zstd", "r"); + struct decompressor* decompr_zstd = decompressor_from_type(COMPR_TYPE_ZSTD); + struct decompr_state* d_state = decompr_zstd->init(f, checksum); + CHECK(d_state); - char buffer[4]; - CHECK(decompr_zstd->read(d_state, buffer, 4)); - CHECK(memcmp(buffer, "foob", 4) == 0); - CHECK(decompr_zstd->read(d_state, buffer, 2)); - CHECK(memcmp(buffer, "ar", 2) == 0); + char buffer[4]; + CHECK(decompr_zstd->read(d_state, buffer, 4)); + CHECK(memcmp(buffer, "foob", 4) == 0); + CHECK(decompr_zstd->read(d_state, buffer, 2)); + CHECK(memcmp(buffer, "ar", 2) == 0); - // Nothing left to read. - CHECK(!decompr_zstd->read(d_state, buffer, 1)); + // Nothing left to read. + CHECK(!decompr_zstd->read(d_state, buffer, 1)); - // Error state is remembered. - CHECK(!decompr_zstd->free(d_state)); - fclose(f); + // Error state is remembered. + CHECK(!decompr_zstd->free(d_state)); + fclose(f); - CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); + CHECK_INT_EQ(XXH64_digest(checksum), expected_foobar_checksum); - XXH64_freeState(checksum); + XXH64_freeState(checksum); } TEST(large_compressible_roundtrip) { - char data[] = "The quick brown fox jumps over the lazy dog"; + char data[] = "The quick brown fox jumps over the lazy dog"; - FILE *f = fopen("data.zstd", "w"); - struct compressor *compr_zstd = compressor_from_type(COMPR_TYPE_ZSTD); - struct compr_state *c_state = compr_zstd->init(f, 1, NULL); - CHECK(c_state); + FILE* f = fopen("data.zstd", "w"); + struct compressor* compr_zstd = compressor_from_type(COMPR_TYPE_ZSTD); + struct compr_state* c_state = compr_zstd->init(f, 1, NULL); + CHECK(c_state); - for (size_t i = 0; i < 1000; i++) { - CHECK(compr_zstd->write(c_state, data, sizeof(data))); - } + for (size_t i = 0; i < 1000; i++) { + CHECK(compr_zstd->write(c_state, data, sizeof(data))); + } - CHECK(compr_zstd->free(c_state)); - fclose(f); + CHECK(compr_zstd->free(c_state)); + fclose(f); - f = fopen("data.zstd", "r"); - struct decompressor *decompr_zstd = decompressor_from_type(COMPR_TYPE_ZSTD); - struct decompr_state *d_state = decompr_zstd->init(f, NULL); - CHECK(d_state); + f = fopen("data.zstd", "r"); + struct decompressor* decompr_zstd = decompressor_from_type(COMPR_TYPE_ZSTD); + struct decompr_state* d_state = decompr_zstd->init(f, NULL); + CHECK(d_state); - char buffer[sizeof(data)]; - for (size_t i = 0; i < 1000; i++) { - CHECK(decompr_zstd->read(d_state, buffer, sizeof(buffer))); - CHECK(memcmp(buffer, data, sizeof(data)) == 0); - } + char buffer[sizeof(data)]; + for (size_t i = 0; i < 1000; i++) { + CHECK(decompr_zstd->read(d_state, buffer, sizeof(buffer))); + CHECK(memcmp(buffer, data, sizeof(data)) == 0); + } - // Nothing left to read. - CHECK(!decompr_zstd->read(d_state, buffer, 1)); + // Nothing left to read. + CHECK(!decompr_zstd->read(d_state, buffer, 1)); - // Error state is remembered. - CHECK(!decompr_zstd->free(d_state)); - fclose(f); + // Error state is remembered. + CHECK(!decompr_zstd->free(d_state)); + fclose(f); } TEST(large_uncompressible_roundtrip) { - char data[100000]; - for (size_t i = 0; i < sizeof(data); i++) { - data[i] = rand() % 256; - } + char data[100000]; + for (size_t i = 0; i < sizeof(data); i++) { + data[i] = rand() % 256; + } - FILE *f = fopen("data.zstd", "w"); - struct compressor *compr_zstd = compressor_from_type(COMPR_TYPE_ZSTD); - struct compr_state *c_state = compr_zstd->init(f, 1, NULL); - CHECK(c_state); + FILE* f = fopen("data.zstd", "w"); + struct compressor* compr_zstd = compressor_from_type(COMPR_TYPE_ZSTD); + struct compr_state* c_state = compr_zstd->init(f, 1, NULL); + CHECK(c_state); - CHECK(compr_zstd->write(c_state, data, sizeof(data))); + CHECK(compr_zstd->write(c_state, data, sizeof(data))); - CHECK(compr_zstd->free(c_state)); - fclose(f); + CHECK(compr_zstd->free(c_state)); + fclose(f); - f = fopen("data.zstd", "r"); - struct decompressor *decompr_zstd = decompressor_from_type(COMPR_TYPE_ZSTD); - struct decompr_state *d_state = decompr_zstd->init(f, NULL); - CHECK(d_state); + f = fopen("data.zstd", "r"); + struct decompressor* decompr_zstd = decompressor_from_type(COMPR_TYPE_ZSTD); + struct decompr_state* d_state = decompr_zstd->init(f, NULL); + CHECK(d_state); - char buffer[sizeof(data)]; - CHECK(decompr_zstd->read(d_state, buffer, sizeof(buffer))); - CHECK(memcmp(buffer, data, sizeof(data)) == 0); + char buffer[sizeof(data)]; + CHECK(decompr_zstd->read(d_state, buffer, sizeof(buffer))); + CHECK(memcmp(buffer, data, sizeof(data)) == 0); - CHECK(decompr_zstd->free(d_state)); - fclose(f); + CHECK(decompr_zstd->free(d_state)); + fclose(f); } TEST_SUITE_END diff --git a/unittest/test_conf.cpp b/unittest/test_conf.cpp index b2a765ae..d4c10bcf 100644 --- a/unittest/test_conf.cpp +++ b/unittest/test_conf.cpp @@ -21,9 +21,10 @@ #include "util.hpp" #define N_CONFIG_ITEMS 35 -static struct { - char *descr; - char *origin; +static struct +{ + char* descr; + char* origin; } received_conf_items[N_CONFIG_ITEMS]; static size_t n_received_conf_items = 0; @@ -32,541 +33,530 @@ static char COMPRESS_1_ENV[] = "CCACHE_COMPRESS=1"; static char NOCOMPRESS_1_ENV[] = "CCACHE_NOCOMPRESS=1"; static void -conf_item_receiver(const char *descr, const char *origin, void *context) +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 = x_strdup(origin); - ++n_received_conf_items; + (void)context; + received_conf_items[n_received_conf_items].descr = x_strdup(descr); + received_conf_items[n_received_conf_items].origin = x_strdup(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); - free(received_conf_items[n_received_conf_items].origin); - } + while (n_received_conf_items > 0) { + --n_received_conf_items; + free(received_conf_items[n_received_conf_items].descr); + free(received_conf_items[n_received_conf_items].origin); + } } 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(0, conf->compression_level); - CHECK_STR_EQ("", conf->cpp_extension); - CHECK(!conf->debug); - CHECK(!conf->depend_mode); - CHECK(conf->direct_mode); - CHECK(!conf->disable); - CHECK_STR_EQ("", conf->extra_files_to_hash); - CHECK(!conf->file_clone); - CHECK(!conf->hard_link); - CHECK(conf->hash_dir); - CHECK_STR_EQ("", conf->ignore_headers_in_manifest); - CHECK(!conf->keep_comments_cpp); - CHECK_DOUBLE_EQ(0.8, conf->limit_multiple); - 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(!conf->pch_external_checksum); - CHECK_STR_EQ("", conf->prefix_command); - CHECK_STR_EQ("", conf->prefix_command_cpp); - CHECK(!conf->read_only); - CHECK(!conf->read_only_direct); - 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); + 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(0, conf->compression_level); + CHECK_STR_EQ("", conf->cpp_extension); + CHECK(!conf->debug); + CHECK(!conf->depend_mode); + CHECK(conf->direct_mode); + CHECK(!conf->disable); + CHECK_STR_EQ("", conf->extra_files_to_hash); + CHECK(!conf->file_clone); + CHECK(!conf->hard_link); + CHECK(conf->hash_dir); + CHECK_STR_EQ("", conf->ignore_headers_in_manifest); + CHECK(!conf->keep_comments_cpp); + CHECK_DOUBLE_EQ(0.8, conf->limit_multiple); + 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(!conf->pch_external_checksum); + CHECK_STR_EQ("", conf->prefix_command); + CHECK_STR_EQ("", conf->prefix_command_cpp); + CHECK(!conf->read_only); + CHECK(!conf->read_only_direct); + 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_ENV); - user = getenv("USER"); - CHECK_STR_EQ("rabbit", user); - create_file( - "ccache.conf", + struct conf* conf = conf_create(); + char *errmsg, *user; + putenv(USER_ENV); + user = getenv("USER"); + CHECK_STR_EQ("rabbit", user); + create_file( + "ccache.conf", #ifndef _WIN32 - "base_dir = /$USER/foo/${USER} \n" + "base_dir = /$USER/foo/${USER} \n" #else - "base_dir = C:/$USER/foo/${USER}\n" + "base_dir = C:/$USER/foo/${USER}\n" #endif - "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=false\n" - "compression_level= 2\n" - "cpp_extension = .foo\n" - "depend_mode = true\n" - "direct_mode = false\n" - "disable = true\n" - "extra_files_to_hash = a:b c:$USER\n" - "file_clone = true\n" - "hard_link = true\n" - "hash_dir = false\n" - "ignore_headers_in_manifest = a:b/c\n" - "keep_comments_cpp = true\n" - "limit_multiple = 1.0\n" - "log_file = $USER${USER} \n" - "max_files = 17\n" - "max_size = 123M\n" - "path = $USER.x\n" - "pch_external_checksum = true\n" - "prefix_command = x$USER\n" - "prefix_command_cpp = y\n" - "read_only = true\n" - "read_only_direct = true\n" - "recache = true\n" - "run_second_cpp = false\n" - "sloppiness = file_macro ,time_macros, include_file_mtime,include_file_ctime,file_stat_matches,file_stat_matches_ctime,pch_defines , no_system_headers,system_headers,clang_index_store\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); + "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=false\n" + "compression_level= 2\n" + "cpp_extension = .foo\n" + "depend_mode = true\n" + "direct_mode = false\n" + "disable = true\n" + "extra_files_to_hash = a:b c:$USER\n" + "file_clone = true\n" + "hard_link = true\n" + "hash_dir = false\n" + "ignore_headers_in_manifest = a:b/c\n" + "keep_comments_cpp = true\n" + "limit_multiple = 1.0\n" + "log_file = $USER${USER} \n" + "max_files = 17\n" + "max_size = 123M\n" + "path = $USER.x\n" + "pch_external_checksum = true\n" + "prefix_command = x$USER\n" + "prefix_command_cpp = y\n" + "read_only = true\n" + "read_only_direct = true\n" + "recache = true\n" + "run_second_cpp = false\n" + "sloppiness = file_macro ,time_macros, " + "include_file_mtime,include_file_ctime,file_stat_matches,file_stat_" + "matches_ctime,pch_defines , " + "no_system_headers,system_headers,clang_index_store\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); #ifndef _WIN32 - CHECK_STR_EQ_FREE1(format("/%s/foo/%s", user, user), conf->base_dir); + CHECK_STR_EQ_FREE1(format("/%s/foo/%s", user, user), conf->base_dir); #else - CHECK_STR_EQ_FREE1(format("C:/%s/foo/%s", user, user), conf->base_dir); + CHECK_STR_EQ_FREE1(format("C:/%s/foo/%s", user, user), conf->base_dir); #endif - 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->depend_mode); - CHECK(!conf->direct_mode); - CHECK(conf->disable); - CHECK_STR_EQ_FREE1(format("a:b c:%s", user), conf->extra_files_to_hash); - CHECK(conf->file_clone); - CHECK(conf->hard_link); - CHECK(!conf->hash_dir); - CHECK_STR_EQ("a:b/c", conf->ignore_headers_in_manifest); - CHECK(conf->keep_comments_cpp); - CHECK_DOUBLE_EQ(1.0, conf->limit_multiple); - 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(conf->pch_external_checksum); - CHECK_STR_EQ_FREE1(format("x%s", user), conf->prefix_command); - CHECK_STR_EQ("y", conf->prefix_command_cpp); - CHECK(conf->read_only); - CHECK(conf->read_only_direct); - 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_CTIME - |SLOPPY_SYSTEM_HEADERS - |SLOPPY_PCH_DEFINES - |SLOPPY_CLANG_INDEX_STORE, - 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); + 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->depend_mode); + CHECK(!conf->direct_mode); + CHECK(conf->disable); + CHECK_STR_EQ_FREE1(format("a:b c:%s", user), conf->extra_files_to_hash); + CHECK(conf->file_clone); + CHECK(conf->hard_link); + CHECK(!conf->hash_dir); + CHECK_STR_EQ("a:b/c", conf->ignore_headers_in_manifest); + CHECK(conf->keep_comments_cpp); + CHECK_DOUBLE_EQ(1.0, conf->limit_multiple); + 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(conf->pch_external_checksum); + CHECK_STR_EQ_FREE1(format("x%s", user), conf->prefix_command); + CHECK_STR_EQ("y", conf->prefix_command_cpp); + CHECK(conf->read_only); + CHECK(conf->read_only_direct); + 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_CTIME + | SLOPPY_SYSTEM_HEADERS | SLOPPY_PCH_DEFINES + | SLOPPY_CLANG_INDEX_STORE, + 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_INT_EQ(errno, 0); - CHECK_STR_EQ_FREE2("ccache.conf:1: missing equal sign", - errmsg); - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + create_file("ccache.conf", "no equal sign"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(errno, 0); + CHECK_STR_EQ_FREE2("ccache.conf:1: missing equal sign", errmsg); + conf_free(conf); } TEST(conf_read_with_unknown_config_key) { - struct conf *conf = conf_create(); - char *errmsg; - create_file("ccache.conf", "# Comment\nfoo = bar"); - CHECK(conf_read(conf, "ccache.conf", &errmsg)); - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + create_file("ccache.conf", "# Comment\nfoo = bar"); + CHECK(conf_read(conf, "ccache.conf", &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_INT_EQ(errno, 0); - 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_INT_EQ(errno, 0); - CHECK_STR_EQ_FREE2("ccache.conf:1: not a boolean value: \"foo\"", - errmsg); - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + + create_file("ccache.conf", "disable="); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(errno, 0); + 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_INT_EQ(errno, 0); + 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_INT_EQ(errno, 0); - CHECK_STR_EQ_FREE2("ccache.conf:1: syntax error: missing '}' after \"foo\"", - errmsg); - // Other cases tested in test_util.c. - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + create_file("ccache.conf", "base_dir = ${foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(errno, 0); + 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); + 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_INT_EQ(errno, 0); - CHECK_STR_EQ_FREE2("ccache.conf:1: invalid size: \"foo\"", - errmsg); - // Other cases tested in test_util.c. - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + create_file("ccache.conf", "max_size = foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(errno, 0); + 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_INT_EQ(errno, 0); - CHECK_STR_EQ_FREE2("ccache.conf:1: unknown sloppiness: \"foo\"", - errmsg); - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + create_file("ccache.conf", "sloppiness = file_macro, foo"); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(errno, 0); + 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_INT_EQ(errno, 0); - 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); + struct conf* conf = conf_create(); + char* errmsg; + + create_file("ccache.conf", "max_files ="); + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(errno, 0); + 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(conf_read_missing_config_file) { - struct conf *conf = conf_create(); - char *errmsg; - CHECK(!conf_read(conf, "ccache.conf", &errmsg)); - CHECK_INT_EQ(errno, ENOENT); - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + CHECK(!conf_read(conf, "ccache.conf", &errmsg)); + CHECK_INT_EQ(errno, ENOENT); + conf_free(conf); } TEST(verify_absolute_base_dir) { - struct conf *conf = conf_create(); - char *errmsg; + 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 = 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)); + create_file("ccache.conf", "base_dir ="); + CHECK(conf_read(conf, "ccache.conf", &errmsg)); - conf_free(conf); + 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); + 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; + struct conf* conf = conf_create(); + char* errmsg; - putenv(COMPRESS_1_ENV); - CHECK(conf_update_from_environment(conf, &errmsg)); - CHECK(conf->compression); + putenv(COMPRESS_1_ENV); + CHECK(conf_update_from_environment(conf, &errmsg)); + CHECK(conf->compression); - x_unsetenv("CCACHE_COMPRESS"); - putenv(NOCOMPRESS_1_ENV); - CHECK(conf_update_from_environment(conf, &errmsg)); - CHECK(!conf->compression); + x_unsetenv("CCACHE_COMPRESS"); + putenv(NOCOMPRESS_1_ENV); + CHECK(conf_update_from_environment(conf, &errmsg)); + CHECK(!conf->compression); - conf_free(conf); + conf_free(conf); } TEST(conf_set_new_value) { - char *errmsg; - char *data; - - create_file("ccache.conf", "path = vanilla\n"); - CHECKM(conf_set_value_in_file("ccache.conf", "compiler", "chocolate", - &errmsg), - errmsg); - data = read_text_file("ccache.conf", 0); - CHECK(data); - CHECK_STR_EQ_FREE2("path = vanilla\ncompiler = chocolate\n", data); + char* errmsg; + char* data; + + create_file("ccache.conf", "path = vanilla\n"); + CHECKM( + conf_set_value_in_file("ccache.conf", "compiler", "chocolate", &errmsg), + errmsg); + data = read_text_file("ccache.conf", 0); + CHECK(data); + CHECK_STR_EQ_FREE2("path = vanilla\ncompiler = chocolate\n", data); } TEST(conf_set_existing_value) { - char *errmsg; - char *data; - - create_file("ccache.conf", "path = chocolate\nstats = chocolate\n"); - CHECKM(conf_set_value_in_file("ccache.conf", "path", "vanilla", &errmsg), - errmsg); - data = read_text_file("ccache.conf", 0); - CHECK(data); - CHECK_STR_EQ_FREE2("path = vanilla\nstats = chocolate\n", data); + char* errmsg; + char* data; + + create_file("ccache.conf", "path = chocolate\nstats = chocolate\n"); + CHECKM(conf_set_value_in_file("ccache.conf", "path", "vanilla", &errmsg), + errmsg); + data = read_text_file("ccache.conf", 0); + CHECK(data); + CHECK_STR_EQ_FREE2("path = vanilla\nstats = chocolate\n", data); } TEST(conf_set_unknown_option) { - char *errmsg; - char *data; + char* errmsg; + char* data; - create_file("ccache.conf", "path = chocolate\nstats = chocolate\n"); - CHECKM(!conf_set_value_in_file("ccache.conf", "foo", "bar", &errmsg), - errmsg); - CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"", errmsg); + create_file("ccache.conf", "path = chocolate\nstats = chocolate\n"); + CHECKM(!conf_set_value_in_file("ccache.conf", "foo", "bar", &errmsg), errmsg); + CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"", errmsg); - data = read_text_file("ccache.conf", 0); - CHECK(data); - CHECK_STR_EQ_FREE2("path = chocolate\nstats = chocolate\n", data); + data = read_text_file("ccache.conf", 0); + CHECK(data); + CHECK_STR_EQ_FREE2("path = chocolate\nstats = chocolate\n", data); } TEST(conf_print_existing_value) { - struct conf *conf = conf_create(); - conf->max_files = 42; - char *errmsg; - { - FILE *log = fopen("log", "w"); - CHECK(log); - CHECK(conf_print_value(conf, "max_files", log, &errmsg)); - fclose(log); - } - { - FILE *log = fopen("log", "r"); - CHECK(log); - char buf[100]; - CHECK(fgets(buf, sizeof(buf), log)); - CHECK_STR_EQ("42\n", buf); - fclose(log); - } - conf_free(conf); + struct conf* conf = conf_create(); + conf->max_files = 42; + char* errmsg; + { + FILE* log = fopen("log", "w"); + CHECK(log); + CHECK(conf_print_value(conf, "max_files", log, &errmsg)); + fclose(log); + } + { + FILE* log = fopen("log", "r"); + CHECK(log); + char buf[100]; + CHECK(fgets(buf, sizeof(buf), log)); + CHECK_STR_EQ("42\n", buf); + fclose(log); + } + conf_free(conf); } TEST(conf_print_unknown_value) { - struct conf *conf = conf_create(); - char *errmsg; - { - FILE *log = fopen("log", "w"); - CHECK(log); - CHECK(!conf_print_value(conf, "foo", log, &errmsg)); - CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"", - errmsg); - fclose(log); - } - { - FILE *log = fopen("log", "r"); - CHECK(log); - char buf[100]; - CHECK(!fgets(buf, sizeof(buf), log)); - fclose(log); - } - conf_free(conf); + struct conf* conf = conf_create(); + char* errmsg; + { + FILE* log = fopen("log", "w"); + CHECK(log); + CHECK(!conf_print_value(conf, "foo", log, &errmsg)); + CHECK_STR_EQ_FREE2("unknown configuration option \"foo\"", errmsg); + fclose(log); + } + { + FILE* log = fopen("log", "r"); + CHECK(log); + char buf[100]; + CHECK(!fgets(buf, sizeof(buf), log)); + fclose(log); + } + conf_free(conf); } TEST(conf_print_items) { - struct conf* conf = conf_create(); - conf->base_dir = x_strdup("bd"); - conf->cache_dir = x_strdup("cd"); - conf->cache_dir_levels = 7; - conf->compiler = x_strdup("c"); - conf->compiler_check = x_strdup("cc"); - conf->compression = true; - conf->compression_level = 8; - conf->cpp_extension = x_strdup("ce"); - conf->debug = false; - conf->depend_mode = true; - conf->direct_mode = false; - conf->disable = true; - conf->extra_files_to_hash = x_strdup("efth"); - conf->file_clone = true; - conf->hard_link = true; - conf->hash_dir = false; - conf->ignore_headers_in_manifest = x_strdup("ihim"); - conf->keep_comments_cpp = true; - conf->limit_multiple = 0.0; - conf->log_file = x_strdup("lf"); - conf->max_files = 4711; - conf->max_size = 98.7 * 1000 * 1000; - conf->path = x_strdup("p"); - conf->pch_external_checksum = true; - conf->prefix_command = x_strdup("pc"); - conf->prefix_command_cpp = x_strdup("pcc"); - conf->read_only = true; - conf->read_only_direct = true; - conf->recache = true; - conf->run_second_cpp = false; - conf->sloppiness = - SLOPPY_FILE_MACRO | SLOPPY_INCLUDE_FILE_MTIME | SLOPPY_INCLUDE_FILE_CTIME - | SLOPPY_TIME_MACROS | SLOPPY_FILE_STAT_MATCHES - | SLOPPY_FILE_STAT_MATCHES_CTIME | SLOPPY_PCH_DEFINES - | SLOPPY_SYSTEM_HEADERS | SLOPPY_CLANG_INDEX_STORE; - conf->stats = false; - conf->temporary_dir = x_strdup("td"); - conf->umask = 022; - conf->unify = true; - - conf->item_origins = - static_cast<const char**>(x_malloc(N_CONFIG_ITEMS * sizeof(char *))); - for (size_t i = 0; i < N_CONFIG_ITEMS; ++i) { + struct conf* conf = conf_create(); + conf->base_dir = x_strdup("bd"); + conf->cache_dir = x_strdup("cd"); + conf->cache_dir_levels = 7; + conf->compiler = x_strdup("c"); + conf->compiler_check = x_strdup("cc"); + conf->compression = true; + conf->compression_level = 8; + conf->cpp_extension = x_strdup("ce"); + conf->debug = false; + conf->depend_mode = true; + conf->direct_mode = false; + conf->disable = true; + conf->extra_files_to_hash = x_strdup("efth"); + conf->file_clone = true; + conf->hard_link = true; + conf->hash_dir = false; + conf->ignore_headers_in_manifest = x_strdup("ihim"); + conf->keep_comments_cpp = true; + conf->limit_multiple = 0.0; + conf->log_file = x_strdup("lf"); + conf->max_files = 4711; + conf->max_size = 98.7 * 1000 * 1000; + conf->path = x_strdup("p"); + conf->pch_external_checksum = true; + conf->prefix_command = x_strdup("pc"); + conf->prefix_command_cpp = x_strdup("pcc"); + conf->read_only = true; + conf->read_only_direct = true; + conf->recache = true; + conf->run_second_cpp = false; + conf->sloppiness = SLOPPY_FILE_MACRO | SLOPPY_INCLUDE_FILE_MTIME + | SLOPPY_INCLUDE_FILE_CTIME | SLOPPY_TIME_MACROS + | SLOPPY_FILE_STAT_MATCHES | SLOPPY_FILE_STAT_MATCHES_CTIME + | SLOPPY_PCH_DEFINES | SLOPPY_SYSTEM_HEADERS + | SLOPPY_CLANG_INDEX_STORE; + conf->stats = false; + conf->temporary_dir = x_strdup("td"); + conf->umask = 022; + conf->unify = true; + + conf->item_origins = + static_cast<const char**>(x_malloc(N_CONFIG_ITEMS * sizeof(char*))); + for (size_t i = 0; i < N_CONFIG_ITEMS; ++i) { #ifndef __MINGW32__ - conf->item_origins[i] = format("origin%zu", i); + conf->item_origins[i] = format("origin%zu", i); #else - conf->item_origins[i] = format("origin%u", (unsigned) i); + conf->item_origins[i] = format("origin%u", (unsigned)i); #endif - } - - size_t n = 0; - 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("debug = false", received_conf_items[n++].descr); - CHECK_STR_EQ("depend_mode = true", 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("file_clone = true", received_conf_items[n++].descr); - CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr); - CHECK_STR_EQ("hash_dir = false", received_conf_items[n++].descr); - CHECK_STR_EQ("ignore_headers_in_manifest = ihim", - received_conf_items[n++].descr); - CHECK_STR_EQ("keep_comments_cpp = true", received_conf_items[n++].descr); - CHECK_STR_EQ("limit_multiple = 0.0", 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("pch_external_checksum = true", received_conf_items[n++].descr); - CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++].descr); - CHECK_STR_EQ("prefix_command_cpp = pcc", received_conf_items[n++].descr); - CHECK_STR_EQ("read_only = true", received_conf_items[n++].descr); - CHECK_STR_EQ("read_only_direct = true", received_conf_items[n++].descr); - CHECK_STR_EQ("recache = true", received_conf_items[n++].descr); - CHECK_STR_EQ("run_second_cpp = false", received_conf_items[n++].descr); - CHECK_STR_EQ("sloppiness = file_macro, include_file_mtime," - " include_file_ctime, time_macros, pch_defines," - " file_stat_matches, file_stat_matches_ctime, system_headers," - " clang_index_store", - 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 (size_t i = 0; i < N_CONFIG_ITEMS; ++i) { + } + + size_t n = 0; + 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("debug = false", received_conf_items[n++].descr); + CHECK_STR_EQ("depend_mode = true", 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("file_clone = true", received_conf_items[n++].descr); + CHECK_STR_EQ("hard_link = true", received_conf_items[n++].descr); + CHECK_STR_EQ("hash_dir = false", received_conf_items[n++].descr); + CHECK_STR_EQ("ignore_headers_in_manifest = ihim", + received_conf_items[n++].descr); + CHECK_STR_EQ("keep_comments_cpp = true", received_conf_items[n++].descr); + CHECK_STR_EQ("limit_multiple = 0.0", 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("pch_external_checksum = true", received_conf_items[n++].descr); + CHECK_STR_EQ("prefix_command = pc", received_conf_items[n++].descr); + CHECK_STR_EQ("prefix_command_cpp = pcc", received_conf_items[n++].descr); + CHECK_STR_EQ("read_only = true", received_conf_items[n++].descr); + CHECK_STR_EQ("read_only_direct = true", received_conf_items[n++].descr); + CHECK_STR_EQ("recache = true", received_conf_items[n++].descr); + CHECK_STR_EQ("run_second_cpp = false", received_conf_items[n++].descr); + CHECK_STR_EQ( + "sloppiness = file_macro, include_file_mtime," + " include_file_ctime, time_macros, pch_defines," + " file_stat_matches, file_stat_matches_ctime, system_headers," + " clang_index_store", + 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 (size_t i = 0; i < N_CONFIG_ITEMS; ++i) { #ifndef __MINGW32__ - char *expected = format("origin%zu", i); + char* expected = format("origin%zu", i); #else - char *expected = format("origin%u", (unsigned) i); + char* expected = format("origin%u", (unsigned)i); #endif - CHECK_STR_EQ_FREE1(expected, received_conf_items[i].origin); - } + CHECK_STR_EQ_FREE1(expected, received_conf_items[i].origin); + } - free_received_conf_items(); - conf_free(conf); + free_received_conf_items(); + conf_free(conf); } TEST_SUITE_END diff --git a/unittest/test_counters.cpp b/unittest/test_counters.cpp index 7f07006d..e8416890 100644 --- a/unittest/test_counters.cpp +++ b/unittest/test_counters.cpp @@ -25,38 +25,38 @@ TEST_SUITE(counters) TEST(counters_init_0_should_allocate_0) { - struct counters *counters = counters_init(0); + struct counters* counters = counters_init(0); - CHECK_INT_EQ(0, counters->allocated); - CHECK_INT_EQ(0, counters->size); + CHECK_INT_EQ(0, counters->allocated); + CHECK_INT_EQ(0, counters->size); - counters_free(counters); + counters_free(counters); } TEST(counters_init_7_should_allocate_32) { - int i; - struct counters *counters = counters_init(7); + int i; + struct counters* counters = counters_init(7); - CHECK_INT_EQ(32, counters->allocated); - CHECK_INT_EQ(7, counters->size); - for (i = 0; i < 7; i++) { - CHECK_INT_EQ(0, counters->data[i]); - } + CHECK_INT_EQ(32, counters->allocated); + CHECK_INT_EQ(7, counters->size); + for (i = 0; i < 7; i++) { + CHECK_INT_EQ(0, counters->data[i]); + } - counters_free(counters); + counters_free(counters); } TEST(counters_resize_50_should_allocate_96) { - struct counters *counters = counters_init(0); + struct counters* counters = counters_init(0); - CHECK_INT_EQ(0, counters->allocated); - counters_resize(counters, 50); - CHECK_INT_EQ(50, counters->size); - CHECK_INT_EQ(96, counters->allocated); + CHECK_INT_EQ(0, counters->allocated); + counters_resize(counters, 50); + CHECK_INT_EQ(50, counters->size); + CHECK_INT_EQ(96, counters->allocated); - counters_free(counters); + counters_free(counters); } TEST_SUITE_END diff --git a/unittest/test_hash.cpp b/unittest/test_hash.cpp index df3bc8b9..6d8795c4 100644 --- a/unittest/test_hash.cpp +++ b/unittest/test_hash.cpp @@ -26,81 +26,81 @@ TEST_SUITE(hash) TEST(test_known_strings) { - char d[DIGEST_STRING_BUFFER_SIZE]; + char d[DIGEST_STRING_BUFFER_SIZE]; - { - struct hash *h = hash_init(); - hash_string(h, ""); - hash_result_as_string(h, d); - CHECK_STR_EQ("3345524abf6bbe1809449224b5972c41790b6cf2", d); - hash_free(h); - } + { + struct hash* h = hash_init(); + hash_string(h, ""); + hash_result_as_string(h, d); + CHECK_STR_EQ("3345524abf6bbe1809449224b5972c41790b6cf2", d); + hash_free(h); + } - { - struct hash *h = hash_init(); - hash_string(h, "a"); - hash_result_as_string(h, d); - CHECK_STR_EQ("948caa2db61bc4cdb4faf7740cd491f195043914", d); - hash_free(h); - } + { + struct hash* h = hash_init(); + hash_string(h, "a"); + hash_result_as_string(h, d); + CHECK_STR_EQ("948caa2db61bc4cdb4faf7740cd491f195043914", d); + hash_free(h); + } - { - struct hash *h = hash_init(); - hash_string(h, "message digest"); - hash_result_as_string(h, d); - CHECK_STR_EQ("6bfec6f65e52962be863d6ea1005fc5e4cc8478c", d); - hash_free(h); - } + { + struct hash* h = hash_init(); + hash_string(h, "message digest"); + hash_result_as_string(h, d); + CHECK_STR_EQ("6bfec6f65e52962be863d6ea1005fc5e4cc8478c", d); + hash_free(h); + } - { - struct hash *h = hash_init(); - hash_string( - h, - "12345678901234567890123456789012345678901234567890123456789012345678901" - "234567890"); - hash_result_as_string(h, d); - CHECK_STR_EQ("c2be0e534a67d25947f0c7e78527b2f82abd260f", d); - hash_free(h); - } + { + struct hash* h = hash_init(); + hash_string( + h, + "1234567890123456789012345678901234567890123456789012345678901234567890" + "1" + "234567890"); + hash_result_as_string(h, d); + CHECK_STR_EQ("c2be0e534a67d25947f0c7e78527b2f82abd260f", d); + hash_free(h); + } } TEST(hash_result_should_not_alter_state) { - char d[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h = hash_init(); - hash_string(h, "message"); - hash_result_as_string(h, d); - hash_string(h, " digest"); - hash_result_as_string(h, d); - CHECK_STR_EQ("6bfec6f65e52962be863d6ea1005fc5e4cc8478c", d); - hash_free(h); + char d[DIGEST_STRING_BUFFER_SIZE]; + struct hash* h = hash_init(); + hash_string(h, "message"); + hash_result_as_string(h, d); + hash_string(h, " digest"); + hash_result_as_string(h, d); + CHECK_STR_EQ("6bfec6f65e52962be863d6ea1005fc5e4cc8478c", d); + hash_free(h); } TEST(hash_result_should_be_idempotent) { - char d[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h = hash_init(); - hash_string(h, ""); - hash_result_as_string(h, d); - CHECK_STR_EQ("3345524abf6bbe1809449224b5972c41790b6cf2", d); - hash_result_as_string(h, d); - CHECK_STR_EQ("3345524abf6bbe1809449224b5972c41790b6cf2", d); + char d[DIGEST_STRING_BUFFER_SIZE]; + struct hash* h = hash_init(); + hash_string(h, ""); + hash_result_as_string(h, d); + CHECK_STR_EQ("3345524abf6bbe1809449224b5972c41790b6cf2", d); + hash_result_as_string(h, d); + CHECK_STR_EQ("3345524abf6bbe1809449224b5972c41790b6cf2", d); - hash_free(h); + hash_free(h); } TEST(hash_result_as_bytes) { - struct hash *h = hash_init(); - hash_string(h, "message digest"); - struct digest d; - hash_result_as_bytes(h, &d); - uint8_t expected[sizeof(d.bytes)] = { - 0x6b, 0xfe, 0xc6, 0xf6, 0x5e, 0x52, 0x96, 0x2b, 0xe8, 0x63, 0xd6, 0xea, - 0x10, 0x05, 0xfc, 0x5e, 0x4c, 0xc8, 0x47, 0x8c - }; - CHECK_DATA_EQ(d.bytes, expected, sizeof(d.bytes)); - hash_free(h); + struct hash* h = hash_init(); + hash_string(h, "message digest"); + struct digest d; + hash_result_as_bytes(h, &d); + uint8_t expected[sizeof(d.bytes)] = {0x6b, 0xfe, 0xc6, 0xf6, 0x5e, 0x52, 0x96, + 0x2b, 0xe8, 0x63, 0xd6, 0xea, 0x10, 0x05, + 0xfc, 0x5e, 0x4c, 0xc8, 0x47, 0x8c}; + CHECK_DATA_EQ(d.bytes, expected, sizeof(d.bytes)); + hash_free(h); } TEST_SUITE_END diff --git a/unittest/test_hashutil.cpp b/unittest/test_hashutil.cpp index a7432281..c42911b7 100644 --- a/unittest/test_hashutil.cpp +++ b/unittest/test_hashutil.cpp @@ -27,222 +27,220 @@ TEST_SUITE(hashutil) TEST(hash_command_output_simple) { - char d1[DIGEST_STRING_BUFFER_SIZE]; - char d2[DIGEST_STRING_BUFFER_SIZE]; + char d1[DIGEST_STRING_BUFFER_SIZE]; + char d2[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h1 = hash_init(); - struct hash *h2 = hash_init(); + struct hash* h1 = hash_init(); + struct hash* h2 = hash_init(); - CHECK(hash_command_output(h1, "echo", "not used")); - CHECK(hash_command_output(h2, "echo", "not used")); - hash_result_as_string(h1, d1); - hash_result_as_string(h2, d2); - CHECK_STR_EQ(d1, d2); + CHECK(hash_command_output(h1, "echo", "not used")); + CHECK(hash_command_output(h2, "echo", "not used")); + hash_result_as_string(h1, d1); + hash_result_as_string(h2, d2); + CHECK_STR_EQ(d1, d2); - hash_free(h2); - hash_free(h1); + hash_free(h2); + hash_free(h1); } TEST(hash_command_output_space_removal) { - char d1[DIGEST_STRING_BUFFER_SIZE]; - char d2[DIGEST_STRING_BUFFER_SIZE]; + char d1[DIGEST_STRING_BUFFER_SIZE]; + char d2[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h1 = hash_init(); - struct hash *h2 = hash_init(); + struct hash* h1 = hash_init(); + struct hash* h2 = hash_init(); - CHECK(hash_command_output(h1, "echo", "not used")); - CHECK(hash_command_output(h2, " echo ", "not used")); - hash_result_as_string(h1, d1); - hash_result_as_string(h2, d2); - CHECK_STR_EQ(d1, d2); + CHECK(hash_command_output(h1, "echo", "not used")); + CHECK(hash_command_output(h2, " echo ", "not used")); + hash_result_as_string(h1, d1); + hash_result_as_string(h2, d2); + CHECK_STR_EQ(d1, d2); - hash_free(h2); - hash_free(h1); + hash_free(h2); + hash_free(h1); } TEST(hash_command_output_hash_inequality) { - char d1[DIGEST_STRING_BUFFER_SIZE]; - char d2[DIGEST_STRING_BUFFER_SIZE]; + char d1[DIGEST_STRING_BUFFER_SIZE]; + char d2[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h1 = hash_init(); - struct hash *h2 = hash_init(); + struct hash* h1 = hash_init(); + struct hash* h2 = hash_init(); - CHECK(hash_command_output(h1, "echo foo", "not used")); - CHECK(hash_command_output(h2, "echo bar", "not used")); - hash_result_as_string(h1, d1); - hash_result_as_string(h2, d2); - CHECK(!str_eq(d1, d2)); + CHECK(hash_command_output(h1, "echo foo", "not used")); + CHECK(hash_command_output(h2, "echo bar", "not used")); + hash_result_as_string(h1, d1); + hash_result_as_string(h2, d2); + CHECK(!str_eq(d1, d2)); - hash_free(h2); - hash_free(h1); + hash_free(h2); + hash_free(h1); } TEST(hash_command_output_compiler_substitution) { - char d1[DIGEST_STRING_BUFFER_SIZE]; - char d2[DIGEST_STRING_BUFFER_SIZE]; + char d1[DIGEST_STRING_BUFFER_SIZE]; + char d2[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h1 = hash_init(); - struct hash *h2 = hash_init(); + struct hash* h1 = hash_init(); + struct hash* h2 = hash_init(); - CHECK(hash_command_output(h1, "echo foo", "not used")); - CHECK(hash_command_output(h2, "%compiler% foo", "echo")); - hash_result_as_string(h1, d1); - hash_result_as_string(h2, d2); - CHECK_STR_EQ(d1, d2); + CHECK(hash_command_output(h1, "echo foo", "not used")); + CHECK(hash_command_output(h2, "%compiler% foo", "echo")); + hash_result_as_string(h1, d1); + hash_result_as_string(h2, d2); + CHECK_STR_EQ(d1, d2); - hash_free(h2); - hash_free(h1); + hash_free(h2); + hash_free(h1); } TEST(hash_command_output_stdout_versus_stderr) { - char d1[DIGEST_STRING_BUFFER_SIZE]; - char d2[DIGEST_STRING_BUFFER_SIZE]; + char d1[DIGEST_STRING_BUFFER_SIZE]; + char d2[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h1 = hash_init(); - struct hash *h2 = hash_init(); + struct hash* h1 = hash_init(); + struct hash* h2 = hash_init(); #ifndef _WIN32 - create_file("stderr.sh", "#!/bin/sh\necho foo >&2\n"); - chmod("stderr.sh", 0555); - CHECK(hash_command_output(h1, "echo foo", "not used")); - CHECK(hash_command_output(h2, "./stderr.sh", "not used")); + create_file("stderr.sh", "#!/bin/sh\necho foo >&2\n"); + chmod("stderr.sh", 0555); + CHECK(hash_command_output(h1, "echo foo", "not used")); + CHECK(hash_command_output(h2, "./stderr.sh", "not used")); #else - create_file("stderr.bat", "@echo off\r\necho foo>&2\r\n"); - CHECK(hash_command_output(h1, "echo foo", "not used")); - CHECK(hash_command_output(h2, "stderr.bat", "not used")); + create_file("stderr.bat", "@echo off\r\necho foo>&2\r\n"); + CHECK(hash_command_output(h1, "echo foo", "not used")); + CHECK(hash_command_output(h2, "stderr.bat", "not used")); #endif - hash_result_as_string(h1, d1); - hash_result_as_string(h2, d2); - CHECK_STR_EQ(d1, d2); + hash_result_as_string(h1, d1); + hash_result_as_string(h2, d2); + CHECK_STR_EQ(d1, d2); - hash_free(h2); - hash_free(h1); + hash_free(h2); + hash_free(h1); } TEST(hash_multicommand_output) { - char d1[DIGEST_STRING_BUFFER_SIZE]; - char d2[DIGEST_STRING_BUFFER_SIZE]; + char d1[DIGEST_STRING_BUFFER_SIZE]; + char d2[DIGEST_STRING_BUFFER_SIZE]; - struct hash *h1 = hash_init(); - struct hash *h2 = hash_init(); + struct hash* h1 = hash_init(); + struct hash* h2 = hash_init(); #ifndef _WIN32 - create_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n"); - chmod("foo.sh", 0555); - CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used")); - CHECK(hash_multicommand_output(h1, "./foo.sh", "not used")); + create_file("foo.sh", "#!/bin/sh\necho foo\necho bar\n"); + chmod("foo.sh", 0555); + CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used")); + CHECK(hash_multicommand_output(h1, "./foo.sh", "not used")); #else - create_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n"); - CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used")); - CHECK(hash_multicommand_output(h1, "foo.bat", "not used")); + create_file("foo.bat", "@echo off\r\necho foo\r\necho bar\r\n"); + CHECK(hash_multicommand_output(h2, "echo foo; echo bar", "not used")); + CHECK(hash_multicommand_output(h1, "foo.bat", "not used")); #endif - hash_result_as_string(h1, d1); - hash_result_as_string(h2, d2); - CHECK_STR_EQ(d1, d2); + hash_result_as_string(h1, d1); + hash_result_as_string(h2, d2); + CHECK_STR_EQ(d1, d2); - hash_free(h2); - hash_free(h1); + hash_free(h2); + hash_free(h1); } TEST(hash_multicommand_output_error_handling) { - struct hash *h1 = hash_init(); - struct hash *h2 = hash_init(); + struct hash* h1 = hash_init(); + struct hash* h2 = hash_init(); - CHECK(!hash_multicommand_output(h2, "false; true", "not used")); + CHECK(!hash_multicommand_output(h2, "false; true", "not used")); - hash_free(h2); - hash_free(h1); + hash_free(h2); + hash_free(h1); } TEST(check_for_temporal_macros) { - const char time_start[] = - "__TIME__\n" - "int a;\n"; - const char time_middle[] = - "#define a __TIME__\n" - "int a;\n"; - const char time_end[] = - "#define a __TIME__"; - - const char date_start[] = - "__DATE__\n" - "int ab;\n"; - const char date_middle[] = - "#define ab __DATE__\n" - "int ab;\n"; - const char date_end[] = - "#define ab __DATE__"; - - const char no_temporal[] = - "#define ab a__DATE__\n" - "#define ab __DATE__a\n" - "#define ab A__DATE__\n" - "#define ab __DATE__A\n" - "#define ab 0__DATE__\n" - "#define ab __DATE__0\n" - "#define ab _ _DATE__\n" - "#define ab _ _DATE__\n" - "#define ab __ DATE__\n" - "#define ab __D ATE__\n" - "#define ab __DA TE__\n" - "#define ab __DAT E__\n" - "#define ab __DATE __\n" - "#define ab __DATE_ _\n" - "#define ab _ _TIME__\n" - "#define ab __ TIME__\n" - "#define ab __T IME__\n" - "#define ab __TI ME__\n" - "#define ab __TIM E__\n" - "#define ab __TIME __\n" - "#define ab __TIME_ _\n"; - - CHECK(check_for_temporal_macros(time_start + 0, sizeof(time_start) - 0)); - CHECK(!check_for_temporal_macros(time_start + 1, sizeof(time_start) - 1)); - - CHECK(check_for_temporal_macros(time_middle + 0, sizeof(time_middle) - 0)); - CHECK(check_for_temporal_macros(time_middle + 1, sizeof(time_middle) - 1)); - CHECK(check_for_temporal_macros(time_middle + 2, sizeof(time_middle) - 2)); - CHECK(check_for_temporal_macros(time_middle + 3, sizeof(time_middle) - 3)); - CHECK(check_for_temporal_macros(time_middle + 4, sizeof(time_middle) - 4)); - CHECK(check_for_temporal_macros(time_middle + 5, sizeof(time_middle) - 5)); - CHECK(check_for_temporal_macros(time_middle + 6, sizeof(time_middle) - 6)); - CHECK(check_for_temporal_macros(time_middle + 7, sizeof(time_middle) - 7)); - - CHECK(check_for_temporal_macros(time_end + 0, sizeof(time_end) - 0)); - CHECK(check_for_temporal_macros(time_end + sizeof(time_end) - 9, 9)); - CHECK(!check_for_temporal_macros(time_end + sizeof(time_end) - 8, 8)); - - CHECK(check_for_temporal_macros(date_start + 0, sizeof(date_start) - 0)); - CHECK(!check_for_temporal_macros(date_start + 1, sizeof(date_start) - 1)); - - CHECK(check_for_temporal_macros(date_middle + 0, sizeof(date_middle) - 0)); - CHECK(check_for_temporal_macros(date_middle + 1, sizeof(date_middle) - 1)); - CHECK(check_for_temporal_macros(date_middle + 2, sizeof(date_middle) - 2)); - CHECK(check_for_temporal_macros(date_middle + 3, sizeof(date_middle) - 3)); - CHECK(check_for_temporal_macros(date_middle + 4, sizeof(date_middle) - 4)); - CHECK(check_for_temporal_macros(date_middle + 5, sizeof(date_middle) - 5)); - CHECK(check_for_temporal_macros(date_middle + 6, sizeof(date_middle) - 6)); - CHECK(check_for_temporal_macros(date_middle + 7, sizeof(date_middle) - 7)); - - CHECK(check_for_temporal_macros(date_end + 0, sizeof(date_end) - 0)); - CHECK(check_for_temporal_macros(date_end + sizeof(date_end) - 9, 9)); - CHECK(!check_for_temporal_macros(date_end + sizeof(date_end) - 8, 8)); - - CHECK(!check_for_temporal_macros(no_temporal + 0, sizeof(no_temporal) - 0)); - CHECK(!check_for_temporal_macros(no_temporal + 1, sizeof(no_temporal) - 1)); - CHECK(!check_for_temporal_macros(no_temporal + 2, sizeof(no_temporal) - 2)); - CHECK(!check_for_temporal_macros(no_temporal + 3, sizeof(no_temporal) - 3)); - CHECK(!check_for_temporal_macros(no_temporal + 4, sizeof(no_temporal) - 4)); - CHECK(!check_for_temporal_macros(no_temporal + 5, sizeof(no_temporal) - 5)); - CHECK(!check_for_temporal_macros(no_temporal + 6, sizeof(no_temporal) - 6)); - CHECK(!check_for_temporal_macros(no_temporal + 7, sizeof(no_temporal) - 7)); + const char time_start[] = + "__TIME__\n" + "int a;\n"; + const char time_middle[] = + "#define a __TIME__\n" + "int a;\n"; + const char time_end[] = "#define a __TIME__"; + + const char date_start[] = + "__DATE__\n" + "int ab;\n"; + const char date_middle[] = + "#define ab __DATE__\n" + "int ab;\n"; + const char date_end[] = "#define ab __DATE__"; + + const char no_temporal[] = + "#define ab a__DATE__\n" + "#define ab __DATE__a\n" + "#define ab A__DATE__\n" + "#define ab __DATE__A\n" + "#define ab 0__DATE__\n" + "#define ab __DATE__0\n" + "#define ab _ _DATE__\n" + "#define ab _ _DATE__\n" + "#define ab __ DATE__\n" + "#define ab __D ATE__\n" + "#define ab __DA TE__\n" + "#define ab __DAT E__\n" + "#define ab __DATE __\n" + "#define ab __DATE_ _\n" + "#define ab _ _TIME__\n" + "#define ab __ TIME__\n" + "#define ab __T IME__\n" + "#define ab __TI ME__\n" + "#define ab __TIM E__\n" + "#define ab __TIME __\n" + "#define ab __TIME_ _\n"; + + CHECK(check_for_temporal_macros(time_start + 0, sizeof(time_start) - 0)); + CHECK(!check_for_temporal_macros(time_start + 1, sizeof(time_start) - 1)); + + CHECK(check_for_temporal_macros(time_middle + 0, sizeof(time_middle) - 0)); + CHECK(check_for_temporal_macros(time_middle + 1, sizeof(time_middle) - 1)); + CHECK(check_for_temporal_macros(time_middle + 2, sizeof(time_middle) - 2)); + CHECK(check_for_temporal_macros(time_middle + 3, sizeof(time_middle) - 3)); + CHECK(check_for_temporal_macros(time_middle + 4, sizeof(time_middle) - 4)); + CHECK(check_for_temporal_macros(time_middle + 5, sizeof(time_middle) - 5)); + CHECK(check_for_temporal_macros(time_middle + 6, sizeof(time_middle) - 6)); + CHECK(check_for_temporal_macros(time_middle + 7, sizeof(time_middle) - 7)); + + CHECK(check_for_temporal_macros(time_end + 0, sizeof(time_end) - 0)); + CHECK(check_for_temporal_macros(time_end + sizeof(time_end) - 9, 9)); + CHECK(!check_for_temporal_macros(time_end + sizeof(time_end) - 8, 8)); + + CHECK(check_for_temporal_macros(date_start + 0, sizeof(date_start) - 0)); + CHECK(!check_for_temporal_macros(date_start + 1, sizeof(date_start) - 1)); + + CHECK(check_for_temporal_macros(date_middle + 0, sizeof(date_middle) - 0)); + CHECK(check_for_temporal_macros(date_middle + 1, sizeof(date_middle) - 1)); + CHECK(check_for_temporal_macros(date_middle + 2, sizeof(date_middle) - 2)); + CHECK(check_for_temporal_macros(date_middle + 3, sizeof(date_middle) - 3)); + CHECK(check_for_temporal_macros(date_middle + 4, sizeof(date_middle) - 4)); + CHECK(check_for_temporal_macros(date_middle + 5, sizeof(date_middle) - 5)); + CHECK(check_for_temporal_macros(date_middle + 6, sizeof(date_middle) - 6)); + CHECK(check_for_temporal_macros(date_middle + 7, sizeof(date_middle) - 7)); + + CHECK(check_for_temporal_macros(date_end + 0, sizeof(date_end) - 0)); + CHECK(check_for_temporal_macros(date_end + sizeof(date_end) - 9, 9)); + CHECK(!check_for_temporal_macros(date_end + sizeof(date_end) - 8, 8)); + + CHECK(!check_for_temporal_macros(no_temporal + 0, sizeof(no_temporal) - 0)); + CHECK(!check_for_temporal_macros(no_temporal + 1, sizeof(no_temporal) - 1)); + CHECK(!check_for_temporal_macros(no_temporal + 2, sizeof(no_temporal) - 2)); + CHECK(!check_for_temporal_macros(no_temporal + 3, sizeof(no_temporal) - 3)); + CHECK(!check_for_temporal_macros(no_temporal + 4, sizeof(no_temporal) - 4)); + CHECK(!check_for_temporal_macros(no_temporal + 5, sizeof(no_temporal) - 5)); + CHECK(!check_for_temporal_macros(no_temporal + 6, sizeof(no_temporal) - 6)); + CHECK(!check_for_temporal_macros(no_temporal + 7, sizeof(no_temporal) - 7)); } TEST_SUITE_END diff --git a/unittest/test_lockfile.cpp b/unittest/test_lockfile.cpp index f75adf36..334b3388 100644 --- a/unittest/test_lockfile.cpp +++ b/unittest/test_lockfile.cpp @@ -26,53 +26,53 @@ TEST_SUITE(lockfile) TEST(acquire_should_create_symlink) { - lockfile_acquire("test", 1000); + lockfile_acquire("test", 1000); #if defined(_WIN32) || defined(__CYGWIN__) - CHECK(path_exists("test.lock")); + CHECK(path_exists("test.lock")); #else - CHECK(is_symlink("test.lock")); + CHECK(is_symlink("test.lock")); #endif } TEST(release_should_delete_file) { - create_file("test.lock", ""); - lockfile_release("test"); + create_file("test.lock", ""); + lockfile_release("test"); - CHECK(!path_exists("test.lock")); + CHECK(!path_exists("test.lock")); } TEST(lock_breaking) { - char *p; + char* p; #if defined(_WIN32) || defined(__CYGWIN__) - create_file("test.lock", "foo"); - create_file("test.lock.lock", "foo"); + create_file("test.lock", "foo"); + create_file("test.lock.lock", "foo"); #else - CHECK_INT_EQ(0, symlink("foo", "test.lock")); - CHECK_INT_EQ(0, symlink("foo", "test.lock.lock")); + CHECK_INT_EQ(0, symlink("foo", "test.lock")); + CHECK_INT_EQ(0, symlink("foo", "test.lock.lock")); #endif - CHECK(lockfile_acquire("test", 1000)); + CHECK(lockfile_acquire("test", 1000)); #if defined(_WIN32) || defined(__CYGWIN__) - p = read_text_file("test.lock", 0); + p = read_text_file("test.lock", 0); #else - p = x_readlink("test.lock"); + p = x_readlink("test.lock"); #endif - CHECK(p); - CHECK(!str_eq(p, "foo")); - CHECK(!path_exists("test.lock.lock")); + CHECK(p); + CHECK(!str_eq(p, "foo")); + CHECK(!path_exists("test.lock.lock")); - free(p); + free(p); } #if !defined(_WIN32) && !defined(__CYGWIN__) TEST(failed_lock_breaking) { - create_file("test.lock", ""); - CHECK(!lockfile_acquire("test", 1000)); + create_file("test.lock", ""); + CHECK(!lockfile_acquire("test", 1000)); } #endif diff --git a/unittest/test_stats.cpp b/unittest/test_stats.cpp index 02a28414..aa3aaa26 100644 --- a/unittest/test_stats.cpp +++ b/unittest/test_stats.cpp @@ -27,25 +27,25 @@ TEST_SUITE(stats) TEST(forward_compatibility) { - unsigned i; - FILE *f; - struct counters *counters = counters_init(0); + unsigned i; + FILE* f; + struct counters* counters = counters_init(0); - f = fopen("stats", "w"); - for (i = 0; i < 100; i++) { - fprintf(f, "%u\n", i); - } - fclose(f); + f = fopen("stats", "w"); + for (i = 0; i < 100; i++) { + fprintf(f, "%u\n", i); + } + fclose(f); - stats_read("stats", counters); - CHECK_INT_EQ(100, counters->size); - CHECK_INT_EQ(73, counters->data[73]); + stats_read("stats", counters); + CHECK_INT_EQ(100, counters->size); + CHECK_INT_EQ(73, counters->data[73]); - stats_write("stats", counters); - CHECK_INT_EQ(100, counters->size); - CHECK_INT_EQ(99, counters->data[99]); + stats_write("stats", counters); + CHECK_INT_EQ(100, counters->size); + CHECK_INT_EQ(99, counters->data[99]); - counters_free(counters); + counters_free(counters); } TEST_SUITE_END diff --git a/unittest/test_util.cpp b/unittest/test_util.cpp index c16c4663..88e6c8c6 100644 --- a/unittest/test_util.cpp +++ b/unittest/test_util.cpp @@ -27,182 +27,180 @@ TEST_SUITE(util) TEST(x_basename) { - CHECK_STR_EQ_FREE2("foo.c", x_basename("foo.c")); - CHECK_STR_EQ_FREE2("foo.c", x_basename("dir1/dir2/foo.c")); - CHECK_STR_EQ_FREE2("foo.c", x_basename("/dir/foo.c")); - CHECK_STR_EQ_FREE2("", x_basename("dir1/dir2/")); + CHECK_STR_EQ_FREE2("foo.c", x_basename("foo.c")); + CHECK_STR_EQ_FREE2("foo.c", x_basename("dir1/dir2/foo.c")); + CHECK_STR_EQ_FREE2("foo.c", x_basename("/dir/foo.c")); + CHECK_STR_EQ_FREE2("", x_basename("dir1/dir2/")); } TEST(x_dirname) { - CHECK_STR_EQ_FREE2(".", x_dirname("foo.c")); - CHECK_STR_EQ_FREE2(".", x_dirname("")); - CHECK_STR_EQ_FREE2("/", x_dirname("/")); - CHECK_STR_EQ_FREE2("/", x_dirname("/foo.c")); - CHECK_STR_EQ_FREE2("dir1/dir2", x_dirname("dir1/dir2/foo.c")); - CHECK_STR_EQ_FREE2("/dir", x_dirname("/dir/foo.c")); - CHECK_STR_EQ_FREE2("dir1/dir2", x_dirname("dir1/dir2/")); + CHECK_STR_EQ_FREE2(".", x_dirname("foo.c")); + CHECK_STR_EQ_FREE2(".", x_dirname("")); + CHECK_STR_EQ_FREE2("/", x_dirname("/")); + CHECK_STR_EQ_FREE2("/", x_dirname("/foo.c")); + CHECK_STR_EQ_FREE2("dir1/dir2", x_dirname("dir1/dir2/foo.c")); + CHECK_STR_EQ_FREE2("/dir", x_dirname("/dir/foo.c")); + CHECK_STR_EQ_FREE2("dir1/dir2", x_dirname("dir1/dir2/")); } TEST(common_dir_prefix_length) { - CHECK_INT_EQ(0, common_dir_prefix_length("", "")); - CHECK_INT_EQ(0, common_dir_prefix_length("/", "/")); - CHECK_INT_EQ(0, common_dir_prefix_length("/", "/b")); - CHECK_INT_EQ(0, common_dir_prefix_length("/a", "/b")); - CHECK_INT_EQ(2, common_dir_prefix_length("/a", "/a")); - CHECK_INT_EQ(2, common_dir_prefix_length("/a", "/a/b")); - CHECK_INT_EQ(2, common_dir_prefix_length("/a/b", "/a/c")); - CHECK_INT_EQ(4, common_dir_prefix_length("/a/b", "/a/b")); - CHECK_INT_EQ(2, common_dir_prefix_length("/a/bc", "/a/b")); - CHECK_INT_EQ(2, common_dir_prefix_length("/a/b", "/a/bc")); + CHECK_INT_EQ(0, common_dir_prefix_length("", "")); + CHECK_INT_EQ(0, common_dir_prefix_length("/", "/")); + CHECK_INT_EQ(0, common_dir_prefix_length("/", "/b")); + CHECK_INT_EQ(0, common_dir_prefix_length("/a", "/b")); + CHECK_INT_EQ(2, common_dir_prefix_length("/a", "/a")); + CHECK_INT_EQ(2, common_dir_prefix_length("/a", "/a/b")); + CHECK_INT_EQ(2, common_dir_prefix_length("/a/b", "/a/c")); + CHECK_INT_EQ(4, common_dir_prefix_length("/a/b", "/a/b")); + CHECK_INT_EQ(2, common_dir_prefix_length("/a/bc", "/a/b")); + CHECK_INT_EQ(2, common_dir_prefix_length("/a/b", "/a/bc")); } TEST(get_relative_path) { #ifdef _WIN32 - CHECK_STR_EQ_FREE2("a", get_relative_path("C:/doesn't matter", "a")); - CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/doesn't matter", "a/b")); - CHECK_STR_EQ_FREE2(".", get_relative_path("C:/a", "C:/a")); - CHECK_STR_EQ_FREE2("..", get_relative_path("C:/a/b", "C:/a")); - CHECK_STR_EQ_FREE2("b", get_relative_path("C:/a", "C:/a/b")); - CHECK_STR_EQ_FREE2("b/c", get_relative_path("C:/a", "C:/a/b/c")); - CHECK_STR_EQ_FREE2("../c", get_relative_path("C:/a/b", "C:/a/c")); - CHECK_STR_EQ_FREE2("../c/d", get_relative_path("C:/a/b", "C:/a/c/d")); - CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("C:/a/b/c", "C:/a/c/d")); - CHECK_STR_EQ_FREE2("../..", get_relative_path("C:/a/b", "C:/")); - CHECK_STR_EQ_FREE2("../../c", get_relative_path("C:/a/b", "C:/c")); - CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/", "C:/a/b")); + CHECK_STR_EQ_FREE2("a", get_relative_path("C:/doesn't matter", "a")); + CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/doesn't matter", "a/b")); + CHECK_STR_EQ_FREE2(".", get_relative_path("C:/a", "C:/a")); + CHECK_STR_EQ_FREE2("..", get_relative_path("C:/a/b", "C:/a")); + CHECK_STR_EQ_FREE2("b", get_relative_path("C:/a", "C:/a/b")); + CHECK_STR_EQ_FREE2("b/c", get_relative_path("C:/a", "C:/a/b/c")); + CHECK_STR_EQ_FREE2("../c", get_relative_path("C:/a/b", "C:/a/c")); + CHECK_STR_EQ_FREE2("../c/d", get_relative_path("C:/a/b", "C:/a/c/d")); + CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("C:/a/b/c", "C:/a/c/d")); + CHECK_STR_EQ_FREE2("../..", get_relative_path("C:/a/b", "C:/")); + CHECK_STR_EQ_FREE2("../../c", get_relative_path("C:/a/b", "C:/c")); + CHECK_STR_EQ_FREE2("a/b", get_relative_path("C:/", "C:/a/b")); #else - CHECK_STR_EQ_FREE2("a", get_relative_path("/doesn't matter", "a")); - CHECK_STR_EQ_FREE2("a/b", get_relative_path("/doesn't matter", "a/b")); - CHECK_STR_EQ_FREE2(".", get_relative_path("/a", "/a")); - CHECK_STR_EQ_FREE2("..", get_relative_path("/a/b", "/a")); - CHECK_STR_EQ_FREE2("b", get_relative_path("/a", "/a/b")); - CHECK_STR_EQ_FREE2("b/c", get_relative_path("/a", "/a/b/c")); - CHECK_STR_EQ_FREE2("../c", get_relative_path("/a/b", "/a/c")); - CHECK_STR_EQ_FREE2("../c/d", get_relative_path("/a/b", "/a/c/d")); - CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("/a/b/c", "/a/c/d")); - CHECK_STR_EQ_FREE2("../..", get_relative_path("/a/b", "/")); - CHECK_STR_EQ_FREE2("../../c", get_relative_path("/a/b", "/c")); - CHECK_STR_EQ_FREE2("a/b", get_relative_path("/", "/a/b")); + CHECK_STR_EQ_FREE2("a", get_relative_path("/doesn't matter", "a")); + CHECK_STR_EQ_FREE2("a/b", get_relative_path("/doesn't matter", "a/b")); + CHECK_STR_EQ_FREE2(".", get_relative_path("/a", "/a")); + CHECK_STR_EQ_FREE2("..", get_relative_path("/a/b", "/a")); + CHECK_STR_EQ_FREE2("b", get_relative_path("/a", "/a/b")); + CHECK_STR_EQ_FREE2("b/c", get_relative_path("/a", "/a/b/c")); + CHECK_STR_EQ_FREE2("../c", get_relative_path("/a/b", "/a/c")); + CHECK_STR_EQ_FREE2("../c/d", get_relative_path("/a/b", "/a/c/d")); + CHECK_STR_EQ_FREE2("../../c/d", get_relative_path("/a/b/c", "/a/c/d")); + CHECK_STR_EQ_FREE2("../..", get_relative_path("/a/b", "/")); + CHECK_STR_EQ_FREE2("../../c", get_relative_path("/a/b", "/c")); + CHECK_STR_EQ_FREE2("a/b", get_relative_path("/", "/a/b")); #endif } TEST(subst_env_in_string) { - char *errmsg; + char* errmsg; - putenv(FOO_ENV); + putenv(FOO_ENV); - CHECK_STR_EQ_FREE2("bar", - subst_env_in_string("$FOO", &errmsg)); - CHECK(!errmsg); + CHECK_STR_EQ_FREE2("bar", subst_env_in_string("$FOO", &errmsg)); + CHECK(!errmsg); - CHECK_STR_EQ_FREE2("$", - subst_env_in_string("$", &errmsg)); - CHECK(!errmsg); + CHECK_STR_EQ_FREE2("$", subst_env_in_string("$", &errmsg)); + CHECK(!errmsg); - CHECK_STR_EQ_FREE2("bar bar:bar", - subst_env_in_string("$FOO $FOO:$FOO", &errmsg)); - CHECK(!errmsg); + CHECK_STR_EQ_FREE2("bar bar:bar", + subst_env_in_string("$FOO $FOO:$FOO", &errmsg)); + CHECK(!errmsg); - CHECK_STR_EQ_FREE2("xbar", - subst_env_in_string("x$FOO", &errmsg)); - CHECK(!errmsg); + CHECK_STR_EQ_FREE2("xbar", subst_env_in_string("x$FOO", &errmsg)); + CHECK(!errmsg); - CHECK_STR_EQ_FREE2("barx", - subst_env_in_string("${FOO}x", &errmsg)); - CHECK(!errmsg); + CHECK_STR_EQ_FREE2("barx", subst_env_in_string("${FOO}x", &errmsg)); + CHECK(!errmsg); - CHECK(!subst_env_in_string("$surelydoesntexist", &errmsg)); - CHECK_STR_EQ_FREE2("environment variable \"surelydoesntexist\" not set", - errmsg); + CHECK(!subst_env_in_string("$surelydoesntexist", &errmsg)); + CHECK_STR_EQ_FREE2("environment variable \"surelydoesntexist\" not set", + errmsg); - CHECK(!subst_env_in_string("${FOO", &errmsg)); - CHECK_STR_EQ_FREE2("syntax error: missing '}' after \"FOO\"", errmsg); + CHECK(!subst_env_in_string("${FOO", &errmsg)); + CHECK_STR_EQ_FREE2("syntax error: missing '}' after \"FOO\"", errmsg); } TEST(format_human_readable_size) { - CHECK_STR_EQ_FREE2("0.0 MB", format_human_readable_size(0)); - CHECK_STR_EQ_FREE2("0.0 MB", format_human_readable_size(49)); - CHECK_STR_EQ_FREE2("0.4 MB", format_human_readable_size(420 * 1000)); - CHECK_STR_EQ_FREE2("1.0 MB", format_human_readable_size(1000 * 1000)); - CHECK_STR_EQ_FREE2("1.2 MB", format_human_readable_size(1234 * 1000)); - CHECK_STR_EQ_FREE2("438.5 MB", - format_human_readable_size(438.5 * 1000 * 1000)); - CHECK_STR_EQ_FREE2("1.0 GB", - format_human_readable_size(1000 * 1000 * 1000)); - CHECK_STR_EQ_FREE2("17.1 GB", - format_human_readable_size(17.11 * 1000 * 1000 * 1000)); + CHECK_STR_EQ_FREE2("0.0 MB", format_human_readable_size(0)); + CHECK_STR_EQ_FREE2("0.0 MB", format_human_readable_size(49)); + CHECK_STR_EQ_FREE2("0.4 MB", format_human_readable_size(420 * 1000)); + CHECK_STR_EQ_FREE2("1.0 MB", format_human_readable_size(1000 * 1000)); + CHECK_STR_EQ_FREE2("1.2 MB", format_human_readable_size(1234 * 1000)); + CHECK_STR_EQ_FREE2("438.5 MB", + format_human_readable_size(438.5 * 1000 * 1000)); + CHECK_STR_EQ_FREE2("1.0 GB", format_human_readable_size(1000 * 1000 * 1000)); + CHECK_STR_EQ_FREE2("17.1 GB", + format_human_readable_size(17.11 * 1000 * 1000 * 1000)); } TEST(format_parsable_size_with_suffix) { - CHECK_STR_EQ_FREE2("0", format_parsable_size_with_suffix(0)); - CHECK_STR_EQ_FREE2("42000", format_parsable_size_with_suffix(42 * 1000)); - CHECK_STR_EQ_FREE2("1.0M", format_parsable_size_with_suffix(1000 * 1000)); - CHECK_STR_EQ_FREE2("1.2M", format_parsable_size_with_suffix(1234 * 1000)); - CHECK_STR_EQ_FREE2("438.5M", - format_parsable_size_with_suffix(438.5 * 1000 * 1000)); - CHECK_STR_EQ_FREE2("1.0G", - format_parsable_size_with_suffix(1000 * 1000 * 1000)); - CHECK_STR_EQ_FREE2( - "17.1G", - format_parsable_size_with_suffix(17.11 * 1000 * 1000 * 1000)); + CHECK_STR_EQ_FREE2("0", format_parsable_size_with_suffix(0)); + CHECK_STR_EQ_FREE2("42000", format_parsable_size_with_suffix(42 * 1000)); + CHECK_STR_EQ_FREE2("1.0M", format_parsable_size_with_suffix(1000 * 1000)); + CHECK_STR_EQ_FREE2("1.2M", format_parsable_size_with_suffix(1234 * 1000)); + CHECK_STR_EQ_FREE2("438.5M", + format_parsable_size_with_suffix(438.5 * 1000 * 1000)); + CHECK_STR_EQ_FREE2("1.0G", + format_parsable_size_with_suffix(1000 * 1000 * 1000)); + CHECK_STR_EQ_FREE2( + "17.1G", format_parsable_size_with_suffix(17.11 * 1000 * 1000 * 1000)); } TEST(parse_size_with_suffix) { - uint64_t size; - size_t i; - struct { const char *size; int64_t expected; } sizes[] = { - {"0", 0}, - {"42", (int64_t)42 * 1000 * 1000 * 1000}, // Default suffix: G - - {"78k", 78 * 1000}, - {"78K", 78 * 1000}, - {"1.1 M", (int64_t)(1.1 * 1000 * 1000)}, - {"438.55M", (int64_t)(438.55 * 1000 * 1000)}, - {"1 G", 1 * 1000 * 1000 * 1000}, - {"2T", (int64_t)2 * 1000 * 1000 * 1000 * 1000}, - - {"78 Ki", 78 * 1024}, - {"1.1Mi", (int64_t)(1.1 * 1024 * 1024)}, - {"438.55 Mi", (int64_t)(438.55 * 1024 * 1024)}, - {"1Gi", 1 * 1024 * 1024 * 1024}, - {"2 Ti", (int64_t)2 * 1024 * 1024 * 1024 * 1024}, - }; - - for (i = 0; i < ARRAY_SIZE(sizes); ++i) { - CHECKM(parse_size_with_suffix(sizes[i].size, &size), sizes[i].size); - CHECK_INT_EQ(sizes[i].expected, size); - } + uint64_t size; + size_t i; + struct + { + const char* size; + int64_t expected; + } sizes[] = { + {"0", 0}, + {"42", (int64_t)42 * 1000 * 1000 * 1000}, // Default suffix: G + + {"78k", 78 * 1000}, + {"78K", 78 * 1000}, + {"1.1 M", (int64_t)(1.1 * 1000 * 1000)}, + {"438.55M", (int64_t)(438.55 * 1000 * 1000)}, + {"1 G", 1 * 1000 * 1000 * 1000}, + {"2T", (int64_t)2 * 1000 * 1000 * 1000 * 1000}, + + {"78 Ki", 78 * 1024}, + {"1.1Mi", (int64_t)(1.1 * 1024 * 1024)}, + {"438.55 Mi", (int64_t)(438.55 * 1024 * 1024)}, + {"1Gi", 1 * 1024 * 1024 * 1024}, + {"2 Ti", (int64_t)2 * 1024 * 1024 * 1024 * 1024}, + }; + + for (i = 0; i < ARRAY_SIZE(sizes); ++i) { + CHECKM(parse_size_with_suffix(sizes[i].size, &size), sizes[i].size); + CHECK_INT_EQ(sizes[i].expected, size); + } } TEST(format_command) { - const char *argv[] = {"foo", "bar", NULL}; + const char* argv[] = {"foo", "bar", NULL}; - CHECK_STR_EQ_FREE2("foo bar\n", format_command(argv)); + CHECK_STR_EQ_FREE2("foo bar\n", format_command(argv)); } TEST(format_hex) { - uint8_t none[] = ""; - uint8_t text[4] = "foo"; // incl. NUL - uint8_t data[4] = {0, 1, 2, 3}; - char result[2 * sizeof(data) + 1] = "."; + uint8_t none[] = ""; + uint8_t text[4] = "foo"; // incl. NUL + uint8_t data[4] = {0, 1, 2, 3}; + char result[2 * sizeof(data) + 1] = "."; - format_hex(none, 0, result); - CHECK_STR_EQ("", result); + format_hex(none, 0, result); + CHECK_STR_EQ("", result); - format_hex(text, sizeof(text), result); - CHECK_STR_EQ("666f6f00", result); + format_hex(text, sizeof(text), result); + CHECK_STR_EQ("666f6f00", result); - format_hex(data, sizeof(data), result); - CHECK_STR_EQ("00010203", result); + format_hex(data, sizeof(data), result); + CHECK_STR_EQ("00010203", result); } TEST_SUITE_END diff --git a/unittest/util.cpp b/unittest/util.cpp index af451586..0a5009fd 100644 --- a/unittest/util.cpp +++ b/unittest/util.cpp @@ -16,28 +16,29 @@ // this program; if not, write to the Free Software Foundation, Inc., 51 // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -#include "../src/system.hpp" #include "util.hpp" +#include "../src/system.hpp" + #ifdef _WIN32 -# define lstat(a, b) stat(a, b) +# define lstat(a, b) stat(a, b) #endif bool -path_exists(const char *path) +path_exists(const char* path) { - struct stat st; - return lstat(path, &st) == 0; + struct stat st; + return lstat(path, &st) == 0; } void -create_file(const char *path, const char *content) +create_file(const char* path, const char* content) { - FILE *f = fopen(path, "w"); - if (!f || fputs(content, f) < 0) { - fprintf(stderr, "create_file: %s: %s\n", path, strerror(errno)); - } - if (f) { - fclose(f); - } + FILE* f = fopen(path, "w"); + if (!f || fputs(content, f) < 0) { + fprintf(stderr, "create_file: %s: %s\n", path, strerror(errno)); + } + if (f) { + fclose(f); + } } diff --git a/unittest/util.hpp b/unittest/util.hpp index 275b7c42..294483e3 100644 --- a/unittest/util.hpp +++ b/unittest/util.hpp @@ -20,6 +20,6 @@ #include <stdbool.h> -bool path_exists(const char *path); -bool is_symlink(const char *path); -void create_file(const char *path, const char *content); +bool path_exists(const char* path); +bool is_symlink(const char* path); +void create_file(const char* path, const char* content); |