]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
fix: Keep original order of compiler arguments
authorJoel Rosdahl <joel@rosdahl.net>
Sun, 20 Jul 2025 13:41:44 +0000 (15:41 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Mon, 21 Jul 2025 12:37:19 +0000 (14:37 +0200)
Now when CCACHE_NOCPP2 (run_second_cpp = false) has been removed (see
fd9de83446aeaf1eb1b33a2d900ea1ff757c4509) we can simplify compiler
argument juggling: instead of partitioning arguments into different
lists and reassemble them later, we can build up the preprocessor and
compiler command lines while we go, thus keeping the original order of
arguments.

Fixes #738.

src/ccache/argprocessing.cpp
src/ccache/argprocessing.hpp
test/suites/base.bash
unittest/test_argprocessing.cpp

index ffff647f2ad90452df14ab9e5ab9d668c5703758..7122d294cd06ac1c1ec6582bcda81896077915aa 100644 (file)
@@ -69,10 +69,11 @@ enum class OutputDepOrigin : uint8_t {
   wp = 2
 };
 
-struct ArgumentProcessingState
+class ArgumentProcessingState
 {
-  bool found_c_opt = false;
-  bool found_dc_opt = false;
+public:
+  std::optional<std::string> found_c_opt;
+  std::optional<std::string> found_dc_opt;
   bool found_S_opt = false;
   bool found_analyze_opt = false;
   bool found_pch = false;
@@ -102,32 +103,57 @@ struct ArgumentProcessingState
   // Arguments classified as input files.
   std::vector<fs::path> input_files;
 
-  // common_args contains all original arguments except:
-  // * those that never should be passed to the preprocessor, and
-  // * dependency options (like -MD and friends).
-  Args common_args;
+  // Whether to include the full command line in the hash.
+  bool hash_full_command_line = false;
 
-  // cpp_args contains arguments that were not added to common_args.
-  Args cpp_args;
+  // Whether to include the actual CWD in the hash.
+  bool hash_actual_cwd = false;
 
-  // dep_args contains dependency options like -MD. They are only passed to the
-  // preprocessor, never to the compiler.
-  Args dep_args;
+  template<typename T>
+  void
+  add_common_arg(T&& arg)
+  {
+    m_preprocessor_args.push_back(std::forward<T>(arg));
+    m_compiler_args.push_back(std::forward<T>(arg));
+  }
 
-  // compiler_only_args contains arguments that should only be passed to the
-  // compiler, not the preprocessor.
-  Args compiler_only_args;
+  template<typename T>
+  void
+  add_compiler_only_arg(T&& arg)
+  {
+    m_compiler_args.push_back(std::forward<T>(arg));
+    m_extra_args_to_hash.push_back(std::forward<T>(arg));
+  }
 
-  // compiler_only_args_no_hash contains arguments that should only be passed to
-  // the compiler, not the preprocessor, and that also should not be part of the
-  // hash identifying the result.
-  Args compiler_only_args_no_hash;
+  template<typename T>
+  void
+  add_compiler_only_arg_no_hash(T&& arg)
+  {
+    m_compiler_args.push_back(std::forward<T>(arg));
+  }
 
-  // Whether to include the full command line in the hash.
-  bool hash_full_command_line = false;
+  template<typename T>
+  void
+  add_extra_args_to_hash(T&& args)
+  {
+    m_extra_args_to_hash.push_back(std::forward<T>(args));
+  }
 
-  // Whether to include the actual CWD in the hash.
-  bool hash_actual_cwd = false;
+  ProcessArgsResult
+  to_result()
+  {
+    return {
+      m_preprocessor_args,
+      m_compiler_args,
+      m_extra_args_to_hash,
+      hash_actual_cwd,
+    };
+  }
+
+private:
+  Args m_preprocessor_args;
+  Args m_compiler_args;
+  Args m_extra_args_to_hash;
 };
 
 bool
@@ -345,7 +371,7 @@ process_option_arg(const Context& ctx,
 
   if (option_should_be_ignored(args[i], ctx.ignore_options())) {
     LOG("Not processing ignored option: {}", args[i]);
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -355,7 +381,7 @@ process_option_arg(const Context& ctx,
       LOG_RAW("--ccache-skip lacks an argument");
       return Statistic::bad_compiler_arguments;
     }
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -432,7 +458,7 @@ process_option_arg(const Context& ctx,
 
   if (arg == "-fdump-ipa-clones") {
     args_info.generating_ipa_clones = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -498,11 +524,9 @@ process_option_arg(const Context& ctx,
           || args[i + 1] == "-include" || args[i + 1] == "--include"
           || args[i + 1] == "-fno-pch-timestamp")) {
     if (compopt_affects_compiler_output(args[i + 1])) {
-      state.compiler_only_args.push_back(args[i]);
-    } else if (compopt_affects_cpp_output(args[i + 1])) {
-      state.cpp_args.push_back(args[i]);
+      state.add_compiler_only_arg(args[i]);
     } else {
-      state.common_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
     }
     ++i;
     arg = make_dash_option(ctx.config, args[i]);
@@ -531,11 +555,11 @@ process_option_arg(const Context& ctx,
       || (i + 1 < args.size() && arg == "-Xclang"
           && compopt_affects_compiler_output(args[i + 1]))) {
     if (i + 1 < args.size() && arg == "-Xclang") {
-      state.compiler_only_args.push_back(args[i]);
+      state.add_compiler_only_arg(args[i]);
       ++i;
       arg = make_dash_option(ctx.config, args[i]);
     }
-    state.compiler_only_args.push_back(args[i]);
+    state.add_compiler_only_arg(args[i]);
     // Note: "-Xclang -option-that-takes-arg -Xclang arg" is not handled below
     // yet.
     if (compopt_takes_arg(arg)
@@ -544,7 +568,7 @@ process_option_arg(const Context& ctx,
         LOG("Missing argument to {}", args[i]);
         return Statistic::bad_compiler_arguments;
       }
-      state.compiler_only_args.push_back(args[i + 1]);
+      state.add_compiler_only_arg(args[i + 1]);
       ++i;
     }
     return Statistic::none;
@@ -553,10 +577,10 @@ process_option_arg(const Context& ctx,
       || (i + 1 < args.size() && arg == "-Xclang"
           && compopt_prefix_affects_compiler_output(args[i + 1]))) {
     if (i + 1 < args.size() && arg == "-Xclang") {
-      state.compiler_only_args.push_back(args[i]);
+      state.add_compiler_only_arg(args[i]);
       ++i;
     }
-    state.compiler_only_args.push_back(args[i]);
+    state.add_compiler_only_arg(args[i]);
     return Statistic::none;
   }
 
@@ -581,9 +605,8 @@ process_option_arg(const Context& ctx,
     }
   }
 
-  // We must have -c.
   if (arg == "-c" || arg == "--compile") { // --compile is NVCC
-    state.found_c_opt = true;
+    state.found_c_opt = args[i];
     return Statistic::none;
   }
 
@@ -604,34 +627,34 @@ process_option_arg(const Context& ctx,
 
     if (arg == "-TC") {
       args_info.actual_language = "c";
-      state.common_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       return Statistic::none;
     }
 
     if (arg == "-TP") {
       args_info.actual_language = "c++";
-      state.common_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       return Statistic::none;
     }
   }
 
-  // when using nvcc with separable compilation, -dc implies -c
+  // -dc implies -c when using NVCC with separable compilation.
   if ((arg == "-dc" || arg == "--device-c")
       && config.compiler_type() == CompilerType::nvcc) {
-    state.found_dc_opt = true;
+    state.found_dc_opt = args[i];
     return Statistic::none;
   }
 
   // -S changes the default extension.
   if (arg == "-S") {
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     state.found_S_opt = true;
     return Statistic::none;
   }
 
   // --analyze changes the default extension too
   if (arg == "--analyze") {
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     state.found_analyze_opt = true;
     return Statistic::none;
   }
@@ -642,7 +665,7 @@ process_option_arg(const Context& ctx,
       // an uppercase letter) is an ordinary Intel compiler option, not a
       // language specification. (GCC's "-x" language argument is always
       // lowercase.)
-      state.common_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       return Statistic::none;
     }
 
@@ -693,7 +716,7 @@ process_option_arg(const Context& ctx,
       || util::starts_with(arg, "-ffile-prefix-map=")) {
     std::string map = arg.substr(arg.find('=') + 1);
     args_info.debug_prefix_maps.push_back(map);
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -706,7 +729,7 @@ process_option_arg(const Context& ctx,
         LOG("Missing argument to {}", args[i]);
         return Statistic::bad_compiler_arguments;
       }
-      state.common_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       compilation_dir = args[i + 1];
       i++;
     } else {
@@ -716,14 +739,14 @@ process_option_arg(const Context& ctx,
       }
     }
     args_info.compilation_dir = std::move(compilation_dir);
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   // Debugging is handled specially, so that we know if we can strip line
   // number info.
   if (util::starts_with(arg, "-g")) {
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
 
     if (util::starts_with(arg, "-gdwarf")) {
       // Selection of DWARF format (-gdwarf or -gdwarf-<version>) enables
@@ -757,18 +780,18 @@ process_option_arg(const Context& ctx,
   if (config.is_compiler_group_msvc() && !config.is_compiler_group_clang()
       && is_msvc_z_debug_option(arg)) {
     state.last_seen_msvc_z_debug_option = args[i];
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   if (config.is_compiler_group_msvc() && util::starts_with(arg, "-Fd")) {
-    state.compiler_only_args_no_hash.push_back(args[i]);
+    state.add_compiler_only_arg_no_hash(args[i]);
     return Statistic::none;
   }
 
   if (config.is_compiler_group_msvc()
       && (util::starts_with(arg, "-MP") || arg == "-FS")) {
-    state.compiler_only_args_no_hash.push_back(args[i]);
+    state.add_compiler_only_arg_no_hash(args[i]);
     return Statistic::none;
   }
 
@@ -777,7 +800,7 @@ process_option_arg(const Context& ctx,
   if ((arg == "-MD" || arg == "-MMD") && !config.is_compiler_group_msvc()) {
     state.found_md_or_mmd_opt = true;
     args_info.generating_dependencies = true;
-    state.dep_args.push_back(args[i]);
+    state.add_compiler_only_arg(args[i]);
     return Statistic::none;
   }
 
@@ -805,10 +828,10 @@ process_option_arg(const Context& ctx,
     }
     // Keep the format of the args the same.
     if (separate_argument) {
-      state.dep_args.push_back("-MF");
-      state.dep_args.push_back(args_info.output_dep);
+      state.add_compiler_only_arg("-MF");
+      state.add_compiler_only_arg(args_info.output_dep);
     } else {
-      state.dep_args.push_back(FMT("-MF{}", args_info.output_dep));
+      state.add_compiler_only_arg(FMT("-MF{}", args_info.output_dep));
     }
     return Statistic::none;
   }
@@ -824,8 +847,8 @@ process_option_arg(const Context& ctx,
         LOG("Missing argument to {}", args[i]);
         return Statistic::bad_compiler_arguments;
       }
-      state.dep_args.push_back(args[i]);
-      state.dep_args.push_back(args[i + 1]);
+      state.add_compiler_only_arg(args[i]);
+      state.add_compiler_only_arg(args[i + 1]);
       dep_target = args[i + 1];
       i++;
     } else {
@@ -833,7 +856,7 @@ process_option_arg(const Context& ctx,
       const std::string_view arg_view(arg);
       const auto arg_opt = arg_view.substr(0, 3);
       dep_target = arg_view.substr(3);
-      state.dep_args.push_back(FMT("{}{}", arg_opt, dep_target));
+      state.add_compiler_only_arg(FMT("{}{}", arg_opt, dep_target));
     }
 
     if (args_info.dependency_target) {
@@ -853,8 +876,7 @@ process_option_arg(const Context& ctx,
       && (util::starts_with(arg, "-MD") || util::starts_with(arg, "-MT")
           || util::starts_with(arg, "-LD"))) {
     // These affect compiler but also #define some things.
-    state.cpp_args.push_back(args[i]);
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -862,39 +884,39 @@ process_option_arg(const Context& ctx,
       // clang-cl:
       || arg == "-showIncludes:user") {
     args_info.generating_includes = true;
-    state.dep_args.push_back(args[i]);
+    state.add_compiler_only_arg(args[i]);
     return Statistic::none;
   }
 
   if (arg == "-fprofile-arcs") {
     args_info.profile_arcs = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   if (arg == "-ftest-coverage") {
     args_info.generating_coverage = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   if (arg == "-fstack-usage") {
     args_info.generating_stackusage = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   // This covers all the different marker cases
   if (util::starts_with(arg, "-fcallgraph-info")) {
     args_info.generating_callgraphinfo = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   // -Zs is MSVC's -fsyntax-only equivalent
   if (arg == "-fsyntax-only" || arg == "-Zs") {
     args_info.expect_output_obj = false;
-    state.compiler_only_args.push_back(args[i]);
+    state.add_compiler_only_arg(args[i]);
     state.found_syntax_only = true;
     return Statistic::none;
   }
@@ -903,7 +925,7 @@ process_option_arg(const Context& ctx,
       || arg == "-coverage") { // Undocumented but still works.
     args_info.profile_arcs = true;
     args_info.generating_coverage = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -913,7 +935,7 @@ process_option_arg(const Context& ctx,
       // the actual CWD in the .gcno file.
       state.hash_actual_cwd = true;
     }
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -924,20 +946,20 @@ process_option_arg(const Context& ctx,
       // The failure is logged by process_profiling_option.
       return Statistic::unsupported_compiler_option;
     }
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   if (util::starts_with(arg, "-fsanitize-blacklist=")) {
     args_info.sanitize_blacklists.emplace_back(args[i].substr(21));
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   if (util::starts_with(arg, "--sysroot=")) {
     auto path = std::string_view(arg).substr(10);
     auto relpath = core::make_relative_path(ctx, path);
-    state.common_args.push_back(FMT("--sysroot={}", relpath));
+    state.add_common_arg(FMT("--sysroot={}", relpath));
     return Statistic::none;
   }
 
@@ -947,9 +969,9 @@ process_option_arg(const Context& ctx,
       LOG("Missing argument to {}", args[i]);
       return Statistic::bad_compiler_arguments;
     }
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     auto relpath = core::make_relative_path(ctx, args[i + 1]);
-    state.common_args.push_back(relpath);
+    state.add_common_arg(relpath);
     i++;
     return Statistic::none;
   }
@@ -960,8 +982,8 @@ process_option_arg(const Context& ctx,
       LOG("Missing argument to {}", args[i]);
       return Statistic::bad_compiler_arguments;
     }
-    state.common_args.push_back(args[i]);
-    state.common_args.push_back(args[i + 1]);
+    state.add_common_arg(args[i]);
+    state.add_common_arg(args[i + 1]);
     i++;
     return Statistic::none;
   }
@@ -969,7 +991,7 @@ process_option_arg(const Context& ctx,
   if (arg == "-P" || arg == "-Wp,-P") {
     // Avoid passing -P to the preprocessor since it removes preprocessor
     // information we need.
-    state.compiler_only_args.push_back(args[i]);
+    state.add_compiler_only_arg(args[i]);
     return Statistic::none;
   }
 
@@ -986,7 +1008,7 @@ process_option_arg(const Context& ctx,
         state.output_dep_origin = OutputDepOrigin::wp;
         args_info.output_dep = arg.substr(8);
       }
-      state.dep_args.push_back(args[i]);
+      state.add_compiler_only_arg(args[i]);
       return Statistic::none;
     } else if (util::starts_with(arg, "-Wp,-MMD,")
                && arg.find(',', 9) == std::string::npos) {
@@ -996,19 +1018,19 @@ process_option_arg(const Context& ctx,
         state.output_dep_origin = OutputDepOrigin::wp;
         args_info.output_dep = arg.substr(9);
       }
-      state.dep_args.push_back(args[i]);
+      state.add_compiler_only_arg(args[i]);
       return Statistic::none;
     } else if ((util::starts_with(arg, "-Wp,-D")
                 || util::starts_with(arg, "-Wp,-U"))
                && arg.find(',', 6) == std::string::npos) {
-      state.cpp_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       return Statistic::none;
     } else if (arg == "-Wp,-MP"
                || (arg.size() > 8 && util::starts_with(arg, "-Wp,-M")
                    && arg[7] == ','
                    && (arg[6] == 'F' || arg[6] == 'Q' || arg[6] == 'T')
                    && arg.find(',', 8) == std::string::npos)) {
-      state.dep_args.push_back(args[i]);
+      state.add_compiler_only_arg(args[i]);
       return Statistic::none;
     } else if (config.direct_mode()) {
       // -Wp, can be used to pass too hard options to the preprocessor.
@@ -1018,12 +1040,12 @@ process_option_arg(const Context& ctx,
     }
 
     // Any other -Wp,* arguments are only relevant for the preprocessor.
-    state.cpp_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   if (arg == "-MP") {
-    state.dep_args.push_back(args[i]);
+    state.add_compiler_only_arg(args[i]);
     return Statistic::none;
   }
 
@@ -1047,16 +1069,16 @@ process_option_arg(const Context& ctx,
   if (config.compiler_type() == CompilerType::gcc) {
     if (arg == "-fdiagnostics-color" || arg == "-fdiagnostics-color=always") {
       state.color_diagnostics = ColorDiagnostics::always;
-      state.compiler_only_args_no_hash.push_back(args[i]);
+      state.add_compiler_only_arg_no_hash(args[i]);
       return Statistic::none;
     } else if (arg == "-fno-diagnostics-color"
                || arg == "-fdiagnostics-color=never") {
       state.color_diagnostics = ColorDiagnostics::never;
-      state.compiler_only_args_no_hash.push_back(args[i]);
+      state.add_compiler_only_arg_no_hash(args[i]);
       return Statistic::none;
     } else if (arg == "-fdiagnostics-color=auto") {
       state.color_diagnostics = ColorDiagnostics::automatic;
-      state.compiler_only_args_no_hash.push_back(args[i]);
+      state.add_compiler_only_arg_no_hash(args[i]);
       return Statistic::none;
     }
   } else if (config.is_compiler_group_clang()) {
@@ -1064,33 +1086,33 @@ process_option_arg(const Context& ctx,
     // -fcolor-diagnostics argument which is passed to cc1 is handled below.
     if (arg == "-Xclang" && i + 1 < args.size()
         && args[i + 1] == "-fcolor-diagnostics") {
-      state.compiler_only_args_no_hash.push_back(args[i]);
+      state.add_compiler_only_arg_no_hash(args[i]);
       ++i;
       arg = make_dash_option(ctx.config, args[i]);
     }
     if (arg == "-fdiagnostics-color" || arg == "-fdiagnostics-color=always"
         || arg == "-fcolor-diagnostics") {
       state.color_diagnostics = ColorDiagnostics::always;
-      state.compiler_only_args_no_hash.push_back(args[i]);
+      state.add_compiler_only_arg_no_hash(args[i]);
       return Statistic::none;
     } else if (arg == "-fno-diagnostics-color"
                || arg == "-fdiagnostics-color=never"
                || arg == "-fno-color-diagnostics") {
       state.color_diagnostics = ColorDiagnostics::never;
-      state.compiler_only_args_no_hash.push_back(args[i]);
+      state.add_compiler_only_arg_no_hash(args[i]);
       return Statistic::none;
     }
   }
 
   if (arg == "-fno-pch-timestamp") {
     args_info.fno_pch_timestamp = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
   if (arg == "-fpch-preprocess") {
     state.found_fpch_preprocess = true;
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -1119,7 +1141,7 @@ process_option_arg(const Context& ctx,
 
   // MSVC -u is something else than GCC -u, handle it specially.
   if (arg == "-u" && ctx.config.is_compiler_group_msvc()) {
-    state.cpp_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     return Statistic::none;
   }
 
@@ -1141,13 +1163,11 @@ process_option_arg(const Context& ctx,
     // rate. A secondary effect is that paths in the standard error output
     // produced by the compiler will be normalized.
     fs::path relpath = core::make_relative_path(ctx, args[i + next]);
-    auto& dest_args =
-      compopt_affects_cpp_output(arg) ? state.cpp_args : state.common_args;
-    dest_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
     if (next == 2) {
-      dest_args.push_back(args[i + 1]);
+      state.add_common_arg(args[i + 1]);
     }
-    dest_args.push_back(relpath);
+    state.add_common_arg(relpath);
 
     i += next;
     return Statistic::none;
@@ -1175,9 +1195,9 @@ process_option_arg(const Context& ctx,
       const auto relpath = core::make_relative_path(ctx, *path);
       std::string new_option = FMT("{}{}", option, relpath);
       if (compopt_affects_cpp_output(option)) {
-        state.cpp_args.push_back(std::move(new_option));
+        state.add_common_arg(std::move(new_option));
       } else {
-        state.common_args.push_back(std::move(new_option));
+        state.add_common_arg(std::move(new_option));
       }
       return Statistic::none;
     }
@@ -1191,11 +1211,11 @@ process_option_arg(const Context& ctx,
     }
 
     if (compopt_affects_cpp_output(arg)) {
-      state.cpp_args.push_back(args[i]);
-      state.cpp_args.push_back(args[i + 1]);
+      state.add_common_arg(args[i]);
+      state.add_common_arg(args[i + 1]);
     } else {
-      state.common_args.push_back(args[i]);
-      state.common_args.push_back(args[i + 1]);
+      state.add_common_arg(args[i]);
+      state.add_common_arg(args[i + 1]);
     }
 
     i++;
@@ -1211,14 +1231,14 @@ process_option_arg(const Context& ctx,
   if (arg[0] == '-') {
     if (compopt_affects_cpp_output(arg)
         || compopt_prefix_affects_cpp_output(arg)) {
-      state.cpp_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       return Statistic::none;
     } else if (ctx.config.is_compiler_group_msvc()
                && args[i][0] == '/' // Intentionally not checking arg here
                && DirEntry(args[i]).is_regular_file()) {
       // Likely the input file, which is handled in process_arg later.
     } else {
-      state.common_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       return Statistic::none;
     }
   }
@@ -1252,7 +1272,7 @@ process_arg(const Context& ctx,
   if (!util::is_dev_null_path(args[i])) {
     if (!DirEntry(args[i]).is_regular_file()) {
       LOG("{} is not a regular file, not considering as input file", args[i]);
-      state.common_args.push_back(args[i]);
+      state.add_common_arg(args[i]);
       return Statistic::none;
     }
   }
@@ -1262,7 +1282,7 @@ process_arg(const Context& ctx,
     state.input_files.emplace_back(args[i]);
   } else {
     LOG("Not considering {} an input file since it doesn't exist", args[i]);
-    state.common_args.push_back(args[i]);
+    state.add_common_arg(args[i]);
   }
   return Statistic::none;
 }
@@ -1295,7 +1315,7 @@ process_args(Context& ctx)
   Args args = ctx.orig_args;
   ArgumentProcessingState state;
 
-  state.common_args.push_back(args[0]); // Compiler
+  state.add_common_arg(args[0]); // Compiler
 
   std::optional<Statistic> argument_error;
   for (size_t i = 1; i < args.size(); i++) {
@@ -1464,7 +1484,7 @@ process_args(Context& ctx)
 
   if (is_link) {
     if (args_info.output_is_precompiled_header) {
-      state.common_args.push_back("-c");
+      state.add_common_arg("-c");
     } else {
       LOG_RAW("No -c option found");
       // Having a separate statistic for autoconf tests is useful, as they are
@@ -1528,14 +1548,14 @@ process_args(Context& ctx)
   // -finput-charset=CHARSET (otherwise conversion happens twice)
   // -x CHARSET (otherwise the wrong language is selected)
   if (!state.input_charset_option.empty()) {
-    state.cpp_args.push_back(state.input_charset_option);
+    state.add_common_arg(state.input_charset_option);
   }
   if (state.found_pch && !ctx.config.is_compiler_group_msvc()) {
-    state.cpp_args.push_back("-fpch-preprocess");
+    state.add_common_arg("-fpch-preprocess");
   }
   if (!state.explicit_language.empty()) {
-    state.cpp_args.push_back("-x");
-    state.cpp_args.push_back(state.explicit_language);
+    state.add_common_arg("-x");
+    state.add_common_arg(state.explicit_language);
   }
 
   args_info.strip_diagnostics_colors =
@@ -1613,19 +1633,6 @@ process_args(Context& ctx)
       ctx, util::add_extension(args_info.orig_input_file, ".000i.ipa-clones"));
   }
 
-  Args compiler_args = state.common_args;
-  compiler_args.push_back(state.compiler_only_args_no_hash);
-  compiler_args.push_back(state.compiler_only_args);
-  compiler_args.push_back(state.cpp_args);
-
-  if (state.found_c_opt) {
-    compiler_args.push_back("-c");
-  }
-
-  if (state.found_dc_opt) {
-    compiler_args.push_back("-dc");
-  }
-
   if (!state.xarch_args.empty()) {
     for (const auto& arch : args_info.arch_args) {
       auto it = state.xarch_args.find(arch);
@@ -1636,47 +1643,44 @@ process_args(Context& ctx)
   }
 
   for (const auto& arch : args_info.arch_args) {
-    compiler_args.push_back("-arch");
-    compiler_args.push_back(arch);
+    state.add_compiler_only_arg_no_hash("-arch");
+    state.add_compiler_only_arg_no_hash(arch);
 
     auto it = args_info.xarch_args.find(arch);
     if (it != args_info.xarch_args.end()) {
       args_info.xarch_args.emplace(arch, it->second);
 
       for (const auto& xarch : it->second) {
-        compiler_args.push_back("-Xarch_" + arch);
-        compiler_args.push_back(xarch);
+        state.add_compiler_only_arg_no_hash("-Xarch_" + arch);
+        state.add_compiler_only_arg_no_hash(xarch);
       }
     }
   }
 
-  Args preprocessor_args = state.common_args;
-  preprocessor_args.push_back(state.cpp_args);
-  compiler_args.push_back(state.dep_args);
-
-  Args extra_args_to_hash = state.compiler_only_args;
-  extra_args_to_hash.push_back(state.dep_args);
   if (state.hash_full_command_line) {
-    extra_args_to_hash.push_back(ctx.orig_args);
+    state.add_extra_args_to_hash(ctx.orig_args);
   }
 
   if (diagnostics_color_arg) {
-    compiler_args.push_back(*diagnostics_color_arg);
+    state.add_compiler_only_arg_no_hash(*diagnostics_color_arg);
   }
 
   if (ctx.config.depend_mode() && !args_info.generating_includes
       && ctx.config.compiler_type() == CompilerType::msvc) {
     ctx.auto_depend_mode = true;
     args_info.generating_includes = true;
-    compiler_args.push_back("/showIncludes");
+    state.add_compiler_only_arg_no_hash("/showIncludes");
   }
 
-  return ProcessArgsResult{
-    preprocessor_args,
-    extra_args_to_hash,
-    compiler_args,
-    state.hash_actual_cwd,
-  };
+  if (state.found_c_opt) {
+    state.add_compiler_only_arg_no_hash(*state.found_c_opt);
+  }
+
+  if (state.found_dc_opt) {
+    state.add_compiler_only_arg_no_hash(*state.found_dc_opt);
+  }
+
+  return state.to_result();
 }
 
 bool
index 61edd6907ab2a26a1f91ce98ff439b4b245dbe02..6ffc3459bce29a05107221d4912dd1e0dc68a2ee 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2020-2024 Joel Rosdahl and other contributors
+// Copyright (C) 2020-2025 Joel Rosdahl and other contributors
 //
 // See doc/AUTHORS.adoc for a complete list of contributors.
 //
@@ -32,16 +32,17 @@ class Context;
 
 struct ProcessArgsResult
 {
-  // Arguments (except -E) to send to the preprocessor.
+  // Arguments (except "-E -o output.i") to send to the preprocessor. These are
+  // part of the input hash (except those marked as AFFECTS_CPP in compopt.cpp).
   Args preprocessor_args;
 
-  // Arguments not sent to the preprocessor but that should be part of the hash.
-  Args extra_args_to_hash;
-
-  // Arguments to send to the real compiler.
+  // Arguments to send to the real compiler. Not part of the input hash.
   Args compiler_args;
 
-  // Whether to include the actual CWD in the hash.
+  // Arguments not sent to the preprocessor but added to the input hash anyway.
+  Args extra_args_to_hash;
+
+  // Whether to include the actual CWD in the input hash.
   bool hash_actual_cwd = false;
 };
 
index ab1bd9cbd761a2d15f626a66ce9a5c1625511795..2c9542807550ccd09d244dbcfee50f43e6ffea49 100644 (file)
@@ -1666,6 +1666,19 @@ EOF
     expect_content_pattern compiler.args "(-E -o * test1.c)(-Werror -rdynamic -c -o test1.o test1.c)"
     rm compiler.args
 
+    # -------------------------------------------------------------------------
+    if $COMPILER -Wdelete-non-virtual-dtor -c test1.c 2>/dev/null && ! $COMPILER -Werror -Wdelete-non-virtual-dtor -c test1.c 2>/dev/null; then
+        TEST "Ordering of warning options"
+
+        if $CCACHE_COMPILE -Werror -Wdelete-non-virtual-dtor -c test1.c 2>stderr.txt; then
+            test_failed "-Werror -Wdelete-non-virtual-dtor did not result in an error; stderr: [$(<stderr.txt)]"
+        fi
+
+        if ! $CCACHE_COMPILE -Wdelete-non-virtual-dtor -Werror -c test1.c 2>stderr.txt; then
+            test_failed "-Wdelete-non-virtual-dtor -Werror resulted in an error; stderr: [$(<stderr.txt)]"
+        fi
+    fi
+
     # -------------------------------------------------------------------------
 if ! $COMPILER_USES_MSVC; then
     for src in test1.c build/test1.c; do
index 919eecc9272bc81796327a8099d4e5b85bb6ff06..b8b75a09b8b68f74786684244291b81ba015eab6 100644 (file)
@@ -111,7 +111,7 @@ TEST_CASE("dependency_args_to_compiler")
   CHECK(result);
   CHECK(result->preprocessor_args.to_string() == "cc");
   CHECK(result->extra_args_to_hash.to_string() == dep_args);
-  CHECK(result->compiler_args.to_string() == "cc -c " + dep_args);
+  CHECK(result->compiler_args.to_string() == "cc " + dep_args + " -c");
 }
 
 TEST_CASE("cpp_only_args_to_preprocessor_and_compiler")
@@ -135,7 +135,7 @@ TEST_CASE("cpp_only_args_to_preprocessor_and_compiler")
   CHECK(result->preprocessor_args.to_string() == "cc " + cpp_args);
   CHECK(result->extra_args_to_hash.to_string() == dep_args);
   CHECK(result->compiler_args.to_string()
-        == "cc " + cpp_args + " -c " + dep_args);
+        == "cc " + cpp_args + " " + dep_args + " -c");
 }
 
 TEST_CASE(
@@ -152,7 +152,7 @@ TEST_CASE(
   CHECK(result);
   CHECK(result->preprocessor_args.to_string() == "cc");
   CHECK(result->extra_args_to_hash.to_string() == dep_args);
-  CHECK(result->compiler_args.to_string() == "cc -c " + dep_args);
+  CHECK(result->compiler_args.to_string() == "cc " + dep_args + " -c");
 }
 
 TEST_CASE("equal_sign_after_MF_should_be_removed")
@@ -167,7 +167,7 @@ TEST_CASE("equal_sign_after_MF_should_be_removed")
   CHECK(result);
   CHECK(result->preprocessor_args.to_string() == "cc");
   CHECK(result->extra_args_to_hash.to_string() == "-MFpath");
-  CHECK(result->compiler_args.to_string() == "cc -c -MFpath");
+  CHECK(result->compiler_args.to_string() == "cc -MFpath -c");
 }
 
 TEST_CASE("sysroot_should_be_rewritten_if_basedir_is_used")
@@ -318,7 +318,7 @@ TEST_CASE("MF_flag_with_immediate_argument_should_work_as_last_argument")
   CHECK(result);
   CHECK(result->preprocessor_args.to_string() == "cc");
   CHECK(result->extra_args_to_hash.to_string() == "-MMD -MT bar -MFfoo.d");
-  CHECK(result->compiler_args.to_string() == "cc -c -MMD -MT bar -MFfoo.d");
+  CHECK(result->compiler_args.to_string() == "cc -MMD -MT bar -MFfoo.d -c");
 }
 
 TEST_CASE("MT_flag_with_immediate_argument_should_work_as_last_argument")
@@ -337,7 +337,7 @@ TEST_CASE("MT_flag_with_immediate_argument_should_work_as_last_argument")
   CHECK(result->extra_args_to_hash.to_string()
         == "-MMD -MFfoo.d -MT foo -MTbar");
   CHECK(result->compiler_args.to_string()
-        == "cc -c -MMD -MFfoo.d -MT foo -MTbar");
+        == "cc -MMD -MFfoo.d -MT foo -MTbar -c");
 }
 
 TEST_CASE("MQ_flag_with_immediate_argument_should_work_as_last_argument")
@@ -356,7 +356,7 @@ TEST_CASE("MQ_flag_with_immediate_argument_should_work_as_last_argument")
   CHECK(result->extra_args_to_hash.to_string()
         == "-MMD -MFfoo.d -MQ foo -MQbar");
   CHECK(result->compiler_args.to_string()
-        == "cc -c -MMD -MFfoo.d -MQ foo -MQbar");
+        == "cc -MMD -MFfoo.d -MQ foo -MQbar -c");
 }
 
 TEST_CASE("MQ_flag_without_immediate_argument_should_not_add_MQobj")
@@ -372,7 +372,7 @@ TEST_CASE("MQ_flag_without_immediate_argument_should_not_add_MQobj")
   CHECK(result->preprocessor_args.to_string() == "gcc");
   CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQ foo.d");
   CHECK(result->compiler_args.to_string()
-        == "gcc -c -MD -MP -MFfoo.d -MQ foo.d");
+        == "gcc -MD -MP -MFfoo.d -MQ foo.d -c");
 }
 
 TEST_CASE("MT_flag_without_immediate_argument_should_not_add_MTobj")
@@ -388,7 +388,7 @@ TEST_CASE("MT_flag_without_immediate_argument_should_not_add_MTobj")
   CHECK(result->preprocessor_args.to_string() == "gcc");
   CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MT foo.d");
   CHECK(result->compiler_args.to_string()
-        == "gcc -c -MD -MP -MFfoo.d -MT foo.d");
+        == "gcc -MD -MP -MFfoo.d -MT foo.d -c");
 }
 
 TEST_CASE("MQ_flag_with_immediate_argument_should_not_add_MQobj")
@@ -404,7 +404,7 @@ TEST_CASE("MQ_flag_with_immediate_argument_should_not_add_MQobj")
   CHECK(result->preprocessor_args.to_string() == "gcc");
   CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MQfoo.d");
   CHECK(result->compiler_args.to_string()
-        == "gcc -c -MD -MP -MFfoo.d -MQfoo.d");
+        == "gcc -MD -MP -MFfoo.d -MQfoo.d -c");
 }
 
 TEST_CASE("MT_flag_with_immediate_argument_should_not_add_MQobj")
@@ -420,7 +420,7 @@ TEST_CASE("MT_flag_with_immediate_argument_should_not_add_MQobj")
   CHECK(result->preprocessor_args.to_string() == "gcc");
   CHECK(result->extra_args_to_hash.to_string() == "-MD -MP -MFfoo.d -MTfoo.d");
   CHECK(result->compiler_args.to_string()
-        == "gcc -c -MD -MP -MFfoo.d -MTfoo.d");
+        == "gcc -MD -MP -MFfoo.d -MTfoo.d -c");
 }
 
 TEST_CASE(
@@ -521,7 +521,7 @@ TEST_CASE("options_not_to_be_passed_to_the_preprocessor")
   CHECK(result->extra_args_to_hash.to_string()
         == "-Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error");
   CHECK(result->compiler_args.to_string()
-        == "cc -g -Wa,foo -Werror -Xlinker fie -Xlinker,fum -Wno-error -DX -c");
+        == "cc -Wa,foo -g -DX -Werror -Xlinker fie -Xlinker,fum -Wno-error -c");
 }
 
 TEST_CASE("cuda_option_file")
@@ -611,7 +611,7 @@ TEST_CASE("-Xclang")
   CHECK(result->extra_args_to_hash.to_string() == extra_args);
   CHECK(result->compiler_args.to_string()
         == "clang " + common_args + " " + color_diag + " " + extra_args + " "
-             + pch_pth_variants + " -c -fcolor-diagnostics");
+             + pch_pth_variants + " -fcolor-diagnostics -c");
 }
 
 TEST_CASE("-x")
@@ -706,7 +706,7 @@ TEST_CASE("MSVC options"
   const auto result = process_args(ctx);
   CHECK(result);
   CHECK(result->preprocessor_args.to_string() == "cl.exe /foobar");
-  CHECK(result->compiler_args.to_string() == "cl.exe /foobar -c");
+  CHECK(result->compiler_args.to_string() == "cl.exe /foobar /c");
 }
 
 TEST_CASE("MSVC PCH options")
@@ -730,7 +730,7 @@ TEST_CASE("MSVC PCH options")
     CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Ycpch.h /Fppch.cpp.pch /FIpch.h");
     CHECK(result->compiler_args.to_string()
-          == "cl.exe /Ycpch.h /Fppch.cpp.pch /FIpch.h -c");
+          == "cl.exe /Ycpch.h /Fppch.cpp.pch /FIpch.h /c");
   }
 
   util::write_file("pch.cpp.pch", "");
@@ -750,7 +750,7 @@ TEST_CASE("MSVC PCH options")
     CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h");
     CHECK(result->compiler_args.to_string()
-          == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h -c");
+          == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h /c");
   }
 }
 
@@ -775,7 +775,7 @@ TEST_CASE("MSVC PCH options with empty -Yc")
     CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yc /Fppch.cpp.pch /FIpch.h");
     CHECK(result->compiler_args.to_string()
-          == "cl.exe /Yc /Fppch.cpp.pch /FIpch.h -c");
+          == "cl.exe /Yc /Fppch.cpp.pch /FIpch.h /c");
   }
 
   util::write_file("pch.cpp.pch", "");
