FROMCACHE_COMPILED_MODE
};
-enum findhash_call_mode {
- FINDHASH_DIRECT_MODE,
- FINDHASH_CPP_MODE
-};
-
/*
* This is a string that identifies the current "version" of the hash sum
* computed by ccache. If, for any reason, we want to force the hash sum to be
/*
* This function hashes an include file and stores the path and hash in the
- * global included_files variable. It also checks if the include file contains
- * __DATE__/__FILE__/__TIME__ macros, in which case the file is not stored and
- * direct mode is disabled. Takes over ownership of path.
+ * global included_files variable. Takes over ownership of path.
*/
static void remember_include_file(char *path, size_t path_len)
{
struct stat st;
int fd = -1;
char *data = (char *)-1;
- enum hash_source_code_result result;
+ int result;
if (!included_files) {
goto ignore;
}
hash_start(&fhash);
- result = hash_source_code_string(&fhash, data, st.st_size, 1);
- switch (result) {
- case HASH_SOURCE_CODE_OK:
- break;
- case HASH_SOURCE_CODE_FOUND_VOLATILE_MACRO:
- cc_log("Found __DATE__/__FILE__/__TIME__ macro in %s", path);
- /* Fall through. */
- case HASH_SOURCE_CODE_ERROR:
+ result = hash_source_code_string(&fhash, data, st.st_size, path);
+ if (result & HASH_SOURCE_CODE_ERROR
+ || result & HASH_SOURCE_CODE_FOUND_TIME) {
goto failure;
}
int i;
char *manifest_name;
struct stat st;
- enum hash_source_code_result result;
+ int result;
struct file_hash *object_hash = NULL;
/* first the arguments */
}
if (direct_mode) {
- result = hash_source_code_file(hash, input_file, 1);
- switch (result) {
- case HASH_SOURCE_CODE_OK:
- break;
- case HASH_SOURCE_CODE_ERROR:
+ /*
+ * The source code file or an include file may contain
+ * __FILE__, so make sure that the hash is unique for the file
+ * name.
+ */
+ hash_string(hash, input_file);
+ hash_delimiter(hash);
+
+ result = hash_source_code_file(hash, input_file);
+ if (result & HASH_SOURCE_CODE_ERROR) {
failed();
- break;
- case HASH_SOURCE_CODE_FOUND_VOLATILE_MACRO:
- cc_log("Found __DATE__/__FILE__/__TIME__ macro in %s",
- input_file);
+ }
+ if (result & HASH_SOURCE_CODE_FOUND_TIME) {
cc_log("Disabling direct mode");
enable_direct = 0;
- break;
+ return NULL;
}
manifest_name = hash_result(hash);
manifest_path = get_path_in_cache(manifest_name, ".manifest");
struct mdfour direct_hash;
struct mdfour cpp_hash;
- /* Argument list to be sent to the preprocessor (except -E). */
+ /* Arguments (except -E) to send to the preprocessor. */
ARGS *preprocessor_args;
- /* Argument list to be sent to the real compiler. */
+ /* Arguments to send to the real compiler. */
ARGS *compiler_args;
t = time(NULL);
* the unifier is enabled (the environment variable *CCACHE_UNIFY* is set)
* a *-Wp,_X_* compiler option other than *-Wp,-MD,_path_* and *-Wp,-MMD,_path_*
is used
-* a *\_\_DATE\_\_*, *\_\_FILE\_\_* or *\_\_TIME__* macro is used in the source
- code
+* the string ``\_\_TIME__'' is present outside comments and string literals in
+ the source code
THE PREPROCESSOR MODE
similar compiler options.
* Paths specified by compiler options (such as *-I*, *-MF*, etc) may be
absolute.
-* The source code file path may be absolute, and that path may be included in
- warnings emitted to standard error by the preprocessor.
+* The source code file path may be absolute, and that path may substituted for
+ *\_\_FILE__* macros in the source code or included in warnings emitted to
+ standard error by the preprocessor.
This means that if you compile the same code in different locations, you can't
share compilation results between the different build directories since you get
base directory since that will make ccache also rewrite paths to system header
files, which doesn't gain anything.)
-The only drawback of using *CCACHE_BASEDIR* is that if you compile with *-g*
-and use absolute paths to the source code files, the source code paths that are
-stored in the object files may point to the wrong directory, which may prevent
-debuggers like GDB from finding the source code. Sometimes, a work-around is to
-change the directory explicitly with the ``cd'' command in GDB.
+The drawbacks of using *CCACHE_BASEDIR* are:
+
+* If you specify an absolute path to the source code file, *\_\_FILE__* macros
+ will be expanded to a relative path instead.
+* If you specify an absolute path to the source code file and compile with
+ *-g*, the source code path stored in the object file may point to the wrong
+ directory, which may prevent debuggers like GDB from finding the source code.
+ Sometimes, a work-around is to change the directory explicitly with the
+ ``cd'' command in GDB.
SHARING A CACHE
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
+#include <time.h>
unsigned int hash_from_string(void *str)
{
} while (0)
/*
- * Hash a string ignoring comments. If check_volatile_macros is true, also
- * check for volatile preprocessor macros (__{DATE,FILE,TIME}__) and, if found,
- * stop hashing.
+ * Hash a string ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
+ * results.
*/
-enum hash_source_code_result
+int
hash_source_code_string(
- struct mdfour *hash, const char *str, size_t len,
- int check_volatile_macros)
+ struct mdfour *hash, const char *str, size_t len, const char *path)
{
const char *p;
const char *end;
char hashbuf[64];
size_t hashbuflen = 0;
+ int result = HASH_SOURCE_CODE_OK;
p = str;
end = str + len;
/* Potential start of volatile macro. */
case '_':
- if (check_volatile_macros
- && p + 7 < end
- && p[1] == '_'
- && p[6] == '_'
- && p[7] == '_'
- && (strncmp(p + 2, "DATE", 4) == 0
- || strncmp(p + 2, "FILE", 4) == 0
- || strncmp(p + 2, "TIME", 4) == 0)) {
- return HASH_SOURCE_CODE_FOUND_VOLATILE_MACRO;
+ if (p + 7 < end
+ && p[1] == '_' && p[5] == 'E'
+ && p[6] == '_' && p[7] == '_') {
+ if (p[2] == 'D' && p[3] == 'A'
+ && p[4] == 'T') {
+ result |= HASH_SOURCE_CODE_FOUND_DATE;
+ } else if (p[2] == 'T' && p[3] == 'I'
+ && p[4] == 'M') {
+ result |= HASH_SOURCE_CODE_FOUND_TIME;
+ }
+ /*
+ * Of course, we can't be sure that we have
+ * found a __{DATE,TIME}__ that's actually
+ * used, but better safe than sorry. And if you
+ * do something like
+ *
+ * #define TIME __TI ## ME__
+ *
+ * in your code, you deserve to get a false
+ * cache hit.
+ */
}
break;
end:
hash_buffer(hash, hashbuf, hashbuflen);
- return HASH_SOURCE_CODE_OK;
+
+ if (result & HASH_SOURCE_CODE_FOUND_DATE) {
+ /*
+ * Make sure that the hash sum changes if the (potential)
+ * expansion of __DATE__ changes.
+ */
+ cc_log("Found __DATE__ in %s", path);
+ time_t t = time(NULL);
+ struct tm *now = localtime(&t);
+ hash_delimiter(hash);
+ hash_buffer(hash, &now->tm_year, sizeof(now->tm_year));
+ hash_buffer(hash, &now->tm_mon, sizeof(now->tm_mon));
+ hash_buffer(hash, &now->tm_mday, sizeof(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;
}
/*
- * Add contents of a source code file to a hash, but don't hash comments.
+ * Hash a file ignoring comments. Returns a bitmask of HASH_SOURCE_CODE_*
+ * results.
*/
-enum hash_source_code_result
-hash_source_code_file(
- struct mdfour *hash, const char *path,
- int check_volatile_macros)
+int
+hash_source_code_file(struct mdfour *hash, const char *path)
{
int fd;
struct stat st;
char *data;
- enum hash_source_code_result result;
+ int result;
fd = open(path, O_RDONLY|O_BINARY);
if (fd == -1) {
return HASH_SOURCE_CODE_ERROR;
}
- result = hash_source_code_string(
- hash, data, st.st_size, check_volatile_macros);
+ result = hash_source_code_string(hash, data, st.st_size, path);
munmap(data, st.st_size);
return result;
}
int strings_equal(void *str1, void *str2);
int file_hashes_equal(struct file_hash *fh1, struct file_hash *fh2);
-enum hash_source_code_result {
- HASH_SOURCE_CODE_OK,
- HASH_SOURCE_CODE_ERROR,
- HASH_SOURCE_CODE_FOUND_VOLATILE_MACRO
-};
+#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
-enum hash_source_code_result
-hash_source_code_string(
- struct mdfour *hash, const char *str, size_t len,
- int check_volatile_macros);
-enum hash_source_code_result
-hash_source_code_file(
- struct mdfour *hash, const char *path,
- int check_volatile_macros);
+int hash_source_code_string(
+ struct mdfour *hash, const char *str, size_t len, const char *path);
+int hash_source_code_file(struct mdfour *hash, const char *path);
#endif
struct file_info *fi;
struct file_hash *actual;
struct mdfour hash;
+ int result;
for (i = 0; i < obj->n_file_info_indexes; i++) {
fi = &mf->file_infos[obj->file_info_indexes[i]];
if (!actual) {
actual = x_malloc(sizeof(*actual));
hash_start(&hash);
- if (hash_source_code_file(&hash, mf->files[fi->index],
- 0)
- != HASH_SOURCE_CODE_OK) {
+ result = hash_source_code_file(&hash,
+ mf->files[fi->index]);
+ if (result & HASH_SOURCE_CODE_ERROR) {
cc_log("Failed hashing %s",
mf->files[fi->index]);
free(actual);
return 0;
}
+ if (result & HASH_SOURCE_CODE_FOUND_TIME) {
+ free(actual);
+ return 0;
+ }
hash_result_as_bytes(&hash, actual->hash);
actual->size = hash.totalN;
hashtable_insert(hashed_files,
checkstat 'cache miss' 1
##################################################################
- # Check that direct mode is disabled if __DATE__, __FILE__ or __TIME__
- # macros are used.
- for x in date file time; do
- X=`echo $x | tr 'a-z' 'A-Z'`
- testname="__${X}__ in source file"
- $CCACHE -Cz >/dev/null
- cat <<EOF >$x.c
-char $x[] = __${X}__;
+ # Check that direct mode correctly detects file name/path changes.
+ testname="__FILE__ in source file"
+ $CCACHE -Cz >/dev/null
+ cat <<EOF >file.c
+#define file __FILE__
+int test;
EOF
- $CCACHE $COMPILER -c $x.c
- checkstat 'cache hit (direct)' 0
- checkstat 'cache hit (preprocessed)' 0
- checkstat 'cache miss' 1
- $CCACHE $COMPILER -c $x.c
- checkstat 'cache hit (direct)' 0
-
- testname="__${X}__ in include file"
- $CCACHE -Cz >/dev/null
- cat <<EOF >$x.h
-char $x[] = __${X}__;
+ $CCACHE $COMPILER -c file.c
+ checkstat 'cache hit (direct)' 0
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 1
+ $CCACHE $COMPILER -c file.c
+ checkstat 'cache hit (direct)' 1
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 1
+ $CCACHE $COMPILER -c $PWD/file.c
+ checkstat 'cache hit (direct)' 1
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 2
+
+ testname="__FILE__ in include file"
+ $CCACHE -Cz >/dev/null
+ cat <<EOF >file.h
+#define file __FILE__
+int test;
EOF
- backdate $x.h
- cat <<EOF >${x}_h.c
-#include "$x.h"
+ backdate file.h
+ cat <<EOF >file_h.c
+#include "file.h"
EOF
- $CCACHE $COMPILER -c ${x}_h.c
- checkstat 'cache hit (direct)' 0
- checkstat 'cache hit (preprocessed)' 0
- checkstat 'cache miss' 1
- $CCACHE $COMPILER -c ${x}_h.c
- checkstat 'cache hit (direct)' 0
- done
+ $CCACHE $COMPILER -c file_h.c
+ checkstat 'cache hit (direct)' 0
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 1
+ $CCACHE $COMPILER -c file_h.c
+ checkstat 'cache hit (direct)' 1
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 1
+ mv file_h.c file2_h.c
+ $CCACHE $COMPILER -c $PWD/file2_h.c
+ checkstat 'cache hit (direct)' 1
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 2
+
+ ##################################################################
+ # Check that we never get direct hits when __TIME__ is used.
+ testname="__TIME__ in source file"
+ $CCACHE -Cz >/dev/null
+ cat <<EOF >time.c
+#define time __TIME__
+int test;
+EOF
+ $CCACHE $COMPILER -c time.c
+ checkstat 'cache hit (direct)' 0
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 1
+ $CCACHE $COMPILER -c time.c
+ checkstat 'cache hit (direct)' 0
+ checkstat 'cache hit (preprocessed)' 1
+ checkstat 'cache miss' 1
+
+ testname="__TIME__ in include time"
+ $CCACHE -Cz >/dev/null
+ cat <<EOF >time.h
+#define time __TIME__
+int test;
+EOF
+ backdate time.h
+ cat <<EOF >time_h.c
+#include "time.h"
+EOF
+ $CCACHE $COMPILER -c time_h.c
+ checkstat 'cache hit (direct)' 0
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 1
+ $CCACHE $COMPILER -c time_h.c
+ checkstat 'cache hit (direct)' 0
+ checkstat 'cache hit (preprocessed)' 1
+ checkstat 'cache miss' 1
##################################################################
# Reset things.
checkstat 'cache miss' 1
cd ..
- CCACHE_NODIRECT=1
- export CCACHE_NODIRECT
-
##################################################################
# CCACHE_BASEDIR="" is the default.
testname="default CCACHE_BASEDIR"
testname="path normalization"
cd dir1
$CCACHE -z >/dev/null
- unset CCACHE_NODIRECT
- CCACHE_BASEDIR=$PWD $CCACHE $COMPILER -I$PWD//include -c $PWD//.///src/test.c
- export CCACHE_NODIRECT=1
+ CCACHE_BASEDIR=$PWD $CCACHE $COMPILER -I$PWD//include -c $PWD//src/test.c
checkstat 'cache hit (direct)' 1
checkstat 'cache hit (preprocessed)' 0
checkstat 'cache miss' 0
fi
CCACHE_BASEDIR=$PWD $CCACHE $COMPILER -Wall -W -I$PWD -c $PWD/stderr.c -o $PWD/stderr.o 2>stderr.txt
- checkstat 'cache hit (direct)' 0
- checkstat 'cache hit (preprocessed)' 1
+ checkstat 'cache hit (direct)' 1
+ checkstat 'cache hit (preprocessed)' 0
checkstat 'cache miss' 1
if grep -q $PWD stderr.txt; then
test_failed "Base dir ($PWD) found in stderr:\n`cat stderr.txt`"
fi
+
+ export CCACHE_NODIRECT=1
}
compression_suite() {