]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
fix: Reduce the risk of false positive direct mode hits
authorJoel Rosdahl <joel@rosdahl.net>
Sat, 16 Mar 2024 13:42:00 +0000 (14:42 +0100)
committerJoel Rosdahl <joel@rosdahl.net>
Sat, 16 Mar 2024 20:54:50 +0000 (21:54 +0100)
To reduce the risk of getting a false positive hit when a directory
early in the include path now exists, record whether include directories
and similar input paths exist. Note that this is not 100% waterproof
since it only detects newly appearing directories and not newly
appearing header files.

src/ccache/ccache.cpp
src/ccache/compopt.cpp
src/ccache/compopt.hpp
test/suites/direct.bash
unittest/test_compopt.cpp

index 6a91a209dd8a24b189fa9cbde4268f9699ded70b..bd0497cd21caee535652bf60b6486e8b6ce7580f 100644 (file)
@@ -1722,6 +1722,29 @@ hash_argument(const Context& ctx,
     }
   }
 
+  // From the manual: "files that were used by the compiler are recorded, but
+  // header files that were *not* used, but would have been used if they
+  // existed, are not". To reduce the risk of getting a false positive hit when
+  // a directory early in the include path now exists, record whether include
+  // directories and similar input paths exist. Note that this is not 100%
+  // waterproof since it only detects newly appearing directories and not newly
+  // appearing header files.
+  if (direct_mode) {
+    std::optional<std::string_view> path;
+    if (compopt_takes_path(args[i]) && i + 1 < args.size()) {
+      path = args[i + 1];
+    } else {
+      auto p = compopt_prefix_takes_path(args[i]);
+      if (p) {
+        path = *p;
+      };
+    }
+    if (path) {
+      hash.hash_delimiter("path exists");
+      hash.hash(FMT("{} {}", *path, fs::exists(*path) ? "1" : "0"));
+    }
+  }
+
   if (ctx.args_info.generating_dependencies) {
     std::optional<std::string_view> option;
     std::optional<std::string_view> value;
index f3c3be0f969022297b355aa524a4839e4d993c12..a112ab9b2632b21ef8ad3bc71a677d40dd31b0c9 100644 (file)
@@ -288,8 +288,6 @@ compopt_takes_concat_arg(std::string_view 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_output(std::string_view option)
 {
@@ -298,8 +296,6 @@ compopt_prefix_affects_cpp_output(std::string_view 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_compiler_output(std::string_view option)
 {
@@ -307,3 +303,15 @@ compopt_prefix_affects_compiler_output(std::string_view option)
   const CompOpt* co = find_prefix(option);
   return co && (co->type & TAKES_CONCAT_ARG) && (co->type & AFFECTS_COMP);
 }
+
+std::optional<std::string_view>
+compopt_prefix_takes_path(std::string_view option)
+{
+  // Prefix options have to take concatenated args.
+  const CompOpt* co = find_prefix(option);
+  if (co && (co->type & TAKES_CONCAT_ARG) && (co->type & TAKES_PATH)) {
+    return option.substr(co->name.length());
+  } else {
+    return std::nullopt;
+  }
+}
index 3adfe46ddb6c680f36f2472cbbda91a857cb3030..99ce2566c2d4a4e87b56e5c48672bd4737711d31 100644 (file)
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <optional>
 #include <string_view>
 
 bool compopt_short(bool (*fn)(std::string_view option),
@@ -31,3 +32,5 @@ bool compopt_takes_arg(std::string_view option);
 bool compopt_takes_concat_arg(std::string_view option);
 bool compopt_prefix_affects_cpp_output(std::string_view option);
 bool compopt_prefix_affects_compiler_output(std::string_view option);
+std::optional<std::string_view>
+compopt_prefix_takes_path(std::string_view option);
index aa3670d055fd83ad5e96c30f27ddbe8fc38ea937..806521e911b93494a6f62de3258716a657a718be 100644 (file)
@@ -1361,4 +1361,42 @@ EOF
     expect_stat files_in_cache 2
 
     expect_equal_content $manifest_file saved.manifest
+
+    # -------------------------------------------------------------------------
+    TEST "Detection of appearing include directories"
+
+    cat <<EOF >main.c
+#include <foo.h>
+EOF
+    backdate main.c
+    mkdir a
+    cat <<EOF >a/foo.h
+char x[] = "content_a";
+EOF
+    backdate a/foo.h
+
+    $CCACHE_COMPILE -c -Ib -Ia main.c
+    expect_contains main.o content_a
+    expect_stat direct_cache_hit 0
+    expect_stat cache_miss 1
+
+    $CCACHE_COMPILE -c -Ib -Ia main.c
+    expect_contains main.o content_a
+    expect_stat direct_cache_hit 1
+    expect_stat cache_miss 1
+
+    mkdir b
+    cat <<EOF >b/foo.h
+char x[] = "content_b";
+EOF
+
+    $CCACHE_COMPILE -c -Ib -Ia main.c
+    expect_contains main.o content_b
+    expect_stat direct_cache_hit 1
+    expect_stat cache_miss 2
+
+    $CCACHE_COMPILE -c -Ib -Ia main.c
+    expect_contains main.o content_b
+    expect_stat direct_cache_hit 2
+    expect_stat cache_miss 2
 }
index 965d55970bacf2927f7975b10458186e37dbbf63..21aeeb0c678c2a4272a5754bf8f25a441171583c 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <doctest.h>
 
+#include <iostream> // for doctest stringification of std::string_view
+
 bool compopt_verify_sortedness_and_flags();
 
 TEST_SUITE_BEGIN("compopt");
@@ -93,4 +95,10 @@ TEST_CASE("prefix_affects_compiler_output")
   CHECK(!compopt_prefix_affects_compiler_output("-Wa"));
 }
 
+TEST_CASE("prefix_takes_path")
+{
+  CHECK(compopt_prefix_takes_path("-Dfoo") == std::nullopt);
+  CHECK(*compopt_prefix_takes_path("-Ifoo") == "foo");
+}
+
 TEST_SUITE_END();