]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
fix: Hash specs file actually used by GCC
authorJoel Rosdahl <joel@rosdahl.net>
Sun, 7 Sep 2025 17:10:47 +0000 (19:10 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Mon, 8 Sep 2025 15:56:07 +0000 (17:56 +0200)
"gcc -specs=foo" (when "foo" doesn't include a slash) makes GCC search
in various system directories for the file, so ccache fails to hash the
file if it's found in such a directory.

Fix this by asking the compiler (using "-print-file-name") which file it
will actually read.

Fixes #1625.

src/ccache/ccache.cpp

index d10f6feadf61b1e6df5bc276d6137a00c2875374..b6cf444af2d07922fa2766e3dcc2f3ba90ae82fc 100644 (file)
@@ -2046,33 +2046,43 @@ hash_argument(const Context& ctx,
     }
   }
 
-  if (util::starts_with(args[i], "-specs=")
-      || util::starts_with(args[i], "--specs=")
-      || (args[i] == "-specs" || args[i] == "--specs")) {
-    std::string path;
-    size_t eq_pos = args[i].find('=');
-    if (eq_pos == std::string::npos) {
+  if (util::starts_with(args[i], "-specs=") || args[i] == "-specs"
+      || util::starts_with(args[i], "--specs=") || args[i] == "--specs") {
+    auto [_option, specs] = util::split_once_into_views(args[i], '=');
+    if (!specs) {
       if (i + 1 >= args.size()) {
-        LOG("missing argument for \"{}\"", args[i]);
+        LOG("Missing argument for \"{}\"", args[i]);
         return tl::unexpected(Statistic::bad_compiler_arguments);
       }
-      path = args[i + 1];
+      specs = args[i + 1];
       i++;
-    } else {
-      path = args[i].substr(eq_pos + 1);
     }
 
-    DirEntry dir_entry(path, DirEntry::LogOnError::yes);
-    if (dir_entry.is_regular_file()) {
-      // If given an explicit specs file, then hash that file, but don't
-      // include the path to it in the hash.
-      hash.hash_delimiter("specs");
-      TRY(hash_compiler(ctx, hash, dir_entry, path, false));
+    if (ctx.config.is_compiler_group_clang()) {
+      // Clang accepts -specs but ignores it.
+      if (!util::ends_with(args[i], "=")) {
+        hash.hash_delimiter("arg");
+        hash.hash(args[i - 1]);
+      }
+      hash.hash_delimiter("arg");
+      hash.hash(args[i]);
       return {};
-    } else {
-      LOG("While processing {}: {} is missing", args[i], path);
+    }
+
+    auto output = util::exec_to_string(
+      {ctx.orig_args[0], FMT("-print-file-name={}", *specs)});
+    if (!output) {
+      LOG("Failed to query specs location: {}", output.error());
+      return tl::unexpected(Statistic::internal_error);
+    }
+    auto path = util::strip_whitespace(*output);
+    LOG("Hashing specs file {}", path);
+    hash.hash_delimiter("specs");
+    if (auto r = hash.hash_file(path); !r) {
+      LOG("Failed to hash specs file {}: {}", path, r.error());
       return tl::unexpected(Statistic::bad_compiler_arguments);
     }
+    return {};
   }
 
   if (args[i] == "--config" || util::starts_with(args[i], "--config=")) {