]> git.ipfire.org Git - thirdparty/ccache.git/blame - src/argprocessing.cpp
feat: Add support for undocumented GCC/Clang --include option
[thirdparty/ccache.git] / src / argprocessing.cpp
CommitLineData
00fe1341 1// Copyright (C) 2020-2023 Joel Rosdahl and other contributors
756e03d1
JR
2//
3// See doc/AUTHORS.adoc for a complete list of contributors.
4//
5// This program is free software; you can redistribute it and/or modify it
6// under the terms of the GNU General Public License as published by the Free
7// Software Foundation; either version 3 of the License, or (at your option)
8// any later version.
9//
10// This program is distributed in the hope that it will be useful, but WITHOUT
11// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13// more details.
14//
15// You should have received a copy of the GNU General Public License along with
16// this program; if not, write to the Free Software Foundation, Inc., 51
17// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19#include "argprocessing.hpp"
20
21#include "Context.hpp"
22#include "compopt.hpp"
23#include "language.hpp"
756e03d1 24
fec40553 25#include <Depfile.hpp>
5e171ed8 26#include <Util.hpp>
e4d64b4b 27#include <util/assertions.hpp>
7f77c031 28#include <util/filesystem.hpp>
ef96a84a 29#include <util/fmtmacros.hpp>
13cb56cd 30#include <util/logging.hpp>
c86a4628 31#include <util/path.hpp>
303c82fe 32#include <util/string.hpp>
7f034685 33#include <util/wincompat.hpp>
ca9ec9cf
JR
34
35#ifdef HAVE_UNISTD_H
36# include <unistd.h>
37#endif
38
d32b0012 39#include <cassert>
7f77c031 40#include <vector>
d32b0012 41
7f77c031 42namespace fs = util::filesystem;
c24446f5 43using core::Statistic;
c86a4628 44using util::DirEntry;
756e03d1
JR
45
46namespace {
47
8932157a 48enum class ColorDiagnostics : int8_t { never, automatic, always };
1c6ccf18 49
fec40553
JR
50// The dependency target in the dependency file is taken from the highest
51// priority source.
52enum class OutputDepOrigin : uint8_t {
53 // Not set
54 none = 0,
55 // From -MF target
56 mf = 1,
57 // From -Wp,-MD,target or -Wp,-MMD,target
58 wp = 2
59};
60
756e03d1
JR
61struct ArgumentProcessingState
62{
63 bool found_c_opt = false;
64 bool found_dc_opt = false;
65 bool found_S_opt = false;
67da9633 66 bool found_analyze_opt = false;
756e03d1
JR
67 bool found_pch = false;
68 bool found_fpch_preprocess = false;
13fcafe3
OS
69 bool found_Yu = false;
70 bool found_valid_Fp = false;
5cfd4b9c 71 bool found_syntax_only = false;
1c6ccf18 72 ColorDiagnostics color_diagnostics = ColorDiagnostics::automatic;
6e77c16a
JR
73 bool found_directives_only = false;
74 bool found_rewrite_includes = false;
ab7dd6ff 75 std::unordered_map<std::string, std::vector<std::string>> xarch_args;
fec40553
JR
76 bool found_mf_opt = false;
77 bool found_wp_md_or_mmd_opt = false;
78 bool found_md_or_mmd_opt = false;
0d95687b 79 bool found_Wa_a_opt = false;
6e77c16a 80
6ee19c33
JR
81 std::string explicit_language; // As specified with -x.
82 std::string input_charset_option; // -finput-charset=...
83 std::string last_seen_msvc_z_debug_option; // /Z7, /Zi or /ZI
756e03d1 84
fec40553
JR
85 // Is the dependency file set via -Wp,-M[M]D,target or -MFtarget?
86 OutputDepOrigin output_dep_origin = OutputDepOrigin::none;
756e03d1
JR
87
88 // Is the compiler being asked to output debug info on level 3?
89 bool generating_debuginfo_level_3 = false;
90
7f77c031
JR
91 // Arguments classified as input files.
92 std::vector<fs::path> input_files;
93
756e03d1
JR
94 // common_args contains all original arguments except:
95 // * those that never should be passed to the preprocessor,
96 // * those that only should be passed to the preprocessor (if run_second_cpp
97 // is false), and
98 // * dependency options (like -MD and friends).
99 Args common_args;
100
101 // cpp_args contains arguments that were not added to common_args, i.e. those
102 // that should only be passed to the preprocessor if run_second_cpp is false.
103 // If run_second_cpp is true, they will be passed to the compiler as well.
104 Args cpp_args;
105
106 // dep_args contains dependency options like -MD. They are only passed to the
107 // preprocessor, never to the compiler.
108 Args dep_args;
109
110 // compiler_only_args contains arguments that should only be passed to the
111 // compiler, not the preprocessor.
112 Args compiler_only_args;
50e8d229
JR
113
114 // compiler_only_args_no_hash contains arguments that should only be passed to
115 // the compiler, not the preprocessor, and that also should not be part of the
116 // hash identifying the result.
117 Args compiler_only_args_no_hash;
0492eb74
JR
118
119 // Whether to include the full command line in the hash.
120 bool hash_full_command_line = false;
195011ad
JR
121
122 // Whether to include the actual CWD in the hash.
123 bool hash_actual_cwd = false;
756e03d1
JR
124};
125
126bool
127color_output_possible()
128{
129 const char* term_env = getenv("TERM");
ca9ec9cf 130 return isatty(STDERR_FILENO) && term_env
b8c704b7 131 && util::to_lowercase(term_env) != "dumb";
756e03d1
JR
132}
133
134bool
e1260404 135detect_pch(const std::string& option,
d32b0012 136 const std::string& arg,
e1260404 137 std::string& included_pch_file,
3ec58dbc 138 bool is_cc1_option,
13fcafe3 139 ArgumentProcessingState& state)
756e03d1
JR
140{
141 // Try to be smart about detecting precompiled headers.
3ec58dbc
AH
142 // If the option is an option for Clang (is_cc1_option), don't accept
143 // anything just because it has a corresponding precompiled header,
144 // because Clang doesn't behave that way either.
d32b0012 145 std::string pch_file;
13fcafe3
OS
146 if (option == "-Yu") {
147 state.found_Yu = true;
148 if (state.found_valid_Fp) { // Use file set by -Fp.
149 LOG("Detected use of precompiled header: {}", included_pch_file);
150 pch_file = included_pch_file;
e8aef251 151 included_pch_file.clear(); // reset pch file set from /Fp
13fcafe3
OS
152 } else {
153 std::string file = Util::change_extension(arg, ".pch");
c86a4628 154 if (DirEntry(file).is_regular_file()) {
13fcafe3
OS
155 LOG("Detected use of precompiled header: {}", file);
156 pch_file = file;
157 }
158 }
159 } else if (option == "-Fp") {
160 std::string file = arg;
161 if (Util::get_extension(file).empty()) {
162 file += ".pch";
163 }
c86a4628 164 if (DirEntry(file).is_regular_file()) {
13fcafe3
OS
165 state.found_valid_Fp = true;
166 if (!state.found_Yu) {
167 LOG("Precompiled header file specified: {}", file);
168 included_pch_file = file; // remember file
169 return true; // -Fp does not turn on PCH
170 }
171 LOG("Detected use of precompiled header: {}", file);
172 pch_file = file;
173 included_pch_file.clear(); // reset pch file set from /Yu
174 // continue and set as if the file was passed to -Yu
175 }
176 } else if (option == "-include-pch" || option == "-include-pth") {
c86a4628 177 if (DirEntry(arg).is_regular_file()) {
60904e7b 178 LOG("Detected use of precompiled header: {}", arg);
d32b0012 179 pch_file = arg;
756e03d1 180 }
3ec58dbc 181 } else if (!is_cc1_option) {
45100a84
JR
182 for (const auto& extension : {".gch", ".pch", ".pth"}) {
183 std::string path = arg + extension;
c86a4628
JR
184 DirEntry de(path);
185 if (de.is_regular_file() || de.is_directory()) {
60904e7b 186 LOG("Detected use of precompiled header: {}", path);
45100a84 187 pch_file = path;
756e03d1 188 }
756e03d1 189 }
756e03d1
JR
190 }
191
d32b0012 192 if (!pch_file.empty()) {
e1260404 193 if (!included_pch_file.empty()) {
60904e7b 194 LOG("Multiple precompiled headers used: {} and {}",
e1260404 195 included_pch_file,
b8a69ab8 196 pch_file);
756e03d1
JR
197 return false;
198 }
e1260404 199 included_pch_file = pch_file;
13fcafe3 200 state.found_pch = true;
756e03d1
JR
201 }
202 return true;
203}
204
205bool
6cbd17cd
JR
206process_profiling_option(const Context& ctx,
207 ArgsInfo& args_info,
208 const std::string& arg)
756e03d1 209{
58895dcf
JR
210 static const std::vector<std::string> known_simple_options = {
211 "-fprofile-correction",
212 "-fprofile-reorder-functions",
213 "-fprofile-sample-accurate",
214 "-fprofile-values",
215 };
216
217 if (std::find(known_simple_options.begin(), known_simple_options.end(), arg)
218 != known_simple_options.end()) {
219 return true;
220 }
221
756e03d1
JR
222 std::string new_profile_path;
223 bool new_profile_use = false;
224
ec94a399 225 if (util::starts_with(arg, "-fprofile-dir=")) {
756e03d1
JR
226 new_profile_path = arg.substr(arg.find('=') + 1);
227 } else if (arg == "-fprofile-generate" || arg == "-fprofile-instr-generate") {
6cbd17cd 228 args_info.profile_generate = true;
32c70852 229 if (ctx.config.is_compiler_group_clang()) {
756e03d1
JR
230 new_profile_path = ".";
231 } else {
232 // GCC uses $PWD/$(basename $obj).
233 new_profile_path = ctx.apparent_cwd;
234 }
ec94a399
JR
235 } else if (util::starts_with(arg, "-fprofile-generate=")
236 || util::starts_with(arg, "-fprofile-instr-generate=")) {
6cbd17cd 237 args_info.profile_generate = true;
756e03d1
JR
238 new_profile_path = arg.substr(arg.find('=') + 1);
239 } else if (arg == "-fprofile-use" || arg == "-fprofile-instr-use"
4b1b7d4b
OL
240 || arg == "-fprofile-sample-use" || arg == "-fbranch-probabilities"
241 || arg == "-fauto-profile") {
756e03d1 242 new_profile_use = true;
6cbd17cd 243 if (args_info.profile_path.empty()) {
756e03d1
JR
244 new_profile_path = ".";
245 }
ec94a399
JR
246 } else if (util::starts_with(arg, "-fprofile-use=")
247 || util::starts_with(arg, "-fprofile-instr-use=")
248 || util::starts_with(arg, "-fprofile-sample-use=")
249 || util::starts_with(arg, "-fauto-profile=")) {
756e03d1
JR
250 new_profile_use = true;
251 new_profile_path = arg.substr(arg.find('=') + 1);
252 } else {
60904e7b 253 LOG("Unknown profiling option: {}", arg);
756e03d1
JR
254 return false;
255 }
256
257 if (new_profile_use) {
6cbd17cd 258 if (args_info.profile_use) {
60904e7b 259 LOG_RAW("Multiple profiling options not supported");
756e03d1
JR
260 return false;
261 }
6cbd17cd 262 args_info.profile_use = true;
756e03d1
JR
263 }
264
265 if (!new_profile_path.empty()) {
6cbd17cd
JR
266 args_info.profile_path = new_profile_path;
267 LOG("Set profile directory to {}", args_info.profile_path);
756e03d1
JR
268 }
269
6cbd17cd 270 if (args_info.profile_generate && args_info.profile_use) {
756e03d1 271 // Too hard to figure out what the compiler will do.
60904e7b 272 LOG_RAW("Both generating and using profile info, giving up");
756e03d1
JR
273 return false;
274 }
275
276 return true;
277}
278
3781f256
JR
279std::string
280make_dash_option(const Config& config, const std::string& arg)
281{
282 std::string new_arg = arg;
d45a5588 283 if (config.is_compiler_group_msvc() && util::starts_with(arg, "/")) {
3781f256
JR
284 // MSVC understands both /option and -option, so convert all /option to
285 // -option to simplify our handling.
286 new_arg[0] = '-';
287 }
288 return new_arg;
289}
290
6ee19c33
JR
291bool
292is_msvc_z_debug_option(std::string_view arg)
293{
294 static const char* debug_options[] = {"-Z7", "-ZI", "-Zi"};
295 return std::find(std::begin(debug_options), std::end(debug_options), arg)
296 != std::end(debug_options);
297}
298
62f3217e
JR
299// Returns std::nullopt if the option wasn't recognized, otherwise the error
300// code (with Statistic::none for "no error").
eea89d4a 301std::optional<Statistic>
62f3217e
JR
302process_option_arg(const Context& ctx,
303 ArgsInfo& args_info,
304 Config& config,
305 Args& args,
306 size_t& args_index,
307 ArgumentProcessingState& state)
756e03d1 308{
756e03d1 309 size_t& i = args_index;
2737d79e
JR
310
311 if (option_should_be_ignored(args[i], ctx.ignore_options())) {
312 LOG("Not processing ignored option: {}", args[i]);
313 state.common_args.push_back(args[i]);
314 return Statistic::none;
315 }
316
f441bc13 317 if (args[i] == "--ccache-skip") {
756e03d1 318 i++;
f441bc13 319 if (i == args.size()) {
60904e7b 320 LOG_RAW("--ccache-skip lacks an argument");
c0248cf7 321 return Statistic::bad_compiler_arguments;
756e03d1 322 }
f441bc13 323 state.common_args.push_back(args[i]);
62f3217e 324 return Statistic::none;
756e03d1
JR
325 }
326
3781f256
JR
327 // arg should only be used when detecting options. It should not be added to
328 // state.*_args since it's potentially != args[i].
329 std::string arg = make_dash_option(ctx.config, args[i]);
330
331 // Exit early if we notice a non-option argument right away.
d45a5588 332 if (arg.empty() || (arg[0] != '-' && arg[0] != '@')) {
3781f256 333 return std::nullopt;
0a644e6e
LL
334 }
335
3781f256 336 if (arg == "-ivfsoverlay"
03023ee7 337 && !(config.sloppiness().contains(core::Sloppy::ivfsoverlay))) {
97f42b96
YN
338 LOG_RAW(
339 "You have to specify \"ivfsoverlay\" sloppiness when using"
340 " -ivfsoverlay to get hits");
930d3b67 341 ++i;
97f42b96
YN
342 return Statistic::unsupported_compiler_option;
343 }
344
756e03d1 345 // Special case for -E.
3781f256 346 if (arg == "-E") {
0a644e6e
LL
347 return Statistic::called_for_preprocessing;
348 }
349 // MSVC -P is -E with output to a file.
3781f256 350 if (arg == "-P" && ctx.config.is_compiler_group_msvc()) {
c0248cf7 351 return Statistic::called_for_preprocessing;
756e03d1
JR
352 }
353
354 // Handle "@file" argument.
3781f256
JR
355 if (util::starts_with(arg, "@") || util::starts_with(arg, "-@")) {
356 const char* argpath = arg.c_str() + 1;
756e03d1
JR
357
358 if (argpath[-1] == '-') {
359 ++argpath;
360 }
80ba758c
JR
361 auto file_args = Args::from_atfile(argpath,
362 config.is_compiler_group_msvc()
363 ? Args::AtFileFormat::msvc
364 : Args::AtFileFormat::gcc);
756e03d1 365 if (!file_args) {
60904e7b 366 LOG("Couldn't read arg file {}", argpath);
c0248cf7 367 return Statistic::bad_compiler_arguments;
756e03d1
JR
368 }
369
f441bc13 370 args.replace(i, *file_args);
756e03d1 371 i--;
62f3217e 372 return Statistic::none;
756e03d1
JR
373 }
374
375 // Handle cuda "-optf" and "--options-file" argument.
e2ab1352 376 if (config.compiler_type() == CompilerType::nvcc
3781f256 377 && (arg == "-optf" || arg == "--options-file")) {
c88ead97 378 if (i == args.size() - 1) {
60904e7b 379 LOG("Expected argument after {}", args[i]);
c0248cf7 380 return Statistic::bad_compiler_arguments;
756e03d1
JR
381 }
382 ++i;
383
384 // Argument is a comma-separated list of files.
32970d78 385 auto paths = util::split_into_strings(args[i], ",");
c88ead97 386 for (auto it = paths.rbegin(); it != paths.rend(); ++it) {
56c18b0d 387 auto file_args = Args::from_atfile(*it);
756e03d1 388 if (!file_args) {
60904e7b 389 LOG("Couldn't read CUDA options file {}", *it);
c0248cf7 390 return Statistic::bad_compiler_arguments;
756e03d1
JR
391 }
392
c88ead97 393 args.insert(i + 1, *file_args);
756e03d1
JR
394 }
395
62f3217e 396 return Statistic::none;
756e03d1
JR
397 }
398
399 // These are always too hard.
3781f256 400 if (compopt_too_hard(arg) || util::starts_with(arg, "-fdump-")
27043359
JR
401 || util::starts_with(arg, "-MJ") || util::starts_with(arg, "-Yc")
402 || util::starts_with(arg, "--config-system-dir=")
403 || util::starts_with(arg, "--config-user-dir=")) {
60904e7b 404 LOG("Compiler option {} is unsupported", args[i]);
c0248cf7 405 return Statistic::unsupported_compiler_option;
756e03d1
JR
406 }
407
408 // These are too hard in direct mode.
3781f256 409 if (config.direct_mode() && compopt_too_hard_for_direct_mode(arg)) {
60904e7b 410 LOG("Unsupported compiler option for direct mode: {}", args[i]);
756e03d1
JR
411 config.set_direct_mode(false);
412 }
413
36e914f8 414 // Handle -Xarch_* options.
3781f256 415 if (util::starts_with(arg, "-Xarch_")) {
b9a810af
JR
416 if (i == args.size() - 1) {
417 LOG("Missing argument to {}", args[i]);
418 return Statistic::bad_compiler_arguments;
419 }
3781f256 420 const auto arch = arg.substr(7);
36e914f8 421 auto it = state.xarch_args.emplace(arch, std::vector<std::string>()).first;
ab7dd6ff
TN
422 it->second.emplace_back(args[i + 1]);
423 ++i;
62f3217e 424 return Statistic::none;
756e03d1
JR
425 }
426
427 // Handle -arch options.
3781f256 428 if (arg == "-arch") {
756e03d1 429 ++i;
4cf3d79f 430 args_info.arch_args.emplace_back(args[i]);
7ca8fd6c 431 if (args_info.arch_args.size() == 2) {
756e03d1
JR
432 config.set_run_second_cpp(true);
433 }
62f3217e 434 return Statistic::none;
756e03d1
JR
435 }
436
3ec58dbc
AH
437 // Some arguments that clang passes directly to cc1 (related to precompiled
438 // headers) need the usual ccache handling. In those cases, the -Xclang
439 // prefix is skipped and the cc1 argument is handled instead.
3781f256 440 if (arg == "-Xclang" && i + 1 < args.size()
3ec58dbc
AH
441 && (args[i + 1] == "-emit-pch" || args[i + 1] == "-emit-pth"
442 || args[i + 1] == "-include-pch" || args[i + 1] == "-include-pth"
d31ab958 443 || args[i + 1] == "-include" || args[i + 1] == "--include"
3ec58dbc 444 || args[i + 1] == "-fno-pch-timestamp")) {
27f00db4 445 if (compopt_affects_compiler_output(args[i + 1])) {
3ec58dbc 446 state.compiler_only_args.push_back(args[i]);
27f00db4 447 } else if (compopt_affects_cpp_output(args[i + 1])) {
3ec58dbc
AH
448 state.cpp_args.push_back(args[i]);
449 } else {
450 state.common_args.push_back(args[i]);
451 }
452 ++i;
3781f256 453 arg = make_dash_option(ctx.config, args[i]);
3ec58dbc
AH
454 }
455
3781f256
JR
456 if (util::starts_with(arg, "-Wa,")) {
457 for (const auto part : util::Tokenizer(&arg[4], ",")) {
b4dfd7b5 458 if (util::starts_with(part, "-a")) {
0d95687b 459 if (state.found_Wa_a_opt) {
b4dfd7b5
JR
460 LOG_RAW(
461 "Multiple assembler listing options (-Wa,-a) are not supported");
462 return Statistic::unsupported_compiler_option;
463 }
0d95687b 464 state.found_Wa_a_opt = true;
b4dfd7b5
JR
465
466 const auto eq_pos = part.find('=');
467 if (eq_pos != std::string_view::npos) {
468 args_info.output_al = part.substr(eq_pos + 1);
469 }
470 }
471 }
b36a8189
JR
472 }
473
756e03d1 474 // Handle options that should not be passed to the preprocessor.
78b2ed08
JR
475 if (compopt_affects_compiler_output(arg)
476 || (i + 1 < args.size() && arg == "-Xclang"
477 && compopt_affects_compiler_output(args[i + 1]))) {
478 if (i + 1 < args.size() && arg == "-Xclang") {
479 state.compiler_only_args.push_back(args[i]);
480 ++i;
481 arg = make_dash_option(ctx.config, args[i]);
482 }
4cf3d79f 483 state.compiler_only_args.push_back(args[i]);
78b2ed08
JR
484 // Note: "-Xclang -option-that-takes-arg -Xclang arg" is not handled below
485 // yet.
3781f256
JR
486 if (compopt_takes_arg(arg)
487 || (config.compiler_type() == CompilerType::nvcc && arg == "-Werror")) {
4cf3d79f 488 if (i == args.size() - 1) {
60904e7b 489 LOG("Missing argument to {}", args[i]);
c0248cf7 490 return Statistic::bad_compiler_arguments;
756e03d1 491 }
4cf3d79f 492 state.compiler_only_args.push_back(args[i + 1]);
756e03d1
JR
493 ++i;
494 }
62f3217e 495 return Statistic::none;
756e03d1 496 }
78b2ed08
JR
497 if (compopt_prefix_affects_compiler_output(arg)
498 || (i + 1 < args.size() && arg == "-Xclang"
499 && compopt_prefix_affects_compiler_output(args[i + 1]))) {
500 if (i + 1 < args.size() && arg == "-Xclang") {
501 state.compiler_only_args.push_back(args[i]);
502 ++i;
503 }
4cf3d79f 504 state.compiler_only_args.push_back(args[i]);
62f3217e 505 return Statistic::none;
756e03d1
JR
506 }
507
4cf3d79f
JR
508 // Modules are handled on demand as necessary in the background, so there is
509 // no need to cache them, they can in practice be ignored. All that is needed
510 // is to correctly depend also on module.modulemap files, and those are
511 // included only in depend mode (preprocessed output does not list them).
512 // Still, not including the modules themselves in the hash could possibly
513 // result in an object file that would be different from the actual
514 // compilation (even though it should be compatible), so require a sloppiness
515 // flag.
3781f256 516 if (arg == "-fmodules") {
756e03d1 517 if (!config.depend_mode() || !config.direct_mode()) {
60904e7b 518 LOG("Compiler option {} is unsupported without direct depend mode",
b8a69ab8 519 args[i]);
c0248cf7 520 return Statistic::could_not_use_modules;
03023ee7 521 } else if (!(config.sloppiness().contains(core::Sloppy::modules))) {
60904e7b 522 LOG_RAW(
756e03d1
JR
523 "You have to specify \"modules\" sloppiness when using"
524 " -fmodules to get hits");
c0248cf7 525 return Statistic::could_not_use_modules;
756e03d1
JR
526 }
527 }
528
529 // We must have -c.
3781f256 530 if (arg == "-c") {
756e03d1 531 state.found_c_opt = true;
62f3217e 532 return Statistic::none;
756e03d1
JR
533 }
534
0a644e6e 535 // MSVC -Fo with no space.
3781f256
JR
536 if (util::starts_with(arg, "-Fo") && config.is_compiler_group_msvc()) {
537 args_info.output_obj = arg.substr(3);
62f3217e 538 return Statistic::none;
56c18b0d
CA
539 }
540
756e03d1 541 // when using nvcc with separable compilation, -dc implies -c
3781f256 542 if ((arg == "-dc" || arg == "--device-c")
e2ab1352 543 && config.compiler_type() == CompilerType::nvcc) {
756e03d1 544 state.found_dc_opt = true;
62f3217e 545 return Statistic::none;
756e03d1
JR
546 }
547
548 // -S changes the default extension.
3781f256 549 if (arg == "-S") {
4cf3d79f 550 state.common_args.push_back(args[i]);
756e03d1 551 state.found_S_opt = true;
62f3217e 552 return Statistic::none;
756e03d1
JR
553 }
554
67da9633
M
555 // --analyze changes the default extension too
556 if (arg == "--analyze") {
557 state.common_args.push_back(args[i]);
558 state.found_analyze_opt = true;
559 return Statistic::none;
560 }
561
3781f256
JR
562 if (util::starts_with(arg, "-x")) {
563 if (arg.length() >= 3 && !islower(arg[2])) {
841593b6
AL
564 // -xCODE (where CODE can be e.g. Host or CORE-AVX2, always starting with
565 // an uppercase letter) is an ordinary Intel compiler option, not a
566 // language specification. (GCC's "-x" language argument is always
567 // lowercase.)
568 state.common_args.push_back(args[i]);
62f3217e 569 return Statistic::none;
756e03d1 570 }
841593b6
AL
571
572 // Special handling for -x: remember the last specified language before the
573 // input file and strip all -x options from the arguments.
3781f256 574 if (arg.length() == 2) {
841593b6 575 if (i == args.size() - 1) {
60904e7b 576 LOG("Missing argument to {}", args[i]);
841593b6
AL
577 return Statistic::bad_compiler_arguments;
578 }
7f77c031 579 if (state.input_files.empty()) {
841593b6
AL
580 state.explicit_language = args[i + 1];
581 }
582 i++;
62f3217e 583 return Statistic::none;
756e03d1 584 }
b2c18fa6 585
3781f256 586 DEBUG_ASSERT(arg.length() >= 3);
7f77c031 587 if (state.input_files.empty()) {
3781f256 588 state.explicit_language = arg.substr(2);
756e03d1 589 }
62f3217e 590 return Statistic::none;
756e03d1
JR
591 }
592
593 // We need to work out where the output was meant to go.
3781f256 594 if (arg == "-o") {
4cf3d79f 595 if (i == args.size() - 1) {
60904e7b 596 LOG("Missing argument to {}", args[i]);
c0248cf7 597 return Statistic::bad_compiler_arguments;
756e03d1 598 }
fec40553 599 args_info.output_obj = args[i + 1];
756e03d1 600 i++;
62f3217e 601 return Statistic::none;
756e03d1
JR
602 }
603
604 // Alternate form of -o with no space. Nvcc does not support this.
b02c0012
OS
605 // Cl does support it as deprecated, but also has -openmp or -link -out
606 // which can confuse this and cause incorrect output_obj (and thus
607 // ccache debug file location), so better ignore it.
3781f256 608 if (util::starts_with(arg, "-o")
b02c0012
OS
609 && config.compiler_type() != CompilerType::nvcc
610 && config.compiler_type() != CompilerType::msvc) {
3781f256 611 args_info.output_obj = arg.substr(2);
62f3217e 612 return Statistic::none;
756e03d1
JR
613 }
614
3781f256
JR
615 if (util::starts_with(arg, "-fdebug-prefix-map=")
616 || util::starts_with(arg, "-ffile-prefix-map=")) {
617 std::string map = arg.substr(arg.find('=') + 1);
0d805579
JR
618 args_info.debug_prefix_maps.push_back(map);
619 state.common_args.push_back(args[i]);
62f3217e 620 return Statistic::none;
756e03d1
JR
621 }
622
623 // Debugging is handled specially, so that we know if we can strip line
624 // number info.
3781f256 625 if (util::starts_with(arg, "-g")) {
4cf3d79f 626 state.common_args.push_back(args[i]);
756e03d1 627
3781f256 628 if (util::starts_with(arg, "-gdwarf")) {
756e03d1
JR
629 // Selection of DWARF format (-gdwarf or -gdwarf-<version>) enables
630 // debug info on level 2.
631 args_info.generating_debuginfo = true;
62f3217e 632 return Statistic::none;
756e03d1
JR
633 }
634
3781f256 635 if (util::starts_with(arg, "-gz")) {
756e03d1 636 // -gz[=type] neither disables nor enables debug info.
62f3217e 637 return Statistic::none;
756e03d1
JR
638 }
639
3781f256 640 char last_char = arg.back();
756e03d1
JR
641 if (last_char == '0') {
642 // "-g0", "-ggdb0" or similar: All debug information disabled.
643 args_info.generating_debuginfo = false;
644 state.generating_debuginfo_level_3 = false;
645 } else {
646 args_info.generating_debuginfo = true;
647 if (last_char == '3') {
648 state.generating_debuginfo_level_3 = true;
649 }
3781f256 650 if (arg == "-gsplit-dwarf") {
756e03d1
JR
651 args_info.seen_split_dwarf = true;
652 }
653 }
62f3217e 654 return Statistic::none;
756e03d1
JR
655 }
656
e9d33e4f 657 if (config.is_compiler_group_msvc() && !config.is_compiler_group_clang()
6ee19c33
JR
658 && is_msvc_z_debug_option(arg)) {
659 state.last_seen_msvc_z_debug_option = args[i];
660 state.common_args.push_back(args[i]);
661 return Statistic::none;
d099c180
JR
662 }
663
908ca1e2
HQJ
664 if (config.is_compiler_group_msvc() && util::starts_with(arg, "-Fd")) {
665 state.compiler_only_args_no_hash.push_back(args[i]);
666 return Statistic::none;
667 }
668
669 if (config.is_compiler_group_msvc()
670 && (util::starts_with(arg, "-MP") || arg == "-FS")) {
671 state.compiler_only_args_no_hash.push_back(args[i]);
672 return Statistic::none;
673 }
674
756e03d1
JR
675 // These options require special handling, because they behave differently
676 // with gcc -E, when the output file is not specified.
3781f256 677 if ((arg == "-MD" || arg == "-MMD") && !config.is_compiler_group_msvc()) {
fec40553 678 state.found_md_or_mmd_opt = true;
756e03d1 679 args_info.generating_dependencies = true;
4cf3d79f 680 state.dep_args.push_back(args[i]);
62f3217e 681 return Statistic::none;
756e03d1 682 }
4cf3d79f 683
3781f256 684 if (util::starts_with(arg, "-MF")) {
fec40553 685 state.found_mf_opt = true;
756e03d1 686
4cf3d79f 687 std::string dep_file;
3781f256 688 bool separate_argument = (arg.size() == 3);
756e03d1
JR
689 if (separate_argument) {
690 // -MF arg
4cf3d79f 691 if (i == args.size() - 1) {
60904e7b 692 LOG("Missing argument to {}", args[i]);
c0248cf7 693 return Statistic::bad_compiler_arguments;
756e03d1 694 }
4cf3d79f 695 dep_file = args[i + 1];
756e03d1
JR
696 i++;
697 } else {
698 // -MFarg or -MF=arg (EDG-based compilers)
3781f256 699 dep_file = arg.substr(arg[3] == '=' ? 4 : 3);
756e03d1 700 }
fec40553
JR
701
702 if (state.output_dep_origin <= OutputDepOrigin::mf) {
703 state.output_dep_origin = OutputDepOrigin::mf;
704 args_info.output_dep = Util::make_relative_path(ctx, dep_file);
705 }
756e03d1
JR
706 // Keep the format of the args the same.
707 if (separate_argument) {
4cf3d79f 708 state.dep_args.push_back("-MF");
756e03d1
JR
709 state.dep_args.push_back(args_info.output_dep);
710 } else {
4cf3d79f 711 state.dep_args.push_back("-MF" + args_info.output_dep);
756e03d1 712 }
62f3217e 713 return Statistic::none;
756e03d1 714 }
4cf3d79f 715
3781f256 716 if ((util::starts_with(arg, "-MQ") || util::starts_with(arg, "-MT"))
caa5bcbb 717 && !config.is_compiler_group_msvc()) {
3781f256 718 const bool is_mq = arg[2] == 'Q';
756e03d1 719
fec40553 720 std::string_view dep_target;
3781f256 721 if (arg.size() == 3) {
756e03d1 722 // -MQ arg or -MT arg
4cf3d79f 723 if (i == args.size() - 1) {
60904e7b 724 LOG("Missing argument to {}", args[i]);
c0248cf7 725 return Statistic::bad_compiler_arguments;
756e03d1 726 }
4cf3d79f 727 state.dep_args.push_back(args[i]);
fec40553
JR
728 state.dep_args.push_back(args[i + 1]);
729 dep_target = args[i + 1];
756e03d1
JR
730 i++;
731 } else {
fec40553 732 // -MQarg or -MTarg
3781f256 733 const std::string_view arg_view(arg);
fec40553
JR
734 const auto arg_opt = arg_view.substr(0, 3);
735 dep_target = arg_view.substr(3);
736 state.dep_args.push_back(FMT("{}{}", arg_opt, dep_target));
756e03d1 737 }
fec40553
JR
738
739 if (args_info.dependency_target) {
740 args_info.dependency_target->push_back(' ');
741 } else {
742 args_info.dependency_target = "";
743 }
744 *args_info.dependency_target +=
745 is_mq ? Depfile::escape_filename(dep_target) : dep_target;
746
62f3217e 747 return Statistic::none;
756e03d1 748 }
4cf3d79f 749
b6e841c0
LL
750 // MSVC -MD[d], -MT[d] and -LT[d] options are something different than GCC's
751 // -MD etc.
caa5bcbb 752 if (config.is_compiler_group_msvc()
3781f256
JR
753 && (util::starts_with(arg, "-MD") || util::starts_with(arg, "-MT")
754 || util::starts_with(arg, "-LD"))) {
b6e841c0
LL
755 // These affect compiler but also #define some things.
756 state.cpp_args.push_back(args[i]);
757 state.common_args.push_back(args[i]);
62f3217e 758 return Statistic::none;
b6e841c0
LL
759 }
760
3781f256 761 if (arg == "-showIncludes") {
b1348e5f
OS
762 args_info.generating_includes = true;
763 state.dep_args.push_back(args[i]);
764 return Statistic::none;
765 }
766
3781f256 767 if (arg == "-fprofile-arcs") {
756e03d1 768 args_info.profile_arcs = true;
6cb3973a 769 state.common_args.push_back(args[i]);
62f3217e 770 return Statistic::none;
756e03d1 771 }
6cb3973a 772
3781f256 773 if (arg == "-ftest-coverage") {
756e03d1 774 args_info.generating_coverage = true;
6cb3973a 775 state.common_args.push_back(args[i]);
62f3217e 776 return Statistic::none;
756e03d1 777 }
6cb3973a 778
3781f256 779 if (arg == "-fstack-usage") {
756e03d1 780 args_info.generating_stackusage = true;
6cb3973a 781 state.common_args.push_back(args[i]);
62f3217e 782 return Statistic::none;
756e03d1 783 }
6cb3973a 784
5cfd4b9c 785 // -Zs is MSVC's -fsyntax-only equivalent
3781f256 786 if (arg == "-fsyntax-only" || arg == "-Zs") {
f258b70e
AL
787 args_info.expect_output_obj = false;
788 state.compiler_only_args.push_back(args[i]);
5cfd4b9c 789 state.found_syntax_only = true;
62f3217e 790 return Statistic::none;
f258b70e
AL
791 }
792
3781f256
JR
793 if (arg == "--coverage" // = -fprofile-arcs -ftest-coverage
794 || arg == "-coverage") { // Undocumented but still works.
756e03d1
JR
795 args_info.profile_arcs = true;
796 args_info.generating_coverage = true;
6cb3973a 797 state.common_args.push_back(args[i]);
62f3217e 798 return Statistic::none;
756e03d1 799 }
6cb3973a 800
3781f256 801 if (arg == "-fprofile-abs-path") {
03023ee7 802 if (!config.sloppiness().contains(core::Sloppy::gcno_cwd)) {
195011ad
JR
803 // -fprofile-abs-path makes the compiler include absolute paths based on
804 // the actual CWD in the .gcno file.
805 state.hash_actual_cwd = true;
806 }
807 return Statistic::none;
808 }
809
3781f256
JR
810 if (util::starts_with(arg, "-fprofile-")
811 || util::starts_with(arg, "-fauto-profile")
812 || arg == "-fbranch-probabilities") {
813 if (!process_profiling_option(ctx, args_info, arg)) {
756e03d1 814 // The failure is logged by process_profiling_option.
c0248cf7 815 return Statistic::unsupported_compiler_option;
756e03d1 816 }
6cb3973a 817 state.common_args.push_back(args[i]);
62f3217e 818 return Statistic::none;
756e03d1 819 }
6cb3973a 820
3781f256 821 if (util::starts_with(arg, "-fsanitize-blacklist=")) {
6cb3973a
JR
822 args_info.sanitize_blacklists.emplace_back(args[i].substr(21));
823 state.common_args.push_back(args[i]);
62f3217e 824 return Statistic::none;
756e03d1 825 }
6cb3973a 826
3781f256
JR
827 if (util::starts_with(arg, "--sysroot=")) {
828 auto path = std::string_view(arg).substr(10);
6cb3973a
JR
829 auto relpath = Util::make_relative_path(ctx, path);
830 state.common_args.push_back("--sysroot=" + relpath);
62f3217e 831 return Statistic::none;
756e03d1 832 }
6cb3973a 833
756e03d1 834 // Alternate form of specifying sysroot without =
3781f256 835 if (arg == "--sysroot") {
6cb3973a 836 if (i == args.size() - 1) {
60904e7b 837 LOG("Missing argument to {}", args[i]);
c0248cf7 838 return Statistic::bad_compiler_arguments;
756e03d1 839 }
6cb3973a
JR
840 state.common_args.push_back(args[i]);
841 auto relpath = Util::make_relative_path(ctx, args[i + 1]);
756e03d1
JR
842 state.common_args.push_back(relpath);
843 i++;
62f3217e 844 return Statistic::none;
756e03d1 845 }
6cb3973a 846
756e03d1 847 // Alternate form of specifying target without =
3781f256 848 if (arg == "-target") {
6cb3973a 849 if (i == args.size() - 1) {
60904e7b 850 LOG("Missing argument to {}", args[i]);
c0248cf7 851 return Statistic::bad_compiler_arguments;
756e03d1 852 }
6cb3973a
JR
853 state.common_args.push_back(args[i]);
854 state.common_args.push_back(args[i + 1]);
756e03d1 855 i++;
62f3217e 856 return Statistic::none;
756e03d1 857 }
6cb3973a 858
3781f256 859 if (arg == "-P" || arg == "-Wp,-P") {
0b9c3a2d
JR
860 // Avoid passing -P to the preprocessor since it removes preprocessor
861 // information we need.
862 state.compiler_only_args.push_back(args[i]);
863 LOG("{} used; not compiling preprocessed code", args[i]);
864 config.set_run_second_cpp(true);
62f3217e 865 return Statistic::none;
0b9c3a2d
JR
866 }
867
3781f256
JR
868 if (util::starts_with(arg, "-Wp,")) {
869 if (arg.find(",-P,") != std::string::npos || util::ends_with(arg, ",-P")) {
1ae1ae2b
JR
870 LOG("-P together with other preprocessor options is too hard: {}",
871 args[i]);
c0248cf7 872 return Statistic::unsupported_compiler_option;
3781f256
JR
873 } else if (util::starts_with(arg, "-Wp,-MD,")
874 && arg.find(',', 8) == std::string::npos) {
fec40553 875 state.found_wp_md_or_mmd_opt = true;
756e03d1 876 args_info.generating_dependencies = true;
fec40553
JR
877 if (state.output_dep_origin <= OutputDepOrigin::wp) {
878 state.output_dep_origin = OutputDepOrigin::wp;
3781f256 879 args_info.output_dep = arg.substr(8);
fec40553 880 }
6cb3973a 881 state.dep_args.push_back(args[i]);
62f3217e 882 return Statistic::none;
3781f256
JR
883 } else if (util::starts_with(arg, "-Wp,-MMD,")
884 && arg.find(',', 9) == std::string::npos) {
fec40553 885 state.found_wp_md_or_mmd_opt = true;
756e03d1 886 args_info.generating_dependencies = true;
fec40553
JR
887 if (state.output_dep_origin <= OutputDepOrigin::wp) {
888 state.output_dep_origin = OutputDepOrigin::wp;
3781f256 889 args_info.output_dep = arg.substr(9);
fec40553 890 }
6cb3973a 891 state.dep_args.push_back(args[i]);
62f3217e 892 return Statistic::none;
3781f256
JR
893 } else if ((util::starts_with(arg, "-Wp,-D")
894 || util::starts_with(arg, "-Wp,-U"))
895 && arg.find(',', 6) == std::string::npos) {
00fe1341 896 state.cpp_args.push_back(args[i]);
62f3217e 897 return Statistic::none;
3781f256
JR
898 } else if (arg == "-Wp,-MP"
899 || (arg.size() > 8 && util::starts_with(arg, "-Wp,-M")
900 && arg[7] == ','
901 && (arg[6] == 'F' || arg[6] == 'Q' || arg[6] == 'T')
902 && arg.find(',', 8) == std::string::npos)) {
6cb3973a 903 state.dep_args.push_back(args[i]);
62f3217e 904 return Statistic::none;
756e03d1
JR
905 } else if (config.direct_mode()) {
906 // -Wp, can be used to pass too hard options to the preprocessor.
907 // Hence, disable direct mode.
60904e7b 908 LOG("Unsupported compiler option for direct mode: {}", args[i]);
756e03d1
JR
909 config.set_direct_mode(false);
910 }
911
912 // Any other -Wp,* arguments are only relevant for the preprocessor.
6cb3973a 913 state.cpp_args.push_back(args[i]);
62f3217e 914 return Statistic::none;
756e03d1 915 }
6cb3973a 916
3781f256 917 if (arg == "-MP") {
6cb3973a 918 state.dep_args.push_back(args[i]);
62f3217e 919 return Statistic::none;
756e03d1
JR
920 }
921
922 // Input charset needs to be handled specially.
3781f256 923 if (util::starts_with(arg, "-finput-charset=")) {
9915191f 924 state.input_charset_option = args[i];
62f3217e 925 return Statistic::none;
756e03d1
JR
926 }
927
3781f256 928 if (arg == "--serialize-diagnostics") {
6cb3973a 929 if (i == args.size() - 1) {
60904e7b 930 LOG("Missing argument to {}", args[i]);
c0248cf7 931 return Statistic::bad_compiler_arguments;
756e03d1
JR
932 }
933 args_info.generating_diagnostics = true;
6cb3973a 934 args_info.output_dia = Util::make_relative_path(ctx, args[i + 1]);
756e03d1 935 i++;
62f3217e 936 return Statistic::none;
756e03d1
JR
937 }
938
96ec6c9d 939 if (config.compiler_type() == CompilerType::gcc) {
3781f256 940 if (arg == "-fdiagnostics-color" || arg == "-fdiagnostics-color=always") {
96ec6c9d
JR
941 state.color_diagnostics = ColorDiagnostics::always;
942 state.compiler_only_args_no_hash.push_back(args[i]);
943 return Statistic::none;
3781f256
JR
944 } else if (arg == "-fno-diagnostics-color"
945 || arg == "-fdiagnostics-color=never") {
96ec6c9d
JR
946 state.color_diagnostics = ColorDiagnostics::never;
947 state.compiler_only_args_no_hash.push_back(args[i]);
948 return Statistic::none;
3781f256 949 } else if (arg == "-fdiagnostics-color=auto") {
96ec6c9d
JR
950 state.color_diagnostics = ColorDiagnostics::automatic;
951 state.compiler_only_args_no_hash.push_back(args[i]);
952 return Statistic::none;
953 }
954 } else if (config.is_compiler_group_clang()) {
955 // In the "-Xclang -fcolor-diagnostics" form, -Xclang is skipped and the
956 // -fcolor-diagnostics argument which is passed to cc1 is handled below.
3781f256 957 if (arg == "-Xclang" && i + 1 < args.size()
96ec6c9d
JR
958 && args[i + 1] == "-fcolor-diagnostics") {
959 state.compiler_only_args_no_hash.push_back(args[i]);
960 ++i;
3781f256 961 arg = make_dash_option(ctx.config, args[i]);
96ec6c9d 962 }
3781f256 963 if (arg == "-fcolor-diagnostics") {
96ec6c9d
JR
964 state.color_diagnostics = ColorDiagnostics::always;
965 state.compiler_only_args_no_hash.push_back(args[i]);
966 return Statistic::none;
3781f256 967 } else if (arg == "-fno-color-diagnostics") {
96ec6c9d
JR
968 state.color_diagnostics = ColorDiagnostics::never;
969 state.compiler_only_args_no_hash.push_back(args[i]);
970 return Statistic::none;
971 }
756e03d1
JR
972 }
973
974 // GCC
3781f256 975 if (arg == "-fdirectives-only") {
756e03d1 976 state.found_directives_only = true;
62f3217e 977 return Statistic::none;
756e03d1 978 }
6cb3973a 979
756e03d1 980 // Clang
3781f256 981 if (arg == "-frewrite-includes") {
756e03d1 982 state.found_rewrite_includes = true;
62f3217e 983 return Statistic::none;
756e03d1
JR
984 }
985
3781f256 986 if (arg == "-fno-pch-timestamp") {
3ec58dbc
AH
987 args_info.fno_pch_timestamp = true;
988 state.common_args.push_back(args[i]);
62f3217e 989 return Statistic::none;
3ec58dbc
AH
990 }
991
3781f256 992 if (arg == "-fpch-preprocess") {
3ec58dbc
AH
993 state.found_fpch_preprocess = true;
994 state.common_args.push_back(args[i]);
62f3217e 995 return Statistic::none;
3ec58dbc
AH
996 }
997
03023ee7 998 if (config.sloppiness().contains(core::Sloppy::clang_index_store)
3781f256 999 && arg == "-index-store-path") {
6cb3973a
JR
1000 // Xcode 9 or later calls Clang with this option. The given path includes a
1001 // UUID that might lead to cache misses, especially when cache is shared
1002 // among multiple users.
756e03d1 1003 i++;
6cb3973a 1004 if (i <= args.size() - 1) {
60904e7b 1005 LOG("Skipping argument -index-store-path {}", args[i]);
756e03d1 1006 }
62f3217e 1007 return Statistic::none;
756e03d1
JR
1008 }
1009
3781f256 1010 if (arg == "-frecord-gcc-switches") {
0492eb74 1011 state.hash_full_command_line = true;
dff6db77
JR
1012 LOG_RAW(
1013 "Found -frecord-gcc-switches, hashing original command line unmodified");
0492eb74
JR
1014 }
1015
0a644e6e 1016 // MSVC -u is something else than GCC -u, handle it specially.
3781f256 1017 if (arg == "-u" && ctx.config.is_compiler_group_msvc()) {
0a644e6e 1018 state.cpp_args.push_back(args[i]);
62f3217e 1019 return Statistic::none;
0a644e6e
LL
1020 }
1021
3781f256 1022 if (compopt_takes_path(arg)) {
6cb3973a 1023 if (i == args.size() - 1) {
60904e7b 1024 LOG("Missing argument to {}", args[i]);
c0248cf7 1025 return Statistic::bad_compiler_arguments;
756e03d1
JR
1026 }
1027
3ec58dbc
AH
1028 // In the -Xclang -include-(pch/pth) -Xclang <path> case, the path is one
1029 // index further behind.
c8fb5395 1030 const size_t next = args[i + 1] == "-Xclang" && i + 2 < args.size() ? 2 : 1;
3ec58dbc 1031
3781f256
JR
1032 if (!detect_pch(
1033 arg, args[i + next], args_info.included_pch_file, next == 2, state)) {
c0248cf7 1034 return Statistic::bad_compiler_arguments;
756e03d1
JR
1035 }
1036
c8fb5395
JR
1037 // Potentially rewrite path argument to relative path to get better hit
1038 // rate. A secondary effect is that paths in the standard error output
1039 // produced by the compiler will be normalized.
3ec58dbc
AH
1040 std::string relpath = Util::make_relative_path(ctx, args[i + next]);
1041 auto& dest_args =
3781f256 1042 compopt_affects_cpp_output(arg) ? state.cpp_args : state.common_args;
3ec58dbc
AH
1043 dest_args.push_back(args[i]);
1044 if (next == 2) {
1045 dest_args.push_back(args[i + 1]);
756e03d1 1046 }
3ec58dbc 1047 dest_args.push_back(relpath);
756e03d1 1048
3ec58dbc 1049 i += next;
62f3217e 1050 return Statistic::none;
756e03d1
JR
1051 }
1052
b4d25746 1053 // Detect PCH for options with concatenated path (relative or absolute).
3781f256
JR
1054 if (util::starts_with(arg, "-include") || util::starts_with(arg, "-Fp")
1055 || util::starts_with(arg, "-Yu")) {
1056 const size_t path_pos = util::starts_with(arg, "-include") ? 8 : 3;
1057 if (!detect_pch(arg.substr(0, path_pos),
1058 arg.substr(path_pos),
b4d25746
JR
1059 args_info.included_pch_file,
1060 false,
1061 state)) {
1062 return Statistic::bad_compiler_arguments;
1063 }
1064
1065 // Fall through to the next section, so intentionally not returning here.
1066 }
1067
c8fb5395 1068 // Potentially rewrite concatenated absolute path argument to relative.
3781f256
JR
1069 if (arg[0] == '-') {
1070 const auto path_pos = Util::is_absolute_path_with_prefix(arg);
09dea223
JR
1071 if (path_pos) {
1072 const std::string option = args[i].substr(0, *path_pos);
756e03d1 1073 if (compopt_takes_concat_arg(option) && compopt_takes_path(option)) {
60005c83 1074 const auto relpath = Util::make_relative_path(
3781f256 1075 ctx, std::string_view(arg).substr(*path_pos));
6cb3973a 1076 std::string new_option = option + relpath;
27f00db4 1077 if (compopt_affects_cpp_output(option)) {
6cb3973a 1078 state.cpp_args.push_back(new_option);
756e03d1 1079 } else {
6cb3973a 1080 state.common_args.push_back(new_option);
756e03d1 1081 }
62f3217e 1082 return Statistic::none;
756e03d1
JR
1083 }
1084 }
1085 }
1086
1087 // Options that take an argument.
3781f256 1088 if (compopt_takes_arg(arg)) {
6cb3973a 1089 if (i == args.size() - 1) {
60904e7b 1090 LOG("Missing argument to {}", args[i]);
c0248cf7 1091 return Statistic::bad_compiler_arguments;
756e03d1
JR
1092 }
1093
3781f256 1094 if (compopt_affects_cpp_output(arg)) {
6cb3973a
JR
1095 state.cpp_args.push_back(args[i]);
1096 state.cpp_args.push_back(args[i + 1]);
756e03d1 1097 } else {
6cb3973a
JR
1098 state.common_args.push_back(args[i]);
1099 state.common_args.push_back(args[i + 1]);
756e03d1
JR
1100 }
1101
1102 i++;
62f3217e 1103 return Statistic::none;
756e03d1
JR
1104 }
1105
dfc7a901
JR
1106 if (args[i] == "--") {
1107 args_info.seen_double_dash = true;
1108 return Statistic::none;
1109 }
1110
756e03d1 1111 // Other options.
3781f256
JR
1112 if (arg[0] == '-') {
1113 if (compopt_affects_cpp_output(arg)
1114 || compopt_prefix_affects_cpp_output(arg)) {
6cb3973a 1115 state.cpp_args.push_back(args[i]);
3781f256
JR
1116 return Statistic::none;
1117 } else if (ctx.config.is_compiler_group_msvc()
1118 && args[i][0] == '/' // Intentionally not checking arg here
c86a4628 1119 && DirEntry(args[i]).is_regular_file()) {
3781f256 1120 // Likely the input file, which is handled in process_arg later.
756e03d1 1121 } else {
6cb3973a 1122 state.common_args.push_back(args[i]);
3781f256 1123 return Statistic::none;
756e03d1 1124 }
756e03d1
JR
1125 }
1126
15e315eb 1127 // It was not a known option.
62f3217e
JR
1128 return std::nullopt;
1129}
1130
1131Statistic
1132process_arg(const Context& ctx,
1133 ArgsInfo& args_info,
1134 Config& config,
1135 Args& args,
1136 size_t& args_index,
1137 ArgumentProcessingState& state)
1138{
1139 const auto processed =
1140 process_option_arg(ctx, args_info, config, args, args_index, state);
1141 if (processed) {
1142 const auto& error = *processed;
1143 return error;
1144 }
1145
1146 size_t& i = args_index;
1147
20ab91b3 1148 // If an argument isn't a plain file then assume it's an option, not an input
6cb3973a 1149 // file. This allows us to cope better with unusual compiler options.
756e03d1
JR
1150 //
1151 // Note that "/dev/null" is an exception that is sometimes used as an input
1152 // file when code is testing compiler flags.
c86a4628
JR
1153 if (!util::is_dev_null_path(args[i])) {
1154 if (!DirEntry(args[i]).is_regular_file()) {
60904e7b 1155 LOG("{} is not a regular file, not considering as input file", args[i]);
6cb3973a 1156 state.common_args.push_back(args[i]);
62f3217e 1157 return Statistic::none;
756e03d1
JR
1158 }
1159 }
1160
7f77c031
JR
1161 if (fs::exists(args[i])) {
1162 LOG("Detected input file: {}", args[i]);
1163 state.input_files.emplace_back(args[i]);
1164 } else {
1165 LOG("Not considering {} an input file since it doesn't exist", args[i]);
1166 state.common_args.push_back(args[i]);
756e03d1 1167 }
62f3217e 1168 return Statistic::none;
756e03d1
JR
1169}
1170
fec40553
JR
1171const char*
1172get_default_object_file_extension(const Config& config)
025f0d03 1173{
fec40553 1174 return config.is_compiler_group_msvc() ? ".obj" : ".o";
025f0d03
JR
1175}
1176
756e03d1
JR
1177} // namespace
1178
225497c0
AL
1179ProcessArgsResult
1180process_args(Context& ctx)
756e03d1 1181{
706f7443 1182 ASSERT(!ctx.orig_args.empty());
756e03d1
JR
1183
1184 ArgsInfo& args_info = ctx.args_info;
1185 Config& config = ctx.config;
1186
1187 // args is a copy of the original arguments given to the compiler but with
1188 // arguments from @file and similar constructs expanded. It's only used as a
1189 // temporary data structure to loop over.
1190 Args args = ctx.orig_args;
1191 ArgumentProcessingState state;
1192
1193 state.common_args.push_back(args[0]); // Compiler
1194
eea89d4a 1195 std::optional<Statistic> argument_error;
756e03d1 1196 for (size_t i = 1; i < args.size(); i++) {
e04705a5 1197 const auto error = process_arg(ctx, args_info, ctx.config, args, i, state);
62f3217e 1198 if (error != Statistic::none && !argument_error) {
9f19f94b 1199 argument_error = error;
756e03d1
JR
1200 }
1201 }
1202
7f77c031
JR
1203 const bool is_link =
1204 !(state.found_c_opt || state.found_dc_opt || state.found_S_opt
1205 || state.found_syntax_only || state.found_analyze_opt);
1206
1207 if (state.input_files.empty()) {
1208 LOG_RAW("No input file found");
1209 return Statistic::no_input_file;
1210 }
1211 if (state.input_files.size() > 1) {
1212 if (is_link) {
1213 LOG_RAW("Called for link");
1214 return state.input_files.front().string().find("conftest.")
1215 != std::string::npos
1216 ? Statistic::autoconf_test
1217 : Statistic::called_for_link;
1218 } else {
1219 LOG_RAW("Multiple input files");
1220 return Statistic::multiple_source_files;
1221 }
1222 }
1223
1224 args_info.orig_input_file = state.input_files.front().string();
1225 // Rewrite to relative to increase hit rate.
1226 args_info.input_file =
1227 Util::make_relative_path(ctx, args_info.orig_input_file);
1228 args_info.normalized_input_file =
1229 Util::normalize_concrete_absolute_path(args_info.input_file);
1230
fec40553
JR
1231 // Bail out on too hard combinations of options.
1232 if (state.found_mf_opt && state.found_wp_md_or_mmd_opt) {
1233 // GCC and Clang behave differently when "-Wp,-M[M]D,wp.d" and "-MF mf.d"
1234 // are used: GCC writes to wp.d but Clang writes to mf.d. We could
1235 // potentially support this by behaving differently depending on the
1236 // compiler type, but let's just bail out for now.
1237 LOG_RAW("-Wp,-M[M]D in combination with -MF is not supported");
1238 return Statistic::unsupported_compiler_option;
d099c180
JR
1239 }
1240
6ee19c33
JR
1241 if (!state.last_seen_msvc_z_debug_option.empty()
1242 && state.last_seen_msvc_z_debug_option.substr(2) != "7") {
d099c180 1243 // /Zi and /ZI are unsupported, but /Z7 is fine.
6ee19c33
JR
1244 LOG("Compiler option {} is unsupported",
1245 state.last_seen_msvc_z_debug_option);
d099c180 1246 return Statistic::unsupported_compiler_option;
fec40553 1247 }
fec40553 1248
9f19f94b
JR
1249 // Don't try to second guess the compiler's heuristics for stdout handling.
1250 if (args_info.output_obj == "-") {
1251 LOG_RAW("Output file is -");
1252 return Statistic::output_to_stdout;
1253 }
1254
1255 // Determine output object file.
cfda479f
OS
1256 bool output_obj_by_source = args_info.output_obj.empty();
1257 if (!output_obj_by_source && ctx.config.is_compiler_group_msvc()) {
1258 if (*args_info.output_obj.rbegin() == '\\') {
1259 output_obj_by_source = true;
c86a4628
JR
1260 } else if (DirEntry(args_info.output_obj).is_directory()) {
1261 args_info.output_obj.append("\\");
1262 output_obj_by_source = true;
cfda479f
OS
1263 }
1264 }
1265
1266 if (output_obj_by_source && !args_info.input_file.empty()) {
67da9633
M
1267 std::string_view extension;
1268 if (state.found_analyze_opt) {
1269 extension = ".plist";
1270 } else if (state.found_S_opt) {
1271 extension = ".s";
1272 } else {
1273 extension = get_default_object_file_extension(ctx.config);
1274 }
e026c786
JR
1275 args_info.output_obj +=
1276 Util::change_extension(Util::base_name(args_info.input_file), extension);
9f19f94b
JR
1277 }
1278
fec40553
JR
1279 args_info.orig_output_obj = args_info.output_obj;
1280 args_info.output_obj = Util::make_relative_path(ctx, args_info.output_obj);
1281
1282 // Determine output dependency file.
1283
9f19f94b
JR
1284 // On argument processing error, return now since we have determined
1285 // args_info.output_obj which is needed to determine the log filename in
1286 // CCACHE_DEBUG mode.
1287 if (argument_error) {
1288 return *argument_error;
1289 }
1290
756e03d1 1291 if (state.generating_debuginfo_level_3 && !config.run_second_cpp()) {
19fa063b
JR
1292 // Debug level 3 makes line number information incorrect when compiling
1293 // preprocessed code.
60904e7b 1294 LOG_RAW("Generating debug info level 3; not compiling preprocessed code");
756e03d1
JR
1295 config.set_run_second_cpp(true);
1296 }
1297
19fa063b
JR
1298#ifdef __APPLE__
1299 // Newer Clang versions on macOS are known to produce different debug
1300 // information when compiling preprocessed code.
1301 if (args_info.generating_debuginfo && !config.run_second_cpp()) {
1302 LOG_RAW("Generating debug info; not compiling preprocessed code");
1303 config.set_run_second_cpp(true);
1304 }
1305#endif
1306
756e03d1
JR
1307 if (state.found_pch || state.found_fpch_preprocess) {
1308 args_info.using_precompiled_header = true;
03023ee7 1309 if (!(config.sloppiness().contains(core::Sloppy::time_macros))) {
60904e7b 1310 LOG_RAW(
756e03d1
JR
1311 "You have to specify \"time_macros\" sloppiness when using"
1312 " precompiled headers to get direct hits");
60904e7b 1313 LOG_RAW("Disabling direct mode");
c0248cf7 1314 return Statistic::could_not_use_precompiled_header;
756e03d1
JR
1315 }
1316 }
1317
1318 if (args_info.profile_path.empty()) {
1319 args_info.profile_path = ctx.apparent_cwd;
1320 }
1321
55507575
JR
1322 if (!state.explicit_language.empty() && state.explicit_language == "none") {
1323 state.explicit_language.clear();
756e03d1 1324 }
55507575 1325 if (!state.explicit_language.empty()) {
9fac7eee 1326 if (!language_is_supported(state.explicit_language)) {
60904e7b 1327 LOG("Unsupported language: {}", state.explicit_language);
c0248cf7 1328 return Statistic::unsupported_source_language;
756e03d1 1329 }
55507575 1330 args_info.actual_language = state.explicit_language;
756e03d1 1331 } else {
50737d3f
JR
1332 args_info.actual_language =
1333 language_for_file(args_info.input_file, config.compiler_type());
756e03d1
JR
1334 }
1335
1336 args_info.output_is_precompiled_header =
3ec58dbc 1337 args_info.actual_language.find("-header") != std::string::npos
e8b54fc8 1338 || is_precompiled_header(args_info.output_obj);
756e03d1 1339
cfda479f 1340 if (args_info.output_is_precompiled_header && output_obj_by_source) {
fec40553
JR
1341 args_info.orig_output_obj = args_info.orig_input_file + ".gch";
1342 args_info.output_obj =
1343 Util::make_relative_path(ctx, args_info.orig_output_obj);
9f19f94b
JR
1344 }
1345
756e03d1 1346 if (args_info.output_is_precompiled_header
03023ee7 1347 && !(config.sloppiness().contains(core::Sloppy::pch_defines))) {
60904e7b 1348 LOG_RAW(
756e03d1
JR
1349 "You have to specify \"pch_defines,time_macros\" sloppiness when"
1350 " creating precompiled headers");
c0248cf7 1351 return Statistic::could_not_use_precompiled_header;
756e03d1
JR
1352 }
1353
7f77c031 1354 if (is_link) {
756e03d1 1355 if (args_info.output_is_precompiled_header) {
cf71924a 1356 state.common_args.push_back("-c");
756e03d1 1357 } else {
60904e7b 1358 LOG_RAW("No -c option found");
cf71924a
JR
1359 // Having a separate statistic for autoconf tests is useful, as they are
1360 // the dominant form of "called for link" in many cases.
1361 return args_info.input_file.find("conftest.") != std::string::npos
c0248cf7
AL
1362 ? Statistic::autoconf_test
1363 : Statistic::called_for_link;
756e03d1
JR
1364 }
1365 }
1366
1367 if (args_info.actual_language.empty()) {
60904e7b 1368 LOG("Unsupported source extension: {}", args_info.input_file);
c0248cf7 1369 return Statistic::unsupported_source_language;
756e03d1
JR
1370 }
1371
b53afeb5
JR
1372 if (args_info.actual_language == "assembler") {
1373 // -MD/-MMD for assembler file does not produce a dependency file.
1374 args_info.generating_dependencies = false;
1375 }
1376
50737d3f
JR
1377 if (!config.run_second_cpp()
1378 && (args_info.actual_language == "cu"
1379 || args_info.actual_language == "cuda")) {
1380 LOG("Source language is \"{}\"; not compiling preprocessed code",
1381 args_info.actual_language);
756e03d1
JR
1382 config.set_run_second_cpp(true);
1383 }
1384
9fac7eee 1385 args_info.direct_i_file = language_is_preprocessed(args_info.actual_language);
756e03d1
JR
1386
1387 if (args_info.output_is_precompiled_header && !config.run_second_cpp()) {
1388 // It doesn't work to create the .gch from preprocessed source.
60904e7b 1389 LOG_RAW("Creating precompiled header; not compiling preprocessed code");
756e03d1
JR
1390 config.set_run_second_cpp(true);
1391 }
1392
1393 if (config.cpp_extension().empty()) {
9fac7eee
JR
1394 std::string p_language = p_language_for_language(args_info.actual_language);
1395 config.set_cpp_extension(extension_for_language(p_language).substr(1));
756e03d1
JR
1396 }
1397
756e03d1 1398 if (args_info.seen_split_dwarf) {
c86a4628 1399 if (util::is_dev_null_path(args_info.output_obj)) {
4caef178
JR
1400 // Outputting to /dev/null -> compiler won't write a .dwo, so just pretend
1401 // we haven't seen the -gsplit-dwarf option.
1402 args_info.seen_split_dwarf = false;
1403 } else {
1404 args_info.output_dwo =
1405 Util::change_extension(args_info.output_obj, ".dwo");
1406 }
756e03d1
JR
1407 }
1408
c86a4628
JR
1409 if (!util::is_dev_null_path(args_info.output_obj)) {
1410 DirEntry entry(args_info.output_obj);
1411 if (entry.exists() && !entry.is_regular_file()) {
60904e7b 1412 LOG("Not a regular file: {}", args_info.output_obj);
c0248cf7 1413 return Statistic::bad_output_file;
756e03d1
JR
1414 }
1415 }
1416
c86a4628
JR
1417 if (util::is_dev_null_path(args_info.output_dep)) {
1418 args_info.generating_dependencies = false;
1419 }
1420
1421 auto output_dir = Util::dir_name(args_info.output_obj);
1422 if (!DirEntry(output_dir).is_directory()) {
60904e7b 1423 LOG("Directory does not exist: {}", output_dir);
c0248cf7 1424 return Statistic::bad_output_file;
756e03d1
JR
1425 }
1426
1427 // Some options shouldn't be passed to the real compiler when it compiles
1428 // preprocessed code:
1429 //
61648752
JR
1430 // -finput-charset=CHARSET (otherwise conversion happens twice)
1431 // -x CHARSET (otherwise the wrong language is selected)
9915191f
JR
1432 if (!state.input_charset_option.empty()) {
1433 state.cpp_args.push_back(state.input_charset_option);
756e03d1 1434 }
b763420f 1435 if (state.found_pch && !ctx.config.is_compiler_group_msvc()) {
cf71924a 1436 state.cpp_args.push_back("-fpch-preprocess");
756e03d1 1437 }
55507575 1438 if (!state.explicit_language.empty()) {
cf71924a
JR
1439 state.cpp_args.push_back("-x");
1440 state.cpp_args.push_back(state.explicit_language);
756e03d1
JR
1441 }
1442
1c6ccf18
MW
1443 args_info.strip_diagnostics_colors =
1444 state.color_diagnostics != ColorDiagnostics::automatic
1445 ? state.color_diagnostics == ColorDiagnostics::never
1446 : !color_output_possible();
50e8d229 1447
756e03d1 1448 // Since output is redirected, compilers will not color their output by
1c6ccf18 1449 // default, so force it explicitly.
eea89d4a 1450 std::optional<std::string> diagnostics_color_arg;
32c70852 1451 if (config.is_compiler_group_clang()) {
0c17dfbb
JR
1452 // Don't pass -fcolor-diagnostics when compiling assembler to avoid an
1453 // "argument unused during compilation" warning.
1c6ccf18 1454 if (args_info.actual_language != "assembler") {
50e8d229 1455 diagnostics_color_arg = "-fcolor-diagnostics";
1c6ccf18 1456 }
e2ab1352 1457 } else if (config.compiler_type() == CompilerType::gcc) {
50e8d229 1458 diagnostics_color_arg = "-fdiagnostics-color";
1c6ccf18
MW
1459 } else {
1460 // Other compilers shouldn't output color, so no need to strip it.
1461 args_info.strip_diagnostics_colors = false;
756e03d1
JR
1462 }
1463
1464 if (args_info.generating_dependencies) {
fec40553
JR
1465 if (state.output_dep_origin == OutputDepOrigin::none) {
1466 args_info.output_dep = Util::change_extension(args_info.output_obj, ".d");
756e03d1
JR
1467 if (!config.run_second_cpp()) {
1468 // If we're compiling preprocessed code we're sending dep_args to the
1469 // preprocessor so we need to use -MF to write to the correct .d file
1470 // location since the preprocessor doesn't know the final object path.
cf71924a 1471 state.dep_args.push_back("-MF");
fec40553 1472 state.dep_args.push_back(args_info.output_dep);
756e03d1
JR
1473 }
1474 }
1475
fec40553 1476 if (!args_info.dependency_target && !config.run_second_cpp()) {
756e03d1
JR
1477 // If we're compiling preprocessed code we're sending dep_args to the
1478 // preprocessor so we need to use -MQ to get the correct target object
1479 // file in the .d file.
cf71924a 1480 state.dep_args.push_back("-MQ");
756e03d1
JR
1481 state.dep_args.push_back(args_info.output_obj);
1482 }
fec40553
JR
1483
1484 if (!args_info.dependency_target) {
84c58a57
JR
1485 std::string dep_target = args_info.orig_output_obj;
1486
1487 // GCC and Clang behave differently when "-Wp,-M[M]D,wp.d" is used with
1488 // "-o" but with neither "-MMD" nor "-MT"/"-MQ": GCC uses a dependency
1489 // target based on the source filename but Clang bases it on the output
1490 // filename.
1491 if (state.found_wp_md_or_mmd_opt && !args_info.output_obj.empty()
1492 && !state.found_md_or_mmd_opt) {
1493 if (config.compiler_type() == CompilerType::clang) {
1494 // Clang does the sane thing: the dependency target is the output file
1495 // so that the dependency file actually makes sense.
1496 } else if (config.compiler_type() == CompilerType::gcc) {
1497 // GCC strangely uses the base name of the source file but with a .o
1498 // extension.
1499 dep_target = Util::change_extension(
1500 Util::base_name(args_info.orig_input_file),
1501 get_default_object_file_extension(ctx.config));
1502 } else {
1503 // How other compilers behave is currently unknown, so bail out.
1504 LOG_RAW(
1505 "-Wp,-M[M]D with -o without -MMD, -MQ or -MT is only supported for"
1506 " GCC or Clang");
1507 return Statistic::unsupported_compiler_option;
1508 }
1509 }
1510
1511 args_info.dependency_target = Depfile::escape_filename(dep_target);
fec40553 1512 }
756e03d1 1513 }
cf71924a 1514
756e03d1 1515 if (args_info.generating_stackusage) {
cf71924a 1516 auto default_sufile_name =
756e03d1
JR
1517 Util::change_extension(args_info.output_obj, ".su");
1518 args_info.output_su = Util::make_relative_path(ctx, default_sufile_name);
1519 }
1520
225497c0 1521 Args compiler_args = state.common_args;
50e8d229 1522 compiler_args.push_back(state.compiler_only_args_no_hash);
cf71924a 1523 compiler_args.push_back(state.compiler_only_args);
756e03d1
JR
1524
1525 if (config.run_second_cpp()) {
cf71924a 1526 compiler_args.push_back(state.cpp_args);
756e03d1
JR
1527 } else if (state.found_directives_only || state.found_rewrite_includes) {
1528 // Need to pass the macros and any other preprocessor directives again.
cf71924a 1529 compiler_args.push_back(state.cpp_args);
756e03d1 1530 if (state.found_directives_only) {
cf71924a 1531 state.cpp_args.push_back("-fdirectives-only");
756e03d1 1532 // The preprocessed source code still needs some more preprocessing.
cf71924a
JR
1533 compiler_args.push_back("-fpreprocessed");
1534 compiler_args.push_back("-fdirectives-only");
756e03d1
JR
1535 }
1536 if (state.found_rewrite_includes) {
cf71924a 1537 state.cpp_args.push_back("-frewrite-includes");
756e03d1 1538 // The preprocessed source code still needs some more preprocessing.
cf71924a 1539 compiler_args.push_back("-x");
756e03d1
JR
1540 compiler_args.push_back(args_info.actual_language);
1541 }
55507575 1542 } else if (!state.explicit_language.empty()) {
756e03d1
JR
1543 // Workaround for a bug in Apple's patched distcc -- it doesn't properly
1544 // reset the language specified with -x, so if -x is given, we have to
1545 // specify the preprocessed language explicitly.
cf71924a
JR
1546 compiler_args.push_back("-x");
1547 compiler_args.push_back(p_language_for_language(state.explicit_language));
756e03d1
JR
1548 }
1549
1550 if (state.found_c_opt) {
cf71924a 1551 compiler_args.push_back("-c");
756e03d1
JR
1552 }
1553
1554 if (state.found_dc_opt) {
cf71924a 1555 compiler_args.push_back("-dc");
756e03d1
JR
1556 }
1557
ab7dd6ff
TN
1558 if (!state.xarch_args.empty()) {
1559 for (const auto& arch : args_info.arch_args) {
1560 auto it = state.xarch_args.find(arch);
1561 if (it != state.xarch_args.end()) {
1562 args_info.xarch_args.emplace(arch, it->second);
1563 }
b9a810af
JR
1564 }
1565 }
1566
7ca8fd6c 1567 for (const auto& arch : args_info.arch_args) {
cf71924a
JR
1568 compiler_args.push_back("-arch");
1569 compiler_args.push_back(arch);
ab7dd6ff
TN
1570
1571 auto it = args_info.xarch_args.find(arch);
1572 if (it != args_info.xarch_args.end()) {
1573 args_info.xarch_args.emplace(arch, it->second);
1574
1575 for (const auto& xarch : it->second) {
1576 compiler_args.push_back("-Xarch_" + arch);
1577 compiler_args.push_back(xarch);
1578 }
1579 }
756e03d1
JR
1580 }
1581
225497c0 1582 Args preprocessor_args = state.common_args;
cf71924a 1583 preprocessor_args.push_back(state.cpp_args);
756e03d1
JR
1584
1585 if (config.run_second_cpp()) {
1586 // When not compiling the preprocessed source code, only pass dependency
1587 // arguments to the compiler to avoid having to add -MQ, supporting e.g.
1588 // EDG-based compilers which don't support -MQ.
cf71924a 1589 compiler_args.push_back(state.dep_args);
756e03d1
JR
1590 } else {
1591 // When compiling the preprocessed source code, pass dependency arguments to
1592 // the preprocessor since the compiler doesn't produce a .d file when
1593 // compiling preprocessed source code.
cf71924a 1594 preprocessor_args.push_back(state.dep_args);
756e03d1
JR
1595 }
1596
225497c0 1597 Args extra_args_to_hash = state.compiler_only_args;
756e03d1 1598 if (config.run_second_cpp()) {
cf71924a 1599 extra_args_to_hash.push_back(state.dep_args);
756e03d1 1600 }
0492eb74
JR
1601 if (state.hash_full_command_line) {
1602 extra_args_to_hash.push_back(ctx.orig_args);
1603 }
756e03d1 1604
50e8d229
JR
1605 if (diagnostics_color_arg) {
1606 compiler_args.push_back(*diagnostics_color_arg);
1607 if (!config.run_second_cpp()) {
1608 // If we're compiling preprocessed code we're keeping any warnings from
1609 // the preprocessor, so we need to make sure that they are in color.
1610 preprocessor_args.push_back(*diagnostics_color_arg);
1611 }
1612 if (ctx.config.depend_mode()) {
1613 // The compiler is invoked with the original arguments in the depend mode.
e04705a5 1614 args_info.depend_extra_args.push_back(*diagnostics_color_arg);
50e8d229
JR
1615 }
1616 }
1617
bda468cf
OS
1618 if (ctx.config.depend_mode() && !args_info.generating_includes
1619 && ctx.config.compiler_type() == CompilerType::msvc) {
1620 ctx.auto_depend_mode = true;
1621 args_info.generating_includes = true;
4ad17e89 1622 args_info.depend_extra_args.push_back("/showIncludes");
bda468cf
OS
1623 }
1624
195011ad
JR
1625 return {
1626 preprocessor_args,
1627 extra_args_to_hash,
1628 compiler_args,
1629 state.hash_actual_cwd,
1630 };
756e03d1 1631}
2737d79e 1632
e8b54fc8
JR
1633bool
1634is_precompiled_header(std::string_view path)
1635{
1636 std::string_view ext = Util::get_extension(path);
1637 return ext == ".gch" || ext == ".pch" || ext == ".pth"
1638 || Util::get_extension(Util::dir_name(path)) == ".gch";
1639}
1640
2737d79e
JR
1641bool
1642option_should_be_ignored(const std::string& arg,
1643 const std::vector<std::string>& patterns)
1644{
1645 return std::any_of(
1646 patterns.cbegin(), patterns.cend(), [&arg](const auto& pattern) {
1647 const auto& prefix =
1648 std::string_view(pattern).substr(0, pattern.length() - 1);
1649 return (
1650 pattern == arg
1651 || (util::ends_with(pattern, "*") && util::starts_with(arg, prefix)));
1652 });
1653}