]>
Commit | Line | Data |
---|---|---|
147ea804 | 1 | // Copyright (C) 2009-2023 Joel Rosdahl and other contributors |
3c98cd28 | 2 | // Copyright (C) 2002-2007 Andrew Tridgell |
5a719cab JR |
3 | // |
4 | // See doc/AUTHORS.adoc for a complete list of contributors. | |
48e33b2e JR |
5 | // |
6 | // This program is free software; you can redistribute it and/or modify it | |
7 | // under the terms of the GNU General Public License as published by the Free | |
8 | // Software Foundation; either version 3 of the License, or (at your option) | |
9 | // any later version. | |
10 | // | |
11 | // This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | // more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public License along with | |
17 | // this program; if not, write to the Free Software Foundation, Inc., 51 | |
18 | // Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
f42859a1 | 19 | |
68e42bd7 | 20 | #include "ccache.hpp" |
f5795cdb | 21 | |
ad52c600 | 22 | #include "Args.hpp" |
bd9897fb | 23 | #include "ArgsInfo.hpp" |
f3652404 | 24 | #include "Context.hpp" |
c964ad4a | 25 | #include "Depfile.hpp" |
a5021452 | 26 | #include "Hash.hpp" |
1a15286a | 27 | #include "MiniTrace.hpp" |
91aa7075 | 28 | #include "SignalHandler.hpp" |
1a88b504 | 29 | #include "Util.hpp" |
756e03d1 | 30 | #include "argprocessing.hpp" |
68e42bd7 | 31 | #include "compopt.hpp" |
42939f09 | 32 | #include "execute.hpp" |
68e42bd7 JR |
33 | #include "hashutil.hpp" |
34 | #include "language.hpp" | |
e53af1a6 | 35 | |
a03f14b7 | 36 | #include <core/CacheEntry.hpp> |
9882ac2d | 37 | #include <core/Manifest.hpp> |
552d736e | 38 | #include <core/MsvcShowIncludesOutput.hpp> |
99c881d1 JR |
39 | #include <core/Result.hpp> |
40 | #include <core/ResultRetriever.hpp> | |
a28af38b JR |
41 | #include <core/Statistics.hpp> |
42 | #include <core/StatsLog.hpp> | |
8630a888 | 43 | #include <core/common.hpp> |
eb266c99 | 44 | #include <core/exceptions.hpp> |
32c037e7 | 45 | #include <core/mainoptions.hpp> |
c7c0837a | 46 | #include <core/types.hpp> |
01e72eaa | 47 | #include <storage/Storage.hpp> |
54cbb06a | 48 | #include <util/Fd.hpp> |
ea9a264e | 49 | #include <util/FileStream.hpp> |
f88e6084 | 50 | #include <util/Finalizer.hpp> |
1da50f6f | 51 | #include <util/TemporaryFile.hpp> |
3c98cd28 | 52 | #include <util/UmaskScope.hpp> |
09c32fb9 | 53 | #include <util/environment.hpp> |
85f5f8af | 54 | #include <util/expected.hpp> |
3c55dbff | 55 | #include <util/file.hpp> |
709701ac | 56 | #include <util/filesystem.hpp> |
ef96a84a | 57 | #include <util/fmtmacros.hpp> |
13cb56cd | 58 | #include <util/logging.hpp> |
303c82fe | 59 | #include <util/path.hpp> |
3c98cd28 | 60 | #include <util/process.hpp> |
303c82fe | 61 | #include <util/string.hpp> |
f657af6a | 62 | #include <util/time.hpp> |
7f034685 | 63 | #include <util/wincompat.hpp> |
c7c0837a | 64 | |
ca9ec9cf JR |
65 | #include <fcntl.h> |
66 | ||
eea89d4a | 67 | #include <optional> |
60005c83 | 68 | #include <string_view> |
eea89d4a | 69 | |
ca9ec9cf JR |
70 | #ifdef HAVE_UNISTD_H |
71 | # include <unistd.h> | |
a4456585 JR |
72 | #endif |
73 | ||
231fef47 | 74 | #include <algorithm> |
dd8f65aa | 75 | #include <cmath> |
e1a53fd0 | 76 | #include <cstring> |
3578d20a | 77 | #include <limits> |
3c0f600d | 78 | #include <memory> |
147ea804 | 79 | #include <unordered_map> |
89e0e093 | 80 | |
709701ac | 81 | namespace fs = util::filesystem; |
fe3aa934 | 82 | |
c24446f5 | 83 | using core::Statistic; |
c86a4628 | 84 | using util::DirEntry; |
30c10e87 | 85 | |
48e33b2e JR |
86 | // This is a string that identifies the current "version" of the hash sum |
87 | // computed by ccache. If, for any reason, we want to force the hash sum to be | |
88 | // different for the same input in a new ccache version, we can just change | |
89 | // this string. A typical example would be if the format of one of the files | |
90 | // stored in the cache changes in a backwards-incompatible way. | |
e83ac28d | 91 | const char HASH_PREFIX[] = "4"; |
5ea294ef | 92 | |
b3c3e799 JR |
93 | // Search for k_ccache_disable_token within the first |
94 | // k_ccache_disable_search_limit bytes of the input file. | |
95 | const size_t k_ccache_disable_search_limit = 4096; | |
96 | ||
97 | // String to look for when checking whether to disable ccache for the input | |
98 | // file. | |
20438ee6 JR |
99 | const char k_ccache_disable_token[] = { |
100 | 'c', 'c', 'a', 'c', 'h', 'e', ':', 'd', 'i', 's', 'a', 'b', 'l', 'e', '\0'}; | |
b3c3e799 | 101 | |
e3bba660 AL |
102 | namespace { |
103 | ||
39606070 | 104 | // Return tl::unexpected<Failure> if ccache did not succeed in getting |
85f5f8af JR |
105 | // or putting a result in the cache. If `exit_code` is set, ccache will just |
106 | // exit with that code directly, otherwise execute the real compiler and exit | |
107 | // with its exit code. Statistics counters will also be incremented. | |
108 | class Failure | |
e3bba660 AL |
109 | { |
110 | public: | |
85f5f8af JR |
111 | Failure(Statistic statistic); |
112 | Failure(std::initializer_list<Statistic> statistics); | |
e3bba660 | 113 | |
85f5f8af | 114 | const core::StatisticsCounters& counters() const; |
eea89d4a | 115 | std::optional<int> exit_code() const; |
85f5f8af | 116 | void set_exit_code(int exit_code); |
e3bba660 AL |
117 | |
118 | private: | |
85f5f8af | 119 | core::StatisticsCounters m_counters; |
eea89d4a | 120 | std::optional<int> m_exit_code; |
e3bba660 AL |
121 | }; |
122 | ||
85f5f8af | 123 | inline Failure::Failure(const Statistic statistic) : m_counters({statistic}) |
e3bba660 AL |
124 | { |
125 | } | |
126 | ||
85f5f8af JR |
127 | inline Failure::Failure(const std::initializer_list<Statistic> statistics) |
128 | : m_counters(statistics) | |
129 | { | |
130 | } | |
131 | ||
132 | inline const core::StatisticsCounters& | |
133 | Failure::counters() const | |
134 | { | |
135 | return m_counters; | |
136 | } | |
137 | ||
eea89d4a | 138 | inline std::optional<int> |
e3bba660 AL |
139 | Failure::exit_code() const |
140 | { | |
141 | return m_exit_code; | |
142 | } | |
143 | ||
85f5f8af JR |
144 | inline void |
145 | Failure::set_exit_code(const int exit_code) | |
e3bba660 | 146 | { |
85f5f8af | 147 | m_exit_code = exit_code; |
e3bba660 AL |
148 | } |
149 | ||
150 | } // namespace | |
151 | ||
b3c3e799 JR |
152 | static bool |
153 | should_disable_ccache_for_input_file(const std::string& path) | |
154 | { | |
155 | auto content = | |
156 | util::read_file_part<std::string>(path, 0, k_ccache_disable_search_limit); | |
157 | return content && content->find(k_ccache_disable_token) != std::string::npos; | |
158 | } | |
159 | ||
b453d43a | 160 | static void |
f65e8766 | 161 | add_prefix(const Context& ctx, Args& args, const std::string& prefix_command) |
b453d43a | 162 | { |
f65e8766 | 163 | if (prefix_command.empty()) { |
f5795cdb JR |
164 | return; |
165 | } | |
166 | ||
ad52c600 | 167 | Args prefix; |
32970d78 | 168 | for (const auto& word : util::split_into_strings(prefix_command, " ")) { |
451d92c8 | 169 | std::string path = find_executable(ctx, word, ctx.orig_args[0]); |
119a7ed0 | 170 | if (path.empty()) { |
a4ab84f9 | 171 | throw core::Fatal(FMT("{}: {}", word, strerror(errno))); |
f5795cdb JR |
172 | } |
173 | ||
04984326 | 174 | prefix.push_back(path); |
f5795cdb | 175 | } |
f5795cdb | 176 | |
60904e7b | 177 | LOG("Using command-line prefix {}", prefix_command); |
ad52c600 | 178 | for (size_t i = prefix.size(); i != 0; i--) { |
ca403520 | 179 | args.push_front(prefix[i - 1]); |
f5795cdb | 180 | } |
b453d43a MS |
181 | } |
182 | ||
a181d44d | 183 | static std::string |
d96b24a9 JR |
184 | prepare_debug_path(const fs::path& cwd, |
185 | const fs::path& debug_dir, | |
653dde81 | 186 | const util::TimePoint& time_of_invocation, |
d96b24a9 | 187 | const fs::path& output_obj, |
60005c83 | 188 | std::string_view suffix) |
a181d44d | 189 | { |
d96b24a9 JR |
190 | auto prefix = |
191 | debug_dir.empty() | |
192 | ? output_obj | |
193 | : (debug_dir | |
194 | / (output_obj.is_absolute() | |
195 | ? output_obj | |
196 | : fs::weakly_canonical(cwd / output_obj).value_or(output_obj)) | |
197 | .relative_path()); | |
fb7ce2f8 | 198 | |
1adaf8ef JR |
199 | // Ignore any error from fs::create_directories since we can't handle an error |
200 | // in another way in this context. The caller takes care of logging when | |
201 | // trying to open the path for writing. | |
d96b24a9 | 202 | fs::create_directories(prefix.parent_path()); |
7c980edc JR |
203 | |
204 | char timestamp[100]; | |
f657af6a | 205 | const auto tm = util::localtime(time_of_invocation); |
7c980edc JR |
206 | if (tm) { |
207 | strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &*tm); | |
208 | } else { | |
209 | snprintf(timestamp, | |
210 | sizeof(timestamp), | |
211 | "%llu", | |
653dde81 | 212 | static_cast<long long unsigned int>(time_of_invocation.sec())); |
7c980edc JR |
213 | } |
214 | return FMT("{}.{}_{:06}.ccache-{}", | |
d96b24a9 | 215 | prefix.string(), |
7c980edc | 216 | timestamp, |
653dde81 | 217 | time_of_invocation.nsec_decimal_part() / 1000, |
7c980edc | 218 | suffix); |
a181d44d JR |
219 | } |
220 | ||
f60a1d9a | 221 | static void |
91aa7075 | 222 | init_hash_debug(Context& ctx, |
a5021452 | 223 | Hash& hash, |
f5795cdb | 224 | char type, |
60005c83 | 225 | std::string_view section_name, |
f5795cdb | 226 | FILE* debug_text_file) |
4c1a92a0 | 227 | { |
f8faf9b4 | 228 | if (!ctx.config.debug() || ctx.config.debug_level() < 2) { |
f5795cdb JR |
229 | return; |
230 | } | |
231 | ||
d96b24a9 JR |
232 | const auto path = prepare_debug_path(ctx.apparent_cwd, |
233 | ctx.config.debug_dir(), | |
7c980edc JR |
234 | ctx.time_of_invocation, |
235 | ctx.args_info.output_obj, | |
236 | FMT("input-{}", type)); | |
ea9a264e | 237 | util::FileStream debug_binary_file(path, "wb"); |
f5795cdb | 238 | if (debug_binary_file) { |
a5021452 | 239 | hash.enable_debug(section_name, debug_binary_file.get(), debug_text_file); |
91aa7075 | 240 | ctx.hash_debug_files.push_back(std::move(debug_binary_file)); |
f5795cdb | 241 | } else { |
60904e7b | 242 | LOG("Failed to open {}: {}", path, strerror(errno)); |
f5795cdb | 243 | } |
4c1a92a0 JR |
244 | } |
245 | ||
4b0be71f | 246 | #ifndef _WIN32 |
46e9192c JR |
247 | std::string |
248 | follow_symlinks(const fs::path& path) | |
249 | { | |
4b0be71f | 250 | // Follow symlinks to the real compiler to learn its name. We're not using |
3cacab8d | 251 | // util::real_path in order to save some unnecessary stat calls. |
46e9192c | 252 | fs::path p = path; |
4b0be71f | 253 | while (true) { |
46e9192c | 254 | auto symlink_target = fs::read_symlink(p); |
709701ac | 255 | if (!symlink_target) { |
fe3aa934 | 256 | // Not a symlink. |
4b0be71f JR |
257 | break; |
258 | } | |
709701ac | 259 | if (symlink_target->is_absolute()) { |
46e9192c | 260 | p = *symlink_target; |
4b0be71f | 261 | } else { |
46e9192c | 262 | p = p.parent_path() / *symlink_target; |
4b0be71f JR |
263 | } |
264 | } | |
46e9192c JR |
265 | return p; |
266 | } | |
4b0be71f JR |
267 | #endif |
268 | ||
46e9192c JR |
269 | static CompilerType |
270 | do_guess_compiler(const fs::path& path) | |
271 | { | |
e2e9eea7 | 272 | const auto name = |
46e9192c JR |
273 | util::to_lowercase(path.filename().replace_extension("").string()); |
274 | if (name.find("clang-cl") != std::string_view::npos) { | |
caa5bcbb | 275 | return CompilerType::clang_cl; |
60005c83 | 276 | } else if (name.find("clang") != std::string_view::npos) { |
5f3de1d8 | 277 | return CompilerType::clang; |
60005c83 JR |
278 | } else if (name.find("gcc") != std::string_view::npos |
279 | || name.find("g++") != std::string_view::npos) { | |
5f3de1d8 | 280 | return CompilerType::gcc; |
60005c83 | 281 | } else if (name.find("nvcc") != std::string_view::npos) { |
5f3de1d8 | 282 | return CompilerType::nvcc; |
6c2f9ee7 DR |
283 | } else if (name == "icl") { |
284 | return CompilerType::icl; | |
e2e9eea7 | 285 | } else if (name == "cl") { |
ceecf43d | 286 | return CompilerType::msvc; |
4b0be71f | 287 | } else { |
e2ab1352 | 288 | return CompilerType::other; |
f5795cdb | 289 | } |
5f6b6ffd JR |
290 | } |
291 | ||
46e9192c JR |
292 | CompilerType |
293 | guess_compiler(const fs::path& path) | |
294 | { | |
295 | CompilerType type = do_guess_compiler(path); | |
296 | #ifdef _WIN32 | |
297 | return type; | |
298 | #else | |
299 | if (type == CompilerType::other) { | |
300 | return do_guess_compiler(follow_symlinks(path)); | |
301 | } else { | |
302 | return type; | |
303 | } | |
304 | #endif | |
305 | } | |
306 | ||
d75dbe26 JR |
307 | // This function hashes an include file and stores the path and hash in |
308 | // ctx.included_files. If the include file is a PCH, cpp_hash is also updated. | |
309 | [[nodiscard]] tl::expected<void, Failure> | |
310 | remember_include_file(Context& ctx, | |
311 | std::string path, | |
312 | Hash& cpp_hash, | |
313 | bool system, | |
314 | Hash* depend_mode_hash) | |
e53af1a6 | 315 | { |
8a58daf2 | 316 | if (path.length() >= 2 && path[0] == '<' && path[path.length() - 1] == '>') { |
f5795cdb | 317 | // Typically <built-in> or <command-line>. |
d75dbe26 | 318 | return {}; |
f5795cdb JR |
319 | } |
320 | ||
236a297a | 321 | if (path == ctx.args_info.normalized_input_file) { |
f5795cdb | 322 | // Don't remember the input file. |
d75dbe26 | 323 | return {}; |
f5795cdb JR |
324 | } |
325 | ||
c1735710 | 326 | if (system |
d75dbe26 | 327 | && ctx.config.sloppiness().contains(core::Sloppy::system_headers)) { |
f5795cdb | 328 | // Don't remember this system header. |
d75dbe26 | 329 | return {}; |
f5795cdb JR |
330 | } |
331 | ||
380beacf | 332 | // Canonicalize path for comparison; Clang uses ./header.h. |
ec94a399 | 333 | if (util::starts_with(path, "./")) { |
380beacf JR |
334 | path.erase(0, 2); |
335 | } | |
336 | ||
971f1183 BL |
337 | if (ctx.included_files.find(path) != ctx.included_files.end()) { |
338 | // Already known include file. | |
d75dbe26 | 339 | return {}; |
971f1183 BL |
340 | } |
341 | ||
37e27eb1 | 342 | #ifdef _WIN32 |
f5795cdb JR |
343 | { |
344 | // stat fails on directories on win32. | |
8a58daf2 | 345 | DWORD attributes = GetFileAttributes(path.c_str()); |
f5795cdb JR |
346 | if (attributes != INVALID_FILE_ATTRIBUTES |
347 | && attributes & FILE_ATTRIBUTE_DIRECTORY) { | |
d75dbe26 | 348 | return {}; |
f5795cdb JR |
349 | } |
350 | } | |
37e27eb1 RP |
351 | #endif |
352 | ||
c86a4628 JR |
353 | DirEntry dir_entry(path, DirEntry::LogOnError::yes); |
354 | if (!dir_entry.exists()) { | |
33d390bb JR |
355 | if (ctx.config.direct_mode()) { |
356 | LOG("Include file {} does not exist, disabling direct mode", | |
357 | dir_entry.path()); | |
358 | ctx.config.set_direct_mode(false); | |
359 | } | |
360 | return {}; | |
f5795cdb | 361 | } |
c86a4628 | 362 | if (dir_entry.is_directory()) { |
f5795cdb | 363 | // Ignore directory, typically $PWD. |
d75dbe26 | 364 | return {}; |
f5795cdb | 365 | } |
c86a4628 | 366 | if (!dir_entry.is_regular_file()) { |
f5795cdb | 367 | // Device, pipe, socket or other strange creature. |
60904e7b | 368 | LOG("Non-regular include file {}", path); |
d75dbe26 | 369 | return tl::unexpected(Statistic::bad_input_file); |
f5795cdb JR |
370 | } |
371 | ||
380beacf | 372 | for (const auto& ignore_header_path : ctx.ignore_header_paths) { |
587b0244 | 373 | if (file_path_matches_dir_prefix_or_file(ignore_header_path, path)) { |
d75dbe26 | 374 | return {}; |
f5795cdb JR |
375 | } |
376 | } | |
377 | ||
f5795cdb | 378 | // Let's hash the include file content. |
0e4e4b63 | 379 | Hash::Digest file_digest; |
f5795cdb | 380 | |
d75dbe26 | 381 | const bool is_pch = is_precompiled_header(path); |
f5795cdb | 382 | if (is_pch) { |
e1260404 | 383 | if (ctx.args_info.included_pch_file.empty()) { |
60904e7b | 384 | LOG("Detected use of precompiled header: {}", path); |
f5795cdb JR |
385 | } |
386 | bool using_pch_sum = false; | |
8b0f2eb6 | 387 | if (ctx.config.pch_external_checksum()) { |
f5795cdb JR |
388 | // hash pch.sum instead of pch when it exists |
389 | // to prevent hashing a very large .pch file every time | |
83b50b30 | 390 | std::string pch_sum_path = FMT("{}.sum", path); |
c86a4628 | 391 | if (DirEntry(pch_sum_path, DirEntry::LogOnError::yes).is_regular_file()) { |
8a58daf2 | 392 | path = std::move(pch_sum_path); |
f5795cdb | 393 | using_pch_sum = true; |
60904e7b | 394 | LOG("Using pch.sum file {}", path); |
f5795cdb | 395 | } |
f5795cdb JR |
396 | } |
397 | ||
561be208 | 398 | if (!hash_binary_file(ctx, file_digest, path)) { |
d75dbe26 | 399 | return tl::unexpected(Statistic::bad_input_file); |
f5795cdb | 400 | } |
a5021452 | 401 | cpp_hash.hash_delimiter(using_pch_sum ? "pch_sum_hash" : "pch_hash"); |
0e4e4b63 | 402 | cpp_hash.hash(util::format_digest(file_digest)); |
f5795cdb JR |
403 | } |
404 | ||
8b0f2eb6 | 405 | if (ctx.config.direct_mode()) { |
f5795cdb | 406 | if (!is_pch) { // else: the file has already been hashed. |
38ab9d38 | 407 | auto ret = hash_source_code_file(ctx, file_digest, path); |
d75dbe26 JR |
408 | if (ret.contains(HashSourceCode::error)) { |
409 | return tl::unexpected(Statistic::bad_input_file); | |
410 | } | |
411 | if (ret.contains(HashSourceCode::found_time)) { | |
412 | LOG_RAW("Disabling direct mode"); | |
413 | ctx.config.set_direct_mode(false); | |
f5795cdb JR |
414 | } |
415 | } | |
416 | ||
f5795cdb | 417 | if (depend_mode_hash) { |
a5021452 | 418 | depend_mode_hash->hash_delimiter("include"); |
0e4e4b63 | 419 | depend_mode_hash->hash(util::format_digest(file_digest)); |
f5795cdb JR |
420 | } |
421 | } | |
422 | ||
d75dbe26 | 423 | ctx.included_files.emplace(path, file_digest); |
5796e9b5 | 424 | |
d75dbe26 | 425 | return {}; |
20187332 AT |
426 | } |
427 | ||
34d666d0 | 428 | static void |
81115751 | 429 | print_included_files(const Context& ctx, FILE* fp) |
34d666d0 | 430 | { |
4812396d JR |
431 | for (const auto& [path, digest] : ctx.included_files) { |
432 | PRINT(fp, "{}\n", path); | |
5d76845a | 433 | } |
34d666d0 AB |
434 | } |
435 | ||
48e33b2e JR |
436 | // This function reads and hashes a file. While doing this, it also does these |
437 | // things: | |
438 | // | |
439 | // - Makes include file paths for which the base directory is a prefix relative | |
440 | // when computing the hash sum. | |
0246f450 | 441 | // - Stores the paths and hashes of included files in ctx.included_files. |
39606070 | 442 | static tl::expected<void, Failure> |
2044fea8 | 443 | process_preprocessed_file(Context& ctx, Hash& hash, const std::string& path) |
e53af1a6 | 444 | { |
3c55dbff JR |
445 | auto data = util::read_file<std::string>(path); |
446 | if (!data) { | |
c5e1f88e | 447 | LOG("Failed to read {}: {}", path, data.error()); |
39606070 | 448 | return tl::unexpected(Statistic::internal_error); |
f5795cdb JR |
449 | } |
450 | ||
147ea804 JR |
451 | std::unordered_map<std::string, std::string> relative_inc_path_cache; |
452 | ||
f5795cdb | 453 | // Bytes between p and q are pending to be hashed. |
3c55dbff JR |
454 | char* q = &(*data)[0]; |
455 | const char* p = q; | |
456 | const char* end = p + data->length(); | |
f5795cdb JR |
457 | |
458 | // There must be at least 7 characters (# 1 "x") left to potentially find an | |
459 | // include file path. | |
460 | while (q < end - 7) { | |
60005c83 | 461 | static const std::string_view pragma_gcc_pch_preprocess = |
68fa4011 | 462 | "pragma GCC pch_preprocess "; |
60005c83 | 463 | static const std::string_view hash_31_command_line_newline = |
68fa4011 | 464 | "# 31 \"<command-line>\"\n"; |
60005c83 | 465 | static const std::string_view hash_32_command_line_2_newline = |
68fa4011 | 466 | "# 32 \"<command-line>\" 2\n"; |
bc97d547 JR |
467 | // Note: Intentionally not using the string form to avoid false positive |
468 | // match by ccache itself. | |
7ee6030b | 469 | static const char incbin_directive[] = {'.', 'i', 'n', 'c', 'b', 'i', 'n'}; |
68fa4011 | 470 | |
f5795cdb JR |
471 | // Check if we look at a line containing the file name of an included file. |
472 | // At least the following formats exist (where N is a positive integer): | |
473 | // | |
474 | // GCC: | |
475 | // | |
476 | // # N "file" | |
477 | // # N "file" N | |
478 | // #pragma GCC pch_preprocess "file" | |
479 | // | |
480 | // HP's compiler: | |
481 | // | |
482 | // #line N "file" | |
483 | // | |
484 | // AIX's compiler: | |
485 | // | |
486 | // #line N "file" | |
487 | // #line N | |
488 | // | |
489 | // Note that there may be other lines starting with '#' left after | |
490 | // preprocessing as well, for instance "# pragma". | |
491 | if (q[0] == '#' | |
492 | // GCC: | |
493 | && ((q[1] == ' ' && q[2] >= '0' && q[2] <= '9') | |
494 | // GCC precompiled header: | |
ec94a399 | 495 | || util::starts_with(&q[1], pragma_gcc_pch_preprocess) |
f5795cdb JR |
496 | // HP/AIX: |
497 | || (q[1] == 'l' && q[2] == 'i' && q[3] == 'n' && q[4] == 'e' | |
498 | && q[5] == ' ')) | |
3c55dbff | 499 | && (q == data->data() || q[-1] == '\n')) { |
f5795cdb JR |
500 | // Workarounds for preprocessor linemarker bugs in GCC version 6. |
501 | if (q[2] == '3') { | |
ec94a399 | 502 | if (util::starts_with(q, hash_31_command_line_newline)) { |
f5795cdb JR |
503 | // Bogus extra line with #31, after the regular #1: Ignore the whole |
504 | // line, and continue parsing. | |
a5021452 | 505 | hash.hash(p, q - p); |
f5795cdb JR |
506 | while (q < end && *q != '\n') { |
507 | q++; | |
508 | } | |
509 | q++; | |
510 | p = q; | |
511 | continue; | |
ec94a399 | 512 | } else if (util::starts_with(q, hash_32_command_line_2_newline)) { |
f5795cdb JR |
513 | // Bogus wrong line with #32, instead of regular #1: Replace the line |
514 | // number with the usual one. | |
a5021452 | 515 | hash.hash(p, q - p); |
f5795cdb JR |
516 | q += 1; |
517 | q[0] = '#'; | |
518 | q[1] = ' '; | |
519 | q[2] = '1'; | |
520 | p = q; | |
521 | } | |
522 | } | |
523 | ||
524 | while (q < end && *q != '"' && *q != '\n') { | |
525 | q++; | |
526 | } | |
527 | if (q < end && *q == '\n') { | |
528 | // A newline before the quotation mark -> no match. | |
529 | continue; | |
530 | } | |
531 | q++; | |
532 | if (q >= end) { | |
60904e7b | 533 | LOG_RAW("Failed to parse included file path"); |
39606070 | 534 | return tl::unexpected(Statistic::internal_error); |
f5795cdb JR |
535 | } |
536 | // q points to the beginning of an include file path | |
a5021452 | 537 | hash.hash(p, q - p); |
f5795cdb JR |
538 | p = q; |
539 | while (q < end && *q != '"') { | |
540 | q++; | |
541 | } | |
eb57a6be OS |
542 | if (p == q) { |
543 | // Skip empty file name. | |
544 | continue; | |
545 | } | |
f5795cdb JR |
546 | // Look for preprocessor flags, after the "filename". |
547 | bool system = false; | |
e9915d53 | 548 | const char* r = q + 1; |
f5795cdb JR |
549 | while (r < end && *r != '\n') { |
550 | if (*r == '3') { // System header. | |
551 | system = true; | |
552 | } | |
553 | r++; | |
554 | } | |
147ea804 | 555 | |
f5795cdb | 556 | // p and q span the include file path. |
35dab842 | 557 | std::string inc_path(p, q - p); |
147ea804 JR |
558 | auto it = relative_inc_path_cache.find(inc_path); |
559 | if (it == relative_inc_path_cache.end()) { | |
560 | auto rel_inc_path = Util::make_relative_path( | |
561 | ctx, Util::normalize_concrete_absolute_path(inc_path)); | |
562 | relative_inc_path_cache.emplace(inc_path, rel_inc_path); | |
563 | inc_path = std::move(rel_inc_path); | |
564 | } else { | |
565 | inc_path = it->second; | |
566 | } | |
f5795cdb | 567 | |
236a297a | 568 | if ((inc_path != ctx.apparent_cwd) || ctx.config.hash_dir()) { |
a5021452 | 569 | hash.hash(inc_path); |
f5795cdb JR |
570 | } |
571 | ||
d75dbe26 | 572 | TRY(remember_include_file(ctx, inc_path, hash, system, nullptr)); |
f5795cdb | 573 | p = q; // Everything of interest between p and q has been hashed now. |
7ee6030b JR |
574 | } else if (strncmp(q, incbin_directive, sizeof(incbin_directive)) == 0 |
575 | && ((q[7] == ' ' | |
576 | && (q[8] == '"' || (q[8] == '\\' && q[9] == '"'))) | |
577 | || q[7] == '"')) { | |
f5795cdb JR |
578 | // An assembler .inc bin (without the space) statement, which could be |
579 | // part of inline assembly, refers to an external file. If the file | |
580 | // changes, the hash should change as well, but finding out what file to | |
581 | // hash is too hard for ccache, so just bail out. | |
60904e7b | 582 | LOG_RAW( |
7ee6030b | 583 | "Found potential unsupported .inc" |
f5795cdb | 584 | "bin directive in source code"); |
39606070 | 585 | return tl::unexpected(Failure(Statistic::unsupported_code_directive)); |
a9b30fd3 | 586 | } else if (strncmp(q, "___________", 10) == 0 |
3c55dbff | 587 | && (q == data->data() || q[-1] == '\n')) { |
f5795cdb JR |
588 | // Unfortunately the distcc-pump wrapper outputs standard output lines: |
589 | // __________Using distcc-pump from /usr/bin | |
590 | // __________Using # distcc servers in pump mode | |
591 | // __________Shutting down distcc-pump include server | |
5a55014f | 592 | hash.hash(p, q - p); |
f5795cdb JR |
593 | while (q < end && *q != '\n') { |
594 | q++; | |
595 | } | |
596 | if (*q == '\n') { | |
597 | q++; | |
598 | } | |
599 | p = q; | |
600 | continue; | |
601 | } else { | |
602 | q++; | |
603 | } | |
604 | } | |
605 | ||
a5021452 | 606 | hash.hash(p, (end - p)); |
f5795cdb JR |
607 | |
608 | // Explicitly check the .gch/.pch/.pth file as Clang does not include any | |
609 | // mention of it in the preprocessed output. | |
e1260404 JR |
610 | if (!ctx.args_info.included_pch_file.empty()) { |
611 | std::string pch_path = | |
612 | Util::make_relative_path(ctx, ctx.args_info.included_pch_file); | |
a5021452 | 613 | hash.hash(pch_path); |
d75dbe26 | 614 | TRY(remember_include_file(ctx, pch_path, hash, false, nullptr)); |
f5795cdb JR |
615 | } |
616 | ||
617 | bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); | |
618 | if (debug_included) { | |
35bcc8dc | 619 | print_included_files(ctx, stdout); |
f5795cdb JR |
620 | } |
621 | ||
85f5f8af | 622 | return {}; |
e53af1a6 | 623 | } |
20187332 | 624 | |
d39fe87b JR |
625 | // Extract the used includes from the dependency file. Note that we cannot |
626 | // distinguish system headers from other includes here. | |
d75dbe26 | 627 | static tl::expected<Hash::Digest, Failure> |
c7c0837a | 628 | result_key_from_depfile(Context& ctx, Hash& hash) |
d39fe87b | 629 | { |
63e2517d JR |
630 | // Make sure that result hash will always be different from the manifest hash |
631 | // since there otherwise may a storage key collision (in case the dependency | |
632 | // file is empty). | |
633 | hash.hash_delimiter("result"); | |
634 | ||
3c55dbff JR |
635 | const auto file_content = |
636 | util::read_file<std::string>(ctx.args_info.output_dep); | |
637 | if (!file_content) { | |
c5e1f88e | 638 | LOG("Failed to read dependency file {}: {}", |
3c55dbff JR |
639 | ctx.args_info.output_dep, |
640 | file_content.error()); | |
d75dbe26 | 641 | return tl::unexpected(Statistic::bad_input_file); |
f5795cdb JR |
642 | } |
643 | ||
3c55dbff | 644 | for (std::string_view token : Depfile::tokenize(*file_content)) { |
60005c83 | 645 | if (util::ends_with(token, ":")) { |
aaadd43d TO |
646 | continue; |
647 | } | |
70dcd287 | 648 | std::string path = Util::make_relative_path(ctx, token); |
d75dbe26 | 649 | TRY(remember_include_file(ctx, path, hash, false, &hash)); |
f5795cdb JR |
650 | } |
651 | ||
f5795cdb JR |
652 | // Explicitly check the .gch/.pch/.pth file as it may not be mentioned in the |
653 | // dependencies output. | |
e1260404 JR |
654 | if (!ctx.args_info.included_pch_file.empty()) { |
655 | std::string pch_path = | |
656 | Util::make_relative_path(ctx, ctx.args_info.included_pch_file); | |
a5021452 | 657 | hash.hash(pch_path); |
d75dbe26 | 658 | TRY(remember_include_file(ctx, pch_path, hash, false, nullptr)); |
f5795cdb JR |
659 | } |
660 | ||
661 | bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); | |
662 | if (debug_included) { | |
35bcc8dc | 663 | print_included_files(ctx, stdout); |
f5795cdb JR |
664 | } |
665 | ||
a5021452 | 666 | return hash.digest(); |
cf48b7df | 667 | } |
64fc42ca JR |
668 | |
669 | struct GetTmpFdResult | |
670 | { | |
54cbb06a | 671 | util::Fd fd; |
230ae9ca | 672 | fs::path path; |
64fc42ca JR |
673 | }; |
674 | ||
675 | static GetTmpFdResult | |
676 | get_tmp_fd(Context& ctx, | |
677 | const std::string_view description, | |
678 | const bool capture_output) | |
679 | { | |
680 | if (capture_output) { | |
1da50f6f JR |
681 | auto tmp_stdout = |
682 | util::value_or_throw<core::Fatal>(util::TemporaryFile::create( | |
683 | FMT("{}/{}", ctx.config.temporary_dir(), description))); | |
230ae9ca | 684 | ctx.register_pending_tmp_file(tmp_stdout.path.string()); |
64fc42ca JR |
685 | return {std::move(tmp_stdout.fd), std::move(tmp_stdout.path)}; |
686 | } else { | |
687 | const auto dev_null_path = util::get_dev_null_path(); | |
54cbb06a | 688 | return {util::Fd(open(dev_null_path, O_WRONLY | O_BINARY)), dev_null_path}; |
64fc42ca JR |
689 | } |
690 | } | |
691 | ||
692 | struct DoExecuteResult | |
693 | { | |
694 | int exit_status; | |
e9ddf680 JR |
695 | util::Bytes stdout_data; |
696 | util::Bytes stderr_data; | |
64fc42ca JR |
697 | }; |
698 | ||
7d679310 | 699 | // Extract the used includes from /showIncludes output in stdout. Note that we |
b1348e5f | 700 | // cannot distinguish system headers from other includes here. |
d75dbe26 | 701 | static tl::expected<Hash::Digest, Failure> |
b1348e5f OS |
702 | result_key_from_includes(Context& ctx, Hash& hash, std::string_view stdout_data) |
703 | { | |
a68cae90 | 704 | for (std::string_view include : core::MsvcShowIncludesOutput::get_includes( |
b1348e5f | 705 | stdout_data, ctx.config.msvc_dep_prefix())) { |
a68cae90 JR |
706 | const std::string path = Util::make_relative_path( |
707 | ctx, Util::normalize_abstract_absolute_path(include)); | |
d75dbe26 | 708 | TRY(remember_include_file(ctx, path, hash, false, &hash)); |
b1348e5f OS |
709 | } |
710 | ||
711 | // Explicitly check the .pch file as it is not mentioned in the | |
712 | // includes output. | |
713 | if (!ctx.args_info.included_pch_file.empty()) { | |
714 | std::string pch_path = | |
715 | Util::make_relative_path(ctx, ctx.args_info.included_pch_file); | |
716 | hash.hash(pch_path); | |
d75dbe26 | 717 | TRY(remember_include_file(ctx, pch_path, hash, false, nullptr)); |
b1348e5f OS |
718 | } |
719 | ||
720 | const bool debug_included = getenv("CCACHE_DEBUG_INCLUDED"); | |
721 | if (debug_included) { | |
722 | print_included_files(ctx, stdout); | |
723 | } | |
724 | ||
725 | return hash.digest(); | |
726 | } | |
727 | ||
1c6ccf18 MW |
728 | // Execute the compiler/preprocessor, with logic to retry without requesting |
729 | // colored diagnostics messages if that fails. | |
39606070 | 730 | static tl::expected<DoExecuteResult, Failure> |
64fc42ca | 731 | do_execute(Context& ctx, Args& args, const bool capture_stdout = true) |
1c6ccf18 | 732 | { |
3c98cd28 | 733 | util::UmaskScope umask_scope(ctx.original_umask); |
106a1d22 | 734 | |
755359d9 JR |
735 | if (ctx.diagnostics_color_failed) { |
736 | DEBUG_ASSERT(ctx.config.compiler_type() == CompilerType::gcc); | |
50e8d229 | 737 | args.erase_last("-fdiagnostics-color"); |
1c6ccf18 | 738 | } |
64fc42ca JR |
739 | |
740 | auto tmp_stdout = get_tmp_fd(ctx, "stdout", capture_stdout); | |
741 | auto tmp_stderr = get_tmp_fd(ctx, "stderr", true); | |
742 | ||
ea433578 AS |
743 | int status = execute(ctx, |
744 | args.to_argv().data(), | |
53b35a42 | 745 | std::move(tmp_stdout.fd), |
ea433578 | 746 | std::move(tmp_stderr.fd)); |
1c6ccf18 | 747 | if (status != 0 && !ctx.diagnostics_color_failed |
e2ab1352 | 748 | && ctx.config.compiler_type() == CompilerType::gcc) { |
3c55dbff JR |
749 | const auto errors = util::read_file<std::string>(tmp_stderr.path); |
750 | if (errors && errors->find("fdiagnostics-color") != std::string::npos) { | |
755359d9 JR |
751 | // GCC versions older than 4.9 don't understand -fdiagnostics-color, and |
752 | // non-GCC compilers misclassified as CompilerType::gcc might not do it | |
753 | // either. We assume that if the error message contains | |
754 | // "fdiagnostics-color" then the compilation failed due to | |
755 | // -fdiagnostics-color being unsupported and we then retry without the | |
756 | // flag. (Note that there intentionally is no leading dash in | |
757 | // "fdiagnostics-color" since some compilers don't include the dash in the | |
758 | // error message.) | |
60904e7b | 759 | LOG_RAW("-fdiagnostics-color is unsupported; trying again without it"); |
53b35a42 | 760 | |
1c6ccf18 | 761 | ctx.diagnostics_color_failed = true; |
64fc42ca | 762 | return do_execute(ctx, args, capture_stdout); |
1c6ccf18 MW |
763 | } |
764 | } | |
64fc42ca | 765 | |
e9ddf680 | 766 | util::Bytes stdout_data; |
3c55dbff | 767 | if (capture_stdout) { |
f8b81c2b | 768 | auto stdout_data_result = util::read_file<util::Bytes>(tmp_stdout.path); |
3c55dbff | 769 | if (!stdout_data_result) { |
c5e1f88e JR |
770 | LOG("Failed to read {} (cleanup in progress?): {}", |
771 | tmp_stdout.path, | |
772 | stdout_data_result.error()); | |
39606070 | 773 | return tl::unexpected(Statistic::missing_cache_file); |
3c55dbff | 774 | } |
e9ddf680 | 775 | stdout_data = *stdout_data_result; |
3c55dbff JR |
776 | } |
777 | ||
f8b81c2b | 778 | auto stderr_data_result = util::read_file<util::Bytes>(tmp_stderr.path); |
3c55dbff | 779 | if (!stderr_data_result) { |
c5e1f88e JR |
780 | LOG("Failed to read {} (cleanup in progress?): {}", |
781 | tmp_stderr.path, | |
782 | stderr_data_result.error()); | |
39606070 | 783 | return tl::unexpected(Statistic::missing_cache_file); |
64fc42ca | 784 | } |
3c55dbff | 785 | |
e9ddf680 | 786 | return DoExecuteResult{status, stdout_data, *stderr_data_result}; |
1c6ccf18 MW |
787 | } |
788 | ||
d663cb51 JR |
789 | static void |
790 | read_manifest(Context& ctx, nonstd::span<const uint8_t> cache_entry_data) | |
9882ac2d | 791 | { |
a03f14b7 | 792 | try { |
a03f14b7 JR |
793 | core::CacheEntry cache_entry(cache_entry_data); |
794 | cache_entry.verify_checksum(); | |
d663cb51 | 795 | ctx.manifest.read(cache_entry.payload()); |
a03f14b7 | 796 | } catch (const core::Error& e) { |
d663cb51 | 797 | LOG("Error reading manifest: {}", e.what()); |
9882ac2d | 798 | } |
9882ac2d JR |
799 | } |
800 | ||
801 | static void | |
d663cb51 | 802 | update_manifest(Context& ctx, |
0e4e4b63 JR |
803 | const Hash::Digest& manifest_key, |
804 | const Hash::Digest& result_key) | |
38301c0d | 805 | { |
c7c0837a | 806 | if (ctx.config.read_only() || ctx.config.read_only_direct()) { |
f5795cdb JR |
807 | return; |
808 | } | |
809 | ||
792b27b8 JR |
810 | ASSERT(ctx.config.direct_mode()); |
811 | ||
1897fd5e | 812 | MTR_SCOPE("manifest", "manifest_put"); |
fd8474e3 | 813 | |
3ec65766 | 814 | // ctime() may be 0, so we have to check time_of_invocation against |
3fcfd751 JR |
815 | // MAX(mtime, ctime). |
816 | // | |
817 | // ccache only reads mtime/ctime if file_stat_matches sloppiness is enabled, | |
818 | // so mtimes/ctimes are stored as a dummy value (-1) if not enabled. This | |
819 | // reduces the number of file_info entries for the common case. | |
fd8474e3 | 820 | const bool save_timestamp = |
03023ee7 | 821 | (ctx.config.sloppiness().contains(core::Sloppy::file_stat_matches)) |
fd8474e3 | 822 | || ctx.args_info.output_is_precompiled_header; |
947030fd | 823 | |
d663cb51 | 824 | const bool added = ctx.manifest.add_result( |
3fcfd751 | 825 | result_key, ctx.included_files, [&](const std::string& path) { |
c86a4628 | 826 | DirEntry de(path, DirEntry::LogOnError::yes); |
3fcfd751 JR |
827 | bool cache_time = |
828 | save_timestamp | |
3ec65766 | 829 | && ctx.time_of_invocation > std::max(de.mtime(), de.ctime()); |
3fcfd751 | 830 | return core::Manifest::FileStats{ |
c86a4628 JR |
831 | de.size(), |
832 | de.is_regular_file() && cache_time ? de.mtime() : util::TimePoint(), | |
833 | de.is_regular_file() && cache_time ? de.ctime() : util::TimePoint(), | |
3fcfd751 JR |
834 | }; |
835 | }); | |
d663cb51 | 836 | if (added) { |
0e4e4b63 | 837 | LOG("Added result key to manifest {}", util::format_digest(manifest_key)); |
d663cb51 JR |
838 | core::CacheEntry::Header header(ctx.config, core::CacheEntryType::manifest); |
839 | ctx.storage.put(manifest_key, | |
840 | core::CacheEntryType::manifest, | |
841 | core::CacheEntry::serialize(header, ctx.manifest)); | |
d663cb51 | 842 | } else { |
0e4e4b63 JR |
843 | LOG("Did not add result key to manifest {}", |
844 | util::format_digest(manifest_key)); | |
d663cb51 | 845 | } |
89e0e093 JR |
846 | } |
847 | ||
1da6d7ed JR |
848 | struct FindCoverageFileResult |
849 | { | |
850 | bool found; | |
851 | std::string path; | |
852 | bool mangled; | |
853 | }; | |
854 | ||
855 | static FindCoverageFileResult | |
856 | find_coverage_file(const Context& ctx) | |
857 | { | |
858 | // GCC 9+ writes coverage data for /dir/to/example.o to #dir#to#example.gcno | |
859 | // (in CWD) if -fprofile-dir=DIR is present (regardless of DIR) instead of the | |
860 | // traditional /dir/to/example.gcno. | |
861 | ||
99c881d1 JR |
862 | std::string mangled_form = core::Result::gcno_file_in_mangled_form(ctx); |
863 | std::string unmangled_form = core::Result::gcno_file_in_unmangled_form(ctx); | |
1da6d7ed | 864 | std::string found_file; |
c86a4628 | 865 | if (DirEntry(mangled_form).is_regular_file()) { |
60904e7b | 866 | LOG("Found coverage file {}", mangled_form); |
1da6d7ed JR |
867 | found_file = mangled_form; |
868 | } | |
c86a4628 | 869 | if (DirEntry(unmangled_form).is_regular_file()) { |
60904e7b | 870 | LOG("Found coverage file {}", unmangled_form); |
1da6d7ed | 871 | if (!found_file.empty()) { |
60904e7b | 872 | LOG_RAW("Found two coverage files, cannot continue"); |
1da6d7ed JR |
873 | return {}; |
874 | } | |
875 | found_file = unmangled_form; | |
876 | } | |
877 | if (found_file.empty()) { | |
60904e7b | 878 | LOG("No coverage file found (tried {} and {}), cannot continue", |
1da6d7ed JR |
879 | unmangled_form, |
880 | mangled_form); | |
881 | return {}; | |
882 | } | |
883 | return {true, found_file, found_file == mangled_form}; | |
884 | } | |
885 | ||
b53afeb5 | 886 | [[nodiscard]] static bool |
c7c0837a | 887 | write_result(Context& ctx, |
0e4e4b63 | 888 | const Hash::Digest& result_key, |
e9ddf680 JR |
889 | const util::Bytes& stdout_data, |
890 | const util::Bytes& stderr_data) | |
c7c0837a | 891 | { |
99c881d1 | 892 | core::Result::Serializer serializer(ctx.config); |
c7c0837a | 893 | |
ef19b0f0 | 894 | if (!stderr_data.empty()) { |
99c881d1 | 895 | serializer.add_data(core::Result::FileType::stderr_output, stderr_data); |
c7c0837a | 896 | } |
d981aee3 LL |
897 | // Write stdout only after stderr (better with MSVC), as ResultRetriever |
898 | // will later print process them in the order they are read. | |
ef19b0f0 | 899 | if (!stdout_data.empty()) { |
99c881d1 | 900 | serializer.add_data(core::Result::FileType::stdout_output, stdout_data); |
d981aee3 | 901 | } |
c86a4628 | 902 | if (ctx.args_info.expect_output_obj |
b53afeb5 JR |
903 | && !serializer.add_file(core::Result::FileType::object, |
904 | ctx.args_info.output_obj)) { | |
905 | LOG("Object file {} missing", ctx.args_info.output_obj); | |
906 | return false; | |
c7c0837a | 907 | } |
b53afeb5 JR |
908 | if (ctx.args_info.generating_dependencies |
909 | && !serializer.add_file(core::Result::FileType::dependency, | |
910 | ctx.args_info.output_dep)) { | |
911 | LOG("Dependency file {} missing", ctx.args_info.output_dep); | |
912 | return false; | |
c7c0837a JR |
913 | } |
914 | if (ctx.args_info.generating_coverage) { | |
915 | const auto coverage_file = find_coverage_file(ctx); | |
916 | if (!coverage_file.found) { | |
b53afeb5 JR |
917 | LOG_RAW("Coverage file not found"); |
918 | return false; | |
919 | } | |
920 | if (!serializer.add_file(coverage_file.mangled | |
921 | ? core::Result::FileType::coverage_mangled | |
922 | : core::Result::FileType::coverage_unmangled, | |
923 | coverage_file.path)) { | |
924 | LOG("Coverage file {} missing", coverage_file.path); | |
85f5f8af | 925 | return false; |
c7c0837a | 926 | } |
c7c0837a | 927 | } |
b53afeb5 JR |
928 | if (ctx.args_info.generating_stackusage |
929 | && !serializer.add_file(core::Result::FileType::stackusage, | |
930 | ctx.args_info.output_su)) { | |
931 | LOG("Stack usage file {} missing", ctx.args_info.output_su); | |
932 | return false; | |
c7c0837a | 933 | } |
b53afeb5 JR |
934 | if (ctx.args_info.generating_diagnostics |
935 | && !serializer.add_file(core::Result::FileType::diagnostic, | |
936 | ctx.args_info.output_dia)) { | |
937 | LOG("Diagnostics file {} missing", ctx.args_info.output_dia); | |
938 | return false; | |
c7c0837a | 939 | } |
b53afeb5 JR |
940 | if (ctx.args_info.seen_split_dwarf |
941 | // Only store .dwo file if it was created by the compiler (GCC and Clang | |
942 | // behave differently e.g. for "-gsplit-dwarf -g1"). | |
c86a4628 | 943 | && DirEntry(ctx.args_info.output_dwo).is_regular_file() |
b53afeb5 JR |
944 | && !serializer.add_file(core::Result::FileType::dwarf_object, |
945 | ctx.args_info.output_dwo)) { | |
946 | LOG("Split dwarf file {} missing", ctx.args_info.output_dwo); | |
947 | return false; | |
c7c0837a | 948 | } |
b53afeb5 JR |
949 | if (!ctx.args_info.output_al.empty() |
950 | && !serializer.add_file(core::Result::FileType::assembler_listing, | |
951 | ctx.args_info.output_al)) { | |
952 | LOG("Assembler listing file {} missing", ctx.args_info.output_al); | |
953 | return false; | |
b4dfd7b5 | 954 | } |
c7c0837a | 955 | |
a03f14b7 JR |
956 | core::CacheEntry::Header header(ctx.config, core::CacheEntryType::result); |
957 | const auto cache_entry_data = core::CacheEntry::serialize(header, serializer); | |
d5782080 JR |
958 | |
959 | if (!ctx.config.remote_only()) { | |
960 | const auto& raw_files = serializer.get_raw_files(); | |
961 | if (!raw_files.empty()) { | |
962 | ctx.storage.local.put_raw_files(result_key, raw_files); | |
963 | } | |
a03f14b7 | 964 | } |
d5782080 | 965 | |
d663cb51 | 966 | ctx.storage.put(result_key, core::CacheEntryType::result, cache_entry_data); |
99c881d1 | 967 | |
85f5f8af | 968 | return true; |
c7c0837a JR |
969 | } |
970 | ||
e9ddf680 JR |
971 | static util::Bytes |
972 | rewrite_stdout_from_compiler(const Context& ctx, util::Bytes&& stdout_data) | |
ef19b0f0 | 973 | { |
ff10b348 OS |
974 | using util::Tokenizer; |
975 | using Mode = Tokenizer::Mode; | |
976 | using IncludeDelimiter = Tokenizer::IncludeDelimiter; | |
2044fea8 | 977 | if (!stdout_data.empty()) { |
3ec0d4ec | 978 | util::Bytes new_stdout_data; |
e9ddf680 JR |
979 | for (const auto line : Tokenizer(util::to_string_view(stdout_data), |
980 | "\n", | |
981 | Mode::include_empty, | |
982 | IncludeDelimiter::yes)) { | |
ef19b0f0 | 983 | if (util::starts_with(line, "__________")) { |
8630a888 | 984 | core::send_to_console(ctx, line, STDOUT_FILENO); |
236a297a VV |
985 | } |
986 | // Ninja uses the lines with 'Note: including file: ' to determine the | |
987 | // used headers. Headers within basedir need to be changed into relative | |
988 | // paths because otherwise Ninja will use the abs path to original header | |
989 | // to check if a file needs to be recompiled. | |
990 | else if (ctx.config.compiler_type() == CompilerType::msvc | |
991 | && !ctx.config.base_dir().empty() | |
b1348e5f | 992 | && util::starts_with(line, ctx.config.msvc_dep_prefix())) { |
236a297a VV |
993 | std::string orig_line(line.data(), line.length()); |
994 | std::string abs_inc_path = | |
b1348e5f | 995 | util::replace_first(orig_line, ctx.config.msvc_dep_prefix(), ""); |
236a297a VV |
996 | abs_inc_path = util::strip_whitespace(abs_inc_path); |
997 | std::string rel_inc_path = Util::make_relative_path( | |
998 | ctx, Util::normalize_concrete_absolute_path(abs_inc_path)); | |
999 | std::string line_with_rel_inc = | |
1000 | util::replace_first(orig_line, abs_inc_path, rel_inc_path); | |
a71cb96c | 1001 | new_stdout_data.insert(new_stdout_data.end(), |
3ec0d4ec JR |
1002 | line_with_rel_inc.data(), |
1003 | line_with_rel_inc.size()); | |
ef19b0f0 | 1004 | } else { |
3ec0d4ec | 1005 | new_stdout_data.insert(new_stdout_data.end(), line.data(), line.size()); |
ef19b0f0 JR |
1006 | } |
1007 | } | |
3ec0d4ec | 1008 | return new_stdout_data; |
ef19b0f0 JR |
1009 | } else { |
1010 | return std::move(stdout_data); | |
1011 | } | |
1012 | } | |
1013 | ||
d75dbe26 JR |
1014 | static std::string |
1015 | format_epoch_time(const util::TimePoint& tp) | |
1016 | { | |
1017 | return FMT("{}.{:09}", tp.sec(), tp.nsec_decimal_part()); | |
1018 | } | |
1019 | ||
1020 | static bool | |
1021 | source_file_is_too_new(const Context& ctx, const fs::path& path) | |
1022 | { | |
1023 | const bool sloppy_ctime = | |
1024 | ctx.config.sloppiness().contains(core::Sloppy::include_file_ctime); | |
1025 | const bool sloppy_mtime = | |
1026 | ctx.config.sloppiness().contains(core::Sloppy::include_file_mtime); | |
1027 | ||
1028 | if ((sloppy_mtime && sloppy_ctime) || util::is_dev_null_path(path)) { | |
1029 | return false; | |
1030 | } | |
1031 | ||
1032 | // It's not enough to check if mtime/ctime >= ctx.time_of_invocation since | |
1033 | // filesystem timestamps are granular. See the comment for | |
1034 | // InodeCache::InodeCache for details. | |
1035 | // | |
1036 | // A relatively small safety margin is used in this case to make things safe | |
1037 | // on common filesystems while also not bailing out when creating a source | |
1038 | // file reasonably close in time before the compilation. | |
1039 | const util::Duration min_age(0, 100'000'000); // 0.1 s | |
1040 | util::TimePoint deadline = ctx.time_of_invocation + min_age; | |
1041 | ||
1042 | DirEntry dir_entry(path); | |
1043 | ||
1044 | if (!sloppy_mtime && dir_entry.mtime() >= deadline) { | |
1045 | LOG( | |
1046 | "{} was modified near or after invocation (mtime {}, invocation time {})", | |
1047 | dir_entry.path(), | |
1048 | format_epoch_time(dir_entry.mtime()), | |
1049 | format_epoch_time(ctx.time_of_invocation)); | |
1050 | return true; | |
1051 | } | |
1052 | ||
1053 | // The same logic as above applies to the change time of the file. | |
1054 | if (!sloppy_ctime && dir_entry.ctime() >= deadline) { | |
1055 | LOG( | |
1056 | "{} had status change near or after invocation (ctime {}, invocation" | |
1057 | " time {})", | |
1058 | dir_entry.path(), | |
1059 | format_epoch_time(dir_entry.ctime()), | |
1060 | format_epoch_time(ctx.time_of_invocation)); | |
1061 | return true; | |
1062 | } | |
1063 | ||
1064 | return false; | |
1065 | } | |
1066 | ||
c7c0837a | 1067 | // Run the real compiler and put the result in cache. Returns the result key. |
39606070 | 1068 | static tl::expected<Hash::Digest, Failure> |
272fb0ed | 1069 | to_cache(Context& ctx, |
ad52c600 | 1070 | Args& args, |
0e4e4b63 | 1071 | std::optional<Hash::Digest> result_key, |
656b50a9 | 1072 | const Args& depend_extra_args, |
a5021452 | 1073 | Hash* depend_mode_hash) |
f42859a1 | 1074 | { |
caa5bcbb | 1075 | if (ctx.config.is_compiler_group_msvc()) { |
56c18b0d CA |
1076 | args.push_back(fmt::format("-Fo{}", ctx.args_info.output_obj)); |
1077 | } else { | |
1078 | args.push_back("-o"); | |
1079 | args.push_back(ctx.args_info.output_obj); | |
1080 | } | |
f5795cdb | 1081 | |
c86a4628 JR |
1082 | if (ctx.config.hard_link() |
1083 | && !util::is_dev_null_path(ctx.args_info.output_obj)) { | |
f5795cdb JR |
1084 | // Workaround for Clang bug where it overwrites an existing object file |
1085 | // when it's compiling an assembler file, see | |
1086 | // <https://bugs.llvm.org/show_bug.cgi?id=39782>. | |
19f67009 | 1087 | util::remove_nfs_safe(ctx.args_info.output_obj); |
f5795cdb JR |
1088 | } |
1089 | ||
fdddb0b6 | 1090 | if (ctx.args_info.generating_diagnostics) { |
a0208732 | 1091 | args.push_back("--serialize-diagnostics"); |
04984326 | 1092 | args.push_back(ctx.args_info.output_dia); |
f5795cdb JR |
1093 | } |
1094 | ||
dfc7a901 JR |
1095 | if (ctx.args_info.seen_double_dash) { |
1096 | args.push_back("--"); | |
1097 | } | |
1098 | ||
8b0f2eb6 | 1099 | if (ctx.config.run_second_cpp()) { |
04984326 | 1100 | args.push_back(ctx.args_info.input_file); |
f5795cdb | 1101 | } else { |
04984326 | 1102 | args.push_back(ctx.i_tmpfile); |
f5795cdb JR |
1103 | } |
1104 | ||
690766ef | 1105 | if (ctx.args_info.seen_split_dwarf) { |
f5795cdb JR |
1106 | // Remove any pre-existing .dwo file since we want to check if the compiler |
1107 | // produced one, intentionally not using x_unlink or tmp_unlink since we're | |
1108 | // not interested in logging successful deletions or failures due to | |
ef5fde60 | 1109 | // nonexistent .dwo files. |
26fd7eab JR |
1110 | if (unlink(ctx.args_info.output_dwo.c_str()) != 0 && errno != ENOENT |
1111 | && errno != ESTALE) { | |
60904e7b | 1112 | LOG("Failed to unlink {}: {}", ctx.args_info.output_dwo, strerror(errno)); |
39606070 | 1113 | return tl::unexpected(Statistic::bad_output_file); |
f5795cdb JR |
1114 | } |
1115 | } | |
1116 | ||
60904e7b | 1117 | LOG_RAW("Running real compiler"); |
f5795cdb | 1118 | MTR_BEGIN("execute", "compiler"); |
62e5c432 | 1119 | |
39606070 | 1120 | tl::expected<DoExecuteResult, Failure> result; |
8b0f2eb6 | 1121 | if (!ctx.config.depend_mode()) { |
64fc42ca | 1122 | result = do_execute(ctx, args); |
a1bc04de | 1123 | args.pop_back(3); |
f5795cdb | 1124 | } else { |
f5795cdb JR |
1125 | // Use the original arguments (including dependency options) in depend |
1126 | // mode. | |
ad52c600 | 1127 | Args depend_mode_args = ctx.orig_args; |
c1fad9ca | 1128 | depend_mode_args.erase_with_prefix("--ccache-"); |
e58da986 JR |
1129 | // Add depend_mode_args directly after the compiler. We can't add them last |
1130 | // since options then may be placed after a "--" option. | |
1131 | depend_mode_args.insert(1, depend_extra_args); | |
f65e8766 | 1132 | add_prefix(ctx, depend_mode_args, ctx.config.prefix_command()); |
f5795cdb | 1133 | |
64fc42ca | 1134 | result = do_execute(ctx, depend_mode_args); |
f5795cdb JR |
1135 | } |
1136 | MTR_END("execute", "compiler"); | |
1137 | ||
64fc42ca | 1138 | if (!result) { |
39606070 | 1139 | return tl::unexpected(result.error()); |
85f5f8af JR |
1140 | } |
1141 | ||
64fc42ca JR |
1142 | // Merge stderr from the preprocessor (if any) and stderr from the real |
1143 | // compiler. | |
1144 | if (!ctx.cpp_stderr_data.empty()) { | |
e9ddf680 JR |
1145 | result->stderr_data.insert(result->stderr_data.begin(), |
1146 | ctx.cpp_stderr_data.begin(), | |
1147 | ctx.cpp_stderr_data.end()); | |
f5795cdb JR |
1148 | } |
1149 | ||
64fc42ca JR |
1150 | result->stdout_data = |
1151 | rewrite_stdout_from_compiler(ctx, std::move(result->stdout_data)); | |
ef19b0f0 | 1152 | |
64fc42ca JR |
1153 | if (result->exit_status != 0) { |
1154 | LOG("Compiler gave exit status {}", result->exit_status); | |
f5795cdb | 1155 | |
1c6ccf18 | 1156 | // We can output stderr immediately instead of rerunning the compiler. |
8630a888 | 1157 | core::send_to_console( |
e9ddf680 | 1158 | ctx, util::to_string_view(result->stderr_data), STDERR_FILENO); |
8630a888 | 1159 | core::send_to_console( |
bda468cf | 1160 | ctx, |
552d736e | 1161 | util::to_string_view(core::MsvcShowIncludesOutput::strip_includes( |
bda468cf OS |
1162 | ctx, std::move(result->stdout_data))), |
1163 | STDOUT_FILENO); | |
f5795cdb | 1164 | |
85f5f8af | 1165 | auto failure = Failure(Statistic::compile_failed); |
64fc42ca | 1166 | failure.set_exit_code(result->exit_status); |
39606070 | 1167 | return tl::unexpected(failure); |
f5795cdb JR |
1168 | } |
1169 | ||
8b0f2eb6 | 1170 | if (ctx.config.depend_mode()) { |
706f7443 | 1171 | ASSERT(depend_mode_hash); |
b1348e5f | 1172 | if (ctx.args_info.generating_dependencies) { |
d75dbe26 JR |
1173 | auto key = result_key_from_depfile(ctx, *depend_mode_hash); |
1174 | if (!key) { | |
1175 | return tl::unexpected(key.error()); | |
1176 | } | |
1177 | result_key = *key; | |
b1348e5f | 1178 | } else if (ctx.args_info.generating_includes) { |
d75dbe26 | 1179 | auto key = result_key_from_includes( |
b1348e5f | 1180 | ctx, *depend_mode_hash, util::to_string_view(result->stdout_data)); |
d75dbe26 JR |
1181 | if (!key) { |
1182 | return tl::unexpected(key.error()); | |
1183 | } | |
1184 | result_key = *key; | |
b1348e5f OS |
1185 | } else { |
1186 | ASSERT(false); | |
1187 | } | |
58a2dfc7 | 1188 | LOG_RAW("Got result key from dependency file"); |
0e4e4b63 | 1189 | LOG("Result key: {}", util::format_digest(*result_key)); |
f5795cdb JR |
1190 | } |
1191 | ||
c7c0837a JR |
1192 | ASSERT(result_key); |
1193 | ||
d75dbe26 JR |
1194 | if (source_file_is_too_new(ctx, ctx.args_info.input_file)) { |
1195 | return tl::unexpected(Statistic::modified_input_file); | |
1196 | } | |
1197 | for (const auto& [path, digest] : ctx.included_files) { | |
1198 | if (source_file_is_too_new(ctx, path)) { | |
1199 | return tl::unexpected(Statistic::modified_input_file); | |
1200 | } | |
1201 | } | |
1202 | ||
c86a4628 | 1203 | if (ctx.args_info.generating_dependencies) { |
c964ad4a | 1204 | Depfile::make_paths_relative_in_output_dep(ctx); |
f5795cdb JR |
1205 | } |
1206 | ||
d4fcdcf2 JR |
1207 | if (!ctx.args_info.expect_output_obj) { |
1208 | // Don't probe for object file when we don't expect one since we otherwise | |
1209 | // will be fooled by an already existing object file. | |
1210 | LOG_RAW("Compiler not expected to produce an object file"); | |
1211 | } else { | |
c86a4628 JR |
1212 | DirEntry dir_entry(ctx.args_info.output_obj); |
1213 | if (!dir_entry.is_regular_file()) { | |
d4fcdcf2 | 1214 | LOG_RAW("Compiler didn't produce an object file"); |
39606070 | 1215 | return tl::unexpected(Statistic::compiler_produced_no_output); |
c86a4628 | 1216 | } else if (dir_entry.size() == 0) { |
d4fcdcf2 | 1217 | LOG_RAW("Compiler produced an empty object file"); |
39606070 | 1218 | return tl::unexpected(Statistic::compiler_produced_empty_output); |
f258b70e | 1219 | } |
f5795cdb JR |
1220 | } |
1221 | ||
c7c0837a | 1222 | MTR_BEGIN("result", "result_put"); |
b53afeb5 | 1223 | if (!write_result( |
c86a4628 | 1224 | ctx, *result_key, result->stdout_data, result->stderr_data)) { |
39606070 | 1225 | return tl::unexpected(Statistic::compiler_produced_no_output); |
b53afeb5 | 1226 | } |
705b14b9 | 1227 | MTR_END("result", "result_put"); |
f5795cdb JR |
1228 | |
1229 | // Everything OK. | |
8630a888 | 1230 | core::send_to_console( |
e9ddf680 | 1231 | ctx, util::to_string_view(result->stderr_data), STDERR_FILENO); |
d981aee3 | 1232 | // Send stdout after stderr, it makes the output clearer with MSVC. |
8630a888 | 1233 | core::send_to_console( |
bda468cf | 1234 | ctx, |
552d736e | 1235 | util::to_string_view(core::MsvcShowIncludesOutput::strip_includes( |
bda468cf OS |
1236 | ctx, std::move(result->stdout_data))), |
1237 | STDOUT_FILENO); | |
c7c0837a JR |
1238 | |
1239 | return *result_key; | |
f42859a1 AT |
1240 | } |
1241 | ||
c7c0837a | 1242 | // Find the result key by running the compiler in preprocessor mode and |
0887dcaa | 1243 | // hashing the result. |
39606070 | 1244 | static tl::expected<Hash::Digest, Failure> |
c7c0837a | 1245 | get_result_key_from_cpp(Context& ctx, Args& args, Hash& hash) |
f42859a1 | 1246 | { |
64fc42ca | 1247 | std::string preprocessed_path; |
e9ddf680 | 1248 | util::Bytes cpp_stderr_data; |
64fc42ca | 1249 | |
49111755 | 1250 | if (ctx.args_info.direct_i_file) { |
f5795cdb JR |
1251 | // We are compiling a .i or .ii file - that means we can skip the cpp stage |
1252 | // and directly form the correct i_tmpfile. | |
64fc42ca | 1253 | preprocessed_path = ctx.args_info.input_file; |
f5795cdb JR |
1254 | } else { |
1255 | // Run cpp on the input file to obtain the .i. | |
1256 | ||
64fc42ca JR |
1257 | // preprocessed_path needs the proper cpp_extension for the compiler to do |
1258 | // its thing correctly. | |
1da50f6f JR |
1259 | auto tmp_stdout = |
1260 | util::value_or_throw<core::Fatal>(util::TemporaryFile::create( | |
1261 | FMT("{}/cpp_stdout", ctx.config.temporary_dir()), | |
1262 | FMT(".{}", ctx.config.cpp_extension()))); | |
230ae9ca | 1263 | preprocessed_path = tmp_stdout.path.string(); |
64fc42ca JR |
1264 | tmp_stdout.fd.close(); // We're only using the path. |
1265 | ctx.register_pending_tmp_file(preprocessed_path); | |
b3297337 | 1266 | |
64fc42ca | 1267 | const size_t orig_args_size = args.size(); |
80a228c3 | 1268 | |
8b0f2eb6 | 1269 | if (ctx.config.keep_comments_cpp()) { |
a0208732 | 1270 | args.push_back("-C"); |
f5795cdb | 1271 | } |
64fc42ca | 1272 | |
80a228c3 JR |
1273 | // Send preprocessor output to a file instead of stdout to work around |
1274 | // compilers that don't exit with a proper status on write error to stdout. | |
1275 | // See also <https://github.com/llvm/llvm-project/issues/56499>. | |
1276 | if (ctx.config.is_compiler_group_msvc()) { | |
1277 | args.push_back("-P"); | |
1278 | args.push_back(FMT("-Fi{}", preprocessed_path)); | |
1279 | } else { | |
1280 | args.push_back("-E"); | |
1281 | args.push_back("-o"); | |
1282 | args.push_back(preprocessed_path); | |
1283 | } | |
64fc42ca | 1284 | |
04984326 | 1285 | args.push_back(ctx.args_info.input_file); |
64fc42ca | 1286 | |
f65e8766 | 1287 | add_prefix(ctx, args, ctx.config.prefix_command_cpp()); |
60904e7b | 1288 | LOG_RAW("Running preprocessor"); |
f5795cdb | 1289 | MTR_BEGIN("execute", "preprocessor"); |
64fc42ca | 1290 | const auto result = do_execute(ctx, args, false); |
f5795cdb | 1291 | MTR_END("execute", "preprocessor"); |
64fc42ca | 1292 | args.pop_back(args.size() - orig_args_size); |
f5795cdb | 1293 | |
64fc42ca | 1294 | if (!result) { |
39606070 | 1295 | return tl::unexpected(result.error()); |
64fc42ca JR |
1296 | } else if (result->exit_status != 0) { |
1297 | LOG("Preprocessor gave exit status {}", result->exit_status); | |
39606070 | 1298 | return tl::unexpected(Statistic::preprocessor_error); |
85f5f8af | 1299 | } |
f5795cdb | 1300 | |
64fc42ca JR |
1301 | cpp_stderr_data = result->stderr_data; |
1302 | } | |
f5795cdb | 1303 | |
64fc42ca JR |
1304 | hash.hash_delimiter("cpp"); |
1305 | TRY(process_preprocessed_file(ctx, hash, preprocessed_path)); | |
1306 | ||
11f3d650 | 1307 | hash.hash_delimiter("cppstderr"); |
e9ddf680 | 1308 | hash.hash(util::to_string_view(cpp_stderr_data)); |
11f3d650 | 1309 | |
64fc42ca | 1310 | ctx.i_tmpfile = preprocessed_path; |
f5795cdb | 1311 | |
b3297337 | 1312 | if (!ctx.config.run_second_cpp()) { |
f5795cdb JR |
1313 | // If we are using the CPP trick, we need to remember this stderr data and |
1314 | // output it just before the main stderr from the compiler pass. | |
64fc42ca | 1315 | ctx.cpp_stderr_data = std::move(cpp_stderr_data); |
a5021452 JR |
1316 | hash.hash_delimiter("runsecondcpp"); |
1317 | hash.hash("false"); | |
f5795cdb JR |
1318 | } |
1319 | ||
a5021452 | 1320 | return hash.digest(); |
e53af1a6 JR |
1321 | } |
1322 | ||
48e33b2e JR |
1323 | // Hash mtime or content of a file, or the output of a command, according to |
1324 | // the CCACHE_COMPILERCHECK setting. | |
39606070 | 1325 | static tl::expected<void, Failure> |
81115751 | 1326 | hash_compiler(const Context& ctx, |
a5021452 | 1327 | Hash& hash, |
c86a4628 | 1328 | const DirEntry& dir_entry, |
aff39ed2 | 1329 | const std::string& path, |
222ce68f JR |
1330 | bool allow_command) |
1331 | { | |
8b0f2eb6 | 1332 | if (ctx.config.compiler_check() == "none") { |
f5795cdb | 1333 | // Do nothing. |
8b0f2eb6 | 1334 | } else if (ctx.config.compiler_check() == "mtime") { |
a5021452 | 1335 | hash.hash_delimiter("cc_mtime"); |
c86a4628 JR |
1336 | hash.hash(dir_entry.size()); |
1337 | hash.hash(dir_entry.mtime().nsec()); | |
ec94a399 | 1338 | } else if (util::starts_with(ctx.config.compiler_check(), "string:")) { |
a5021452 | 1339 | hash.hash_delimiter("cc_hash"); |
aff39ed2 | 1340 | hash.hash(&ctx.config.compiler_check()[7]); |
8b0f2eb6 | 1341 | } else if (ctx.config.compiler_check() == "content" || !allow_command) { |
a5021452 | 1342 | hash.hash_delimiter("cc_content"); |
213d9883 | 1343 | hash_binary_file(ctx, hash, path); |
f5795cdb | 1344 | } else { // command string |
d536ae7e JR |
1345 | if (!hash_multicommand_output( |
1346 | hash, ctx.config.compiler_check(), ctx.orig_args[0])) { | |
60904e7b | 1347 | LOG("Failure running compiler check command: {}", |
b8a69ab8 | 1348 | ctx.config.compiler_check()); |
39606070 | 1349 | return tl::unexpected(Statistic::compiler_check_failed); |
f5795cdb JR |
1350 | } |
1351 | } | |
85f5f8af | 1352 | return {}; |
222ce68f JR |
1353 | } |
1354 | ||
9c051cc6 JR |
1355 | // Hash the host compiler(s) invoked by nvcc. |
1356 | // | |
aff39ed2 JR |
1357 | // If `ccbin_st` and `ccbin` are set, they refer to a directory or compiler set |
1358 | // with -ccbin/--compiler-bindir. If `ccbin_st` is nullptr or `ccbin` is the | |
1359 | // empty string, the compilers are looked up in PATH instead. | |
39606070 | 1360 | static tl::expected<void, Failure> |
81115751 | 1361 | hash_nvcc_host_compiler(const Context& ctx, |
a5021452 | 1362 | Hash& hash, |
c86a4628 | 1363 | const DirEntry* ccbin_st = nullptr, |
aff39ed2 | 1364 | const std::string& ccbin = {}) |
9c051cc6 | 1365 | { |
f5795cdb JR |
1366 | // From <http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html>: |
1367 | // | |
1368 | // "[...] Specify the directory in which the compiler executable resides. | |
1369 | // The host compiler executable name can be also specified to ensure that | |
1370 | // the correct host compiler is selected." | |
1371 | // | |
1372 | // and | |
1373 | // | |
1374 | // "On all platforms, the default host compiler executable (gcc and g++ on | |
1375 | // Linux, clang and clang++ on Mac OS X, and cl.exe on Windows) found in | |
1376 | // the current execution search path will be used". | |
1377 | ||
aff39ed2 | 1378 | if (ccbin.empty() || !ccbin_st || ccbin_st->is_directory()) { |
9c051cc6 | 1379 | #if defined(__APPLE__) |
f5795cdb | 1380 | const char* compilers[] = {"clang", "clang++"}; |
9c051cc6 | 1381 | #elif defined(_WIN32) |
f5795cdb | 1382 | const char* compilers[] = {"cl.exe"}; |
9c051cc6 | 1383 | #else |
f5795cdb | 1384 | const char* compilers[] = {"gcc", "g++"}; |
9c051cc6 | 1385 | #endif |
9b11996d | 1386 | for (const char* compiler : compilers) { |
aff39ed2 | 1387 | if (!ccbin.empty()) { |
83b50b30 | 1388 | std::string path = FMT("{}/{}", ccbin, compiler); |
c86a4628 JR |
1389 | DirEntry de(path); |
1390 | if (de.is_regular_file()) { | |
1391 | TRY(hash_compiler(ctx, hash, de, path, false)); | |
f5795cdb | 1392 | } |
f5795cdb | 1393 | } else { |
451d92c8 | 1394 | std::string path = find_executable(ctx, compiler, ctx.orig_args[0]); |
119a7ed0 | 1395 | if (!path.empty()) { |
c86a4628 JR |
1396 | DirEntry de(path, DirEntry::LogOnError::yes); |
1397 | TRY(hash_compiler(ctx, hash, de, ccbin, false)); | |
f5795cdb JR |
1398 | } |
1399 | } | |
1400 | } | |
1401 | } else { | |
85f5f8af | 1402 | TRY(hash_compiler(ctx, hash, *ccbin_st, ccbin, false)); |
f5795cdb | 1403 | } |
85f5f8af JR |
1404 | |
1405 | return {}; | |
9c051cc6 JR |
1406 | } |
1407 | ||
c9c54f6e | 1408 | // update a hash with information common for the direct and preprocessor modes. |
39606070 | 1409 | static tl::expected<void, Failure> |
81115751 | 1410 | hash_common_info(const Context& ctx, |
ad52c600 | 1411 | const Args& args, |
a5021452 | 1412 | Hash& hash, |
bd9897fb | 1413 | const ArgsInfo& args_info) |
a60d2af7 | 1414 | { |
a5021452 | 1415 | hash.hash(HASH_PREFIX); |
a60d2af7 | 1416 | |
9217417c JR |
1417 | if (!ctx.config.namespace_().empty()) { |
1418 | hash.hash_delimiter("namespace"); | |
1419 | hash.hash(ctx.config.namespace_()); | |
1420 | } | |
1421 | ||
f5795cdb JR |
1422 | // We have to hash the extension, as a .i file isn't treated the same by the |
1423 | // compiler as a .ii file. | |
a5021452 | 1424 | hash.hash_delimiter("ext"); |
18dd14a1 | 1425 | hash.hash(ctx.config.cpp_extension()); |
37739bc9 | 1426 | |
971a78da | 1427 | #ifdef _WIN32 |
29e90bef | 1428 | const std::string compiler_path = util::add_exe_suffix(args[0]); |
8ef5d30a | 1429 | #else |
a4456585 | 1430 | const std::string compiler_path = args[0]; |
1f18f0f4 | 1431 | #endif |
50ede96f | 1432 | |
c86a4628 JR |
1433 | DirEntry dir_entry(compiler_path, DirEntry::LogOnError::yes); |
1434 | if (!dir_entry.is_regular_file()) { | |
39606070 | 1435 | return tl::unexpected(Statistic::could_not_find_compiler); |
f5795cdb JR |
1436 | } |
1437 | ||
1438 | // Hash information about the compiler. | |
c86a4628 | 1439 | TRY(hash_compiler(ctx, hash, dir_entry, compiler_path, true)); |
f5795cdb JR |
1440 | |
1441 | // Also hash the compiler name as some compilers use hard links and behave | |
1442 | // differently depending on the real name. | |
a5021452 JR |
1443 | hash.hash_delimiter("cc_name"); |
1444 | hash.hash(Util::base_name(args[0])); | |
f5795cdb | 1445 | |
3b6762e1 | 1446 | // Hash variables that may affect the compilation. |
cad24162 | 1447 | const char* always_hash_env_vars[] = { |
a862e32a JR |
1448 | // From <https://gcc.gnu.org/onlinedocs/gcc/Environment-Variables.html> |
1449 | // (note: SOURCE_DATE_EPOCH is handled in hash_source_code_string()): | |
cad24162 JR |
1450 | "COMPILER_PATH", |
1451 | "GCC_COMPARE_DEBUG", | |
1452 | "GCC_EXEC_PREFIX", | |
d9df8ed1 JR |
1453 | // Variables that affect which underlying compiler ICC uses. Reference: |
1454 | // <https://www.intel.com/content/www/us/en/develop/documentation/ | |
1455 | // mpi-developer-reference-windows/top/environment-variable-reference/ | |
1456 | // compilation-environment-variables.html> | |
1457 | "I_MPI_CC", | |
1458 | "I_MPI_CXX", | |
a862e32a JR |
1459 | #ifdef __APPLE__ |
1460 | // On macOS, /usr/bin/clang is a compiler wrapper that switches compiler | |
1461 | // based on at least these variables: | |
1462 | "DEVELOPER_DIR", | |
1463 | "MACOSX_DEPLOYMENT_TARGET", | |
1464 | #endif | |
3b6762e1 | 1465 | }; |
cad24162 JR |
1466 | for (const char* name : always_hash_env_vars) { |
1467 | const char* value = getenv(name); | |
3b6762e1 | 1468 | if (value) { |
cad24162 | 1469 | hash.hash_delimiter(name); |
3b6762e1 JR |
1470 | hash.hash(value); |
1471 | } | |
1472 | } | |
1473 | ||
03023ee7 | 1474 | if (!(ctx.config.sloppiness().contains(core::Sloppy::locale))) { |
f5795cdb JR |
1475 | // Hash environment variables that may affect localization of compiler |
1476 | // warning messages. | |
9b11996d AL |
1477 | const char* envvars[] = { |
1478 | "LANG", "LC_ALL", "LC_CTYPE", "LC_MESSAGES", nullptr}; | |
f5795cdb | 1479 | for (const char** p = envvars; *p; ++p) { |
aff39ed2 | 1480 | const char* v = getenv(*p); |
f5795cdb | 1481 | if (v) { |
a5021452 JR |
1482 | hash.hash_delimiter(*p); |
1483 | hash.hash(v); | |
f5795cdb JR |
1484 | } |
1485 | } | |
1486 | } | |
1487 | ||
1488 | // Possibly hash the current working directory. | |
8b0f2eb6 | 1489 | if (args_info.generating_debuginfo && ctx.config.hash_dir()) { |
20696244 | 1490 | std::string dir_to_hash = ctx.apparent_cwd; |
df499792 JR |
1491 | for (const auto& map : args_info.debug_prefix_maps) { |
1492 | size_t sep_pos = map.find('='); | |
1493 | if (sep_pos != std::string::npos) { | |
1494 | std::string old_path = map.substr(0, sep_pos); | |
1495 | std::string new_path = map.substr(sep_pos + 1); | |
60904e7b | 1496 | LOG("Relocating debuginfo from {} to {} (CWD: {})", |
b8a69ab8 JR |
1497 | old_path, |
1498 | new_path, | |
1499 | ctx.apparent_cwd); | |
ec94a399 | 1500 | if (util::starts_with(ctx.apparent_cwd, old_path)) { |
df499792 | 1501 | dir_to_hash = new_path + ctx.apparent_cwd.substr(old_path.size()); |
f5795cdb | 1502 | } |
f5795cdb JR |
1503 | } |
1504 | } | |
60904e7b | 1505 | LOG("Hashing CWD {}", dir_to_hash); |
a5021452 JR |
1506 | hash.hash_delimiter("cwd"); |
1507 | hash.hash(dir_to_hash); | |
f5795cdb JR |
1508 | } |
1509 | ||
35e181c9 | 1510 | // The object file produced by MSVC includes the full path to the source file |
6cde83fb JR |
1511 | // even without debug flags. Hashing the directory should be enough since the |
1512 | // filename is included in the hash anyway. | |
1513 | if (ctx.config.is_compiler_group_msvc() && ctx.config.hash_dir()) { | |
1514 | const std::string output_obj_dir = | |
887de1a0 | 1515 | fs::path(args_info.output_obj).is_absolute() |
6cde83fb JR |
1516 | ? std::string(Util::dir_name(args_info.output_obj)) |
1517 | : ctx.actual_cwd; | |
1518 | LOG("Hashing object file directory {}", output_obj_dir); | |
1519 | hash.hash_delimiter("source path"); | |
1520 | hash.hash(output_obj_dir); | |
1521 | } | |
1522 | ||
fec40553 | 1523 | if (ctx.args_info.seen_split_dwarf || ctx.args_info.profile_arcs) { |
ea644051 JR |
1524 | // When using -gsplit-dwarf: Object files include a link to the |
1525 | // corresponding .dwo file based on the target object filename, so hashing | |
1526 | // the object file path will do it, although just hashing the object file | |
1527 | // base name would be enough. | |
2fd39b06 | 1528 | // |
f15da09b | 1529 | // When using -fprofile-arcs (including implicitly via --coverage): the |
2fd39b06 | 1530 | // object file contains a .gcda path based on the object file path. |
a5021452 JR |
1531 | hash.hash_delimiter("object file"); |
1532 | hash.hash(ctx.args_info.output_obj); | |
f5795cdb JR |
1533 | } |
1534 | ||
492f3e4a | 1535 | if (ctx.args_info.generating_coverage |
03023ee7 | 1536 | && !(ctx.config.sloppiness().contains(core::Sloppy::gcno_cwd))) { |
492f3e4a JR |
1537 | // GCC 9+ includes $PWD in the .gcno file. Since we don't have knowledge |
1538 | // about compiler version we always (unless sloppiness is wanted) include | |
1539 | // the directory in the hash for now. | |
1540 | LOG_RAW("Hashing apparent CWD due to generating a .gcno file"); | |
1541 | hash.hash_delimiter("CWD in .gcno"); | |
1542 | hash.hash(ctx.apparent_cwd); | |
1543 | } | |
1544 | ||
f5795cdb | 1545 | // Possibly hash the coverage data file path. |
88f89af4 | 1546 | if (ctx.args_info.generating_coverage && ctx.args_info.profile_arcs) { |
6c1dffd9 | 1547 | std::string dir; |
d0eb89de JR |
1548 | if (!ctx.args_info.profile_path.empty()) { |
1549 | dir = ctx.args_info.profile_path; | |
f5795cdb | 1550 | } else { |
3cacab8d | 1551 | dir = util::real_path(Util::dir_name(ctx.args_info.output_obj)); |
f5795cdb | 1552 | } |
60005c83 | 1553 | std::string_view stem = |
6c1dffd9 | 1554 | Util::remove_extension(Util::base_name(ctx.args_info.output_obj)); |
83b50b30 | 1555 | std::string gcda_path = FMT("{}/{}.gcda", dir, stem); |
60904e7b | 1556 | LOG("Hashing coverage path {}", gcda_path); |
a5021452 JR |
1557 | hash.hash_delimiter("gcda"); |
1558 | hash.hash(gcda_path); | |
f5795cdb JR |
1559 | } |
1560 | ||
1561 | // Possibly hash the sanitize blacklist file path. | |
f859341d | 1562 | for (const auto& sanitize_blacklist : args_info.sanitize_blacklists) { |
60904e7b | 1563 | LOG("Hashing sanitize blacklist {}", sanitize_blacklist); |
df2602bb | 1564 | hash.hash_delimiter("sanitizeblacklist"); |
d536ae7e | 1565 | if (!hash_binary_file(ctx, hash, sanitize_blacklist)) { |
39606070 | 1566 | return tl::unexpected(Statistic::error_hashing_extra_file); |
f5795cdb JR |
1567 | } |
1568 | } | |
1569 | ||
8b0f2eb6 | 1570 | if (!ctx.config.extra_files_to_hash().empty()) { |
587b0244 | 1571 | for (const auto& path : |
49548d72 | 1572 | util::split_path_list(ctx.config.extra_files_to_hash())) { |
60904e7b | 1573 | LOG("Hashing extra file {}", path); |
a5021452 | 1574 | hash.hash_delimiter("extrafile"); |
587b0244 | 1575 | if (!hash_binary_file(ctx, hash, path.string())) { |
39606070 | 1576 | return tl::unexpected(Statistic::error_hashing_extra_file); |
f5795cdb | 1577 | } |
f5795cdb | 1578 | } |
f5795cdb JR |
1579 | } |
1580 | ||
1581 | // Possibly hash GCC_COLORS (for color diagnostics). | |
e2ab1352 | 1582 | if (ctx.config.compiler_type() == CompilerType::gcc) { |
f5795cdb JR |
1583 | const char* gcc_colors = getenv("GCC_COLORS"); |
1584 | if (gcc_colors) { | |
a5021452 JR |
1585 | hash.hash_delimiter("gcccolors"); |
1586 | hash.hash(gcc_colors); | |
f5795cdb JR |
1587 | } |
1588 | } | |
85f5f8af JR |
1589 | |
1590 | return {}; | |
a60d2af7 JR |
1591 | } |
1592 | ||
fec40553 JR |
1593 | static std::tuple<std::optional<std::string_view>, |
1594 | std::optional<std::string_view>> | |
1595 | get_option_and_value(std::string_view option, const Args& args, size_t& i) | |
1596 | { | |
1597 | if (args[i] == option) { | |
1598 | if (i + 1 < args.size()) { | |
1599 | ++i; | |
1600 | return {option, args[i]}; | |
1601 | } else { | |
1602 | return {std::nullopt, std::nullopt}; | |
1603 | } | |
1604 | } else if (util::starts_with(args[i], option)) { | |
1605 | return {option, std::string_view(args[i]).substr(option.length())}; | |
1606 | } else { | |
1607 | return {std::nullopt, std::nullopt}; | |
1608 | } | |
1609 | } | |
1610 | ||
39606070 | 1611 | static tl::expected<void, Failure> |
98f5fcef JR |
1612 | hash_argument(const Context& ctx, |
1613 | const Args& args, | |
1614 | size_t& i, | |
1615 | Hash& hash, | |
1616 | const bool is_clang, | |
1617 | const bool direct_mode, | |
1618 | bool& found_ccbin) | |
a60d2af7 | 1619 | { |
98f5fcef JR |
1620 | // Trust the user if they've said we should not hash a given option. |
1621 | if (option_should_be_ignored(args[i], ctx.ignore_options())) { | |
1622 | LOG("Not hashing ignored option: {}", args[i]); | |
1623 | if (i + 1 < args.size() && compopt_takes_arg(args[i])) { | |
1624 | i++; | |
1625 | LOG("Not hashing argument of ignored option: {}", args[i]); | |
1626 | } | |
1627 | return {}; | |
1628 | } | |
f5795cdb | 1629 | |
98f5fcef JR |
1630 | // -L doesn't affect compilation (except for clang). |
1631 | if (i < args.size() - 1 && args[i] == "-L" && !is_clang) { | |
1632 | i++; | |
1633 | return {}; | |
1634 | } | |
1635 | if (util::starts_with(args[i], "-L") && !is_clang) { | |
1636 | return {}; | |
1637 | } | |
f5795cdb | 1638 | |
98f5fcef JR |
1639 | // -Wl,... doesn't affect compilation (except for clang). |
1640 | if (util::starts_with(args[i], "-Wl,") && !is_clang) { | |
1641 | return {}; | |
f5795cdb JR |
1642 | } |
1643 | ||
98f5fcef JR |
1644 | if (util::starts_with(args[i], "-Wa,")) { |
1645 | // We have to distinguish between three cases: | |
1646 | // | |
1647 | // Case 1: -Wa,-a (write to stdout) | |
1648 | // Case 2: -Wa,-a= (write to stdout and stderr) | |
1649 | // Case 3: -Wa,-a=file (write to file) | |
1650 | // | |
1651 | // No need to include the file part in case 3 in the hash since the filename | |
1652 | // is not part of the output. | |
f5795cdb | 1653 | |
98f5fcef JR |
1654 | hash.hash_delimiter("arg"); |
1655 | bool first = true; | |
1656 | for (const auto part : | |
1657 | util::Tokenizer(args[i], ",", util::Tokenizer::Mode::include_empty)) { | |
1658 | if (first) { | |
1659 | first = false; | |
1660 | } else { | |
1661 | hash.hash(","); | |
231fef47 | 1662 | } |
98f5fcef JR |
1663 | if (util::starts_with(part, "-a")) { |
1664 | const auto eq_pos = part.find('='); | |
1665 | if (eq_pos < part.size() - 1) { | |
1666 | // Case 3: | |
1667 | hash.hash(part.substr(0, eq_pos + 1)); | |
1668 | hash.hash("file"); | |
1669 | continue; | |
b4dfd7b5 | 1670 | } |
b4dfd7b5 | 1671 | } |
98f5fcef JR |
1672 | // Case 1 and 2: |
1673 | hash.hash(part); | |
b4dfd7b5 | 1674 | } |
98f5fcef JR |
1675 | return {}; |
1676 | } | |
b4dfd7b5 | 1677 | |
98f5fcef JR |
1678 | // The -fdebug-prefix-map option may be used in combination with |
1679 | // CCACHE_BASEDIR to reuse results across different directories. Skip using | |
1680 | // the value of the option from hashing but still hash the existence of the | |
1681 | // option. | |
1682 | if (util::starts_with(args[i], "-fdebug-prefix-map=")) { | |
1683 | hash.hash_delimiter("arg"); | |
1684 | hash.hash("-fdebug-prefix-map="); | |
1685 | return {}; | |
1686 | } | |
1687 | if (util::starts_with(args[i], "-ffile-prefix-map=")) { | |
1688 | hash.hash_delimiter("arg"); | |
1689 | hash.hash("-ffile-prefix-map="); | |
1690 | return {}; | |
1691 | } | |
1692 | if (util::starts_with(args[i], "-fmacro-prefix-map=")) { | |
1693 | hash.hash_delimiter("arg"); | |
1694 | hash.hash("-fmacro-prefix-map="); | |
1695 | return {}; | |
1696 | } | |
f5795cdb | 1697 | |
923e0b95 | 1698 | if (util::starts_with(args[i], "-frandom-seed=") |
03023ee7 | 1699 | && ctx.config.sloppiness().contains(core::Sloppy::random_seed)) { |
d097f32f | 1700 | LOG("Ignoring {} since random_seed sloppiness is requested", args[i]); |
923e0b95 RS |
1701 | return {}; |
1702 | } | |
1703 | ||
98f5fcef JR |
1704 | // When using the preprocessor, some arguments don't contribute to the hash. |
1705 | // The theory is that these arguments will change the output of -E if they are | |
1706 | // going to have any effect at all. For precompiled headers this might not be | |
1707 | // the case. | |
1708 | if (!direct_mode && !ctx.args_info.output_is_precompiled_header | |
1709 | && !ctx.args_info.using_precompiled_header) { | |
1710 | if (compopt_affects_cpp_output(args[i])) { | |
1711 | if (compopt_takes_arg(args[i])) { | |
1712 | i++; | |
f5795cdb | 1713 | } |
98f5fcef | 1714 | return {}; |
f5795cdb | 1715 | } |
98f5fcef JR |
1716 | if (compopt_affects_cpp_output(args[i].substr(0, 2))) { |
1717 | return {}; | |
1718 | } | |
1719 | } | |
f5795cdb | 1720 | |
98f5fcef JR |
1721 | if (ctx.args_info.generating_dependencies) { |
1722 | std::optional<std::string_view> option; | |
1723 | std::optional<std::string_view> value; | |
1724 | ||
1725 | if (util::starts_with(args[i], "-Wp,")) { | |
1726 | // Skip the dependency filename since it doesn't impact the output. | |
1727 | if (util::starts_with(args[i], "-Wp,-MD,") | |
1728 | && args[i].find(',', 8) == std::string::npos) { | |
1729 | hash.hash(args[i].data(), 8); | |
1730 | return {}; | |
1731 | } else if (util::starts_with(args[i], "-Wp,-MMD,") | |
1732 | && args[i].find(',', 9) == std::string::npos) { | |
1733 | hash.hash(args[i].data(), 9); | |
1734 | return {}; | |
f5795cdb | 1735 | } |
98f5fcef JR |
1736 | } else if (std::tie(option, value) = get_option_and_value("-MF", args, i); |
1737 | option) { | |
1738 | // Skip the dependency filename since it doesn't impact the output. | |
1739 | hash.hash(*option); | |
1740 | return {}; | |
1741 | } else if (std::tie(option, value) = get_option_and_value("-MQ", args, i); | |
1742 | option) { | |
1743 | hash.hash(*option); | |
1744 | // No need to hash the dependency target since we always calculate it on | |
1745 | // a cache hit. | |
1746 | return {}; | |
1747 | } else if (std::tie(option, value) = get_option_and_value("-MT", args, i); | |
1748 | option) { | |
1749 | hash.hash(*option); | |
1750 | // No need to hash the dependency target since we always calculate it on | |
1751 | // a cache hit. | |
1752 | return {}; | |
f5795cdb | 1753 | } |
98f5fcef | 1754 | } |
f5795cdb | 1755 | |
98f5fcef JR |
1756 | if (util::starts_with(args[i], "-specs=") |
1757 | || util::starts_with(args[i], "--specs=") | |
1758 | || (args[i] == "-specs" || args[i] == "--specs") | |
1759 | || args[i] == "--config") { | |
1760 | std::string path; | |
1761 | size_t eq_pos = args[i].find('='); | |
1762 | if (eq_pos == std::string::npos) { | |
1763 | if (i + 1 >= args.size()) { | |
1764 | LOG("missing argument for \"{}\"", args[i]); | |
39606070 | 1765 | return tl::unexpected(Statistic::bad_compiler_arguments); |
8bf69a1d | 1766 | } |
98f5fcef JR |
1767 | path = args[i + 1]; |
1768 | i++; | |
1769 | } else { | |
1770 | path = args[i].substr(eq_pos + 1); | |
f5795cdb | 1771 | } |
27043359 JR |
1772 | |
1773 | if (args[i] == "--config" && path.find('/') == std::string::npos) { | |
1774 | // --config FILE without / in FILE: the file is searched for in Clang's | |
1775 | // user/system/executable directories. | |
1776 | LOG("Argument to compiler option {} is too hard", args[i]); | |
1777 | return tl::unexpected(Statistic::unsupported_compiler_option); | |
1778 | } | |
1779 | ||
c86a4628 JR |
1780 | DirEntry dir_entry(path, DirEntry::LogOnError::yes); |
1781 | if (dir_entry.is_regular_file()) { | |
98f5fcef JR |
1782 | // If given an explicit specs file, then hash that file, but don't |
1783 | // include the path to it in the hash. | |
1784 | hash.hash_delimiter("specs"); | |
c86a4628 | 1785 | TRY(hash_compiler(ctx, hash, dir_entry, path, false)); |
98f5fcef | 1786 | return {}; |
27043359 JR |
1787 | } else { |
1788 | LOG("While processing {}: {} is missing", args[i], path); | |
1789 | return tl::unexpected(Statistic::bad_compiler_arguments); | |
98f5fcef JR |
1790 | } |
1791 | } | |
f5795cdb | 1792 | |
98f5fcef | 1793 | if (util::starts_with(args[i], "-fplugin=")) { |
c86a4628 JR |
1794 | DirEntry dir_entry(&args[i][9], DirEntry::LogOnError::yes); |
1795 | if (dir_entry.is_regular_file()) { | |
98f5fcef | 1796 | hash.hash_delimiter("plugin"); |
c86a4628 | 1797 | TRY(hash_compiler(ctx, hash, dir_entry, &args[i][9], false)); |
98f5fcef | 1798 | return {}; |
f5795cdb | 1799 | } |
98f5fcef | 1800 | } |
f5795cdb | 1801 | |
98f5fcef JR |
1802 | if (args[i] == "-Xclang" && i + 3 < args.size() && args[i + 1] == "-load" |
1803 | && args[i + 2] == "-Xclang") { | |
c86a4628 JR |
1804 | DirEntry dir_entry(args[i + 3], DirEntry::LogOnError::yes); |
1805 | if (dir_entry.is_regular_file()) { | |
98f5fcef | 1806 | hash.hash_delimiter("plugin"); |
c86a4628 | 1807 | TRY(hash_compiler(ctx, hash, dir_entry, args[i + 3], false)); |
98f5fcef JR |
1808 | i += 3; |
1809 | return {}; | |
f5795cdb | 1810 | } |
98f5fcef | 1811 | } |
f5795cdb | 1812 | |
98f5fcef JR |
1813 | if ((args[i] == "-ccbin" || args[i] == "--compiler-bindir") |
1814 | && i + 1 < args.size()) { | |
c86a4628 JR |
1815 | DirEntry dir_entry(args[i + 1]); |
1816 | if (dir_entry.exists()) { | |
98f5fcef JR |
1817 | found_ccbin = true; |
1818 | hash.hash_delimiter("ccbin"); | |
c86a4628 | 1819 | TRY(hash_nvcc_host_compiler(ctx, hash, &dir_entry, args[i + 1])); |
98f5fcef JR |
1820 | i++; |
1821 | return {}; | |
f5795cdb | 1822 | } |
98f5fcef | 1823 | } |
f5795cdb | 1824 | |
98f5fcef JR |
1825 | // All other arguments are included in the hash. |
1826 | hash.hash_delimiter("arg"); | |
1827 | hash.hash(args[i]); | |
1828 | if (i + 1 < args.size() && compopt_takes_arg(args[i])) { | |
1829 | i++; | |
a5021452 JR |
1830 | hash.hash_delimiter("arg"); |
1831 | hash.hash(args[i]); | |
98f5fcef JR |
1832 | } |
1833 | ||
1834 | return {}; | |
1835 | } | |
1836 | ||
39606070 | 1837 | static tl::expected<std::optional<Hash::Digest>, Failure> |
58a2dfc7 | 1838 | get_manifest_key(Context& ctx, Hash& hash) |
98f5fcef JR |
1839 | { |
1840 | // Hash environment variables that affect the preprocessor output. | |
27043359 JR |
1841 | const char* envvars[] = { |
1842 | "CPATH", | |
1843 | "C_INCLUDE_PATH", | |
1844 | "CPLUS_INCLUDE_PATH", | |
1845 | "OBJC_INCLUDE_PATH", | |
1846 | "OBJCPLUS_INCLUDE_PATH", // Clang | |
1847 | "CLANG_CONFIG_FILE_SYSTEM_DIR", // Clang | |
1848 | "CLANG_CONFIG_FILE_USER_DIR", // Clang | |
1849 | nullptr, | |
1850 | }; | |
98f5fcef JR |
1851 | for (const char** p = envvars; *p; ++p) { |
1852 | const char* v = getenv(*p); | |
1853 | if (v) { | |
1854 | hash.hash_delimiter(*p); | |
1855 | hash.hash(v); | |
f5795cdb JR |
1856 | } |
1857 | } | |
1858 | ||
98f5fcef JR |
1859 | // Make sure that the direct mode hash is unique for the input file path. If |
1860 | // this would not be the case: | |
1861 | // | |
1862 | // * A false cache hit may be produced. Scenario: | |
1863 | // - a/r.h exists. | |
1864 | // - a/x.c has #include "r.h". | |
1865 | // - b/x.c is identical to a/x.c. | |
1866 | // - Compiling a/x.c records a/r.h in the manifest. | |
1867 | // - Compiling b/x.c results in a false cache hit since a/x.c and b/x.c | |
1868 | // share manifests and a/r.h exists. | |
1869 | // * The expansion of __FILE__ may be incorrect. | |
1870 | hash.hash_delimiter("inputfile"); | |
1871 | hash.hash(ctx.args_info.input_file); | |
1872 | ||
1873 | hash.hash_delimiter("sourcecode hash"); | |
0e4e4b63 | 1874 | Hash::Digest input_file_digest; |
38ab9d38 | 1875 | auto ret = |
98f5fcef | 1876 | hash_source_code_file(ctx, input_file_digest, ctx.args_info.input_file); |
38ab9d38 | 1877 | if (ret.contains(HashSourceCode::error)) { |
39606070 | 1878 | return tl::unexpected(Statistic::internal_error); |
98f5fcef | 1879 | } |
38ab9d38 | 1880 | if (ret.contains(HashSourceCode::found_time)) { |
98f5fcef JR |
1881 | LOG_RAW("Disabling direct mode"); |
1882 | ctx.config.set_direct_mode(false); | |
1883 | return {}; | |
f5795cdb | 1884 | } |
0e4e4b63 | 1885 | hash.hash(util::format_digest(input_file_digest)); |
58a2dfc7 | 1886 | return hash.digest(); |
98f5fcef JR |
1887 | } |
1888 | ||
1889 | static bool | |
1890 | hash_profile_data_file(const Context& ctx, Hash& hash) | |
1891 | { | |
1892 | const std::string& profile_path = ctx.args_info.profile_path; | |
1893 | std::string_view base_name = Util::remove_extension(ctx.args_info.output_obj); | |
1894 | std::string hashified_cwd = ctx.apparent_cwd; | |
1895 | std::replace(hashified_cwd.begin(), hashified_cwd.end(), '/', '#'); | |
1896 | ||
1897 | std::vector<std::string> paths_to_try{ | |
1898 | // -fprofile-use[=dir]/-fbranch-probabilities (GCC <9) | |
1899 | FMT("{}/{}.gcda", profile_path, base_name), | |
1900 | // -fprofile-use[=dir]/-fbranch-probabilities (GCC >=9) | |
1901 | FMT("{}/{}#{}.gcda", profile_path, hashified_cwd, base_name), | |
1902 | // -fprofile(-instr|-sample)-use=file (Clang), -fauto-profile=file (GCC >=5) | |
1903 | profile_path, | |
1904 | // -fprofile(-instr|-sample)-use=dir (Clang) | |
1905 | FMT("{}/default.profdata", profile_path), | |
1906 | // -fauto-profile (GCC >=5) | |
1907 | "fbdata.afdo", // -fprofile-dir is not used | |
1908 | }; | |
1909 | ||
1910 | bool found = false; | |
1911 | for (const std::string& p : paths_to_try) { | |
1912 | LOG("Checking for profile data file {}", p); | |
c86a4628 | 1913 | if (DirEntry(p).is_regular_file()) { |
98f5fcef JR |
1914 | LOG("Adding profile data {} to the hash", p); |
1915 | hash.hash_delimiter("-fprofile-use"); | |
1916 | if (hash_binary_file(ctx, hash, p)) { | |
1917 | found = true; | |
1918 | } | |
1919 | } | |
f5795cdb JR |
1920 | } |
1921 | ||
98f5fcef JR |
1922 | return found; |
1923 | } | |
1924 | ||
39606070 | 1925 | static tl::expected<void, Failure> |
98f5fcef JR |
1926 | hash_profiling_related_data(const Context& ctx, Hash& hash) |
1927 | { | |
d0eb89de JR |
1928 | // For profile generation (-fprofile(-instr)-generate[=path]) |
1929 | // - hash profile path | |
f5795cdb | 1930 | // |
4b1b7d4b | 1931 | // For profile usage (-fprofile(-instr|-sample)-use, -fbranch-probabilities): |
f5795cdb JR |
1932 | // - hash profile data |
1933 | // | |
1934 | // -fbranch-probabilities and -fvpt usage is covered by | |
1935 | // -fprofile-generate/-fprofile-use. | |
1936 | // | |
1937 | // The profile directory can be specified as an argument to | |
4b1b7d4b OL |
1938 | // -fprofile(-instr)-generate=, -fprofile(-instr|-sample)-use= or |
1939 | // -fprofile-dir=. | |
d0eb89de | 1940 | |
0902d812 | 1941 | if (ctx.args_info.profile_generate) { |
706f7443 | 1942 | ASSERT(!ctx.args_info.profile_path.empty()); |
2c286f1f JR |
1943 | |
1944 | // For a relative profile directory D the compiler stores $PWD/D as part of | |
1945 | // the profile filename so we need to include the same information in the | |
1946 | // hash. | |
1947 | const std::string profile_path = | |
887de1a0 | 1948 | fs::path(ctx.args_info.profile_path).is_absolute() |
2c286f1f JR |
1949 | ? ctx.args_info.profile_path |
1950 | : FMT("{}/{}", ctx.apparent_cwd, ctx.args_info.profile_path); | |
1951 | LOG("Adding profile directory {} to our hash", profile_path); | |
a5021452 | 1952 | hash.hash_delimiter("-fprofile-dir"); |
2c286f1f | 1953 | hash.hash(profile_path); |
f5795cdb JR |
1954 | } |
1955 | ||
d0eb89de | 1956 | if (ctx.args_info.profile_use && !hash_profile_data_file(ctx, hash)) { |
60904e7b | 1957 | LOG_RAW("No profile data file found"); |
39606070 | 1958 | return tl::unexpected(Statistic::no_input_file); |
f5795cdb JR |
1959 | } |
1960 | ||
98f5fcef JR |
1961 | return {}; |
1962 | } | |
1963 | ||
0e4e4b63 JR |
1964 | static std::optional<Hash::Digest> |
1965 | get_result_key_from_manifest(Context& ctx, const Hash::Digest& manifest_key) | |
58a2dfc7 JR |
1966 | { |
1967 | MTR_BEGIN("manifest", "manifest_get"); | |
0e4e4b63 | 1968 | std::optional<Hash::Digest> result_key; |
58a2dfc7 JR |
1969 | size_t read_manifests = 0; |
1970 | ctx.storage.get( | |
1971 | manifest_key, core::CacheEntryType::manifest, [&](util::Bytes&& value) { | |
1972 | try { | |
1973 | read_manifest(ctx, value); | |
1974 | ++read_manifests; | |
1975 | result_key = ctx.manifest.look_up_result_digest(ctx); | |
1976 | } catch (const core::Error& e) { | |
1977 | LOG("Failed to look up result key in manifest: {}", e.what()); | |
1978 | } | |
1979 | if (result_key) { | |
1980 | LOG_RAW("Got result key from manifest"); | |
1981 | return true; | |
1982 | } else { | |
1983 | LOG_RAW("Did not find result key in manifest"); | |
1984 | return false; | |
1985 | } | |
1986 | }); | |
1987 | MTR_END("manifest", "manifest_get"); | |
1988 | if (read_manifests > 1 && !ctx.config.remote_only()) { | |
1989 | MTR_SCOPE("manifest", "merge"); | |
0e4e4b63 JR |
1990 | LOG("Storing merged manifest {} locally", |
1991 | util::format_digest(manifest_key)); | |
58a2dfc7 JR |
1992 | core::CacheEntry::Header header(ctx.config, core::CacheEntryType::manifest); |
1993 | ctx.storage.local.put(manifest_key, | |
1994 | core::CacheEntryType::manifest, | |
1995 | core::CacheEntry::serialize(header, ctx.manifest)); | |
1996 | } | |
1997 | ||
1998 | return result_key; | |
1999 | } | |
2000 | ||
98f5fcef JR |
2001 | // Update a hash sum with information specific to the direct and preprocessor |
2002 | // modes and calculate the result key. Returns the result key on success, and | |
2003 | // if direct_mode is true also the manifest key. | |
39606070 | 2004 | static tl::expected< |
0e4e4b63 JR |
2005 | std::pair<std::optional<Hash::Digest>, std::optional<Hash::Digest>>, |
2006 | Failure> | |
98f5fcef JR |
2007 | calculate_result_and_manifest_key(Context& ctx, |
2008 | const Args& args, | |
98f5fcef | 2009 | Hash& hash, |
dda86ed9 | 2010 | Args* preprocessor_args) |
98f5fcef | 2011 | { |
dda86ed9 | 2012 | bool direct_mode = !preprocessor_args; |
98f5fcef JR |
2013 | bool found_ccbin = false; |
2014 | ||
a03f14b7 | 2015 | hash.hash_delimiter("cache entry version"); |
653dde81 | 2016 | hash.hash(core::CacheEntry::k_format_version); |
a03f14b7 | 2017 | |
98f5fcef | 2018 | hash.hash_delimiter("result version"); |
a03f14b7 | 2019 | hash.hash(core::Result::k_format_version); |
98f5fcef JR |
2020 | |
2021 | if (direct_mode) { | |
2022 | hash.hash_delimiter("manifest version"); | |
2023 | hash.hash(core::Manifest::k_format_version); | |
2024 | } | |
2025 | ||
2026 | // clang will emit warnings for unused linker flags, so we shouldn't skip | |
2027 | // those arguments. | |
2028 | int is_clang = ctx.config.is_compiler_group_clang() | |
2029 | || ctx.config.compiler_type() == CompilerType::other; | |
2030 | ||
2031 | // First the arguments. | |
2032 | for (size_t i = 1; i < args.size(); i++) { | |
2033 | TRY(hash_argument(ctx, args, i, hash, is_clang, direct_mode, found_ccbin)); | |
2034 | } | |
2035 | ||
98f5fcef JR |
2036 | if (!found_ccbin && ctx.args_info.actual_language == "cu") { |
2037 | TRY(hash_nvcc_host_compiler(ctx, hash)); | |
2038 | } | |
2039 | ||
2040 | TRY(hash_profiling_related_data(ctx, hash)); | |
2041 | ||
f5795cdb | 2042 | // Adding -arch to hash since cpp output is affected. |
7ca8fd6c | 2043 | for (const auto& arch : ctx.args_info.arch_args) { |
a5021452 JR |
2044 | hash.hash_delimiter("-arch"); |
2045 | hash.hash(arch); | |
ab7dd6ff TN |
2046 | |
2047 | // Adding -Xarch_* to hash since cpp output is affected. | |
2048 | auto it = ctx.args_info.xarch_args.find(arch); | |
2049 | if (it != ctx.args_info.xarch_args.end()) { | |
2050 | for (const auto& xarch : it->second) { | |
2051 | hash.hash_delimiter("-Xarch_" + arch); | |
2052 | hash.hash(xarch); | |
2053 | } | |
2054 | } | |
f5795cdb JR |
2055 | } |
2056 | ||
0e4e4b63 JR |
2057 | std::optional<Hash::Digest> result_key; |
2058 | std::optional<Hash::Digest> manifest_key; | |
c7c0837a | 2059 | |
f5795cdb | 2060 | if (direct_mode) { |
58a2dfc7 JR |
2061 | const auto manifest_key_result = get_manifest_key(ctx, hash); |
2062 | if (!manifest_key_result) { | |
39606070 | 2063 | return tl::unexpected(manifest_key_result.error()); |
58a2dfc7 JR |
2064 | } |
2065 | manifest_key = *manifest_key_result; | |
2066 | if (manifest_key) { | |
0e4e4b63 | 2067 | LOG("Manifest key: {}", util::format_digest(*manifest_key)); |
58a2dfc7 JR |
2068 | result_key = get_result_key_from_manifest(ctx, *manifest_key); |
2069 | } | |
85f5f8af | 2070 | } else if (ctx.args_info.arch_args.empty()) { |
dda86ed9 | 2071 | const auto digest = get_result_key_from_cpp(ctx, *preprocessor_args, hash); |
85f5f8af | 2072 | if (!digest) { |
39606070 | 2073 | return tl::unexpected(digest.error()); |
85f5f8af JR |
2074 | } |
2075 | result_key = *digest; | |
2076 | LOG_RAW("Got result key from preprocessor"); | |
f5795cdb | 2077 | } else { |
dda86ed9 | 2078 | preprocessor_args->push_back("-arch"); |
85f5f8af | 2079 | for (size_t i = 0; i < ctx.args_info.arch_args.size(); ++i) { |
ab7dd6ff | 2080 | const auto& arch = ctx.args_info.arch_args[i]; |
36e914f8 | 2081 | size_t xarch_count = 0; |
ab7dd6ff TN |
2082 | preprocessor_args->push_back(arch); |
2083 | auto it = ctx.args_info.xarch_args.find(arch); | |
2084 | if (it != ctx.args_info.xarch_args.end()) { | |
2085 | for (const auto& xarch : it->second) { | |
2086 | preprocessor_args->push_back("-Xarch_" + arch); | |
2087 | preprocessor_args->push_back(xarch); | |
2088 | xarch_count += 2; | |
2089 | } | |
2090 | } | |
dda86ed9 JR |
2091 | const auto digest = |
2092 | get_result_key_from_cpp(ctx, *preprocessor_args, hash); | |
85f5f8af | 2093 | if (!digest) { |
39606070 | 2094 | return tl::unexpected(digest.error()); |
85f5f8af JR |
2095 | } |
2096 | result_key = *digest; | |
ab7dd6ff | 2097 | LOG("Got result key from preprocessor with -arch {}", arch); |
85f5f8af | 2098 | if (i != ctx.args_info.arch_args.size() - 1) { |
eea89d4a | 2099 | result_key = std::nullopt; |
f5795cdb | 2100 | } |
36e914f8 | 2101 | preprocessor_args->pop_back(1 + xarch_count); |
f5795cdb | 2102 | } |
dda86ed9 | 2103 | preprocessor_args->pop_back(); |
f5795cdb JR |
2104 | } |
2105 | ||
58a2dfc7 | 2106 | if (result_key) { |
0e4e4b63 | 2107 | LOG("Result key: {}", util::format_digest(*result_key)); |
58a2dfc7 | 2108 | } |
85f5f8af | 2109 | return std::make_pair(result_key, manifest_key); |
e53af1a6 | 2110 | } |
f42859a1 | 2111 | |
d5390135 JR |
2112 | enum class FromCacheCallMode { direct, cpp }; |
2113 | ||
b646eb0a | 2114 | // Try to return the compile result from cache. |
39606070 | 2115 | static tl::expected<bool, Failure> |
0e4e4b63 | 2116 | from_cache(Context& ctx, FromCacheCallMode mode, const Hash::Digest& result_key) |
f42859a1 | 2117 | { |
f5795cdb | 2118 | // The user might be disabling cache hits. |
8b0f2eb6 | 2119 | if (ctx.config.recache()) { |
a9567b9a | 2120 | return false; |
f5795cdb JR |
2121 | } |
2122 | ||
2123 | // If we're using Clang, we can't trust a precompiled header object based on | |
2124 | // running the preprocessor since clang will produce a fatal error when the | |
2125 | // precompiled header is used and one of the included files has an updated | |
2126 | // timestamp: | |
2127 | // | |
2128 | // file 'foo.h' has been modified since the precompiled header 'foo.pch' | |
2129 | // was built | |
32c70852 | 2130 | if ((ctx.config.is_compiler_group_clang() |
e2ab1352 | 2131 | || ctx.config.compiler_type() == CompilerType::other) |
f2cd5dec | 2132 | && ctx.args_info.output_is_precompiled_header |
fd1e2683 | 2133 | && mode == FromCacheCallMode::cpp) { |
60904e7b | 2134 | LOG_RAW("Not considering cached precompiled header in preprocessor mode"); |
a9567b9a | 2135 | return false; |
f5795cdb JR |
2136 | } |
2137 | ||
705b14b9 | 2138 | MTR_SCOPE("cache", "from_cache"); |
f5795cdb | 2139 | |
f5795cdb | 2140 | // Get result from cache. |
0db6d06d JR |
2141 | util::Bytes cache_entry_data; |
2142 | ctx.storage.get( | |
2143 | result_key, core::CacheEntryType::result, [&](util::Bytes&& value) { | |
2144 | cache_entry_data = std::move(value); | |
2145 | return true; | |
2146 | }); | |
2147 | if (cache_entry_data.empty()) { | |
a9567b9a | 2148 | return false; |
fd8474e3 | 2149 | } |
c7c0837a | 2150 | |
02dac1f1 | 2151 | try { |
0db6d06d | 2152 | core::CacheEntry cache_entry(cache_entry_data); |
a03f14b7 JR |
2153 | cache_entry.verify_checksum(); |
2154 | core::Result::Deserializer deserializer(cache_entry.payload()); | |
d663cb51 | 2155 | core::ResultRetriever result_retriever(ctx, result_key); |
3c98cd28 | 2156 | util::UmaskScope umask_scope(ctx.original_umask); |
99c881d1 | 2157 | deserializer.visit(result_retriever); |
99c881d1 | 2158 | } catch (core::ResultRetriever::WriteError& e) { |
d663cb51 | 2159 | LOG("Write error when retrieving result from {}: {}", |
0e4e4b63 | 2160 | util::format_digest(result_key), |
d663cb51 | 2161 | e.what()); |
39606070 | 2162 | return tl::unexpected(Statistic::bad_output_file); |
02dac1f1 | 2163 | } catch (core::Error& e) { |
0e4e4b63 JR |
2164 | LOG("Failed to get result from {}: {}", |
2165 | util::format_digest(result_key), | |
2166 | e.what()); | |
a9567b9a | 2167 | return false; |
f5795cdb JR |
2168 | } |
2169 | ||
60904e7b | 2170 | LOG_RAW("Succeeded getting cached result"); |
a9567b9a | 2171 | return true; |
f42859a1 AT |
2172 | } |
2173 | ||
41fa24ab | 2174 | // Find the real compiler and put it into ctx.orig_args[0]. We just search the |
451d92c8 | 2175 | // PATH to find an executable of the same name that isn't ourselves. |
41fa24ab AL |
2176 | void |
2177 | find_compiler(Context& ctx, | |
e1a53fd0 JR |
2178 | const FindExecutableFunction& find_executable_function, |
2179 | bool masquerading_as_compiler) | |
f42859a1 | 2180 | { |
697390ed | 2181 | // Support user override of the compiler. |
cb8aec82 JR |
2182 | const std::string compiler = |
2183 | !ctx.config.compiler().empty() | |
2184 | ? ctx.config.compiler() | |
1e7f56fa JR |
2185 | // In case ccache is masquerading as the compiler, use only base_name so |
2186 | // the real compiler can be determined. | |
e1a53fd0 JR |
2187 | : (masquerading_as_compiler |
2188 | ? std::string(Util::base_name(ctx.orig_args[0])) | |
2189 | : ctx.orig_args[0]); | |
f5795cdb | 2190 | |
697390ed | 2191 | const std::string resolved_compiler = |
e3e29c94 | 2192 | util::is_full_path(compiler) |
697390ed | 2193 | ? compiler |
451d92c8 | 2194 | : find_executable_function(ctx, compiler, ctx.orig_args[0]); |
f5795cdb | 2195 | |
697390ed | 2196 | if (resolved_compiler.empty()) { |
a4ab84f9 | 2197 | throw core::Fatal(FMT("Could not find compiler \"{}\" in PATH", compiler)); |
f5795cdb | 2198 | } |
697390ed | 2199 | |
73f6c52a | 2200 | if (is_ccache_executable(resolved_compiler)) { |
451d92c8 | 2201 | throw core::Fatal("Recursive invocation of ccache"); |
f5795cdb | 2202 | } |
697390ed | 2203 | |
697390ed | 2204 | ctx.orig_args[0] = resolved_compiler; |
f42859a1 AT |
2205 | } |
2206 | ||
91aa7075 | 2207 | static void |
e1a53fd0 | 2208 | initialize(Context& ctx, const char* const* argv, bool masquerading_as_compiler) |
107e2e36 | 2209 | { |
60904e7b | 2210 | LOG("=== CCACHE {} STARTED =========================================", |
b8a69ab8 | 2211 | CCACHE_VERSION); |
f5795cdb | 2212 | |
a2e7f910 JR |
2213 | LOG("Configuration file: {}", ctx.config.config_path()); |
2214 | LOG("System configuration file: {}", ctx.config.system_config_path()); | |
61c70a6f | 2215 | |
1a15286a | 2216 | if (getenv("CCACHE_INTERNAL_TRACE")) { |
f08edbb7 | 2217 | #ifdef MTR_ENABLED |
98495aa4 | 2218 | ctx.mini_trace = std::make_unique<MiniTrace>(ctx.args_info); |
f08edbb7 | 2219 | #else |
60904e7b | 2220 | LOG_RAW("Error: tracing is not enabled!"); |
00c6b35b | 2221 | #endif |
f5795cdb | 2222 | } |
e1a53fd0 JR |
2223 | |
2224 | if (!ctx.config.log_file().empty() || ctx.config.debug()) { | |
2225 | ctx.config.visit_items([&ctx](const std::string& key, | |
2226 | const std::string& value, | |
2227 | const std::string& origin) { | |
2228 | const auto& log_value = | |
2229 | key == "remote_storage" | |
2230 | ? ctx.storage.get_remote_storage_config_for_logging() | |
2231 | : value; | |
2232 | BULK_LOG("Config: ({}) {} = {}", origin, key, log_value); | |
2233 | }); | |
2234 | } | |
2235 | ||
7019a3d0 | 2236 | LOG("Command line: {}", util::format_argv_for_logging(argv)); |
0d4f1ce2 | 2237 | LOG("Hostname: {}", util::get_hostname()); |
e1a53fd0 JR |
2238 | LOG("Working directory: {}", ctx.actual_cwd); |
2239 | if (ctx.apparent_cwd != ctx.actual_cwd) { | |
2240 | LOG("Apparent working directory: {}", ctx.apparent_cwd); | |
2241 | } | |
2242 | ||
2243 | ctx.storage.initialize(); | |
2244 | ||
2245 | MTR_BEGIN("main", "find_compiler"); | |
2246 | find_compiler(ctx, &find_executable, masquerading_as_compiler); | |
2247 | MTR_END("main", "find_compiler"); | |
2248 | ||
2249 | // Guess compiler after logging the config value in order to be able to | |
2250 | // display "compiler_type = auto" before overwriting the value with the | |
2251 | // guess. | |
2252 | if (ctx.config.compiler_type() == CompilerType::auto_guess) { | |
2253 | ctx.config.set_compiler_type(guess_compiler(ctx.orig_args[0])); | |
2254 | } | |
2255 | DEBUG_ASSERT(ctx.config.compiler_type() != CompilerType::auto_guess); | |
2256 | ||
2257 | LOG("Compiler: {}", ctx.orig_args[0]); | |
2258 | LOG("Compiler type: {}", compiler_type_to_string(ctx.config.compiler_type())); | |
b55be7ec JR |
2259 | } |
2260 | ||
48e33b2e JR |
2261 | // Make a copy of stderr that will not be cached, so things like distcc can |
2262 | // send networking errors to it. | |
39606070 | 2263 | static tl::expected<void, Failure> |
b646eb0a | 2264 | set_up_uncached_err() |
b55be7ec | 2265 | { |
47923581 JR |
2266 | int uncached_fd = |
2267 | dup(STDERR_FILENO); // The file descriptor is intentionally leaked. | |
f5795cdb | 2268 | if (uncached_fd == -1) { |
60904e7b | 2269 | LOG("dup(2) failed: {}", strerror(errno)); |
39606070 | 2270 | return tl::unexpected(Statistic::internal_error); |
f5795cdb JR |
2271 | } |
2272 | ||
09c32fb9 | 2273 | util::setenv("UNCACHED_ERR_FD", FMT("{}", uncached_fd)); |
85f5f8af | 2274 | return {}; |
b55be7ec JR |
2275 | } |
2276 | ||
9131b059 | 2277 | static int cache_compilation(int argc, const char* const* argv); |
85f5f8af | 2278 | |
39606070 | 2279 | static tl::expected<core::StatisticsCounters, Failure> |
e1a53fd0 | 2280 | do_cache_compilation(Context& ctx); |
3896b68f | 2281 | |
a28af38b JR |
2282 | static void |
2283 | log_result_to_debug_log(Context& ctx) | |
2284 | { | |
2285 | if (ctx.config.log_file().empty() && !ctx.config.debug()) { | |
2286 | return; | |
2287 | } | |
2288 | ||
0cd6f70b | 2289 | core::Statistics statistics(ctx.storage.local.get_statistics_updates()); |
190d2f2e | 2290 | for (const auto& message : statistics.get_statistics_ids()) { |
9b1e89d2 | 2291 | LOG("Result: {}", message); |
a28af38b JR |
2292 | } |
2293 | } | |
2294 | ||
2295 | static void | |
2296 | log_result_to_stats_log(Context& ctx) | |
2297 | { | |
2298 | if (ctx.config.stats_log().empty()) { | |
2299 | return; | |
2300 | } | |
2301 | ||
0cd6f70b | 2302 | core::Statistics statistics(ctx.storage.local.get_statistics_updates()); |
9b1e89d2 JR |
2303 | const auto ids = statistics.get_statistics_ids(); |
2304 | if (ids.empty()) { | |
a28af38b JR |
2305 | return; |
2306 | } | |
2307 | ||
2308 | core::StatsLog(ctx.config.stats_log()) | |
9b1e89d2 | 2309 | .log_result(ctx.args_info.input_file, ids); |
a28af38b JR |
2310 | } |
2311 | ||
c7c0837a JR |
2312 | static void |
2313 | finalize_at_exit(Context& ctx) | |
fd8474e3 | 2314 | { |
c7c0837a JR |
2315 | try { |
2316 | if (ctx.config.disable()) { | |
2317 | // Just log result, don't update statistics. | |
2318 | LOG_RAW("Result: disabled"); | |
2319 | return; | |
fd8474e3 | 2320 | } |
fb7c0327 | 2321 | |
a28af38b JR |
2322 | log_result_to_debug_log(ctx); |
2323 | log_result_to_stats_log(ctx); | |
fe22a1de | 2324 | |
c7c0837a | 2325 | ctx.storage.finalize(); |
eb266c99 | 2326 | } catch (const core::ErrorBase& e) { |
fd8474e3 | 2327 | // finalize_at_exit must not throw since it's called by a destructor. |
60904e7b | 2328 | LOG("Error while finalizing stats: {}", e.what()); |
fd8474e3 | 2329 | } |
dd8f65aa JR |
2330 | |
2331 | // Dump log buffer last to not lose any logs. | |
2332 | if (ctx.config.debug() && !ctx.args_info.output_obj.empty()) { | |
d96b24a9 JR |
2333 | util::logging::dump_log(prepare_debug_path(ctx.apparent_cwd, |
2334 | ctx.config.debug_dir(), | |
13cb56cd JR |
2335 | ctx.time_of_invocation, |
2336 | ctx.args_info.output_obj, | |
2337 | "log")); | |
dd8f65aa JR |
2338 | } |
2339 | } | |
2340 | ||
e1a53fd0 JR |
2341 | ArgvParts |
2342 | split_argv(int argc, const char* const* argv) | |
2343 | { | |
2344 | ArgvParts argv_parts; | |
2345 | int i = 0; | |
73f6c52a | 2346 | while (i < argc && is_ccache_executable(argv[i])) { |
e1a53fd0 JR |
2347 | argv_parts.masquerading_as_compiler = false; |
2348 | ++i; | |
2349 | } | |
2350 | while (i < argc && std::strchr(argv[i], '=')) { | |
2351 | argv_parts.config_settings.emplace_back(argv[i]); | |
2352 | ++i; | |
2353 | } | |
2354 | argv_parts.compiler_and_args = Args::from_argv(argc - i, argv + i); | |
2355 | return argv_parts; | |
2356 | } | |
2357 | ||
2adbab62 | 2358 | // The entry point when invoked to cache a compilation. |
cc2236b2 | 2359 | static int |
9131b059 | 2360 | cache_compilation(int argc, const char* const* argv) |
f42859a1 | 2361 | { |
e521c750 | 2362 | tzset(); // Needed for localtime_r. |
f5795cdb | 2363 | |
dd8f65aa JR |
2364 | bool fall_back_to_original_compiler = false; |
2365 | Args saved_orig_args; | |
eea89d4a | 2366 | std::optional<uint32_t> original_umask; |
ea433578 | 2367 | std::string saved_temp_dir; |
2adbab62 | 2368 | |
e1a53fd0 JR |
2369 | auto argv_parts = split_argv(argc, argv); |
2370 | if (argv_parts.compiler_and_args.empty()) { | |
2371 | throw core::Fatal("no compiler given, see \"ccache --help\""); | |
2372 | } | |
2373 | ||
dd8f65aa JR |
2374 | { |
2375 | Context ctx; | |
e1a53fd0 JR |
2376 | ctx.initialize(std::move(argv_parts.compiler_and_args), |
2377 | argv_parts.config_settings); | |
dd8f65aa | 2378 | SignalHandler signal_handler(ctx); |
f88e6084 | 2379 | util::Finalizer finalizer([&ctx] { finalize_at_exit(ctx); }); |
dd8f65aa | 2380 | |
e1a53fd0 | 2381 | initialize(ctx, argv, argv_parts.masquerading_as_compiler); |
dd8f65aa | 2382 | |
e1a53fd0 | 2383 | const auto result = do_cache_compilation(ctx); |
23bbc238 JR |
2384 | ctx.storage.local.increment_statistics(result ? *result |
2385 | : result.error().counters()); | |
2386 | const auto& counters = ctx.storage.local.get_statistics_updates(); | |
2387 | ||
2388 | if (counters.get(Statistic::cache_miss) > 0) { | |
2389 | if (!ctx.config.remote_only()) { | |
2390 | ctx.storage.local.increment_statistic(Statistic::local_storage_miss); | |
2391 | } | |
2392 | if (ctx.storage.has_remote_storage()) { | |
2393 | ctx.storage.local.increment_statistic(Statistic::remote_storage_miss); | |
2394 | } | |
2395 | } else if ((counters.get(Statistic::direct_cache_hit) > 0 | |
2396 | || counters.get(Statistic::preprocessed_cache_hit) > 0) | |
2397 | && counters.get(Statistic::remote_storage_hit) > 0 | |
2398 | && !ctx.config.remote_only()) { | |
2399 | ctx.storage.local.increment_statistic(Statistic::local_storage_miss); | |
2400 | } | |
2401 | ||
85f5f8af JR |
2402 | if (!result) { |
2403 | if (result.error().exit_code()) { | |
2404 | return *result.error().exit_code(); | |
dd8f65aa JR |
2405 | } |
2406 | // Else: Fall back to running the real compiler. | |
2407 | fall_back_to_original_compiler = true; | |
2adbab62 | 2408 | |
3c201cf4 | 2409 | original_umask = ctx.original_umask; |
cc2236b2 | 2410 | |
706f7443 | 2411 | ASSERT(!ctx.orig_args.empty()); |
106a1d22 | 2412 | |
dd8f65aa JR |
2413 | ctx.orig_args.erase_with_prefix("--ccache-"); |
2414 | add_prefix(ctx, ctx.orig_args, ctx.config.prefix_command()); | |
2adbab62 | 2415 | |
60904e7b | 2416 | LOG_RAW("Failed; falling back to running the real compiler"); |
2adbab62 | 2417 | |
ea433578 | 2418 | saved_temp_dir = ctx.config.temporary_dir(); |
dd8f65aa JR |
2419 | saved_orig_args = std::move(ctx.orig_args); |
2420 | auto execv_argv = saved_orig_args.to_argv(); | |
7019a3d0 | 2421 | LOG("Executing {}", util::format_argv_for_logging(execv_argv.data())); |
bfb7813a | 2422 | // Execute the original command below after ctx and finalizer have been |
ea433578 | 2423 | // destructed. |
dd8f65aa JR |
2424 | } |
2425 | } | |
ad52c600 | 2426 | |
dd8f65aa | 2427 | if (fall_back_to_original_compiler) { |
3c201cf4 | 2428 | if (original_umask) { |
6b53a79a | 2429 | util::set_umask(*original_umask); |
3c201cf4 | 2430 | } |
ad52c600 | 2431 | auto execv_argv = saved_orig_args.to_argv(); |
bfb7813a | 2432 | execute_noreturn(execv_argv.data(), saved_temp_dir); |
eb266c99 | 2433 | throw core::Fatal( |
a4ab84f9 | 2434 | FMT("execute_noreturn of {} failed: {}", execv_argv[0], strerror(errno))); |
2adbab62 | 2435 | } |
dd8f65aa JR |
2436 | |
2437 | return EXIT_SUCCESS; | |
2adbab62 JR |
2438 | } |
2439 | ||
39606070 | 2440 | static tl::expected<core::StatisticsCounters, Failure> |
e1a53fd0 | 2441 | do_cache_compilation(Context& ctx) |
2adbab62 | 2442 | { |
8b0f2eb6 | 2443 | if (ctx.config.disable()) { |
60904e7b | 2444 | LOG_RAW("ccache is disabled"); |
39606070 | 2445 | return tl::unexpected(Statistic::none); |
f5795cdb JR |
2446 | } |
2447 | ||
62ce4a31 JR |
2448 | if (ctx.actual_cwd.empty()) { |
2449 | LOG("Unable to determine current working directory: {}", strerror(errno)); | |
39606070 | 2450 | return tl::unexpected(Statistic::internal_error); |
20696244 | 2451 | } |
f5795cdb | 2452 | |
5b4fd92c JR |
2453 | // Set CCACHE_DISABLE so no process ccache executes from now on will risk |
2454 | // calling ccache second time. For instance, if the real compiler is a wrapper | |
2455 | // script that calls "ccache $compiler ..." we want that inner ccache call to | |
2456 | // be disabled. | |
09c32fb9 | 2457 | util::setenv("CCACHE_DISABLE", "1"); |
5b4fd92c | 2458 | |
f5795cdb | 2459 | MTR_BEGIN("main", "process_args"); |
225497c0 | 2460 | ProcessArgsResult processed = process_args(ctx); |
fd8474e3 JR |
2461 | MTR_END("main", "process_args"); |
2462 | ||
225497c0 | 2463 | if (processed.error) { |
39606070 | 2464 | return tl::unexpected(*processed.error); |
f5795cdb | 2465 | } |
bd9897fb | 2466 | |
85f5f8af | 2467 | TRY(set_up_uncached_err()); |
c4284c78 | 2468 | |
c49fca9a OS |
2469 | // VS_UNICODE_OUTPUT prevents capturing stdout/stderr, as the output is sent |
2470 | // directly to Visual Studio. | |
2471 | if (ctx.config.compiler_type() == CompilerType::msvc) { | |
09c32fb9 | 2472 | util::unsetenv("VS_UNICODE_OUTPUT"); |
c49fca9a OS |
2473 | } |
2474 | ||
fec40553 JR |
2475 | for (const auto& name : {"DEPENDENCIES_OUTPUT", "SUNPRO_DEPENDENCIES"}) { |
2476 | if (getenv(name)) { | |
2477 | LOG("Unsupported environment variable: {}", name); | |
2478 | return Statistic::unsupported_environment_variable; | |
2479 | } | |
2480 | } | |
2481 | ||
a2988ace JR |
2482 | if (ctx.config.is_compiler_group_msvc()) { |
2483 | for (const auto& name : {"CL", "_CL_"}) { | |
2484 | if (getenv(name)) { | |
2485 | LOG("Unsupported environment variable: {}", name); | |
2486 | return Statistic::unsupported_environment_variable; | |
2487 | } | |
2488 | } | |
2489 | } | |
2490 | ||
caa5bcbb | 2491 | if (!ctx.config.run_second_cpp() && ctx.config.is_compiler_group_msvc()) { |
543a919a LL |
2492 | LOG_RAW("Second preprocessor cannot be disabled"); |
2493 | ctx.config.set_run_second_cpp(true); | |
2494 | } | |
2495 | ||
c86a4628 JR |
2496 | if (ctx.config.depend_mode() |
2497 | && !(ctx.config.run_second_cpp() | |
2498 | && (ctx.args_info.generating_dependencies | |
2499 | || ctx.args_info.generating_includes))) { | |
2500 | LOG_RAW("Disabling depend mode"); | |
2501 | ctx.config.set_depend_mode(false); | |
f5795cdb JR |
2502 | } |
2503 | ||
0cd6f70b | 2504 | if (ctx.storage.has_remote_storage()) { |
182eeb9f | 2505 | if (ctx.config.file_clone()) { |
0cd6f70b | 2506 | LOG_RAW("Disabling file clone mode since remote storage is enabled"); |
182eeb9f JR |
2507 | ctx.config.set_file_clone(false); |
2508 | } | |
2509 | if (ctx.config.hard_link()) { | |
0cd6f70b | 2510 | LOG_RAW("Disabling hard link mode since remote storage is enabled"); |
182eeb9f JR |
2511 | ctx.config.set_hard_link(false); |
2512 | } | |
2513 | } | |
2514 | ||
60904e7b | 2515 | LOG("Source file: {}", ctx.args_info.input_file); |
0ca27a6b | 2516 | if (ctx.args_info.generating_dependencies) { |
60904e7b | 2517 | LOG("Dependency file: {}", ctx.args_info.output_dep); |
f5795cdb | 2518 | } |
b0cf85fc | 2519 | if (ctx.args_info.generating_coverage) { |
60904e7b | 2520 | LOG_RAW("Coverage file is being generated"); |
f5795cdb | 2521 | } |
177e6d86 | 2522 | if (ctx.args_info.generating_stackusage) { |
60904e7b | 2523 | LOG("Stack usage file: {}", ctx.args_info.output_su); |
f5795cdb | 2524 | } |
fdddb0b6 | 2525 | if (ctx.args_info.generating_diagnostics) { |
60904e7b | 2526 | LOG("Diagnostics file: {}", ctx.args_info.output_dia); |
f5795cdb | 2527 | } |
b18be33b | 2528 | if (!ctx.args_info.output_dwo.empty()) { |
60904e7b | 2529 | LOG("Split dwarf file: {}", ctx.args_info.output_dwo); |
f5795cdb JR |
2530 | } |
2531 | ||
60904e7b | 2532 | LOG("Object file: {}", ctx.args_info.output_obj); |
4aa703f8 | 2533 | MTR_META_THREAD_NAME(ctx.args_info.output_obj.c_str()); |
f5795cdb | 2534 | |
f8faf9b4 | 2535 | if (ctx.config.debug() && ctx.config.debug_level() >= 2) { |
d96b24a9 JR |
2536 | const auto path = prepare_debug_path(ctx.apparent_cwd, |
2537 | ctx.config.debug_dir(), | |
7c980edc | 2538 | ctx.time_of_invocation, |
fec40553 | 2539 | ctx.args_info.orig_output_obj, |
7c980edc | 2540 | "input-text"); |
ea9a264e | 2541 | util::FileStream debug_text_file(path, "w"); |
f5795cdb | 2542 | if (debug_text_file) { |
91aa7075 | 2543 | ctx.hash_debug_files.push_back(std::move(debug_text_file)); |
f5795cdb | 2544 | } else { |
60904e7b | 2545 | LOG("Failed to open {}: {}", path, strerror(errno)); |
f5795cdb | 2546 | } |
f5795cdb JR |
2547 | } |
2548 | ||
91aa7075 JR |
2549 | FILE* debug_text_file = !ctx.hash_debug_files.empty() |
2550 | ? ctx.hash_debug_files.front().get() | |
2551 | : nullptr; | |
2552 | ||
a5021452 | 2553 | Hash common_hash; |
a181d44d | 2554 | init_hash_debug(ctx, common_hash, 'c', "COMMON", debug_text_file); |
f5795cdb | 2555 | |
b3c3e799 JR |
2556 | if (should_disable_ccache_for_input_file(ctx.args_info.input_file)) { |
2557 | LOG("{} found in {}, disabling ccache", | |
2558 | k_ccache_disable_token, | |
2559 | ctx.args_info.input_file); | |
39606070 | 2560 | return tl::unexpected(Statistic::disabled); |
b3c3e799 JR |
2561 | } |
2562 | ||
705b14b9 JR |
2563 | { |
2564 | MTR_SCOPE("hash", "common_hash"); | |
2565 | TRY(hash_common_info( | |
2566 | ctx, processed.preprocessor_args, common_hash, ctx.args_info)); | |
2567 | } | |
f5795cdb | 2568 | |
195011ad JR |
2569 | if (processed.hash_actual_cwd) { |
2570 | common_hash.hash_delimiter("actual_cwd"); | |
2571 | common_hash.hash(ctx.actual_cwd); | |
2572 | } | |
2573 | ||
f5795cdb | 2574 | // Try to find the hash using the manifest. |
a5021452 | 2575 | Hash direct_hash = common_hash; |
a181d44d | 2576 | init_hash_debug(ctx, direct_hash, 'd', "DIRECT MODE", debug_text_file); |
f5795cdb | 2577 | |
225497c0 AL |
2578 | Args args_to_hash = processed.preprocessor_args; |
2579 | args_to_hash.push_back(processed.extra_args_to_hash); | |
25e73c1f | 2580 | |
f5795cdb | 2581 | bool put_result_in_manifest = false; |
0e4e4b63 JR |
2582 | std::optional<Hash::Digest> result_key; |
2583 | std::optional<Hash::Digest> result_key_from_manifest; | |
2584 | std::optional<Hash::Digest> manifest_key; | |
c7c0837a | 2585 | |
8b0f2eb6 | 2586 | if (ctx.config.direct_mode()) { |
60904e7b | 2587 | LOG_RAW("Trying direct lookup"); |
705b14b9 | 2588 | MTR_BEGIN("hash", "direct_hash"); |
85f5f8af | 2589 | const auto result_and_manifest_key = calculate_result_and_manifest_key( |
dda86ed9 | 2590 | ctx, args_to_hash, direct_hash, nullptr); |
705b14b9 | 2591 | MTR_END("hash", "direct_hash"); |
85f5f8af | 2592 | if (!result_and_manifest_key) { |
39606070 | 2593 | return tl::unexpected(result_and_manifest_key.error()); |
85f5f8af JR |
2594 | } |
2595 | std::tie(result_key, manifest_key) = *result_and_manifest_key; | |
c7c0837a | 2596 | if (result_key) { |
f5795cdb | 2597 | // If we can return from cache at this point then do so. |
e08af162 | 2598 | const auto from_cache_result = |
a9567b9a | 2599 | from_cache(ctx, FromCacheCallMode::direct, *result_key); |
e08af162 | 2600 | if (!from_cache_result) { |
39606070 | 2601 | return tl::unexpected(from_cache_result.error()); |
e08af162 | 2602 | } else if (*from_cache_result) { |
a9567b9a | 2603 | return Statistic::direct_cache_hit; |
b646eb0a | 2604 | } |
f5795cdb JR |
2605 | |
2606 | // Wasn't able to return from cache at this point. However, the result | |
2607 | // was already found in manifest, so don't re-add it later. | |
2608 | put_result_in_manifest = false; | |
2609 | ||
c7c0837a | 2610 | result_key_from_manifest = result_key; |
f5795cdb JR |
2611 | } else { |
2612 | // Add result to manifest later. | |
2613 | put_result_in_manifest = true; | |
2614 | } | |
a9567b9a | 2615 | |
1dd9b230 | 2616 | if (!ctx.config.recache()) { |
0cd6f70b | 2617 | ctx.storage.local.increment_statistic(Statistic::direct_cache_miss); |
1dd9b230 | 2618 | } |
f5795cdb JR |
2619 | } |
2620 | ||
8b0f2eb6 | 2621 | if (ctx.config.read_only_direct()) { |
60904e7b | 2622 | LOG_RAW("Read-only direct mode; running real compiler"); |
39606070 | 2623 | return tl::unexpected(Statistic::cache_miss); |
f5795cdb JR |
2624 | } |
2625 | ||
8b0f2eb6 | 2626 | if (!ctx.config.depend_mode()) { |
f5795cdb | 2627 | // Find the hash using the preprocessed output. Also updates |
8b595b04 | 2628 | // ctx.included_files. |
a5021452 | 2629 | Hash cpp_hash = common_hash; |
a181d44d | 2630 | init_hash_debug(ctx, cpp_hash, 'p', "PREPROCESSOR MODE", debug_text_file); |
f5795cdb JR |
2631 | |
2632 | MTR_BEGIN("hash", "cpp_hash"); | |
85f5f8af | 2633 | const auto result_and_manifest_key = calculate_result_and_manifest_key( |
dda86ed9 | 2634 | ctx, args_to_hash, cpp_hash, &processed.preprocessor_args); |
705b14b9 | 2635 | MTR_END("hash", "cpp_hash"); |
85f5f8af | 2636 | if (!result_and_manifest_key) { |
39606070 | 2637 | return tl::unexpected(result_and_manifest_key.error()); |
85f5f8af JR |
2638 | } |
2639 | result_key = result_and_manifest_key->first; | |
23aa971f | 2640 | |
c7c0837a | 2641 | // calculate_result_and_manifest_key always returns a non-nullopt result_key |
dda86ed9 | 2642 | // in preprocessor mode (non-nullptr last argument). |
c7c0837a | 2643 | ASSERT(result_key); |
fd8474e3 | 2644 | |
c7c0837a | 2645 | if (result_key_from_manifest && result_key_from_manifest != result_key) { |
f5795cdb JR |
2646 | // The hash from manifest differs from the hash of the preprocessor |
2647 | // output. This could be because: | |
2648 | // | |
2649 | // - The preprocessor produces different output for the same input (not | |
2650 | // likely). | |
2651 | // - There's a bug in ccache (maybe incorrect handling of compiler | |
2652 | // arguments). | |
2653 | // - The user has used a different CCACHE_BASEDIR (most likely). | |
2654 | // | |
2655 | // The best thing here would probably be to remove the hash entry from | |
2656 | // the manifest. For now, we use a simpler method: just remove the | |
2657 | // manifest file. | |
60904e7b JR |
2658 | LOG_RAW("Hash from manifest doesn't match preprocessor output"); |
2659 | LOG_RAW("Likely reason: different CCACHE_BASEDIRs used"); | |
2660 | LOG_RAW("Removing manifest as a safety measure"); | |
c7c0837a | 2661 | ctx.storage.remove(*result_key, core::CacheEntryType::result); |
f5795cdb JR |
2662 | |
2663 | put_result_in_manifest = true; | |
2664 | } | |
2665 | ||
2666 | // If we can return from cache at this point then do. | |
e08af162 JR |
2667 | const auto from_cache_result = |
2668 | from_cache(ctx, FromCacheCallMode::cpp, *result_key); | |
2669 | if (!from_cache_result) { | |
39606070 | 2670 | return tl::unexpected(from_cache_result.error()); |
e08af162 | 2671 | } else if (*from_cache_result) { |
792b27b8 | 2672 | if (ctx.config.direct_mode() && manifest_key && put_result_in_manifest) { |
d663cb51 JR |
2673 | MTR_SCOPE("cache", "update_manifest"); |
2674 | update_manifest(ctx, *manifest_key, *result_key); | |
2fad4567 | 2675 | } |
a9567b9a | 2676 | return Statistic::preprocessed_cache_hit; |
b646eb0a | 2677 | } |
a9567b9a | 2678 | |
fbe45b77 JR |
2679 | if (!ctx.config.recache()) { |
2680 | ctx.storage.local.increment_statistic(Statistic::preprocessed_cache_miss); | |
2681 | } | |
f5795cdb JR |
2682 | } |
2683 | ||
8b0f2eb6 | 2684 | if (ctx.config.read_only()) { |
60904e7b | 2685 | LOG_RAW("Read-only mode; running real compiler"); |
39606070 | 2686 | return tl::unexpected(Statistic::cache_miss); |
f5795cdb JR |
2687 | } |
2688 | ||
225497c0 | 2689 | add_prefix(ctx, processed.compiler_args, ctx.config.prefix_command()); |
f5795cdb JR |
2690 | |
2691 | // In depend_mode, extend the direct hash. | |
a5021452 | 2692 | Hash* depend_mode_hash = ctx.config.depend_mode() ? &direct_hash : nullptr; |
f5795cdb JR |
2693 | |
2694 | // Run real compiler, sending output to cache. | |
2695 | MTR_BEGIN("cache", "to_cache"); | |
85f5f8af JR |
2696 | const auto digest = to_cache(ctx, |
2697 | processed.compiler_args, | |
2698 | result_key, | |
2699 | ctx.args_info.depend_extra_args, | |
2700 | depend_mode_hash); | |
705b14b9 | 2701 | MTR_END("cache", "to_cache"); |
85f5f8af | 2702 | if (!digest) { |
39606070 | 2703 | return tl::unexpected(digest.error()); |
85f5f8af JR |
2704 | } |
2705 | result_key = *digest; | |
c7c0837a JR |
2706 | if (ctx.config.direct_mode()) { |
2707 | ASSERT(manifest_key); | |
923b71c2 | 2708 | MTR_SCOPE("cache", "update_manifest"); |
d663cb51 | 2709 | update_manifest(ctx, *manifest_key, *result_key); |
c7c0837a | 2710 | } |
f5795cdb | 2711 | |
5fcec2b2 | 2712 | return ctx.config.recache() ? Statistic::recache : Statistic::cache_miss; |
f42859a1 AT |
2713 | } |
2714 | ||
73f6c52a | 2715 | bool |
f6ac29e8 | 2716 | is_ccache_executable(const fs::path& path) |
73f6c52a | 2717 | { |
f6ac29e8 | 2718 | std::string name = path.filename().string(); |
73f6c52a JR |
2719 | #ifdef _WIN32 |
2720 | name = util::to_lowercase(name); | |
2721 | #endif | |
2722 | return util::starts_with(name, "ccache"); | |
2723 | } | |
2724 | ||
587b0244 JR |
2725 | bool |
2726 | file_path_matches_dir_prefix_or_file(const fs::path& dir_prefix_or_file, | |
2727 | const fs::path& file_path) | |
2728 | { | |
2729 | DEBUG_ASSERT(!dir_prefix_or_file.empty()); | |
2730 | DEBUG_ASSERT(!file_path.filename().empty()); | |
2731 | ||
2732 | auto end = std::mismatch(dir_prefix_or_file.begin(), | |
2733 | dir_prefix_or_file.end(), | |
2734 | file_path.begin(), | |
2735 | file_path.end()) | |
2736 | .first; | |
2737 | return end == dir_prefix_or_file.end() || end->empty(); | |
2738 | } | |
2739 | ||
530dfe7c | 2740 | int |
9131b059 | 2741 | ccache_main(int argc, const char* const* argv) |
f42859a1 | 2742 | { |
728acfac | 2743 | try { |
73f6c52a | 2744 | if (is_ccache_executable(argv[0])) { |
728acfac | 2745 | if (argc < 2) { |
451d92c8 | 2746 | PRINT_RAW(stderr, core::get_usage_text(Util::base_name(argv[0]))); |
47e5952e | 2747 | exit(EXIT_FAILURE); |
728acfac | 2748 | } |
c7c0837a JR |
2749 | // If the first argument isn't an option, then assume we are being |
2750 | // passed a compiler name and options. | |
728acfac | 2751 | if (argv[1][0] == '-') { |
32c037e7 | 2752 | return core::process_main_options(argc, argv); |
728acfac | 2753 | } |
f5795cdb | 2754 | } |
f5795cdb | 2755 | |
cc2236b2 | 2756 | return cache_compilation(argc, argv); |
eb266c99 | 2757 | } catch (const core::ErrorBase& e) { |
01929c5c | 2758 | PRINT(stderr, "ccache: error: {}\n", e.what()); |
2adbab62 | 2759 | return EXIT_FAILURE; |
728acfac | 2760 | } |
f42859a1 | 2761 | } |