compiler's source has not changed, or if the compiler only has changes that
don't affect code generation). You should only use the *none* setting if
you know what you are doing.
+_a command string_::
+ Hash the standard output and standard error output of the specified
+ command. The command is passed to +/bin/sh+ for execution. You can use
+ ``$compiler'' in the command string to refer to the compiler. Example
+ commands:
++
+--
+* +$compiler -v+
+* +$compiler -dumpmachine && $compiler -dumpversion+
+
+You should make sure that the specified command is as fast as possible since it
+will be run once for each ccache invocation.
+
+Identifying the compiler using a command is useful if you want to avoid cache
+misses when the compiler has been rebuilt but not changed.
+
+Another case is when the compiler (as seen by ccache) actually isn't the real
+compiler but another compiler wrapper -- in that case, the default *mtime*
+method will hash the mtime and size of the other compiler wrapper, which means
+that ccache won't be able to detect a compiler upgrade. Using a suitable
+command to identify the compiler is thus safer, but it's also slower, so you
+should consider continue using the *mtime* method in combination with
+*CCACHE_PREFIX* if possible. See
+<<_using_ccache_with_other_compiler_wrappers,USING CCACHE WITH OTHER COMPILER
+WRAPPERS>>.
+--
--
*CCACHE_CPP2*::
and ccache will prefix the command line with the specified command when running
the compiler.
-It is not recommended to use the form *ccache anotherwrapper compiler args* as
-the compilation command. It's also not recommended to use the masquerading
-technique for the other compiler wrapper. The reason is that ccache will in
-both cases hash the mtime of the other wrapper instead of the real compiler
-(see the *CCACHE_COMPILERCHECK* option), which means that:
+Unless you set *CCACHE_COMPILERCHECK* to a suitable command (see the
+description of that configuration option), it is not recommended to use the
+form *ccache anotherwrapper compiler args* as the compilation command. It's
+also not recommended to use the masquerading technique for the other compiler
+wrapper. The reason is that by default, ccache will in both cases hash the
+mtime and size of the other wrapper instead of the real compiler, which means
+that:
* Compiler upgrades will not be detected properly.
* The cached results will not be shared between compilations with and without
the other wrapper.
-* ccache will needlessly invoke the other wrapper when running the
- preprocessor.
+
+Another minor thing is that if *CCACHE_PREFIX* is not used, ccache will
+needlessly invoke the other wrapper when running the preprocessor.
Bugs
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
} else if (str_eq(compilercheck, "content")) {
hash_delimiter(hash, "cc_content");
hash_file(hash, args->argv[0]);
- } else { /* mtime */
+ } else if (str_eq(compilercheck, "mtime")) {
hash_delimiter(hash, "cc_mtime");
hash_int(hash, st.st_size);
hash_int(hash, st.st_mtime);
+ } else { /* command string */
+ char buf[8192];
+ static char compiler_env[1024];
+ size_t n;
+ char *command;
+ FILE *f;
+ int status;
+
+ cc_log("Running compiler check command: %s", compilercheck);
+ snprintf(compiler_env, sizeof(compiler_env),
+ "compiler=%s", orig_args->argv[0]);
+ putenv(compiler_env);
+ command = format("{ %s ; } </dev/null 2>&1", compilercheck);
+ f = popen(command, "r");
+ free(command);
+ if (!f) {
+ stats_update(STATS_COMPCHECK);
+ fatal("Compiler check popen failed");
+ }
+ hash_delimiter(hash, "cc_command");
+ while (1) {
+ n = fread(buf, 1, sizeof(buf), f);
+ hash_buffer(hash, buf, n);
+ if (n < sizeof(buf)) {
+ if (feof(f)) {
+ break;
+ } else {
+ stats_update(STATS_COMPCHECK);
+ fatal("Failed reading from compiler check command");
+ }
+ }
+ }
+ status = pclose(f);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ stats_update(STATS_COMPCHECK);
+ fatal("Compiler check command returned %d", WEXITSTATUS(status));
+ }
}
/*
STATS_NOOUTPUT = 23,
STATS_EMPTYOUTPUT = 24,
STATS_BADEXTRAFILE = 25,
+ STATS_COMPCHECK = 26,
STATS_END
};
fp.close()
environment = {"CCACHE_DIR": ccache_dir, "PATH": environ["PATH"]}
+ environment["CCACHE_COMPILERCHECK"] = options.compilercheck
if options.compression:
environment["CCACHE_COMPRESS"] = "1"
if options.hardlink:
op.add_option(
"--ccache",
help="location of ccache (default: %s)" % DEFAULT_CCACHE)
+ op.add_option(
+ "--compilercheck",
+ help="specify compilercheck (default: mtime)")
op.add_option(
"--compression",
help="use compression",
action="store_true")
op.set_defaults(
ccache=DEFAULT_CCACHE,
+ compilercheck="mtime",
directory=DEFAULT_DIRECTORY,
times=DEFAULT_TIMES)
(options, args) = op.parse_args(argv[1:])
print "Compilation command: %s -c -o %s.o" % (
" ".join(args),
splitext(argv[-1])[0])
+ print "Compilercheck:", options.compilercheck
print "Compression:", on_off(options.compression)
print "Hardlink:", on_off(options.hardlink)
print "Nostats:", on_off(options.nostats)
{ STATS_MISSING, "cache file missing ", NULL, 0 },
{ STATS_ARGS, "bad compiler arguments ", NULL, 0 },
{ STATS_SOURCELANG, "unsupported source language ", NULL, 0 },
+ { STATS_COMPCHECK, "compiler check failed ", NULL, 0 },
{ STATS_CONFTEST, "autoconf compile/link ", NULL, 0 },
{ STATS_UNSUPPORTED, "unsupported compiler option ", NULL, 0 },
{ STATS_OUTSTDOUT, "output to stdout ", NULL, 0 },
checkstat 'cache hit (preprocessed)' 2
checkstat 'cache miss' 1
+ testname="compilercheck=command"
+ $CCACHE -z >/dev/null
+ backdate compiler.sh
+ CCACHE_COMPILERCHECK='echo $compiler' $CCACHE ./compiler.sh -c test1.c
+ checkstat 'cache hit (preprocessed)' 0
+ checkstat 'cache miss' 1
+ echo "# Compiler upgrade" >>compiler.sh
+ CCACHE_COMPILERCHECK="echo ./compiler.sh" $CCACHE ./compiler.sh -c test1.c
+ checkstat 'cache hit (preprocessed)' 1
+ checkstat 'cache miss' 1
+ CCACHE_COMPILERCHECK='echo bar >&2' $CCACHE ./compiler.sh -c test1.c
+ checkstat 'cache hit (preprocessed)' 1
+ checkstat 'cache miss' 2
+ CCACHE_COMPILERCHECK='read x; echo -n b >&2; echo ar >&2' $CCACHE ./compiler.sh -c test1.c
+ checkstat 'cache hit (preprocessed)' 2
+ checkstat 'cache miss' 2
+
+ testname="compilercheck=unknown_command"
+ $CCACHE -z >/dev/null
+ backdate compiler.sh
+ CCACHE_COMPILERCHECK="unknown_command" $CCACHE ./compiler.sh -c test1.c 2>/dev/null
+ if [ "$?" -eq 0 ]; then
+ test_failed "Expected failure running unknown_command to verify compiler but was success"
+ fi
+ checkstat 'compiler check failed' 1
+
testname="no object file"
cat <<'EOF' >test_no_obj.c
int test_no_obj;
vsnprintf(msg, sizeof(msg), format, ap);
va_end(ap);
- if (cache_logfile) {
- if (!logfile) {
- logfile = fopen(cache_logfile, "a");
- }
- if (logfile) {
- fprintf(logfile, "[%-5d] FATAL: %s\n", (int)getpid(), msg);
- fflush(logfile);
- }
- }
-
+ cc_log("FATAL: %s", msg);
fprintf(stderr, "ccache: FATAL: %s\n", msg);
exit(1);