Ignore ctimes when *file_stat_matches* is enabled. This can be useful when
backdating files' mtimes in a controlled way.
*include_file_ctime*::
- By default, ccache will not cache a file if it includes a header whose
- ctime is too new. This sloppiness disables that check.
+ By default, ccache will not cache a file if it includes a header whose ctime
+ is too new. This sloppiness disables that check. See also
+ _<<_handling_of_newly_created_header_files,Handling of newly created header
+ files>>_.
*include_file_mtime*::
By default, ccache will not cache a file if it includes a header whose
- mtime is too new. This sloppiness disables that check.
+ mtime is too new. This sloppiness disables that check. See also
+ _<<_handling_of_newly_created_header_files,Handling of newly created header
+ files>>_.
*locale*::
Ccache includes the environment variables *LANG*, *LC_ALL*, *LC_CTYPE* and
*LC_MESSAGES* in the hash by default since they may affect localization of
* The compiler is not generating dependencies using *-MD* or *-MMD*.
+Handling of newly created header files
+--------------------------------------
+
+If modification time (mtime) or status change time (ctime) of one of the include
+files is the same second as the time compilation is being done, ccache disables
+the direct mode (or, in the case of a <<_precompiled_headers,precompiled
+header>>, disables caching completely). This done as a safety measure to avoid a
+race condition (see below).
+
+To be able to use a newly created header files in direct mode (or use a newly
+precompiled header), either:
+
+* create the include file earlier in the build process, or
+* set <<config_sloppiness,*sloppiness*>> to
+ *include_file_ctime,include_file_mtime* if you are willing to take the risk,
+ for instance if you know that your build system is robust enough not to
+ trigger the race condition.
+
+For reference, the race condition mentioned above consists of these events:
+
+1. The preprocessor is run.
+2. An include file is modified by someone.
+3. The new include file is hashed by ccache.
+4. The real compiler is run on the preprocessor's output, which contains data
+ from the old header file.
+5. The wrong object file is stored in the cache.
+
+
Cache debugging
---------------
`__TIMESTAMP__` is used when using a precompiled header. Further, it can't
detect changes in **#define**s in the source code because of how
preprocessing works in combination with precompiled headers.
+* You may also want to include *include_file_mtime,include_file_ctime* in
+ <<config_sloppiness,*sloppiness*>>. See
+ _<<_handling_of_newly_created_header_files,Handling of newly created header
+ files>>_.
* You must either:
+
--
*-Wp,-MMD,_path_*, and *-Wp,-D_define_*) is used.
** This was the first compilation with a new value of the
<<config_base_dir,base directory>>.
-** A modification time of one of the include files is too new (created the same
- second as the compilation is being done). This check is made to avoid a race
- condition. To fix this, create the include file earlier in the build
- process, if possible, or set <<config_sloppiness,*sloppiness*>> to
- *include_file_ctime, include_file_mtime* if you are willing to take the risk.
- (The race condition consists of these events: the preprocessor is run; an
- include file is modified by someone; the new include file is hashed by
- ccache; the real compiler is run on the preprocessor's output, which contains
- data from the old header file; the wrong object file is stored in the cache.)
+** A modification or status change time of one of the include files is too new
+ (created the same second as the compilation is being done). See
+ _<<_handling_of_newly_created_header_files,Handling of newly created header
+ files>>_.
** The `__TIME__` preprocessor macro is (potentially) being used. Ccache turns
off direct mode if `__TIME__` is present in the source code. This is done as
a safety measure since the string indicates that a `__TIME__` macro _may_
return true;
}
+enum class RememberIncludeFileResult { ok, cannot_use_pch };
+
// This function hashes an include file and stores the path and hash in
// ctx.included_files. If the include file is a PCH, cpp_hash is also updated.
-static void
+static RememberIncludeFileResult
remember_include_file(Context& ctx,
const std::string& path,
Hash& cpp_hash,
bool system,
Hash* depend_mode_hash)
{
- if (!do_remember_include_file(ctx, path, cpp_hash, system, depend_mode_hash)
- && ctx.config.direct_mode()) {
- LOG_RAW("Disabling direct mode");
- ctx.config.set_direct_mode(false);
+ if (!do_remember_include_file(
+ ctx, path, cpp_hash, system, depend_mode_hash)) {
+ if (Util::is_precompiled_header(path)) {
+ return RememberIncludeFileResult::cannot_use_pch;
+ } else if (ctx.config.direct_mode()) {
+ LOG_RAW("Disabling direct mode");
+ ctx.config.set_direct_mode(false);
+ }
}
+
+ return RememberIncludeFileResult::ok;
}
static void
// - Makes include file paths for which the base directory is a prefix relative
// when computing the hash sum.
// - Stores the paths and hashes of included files in ctx.included_files.
-static bool
+//
+// Returns Statistic::none on success, otherwise a statistics counter to be
+// incremented.
+static Statistic
process_preprocessed_file(Context& ctx,
Hash& hash,
const std::string& path,
try {
data = Util::read_file(path);
} catch (Error&) {
- return false;
+ return Statistic::internal_error;
}
// Bytes between p and q are pending to be hashed.
q++;
if (q >= end) {
LOG_RAW("Failed to parse included file path");
- return false;
+ return Statistic::internal_error;
}
// q points to the beginning of an include file path
hash.hash(p, q - p);
hash.hash(inc_path);
}
- remember_include_file(ctx, inc_path, hash, system, nullptr);
+ if (remember_include_file(ctx, inc_path, hash, system, nullptr)
+ == RememberIncludeFileResult::cannot_use_pch) {
+ return Statistic::could_not_use_precompiled_header;
+ }
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') {
print_included_files(ctx, stdout);
}
- return true;
+ return Statistic::none;
}
// Extract the used includes from the dependency file. Note that we cannot
}
hash.hash_delimiter("cpp");
- bool is_pump = ctx.config.compiler_type() == CompilerType::pump;
- if (!process_preprocessed_file(ctx, hash, stdout_path, is_pump)) {
- throw Failure(Statistic::internal_error);
+ const bool is_pump = ctx.config.compiler_type() == CompilerType::pump;
+ const Statistic error =
+ process_preprocessed_file(ctx, hash, stdout_path, is_pump);
+ if (error != Statistic::none) {
+ throw Failure(error);
}
hash.hash_delimiter("cppstderr");
expect_stat 'cache hit (direct)' 2
expect_stat 'cache hit (preprocessed)' 0
expect_stat 'cache miss' 1
+
+ # -------------------------------------------------------------------------
+ TEST "Too new PCH file"
+
+ # If the precompiled header is too new we shouldn't cache the result at all
+ # since:
+ #
+ # - the precompiled header content must be included in the hash, but
+ # - we don't trust the precompiled header content so we can't hash it
+ # ourselves, and
+ # - the preprocessed output doesn't contain the preprocessed header content.
+
+ touch lib.h
+ touch main.c
+
+ $REAL_COMPILER $SYSROOT -c lib.h
+ touch -d "@$(($(date +%s) + 60))" lib.h.gch # 1 minute in the future
+
+ CCACHE_SLOPPINESS="$DEFAULT_SLOPPINESS pch_defines,time_macros" $CCACHE_COMPILE -include lib.h -c main.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache hit (preprocessed)' 0
+ expect_stat 'cache miss' 0
+ expect_stat "can't use precompiled header" 1
}
pch_suite_clang() {