@@ -795,7 +795,7 @@ TEST_CASE("MSVC PCH options with empty -Yc")
     CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h");
     CHECK(result->compiler_args.to_string()
-          == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h -c");
+          == "cl.exe /Yupch.h /Fppch.cpp.pch /FIpch.h /c");
   }
 }
 
@@ -817,7 +817,7 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp")
     CHECK(ctx.args_info.included_pch_file == "pch.pch");
     CHECK(ctx.args_info.output_obj == "pch.cpp.obj");
     CHECK(result->preprocessor_args.to_string() == "cl.exe /Yc");
-    CHECK(result->compiler_args.to_string() == "cl.exe /Yc -c");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Yc /c");
   }
 
   util::write_file("pch.pch", "");
@@ -837,7 +837,7 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp")
     CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.pch /FIpch.h");
     CHECK(result->compiler_args.to_string()
-          == "cl.exe /Yupch.h /Fppch.pch /FIpch.h -c");
+          == "cl.exe /Yupch.h /Fppch.pch /FIpch.h /c");
   }
 }
 
@@ -859,7 +859,7 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp and -Fo")
     CHECK(ctx.args_info.included_pch_file == "pch.pch");
     CHECK(ctx.args_info.output_obj == "pch.obj");
     CHECK(result->preprocessor_args.to_string() == "cl.exe /Yc");
