return result;
}
-/* Takes over ownership of path. */
+/*
+ * 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.
+ */
static void remember_include_file(char *path, size_t path_len)
{
struct file_hash *h;
struct stat st;
int fd = -1;
char *data = (char *)-1;
+ enum hash_source_code_result result;
if (!included_files) {
goto ignore;
}
hash_start(&fhash);
- hash_include_file_string(&fhash, data, st.st_size);
+ 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:
+ goto failure;
+ }
h = x_malloc(sizeof(*h));
hash_result_as_bytes(&fhash, h->hash);
failure:
cc_log("Disabling direct mode");
enable_direct = 0;
- hashtable_destroy(included_files, 1);
- included_files = NULL;
/* Fall through. */
ignore:
free(path);
/*
* This function reads and hashes a file. While doing this, it also does these
- * things with preprocessor lines starting with a hash:
+ * things:
*
- * - Makes include file paths whose prefix is CCACHE_BASEDIR relative.
- * - Stores the paths of included files in the global variable included_files.
+ * - Makes include file paths whose prefix is CCACHE_BASEDIR relative when
+ * computing the hash sum.
+ * - Stores the paths and hashes of included files in the global variable
+ * included_files.
*/
static int process_preprocessed_file(struct mdfour *hash, const char *path)
{
char *object_name;
char *manifest_name;
const char *compilercheck;
+ enum hash_source_code_result result;
switch (mode) {
case FINDHASH_DIRECT_MODE:
switch (mode) {
case FINDHASH_DIRECT_MODE:
- if (!hash_include_file(&hash, input_file)) {
- cc_log("Failed to hash %s", input_file);
+ result = hash_source_code_file(&hash, input_file, 1);
+ switch (result) {
+ case HASH_SOURCE_CODE_OK:
+ break;
+ case HASH_SOURCE_CODE_ERROR:
failed();
+ break;
+ case HASH_SOURCE_CODE_FOUND_VOLATILE_MACRO:
+ cc_log("Found __DATE__/__FILE__/__TIME__ macro in %s",
+ input_file);
+ cc_log("Disabling direct mode");
+ enable_direct = 0;
+ break;
}
manifest_name = hash_result(&hash);
manifest_path = get_path_in_cache(manifest_name, ".manifest");
}
/* Create or update the manifest file. */
- if (put_object_in_manifest
+ if (enable_direct
+ && put_object_in_manifest
&& included_files
&& !getenv("CCACHE_READONLY")) {
if (manifest_put(manifest_path, object_hash, included_files)) {
The direct mode will be disabled if any of the following holds:
-* the environment variable CCACHE_NODIRECT is set
+* the environment variable *CCACHE_NODIRECT* is set
* a modification time of one of the include files is too new (needed to avoid a
race condition)
* the unifier is enabled (the environment variable *CCACHE_UNIFY* is set)
-* a compiler option unsupported by the direct mode is used
-** Currently, *-Wp,_X_* options are not supported, except if _X_ is
- *-MD,_path_* or *-MMD,_path_*.
+* 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 PREPROCESSOR MODE
} \
} while (0)
-void hash_include_file_string(
- struct mdfour *hash, const char *str, size_t len)
+/*
+ * 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.
+ */
+enum hash_source_code_result
+hash_source_code_string(
+ struct mdfour *hash, const char *str, size_t len,
+ int check_volatile_macros)
{
const char *p;
const char *end;
}
break;
+ /* 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;
+ }
+ break;
+
default:
break;
}
end:
hash_buffer(hash, hashbuf, hashbuflen);
+ return HASH_SOURCE_CODE_OK;
}
/*
- * Add contents of a file to a hash, but don't hash comments. Returns 1 on
- * success, otherwise 0.
+ * Add contents of a source code file to a hash, but don't hash comments.
*/
-int hash_include_file(struct mdfour *hash, const char *path)
+enum hash_source_code_result
+hash_source_code_file(
+ struct mdfour *hash, const char *path,
+ int check_volatile_macros)
{
int fd;
struct stat st;
char *data;
+ enum hash_source_code_result result;
- fd = open(path, O_RDONLY);
+ fd = open(path, O_RDONLY|O_BINARY);
if (fd == -1) {
- return 0;
+ cc_log("Failed to open %s", path);
+ return HASH_SOURCE_CODE_ERROR;
}
if (fstat(fd, &st) == -1) {
+ cc_log("Failed to fstat %s", path);
close(fd);
- return 0;
+ return HASH_SOURCE_CODE_ERROR;
}
if (st.st_size == 0) {
close(fd);
- return 1;
+ return HASH_SOURCE_CODE_OK;
}
data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
if (data == (void *)-1) {
- return 0;
+ cc_log("Failed to mmap %s", path);
+ return HASH_SOURCE_CODE_ERROR;
}
- hash_include_file_string(hash, data, st.st_size);
-
+ result = hash_source_code_string(
+ hash, data, st.st_size, check_volatile_macros);
munmap(data, st.st_size);
- return 1;
+ return result;
}
int strings_equal(void *str1, void *str2);
int file_hashes_equal(struct file_hash *fh1, struct file_hash *fh2);
-void hash_include_file_string(
- struct mdfour *hash, const char *str, size_t len);
-int hash_include_file(struct mdfour *hash, const char *path);
+enum hash_source_code_result {
+ HASH_SOURCE_CODE_OK,
+ HASH_SOURCE_CODE_ERROR,
+ HASH_SOURCE_CODE_FOUND_VOLATILE_MACRO
+};
+
+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);
#endif
if (!actual) {
actual = x_malloc(sizeof(*actual));
hash_start(&hash);
- if (!hash_include_file(&hash, mf->files[fi->index])) {
+ if (hash_source_code_file(&hash, mf->files[fi->index],
+ 0)
+ != HASH_SOURCE_CODE_OK) {
cc_log("Failed hashing %s",
mf->files[fi->index]);
free(actual);
test_failed "$dep_file missing"
fi
done
+ rm -rf test.dir
##################################################################
# Check that -Wp,-MD,file.d works.
checkstat 'cache hit (preprocessed)' 0
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}__;
+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}__;
+EOF
+ backdate $x.h
+ cat <<EOF >${x}_h.c
+#include "$x.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
+
##################################################################
# Reset things.
CCACHE_NODIRECT=1