-    CHECK(result->compiler_args.to_string() == "cl.exe /Yc -c");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Yc /c");
   }
 
   util::write_file("pch.pch", "");
@@ -879,7 +879,7 @@ TEST_CASE("MSVC PCH options with empty -Yc and without -Fp and -Fo")
     CHECK(result->preprocessor_args.to_string()
           == "cl.exe /Yupch.h /Fppch.pch /FIpch.h");
     CHECK(result->compiler_args.to_string()
-          == "cl.exe /Yupch.h /Fppch.pch /FIpch.h -c");
+          == "cl.exe /Yupch.h /Fppch.pch /FIpch.h /c");
   }
 }
 
@@ -926,7 +926,7 @@ TEST_CASE("MSVC debug information format options")
     const auto result = process_args(ctx);
     REQUIRE(result);
     CHECK(result->preprocessor_args.to_string() == "cl.exe /Z7");
-    CHECK(result->compiler_args.to_string() == "cl.exe /Z7 -c");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Z7 /c");
   }
 
   SUBCASE("Only /Zi")
@@ -956,7 +956,7 @@ TEST_CASE("MSVC debug information format options")
     const auto result = process_args(ctx);
     REQUIRE(result);
     CHECK(result->preprocessor_args.to_string() == "cl.exe /Zi /Z7");
-    CHECK(result->compiler_args.to_string() == "cl.exe /Zi /Z7 -c");
+    CHECK(result->compiler_args.to_string() == "cl.exe /Zi /Z7 /c");
   }
 }