]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
feat: Visual C/C++ compiler support (#506)
authorCristian Adam <cristian.adam@gmail.com>
Mon, 15 Nov 2021 19:06:16 +0000 (20:06 +0100)
committerGitHub <noreply@github.com>
Mon, 15 Nov 2021 19:06:16 +0000 (20:06 +0100)
misc/codespell-allowlist.txt
src/Args.cpp
src/Args.hpp
src/Config.cpp
src/Config.hpp
src/argprocessing.cpp
src/ccache.cpp
src/compopt.cpp
unittest/test_Args.cpp

index 952724dfafd0a190f9beacb6bab24b552ebd4aa1..4a337e9b5614590ffaf8e6b2b369234ad66dba9c 100644 (file)
@@ -1,3 +1,4 @@
+fo
 copyable
 creat
 files'
index e918c6a481823c5662f2c31274abde9a83cf99bc..3731e26b66f29e91994c3c60df068b66c6e6b6dd 100644 (file)
@@ -50,7 +50,7 @@ Args::from_string(const std::string& command)
 }
 
 optional<Args>
-Args::from_gcc_atfile(const std::string& filename)
+Args::from_atfile(const std::string& filename, bool ignore_backslash)
 {
   std::string argtext;
   try {
@@ -72,6 +72,9 @@ Args::from_gcc_atfile(const std::string& filename)
   while (true) {
     switch (*pos) {
     case '\\':
+      if (ignore_backslash) {
+        break;
+      }
       pos++;
       if (*pos == '\0') {
         continue;
index a93d1881bb0cd12db9d63f83b3cf0b5d86d6b17c..1a07ddc7169d4bc47941928e606488300e3a0aa0 100644 (file)
@@ -36,7 +36,8 @@ public:
 
   static Args from_argv(int argc, const char* const* argv);
   static Args from_string(const std::string& command);
-  static nonstd::optional<Args> from_gcc_atfile(const std::string& filename);
+  static nonstd::optional<Args> from_atfile(const std::string& filename,
+                                            bool ignore_backslash = false);
 
   Args& operator=(const Args& other) = default;
   Args& operator=(Args&& other) noexcept;
index 9b619b211e6f10eab2e04511996209b15aaafe59..2dd1e44be2b44c2297e4d0f380dc69e7a9fef386 100644 (file)
@@ -455,6 +455,7 @@ compiler_type_to_string(CompilerType compiler_type)
     CASE(nvcc);
     CASE(other);
     CASE(pump);
+    CASE(cl);
   }
 #undef CASE
 
index 4e386870d1675186f60ed239fe20cdee6a75c465..982511222c4ef7ebe2c7eba12a472c5bd72f2add 100644 (file)
@@ -31,7 +31,7 @@
 #include <string>
 #include <unordered_map>
 
-enum class CompilerType { auto_guess, clang, gcc, nvcc, other, pump };
+enum class CompilerType { auto_guess, clang, gcc, nvcc, other, pump, cl };
 
 std::string compiler_type_to_string(CompilerType compiler_type);
 
index 5b5b60540e884498fa7f17f7b48350fe3ac13d24..adab3e7c06306280719dda1e8341509f177fc61d 100644 (file)
@@ -252,7 +252,7 @@ process_arg(const Context& ctx,
   }
 
   // Special case for -E.
-  if (args[i] == "-E") {
+  if (args[i] == "-E" || args[i] == "/E") {
     return Statistic::called_for_preprocessing;
   }
 
@@ -263,7 +263,8 @@ process_arg(const Context& ctx,
     if (argpath[-1] == '-') {
       ++argpath;
     }
-    auto file_args = Args::from_gcc_atfile(argpath);
+    auto file_args =
+      Args::from_atfile(argpath, config.compiler_type() == CompilerType::cl);
     if (!file_args) {
       LOG("Couldn't read arg file {}", argpath);
       return Statistic::bad_compiler_arguments;
@@ -286,7 +287,7 @@ process_arg(const Context& ctx,
     // Argument is a comma-separated list of files.
     auto paths = Util::split_into_strings(args[i], ",");
     for (auto it = paths.rbegin(); it != paths.rend(); ++it) {
-      auto file_args = Args::from_gcc_atfile(*it);
+      auto file_args = Args::from_atfile(*it);
       if (!file_args) {
         LOG("Couldn't read CUDA options file {}", *it);
         return Statistic::bad_compiler_arguments;
@@ -300,7 +301,8 @@ process_arg(const Context& ctx,
 
   // These are always too hard.
   if (compopt_too_hard(args[i]) || util::starts_with(args[i], "-fdump-")
-      || util::starts_with(args[i], "-MJ")) {
+      || util::starts_with(args[i], "-MJ") || util::starts_with(args[i], "-Yc")
+      || util::starts_with(args[i], "/Yc")) {
     LOG("Compiler option {} is unsupported", args[i]);
     return Statistic::unsupported_compiler_option;
   }
@@ -398,11 +400,19 @@ process_arg(const Context& ctx,
   }
 
   // We must have -c.
-  if (args[i] == "-c") {
+  if (args[i] == "-c" || args[i] == "/c") {
     state.found_c_opt = true;
     return nullopt;
   }
 
+  // MSVC /Fo with no space.
+  if (util::starts_with(args[i], "/Fo")
+      && config.compiler_type() == CompilerType::cl) {
+    args_info.output_obj =
+      Util::make_relative_path(ctx, string_view(args[i]).substr(3));
+    return nullopt;
+  }
+
   // when using nvcc with separable compilation, -dc implies -c
   if ((args[i] == "-dc" || args[i] == "--device-c")
       && config.compiler_type() == CompilerType::nvcc) {
@@ -511,7 +521,8 @@ process_arg(const Context& ctx,
 
   // These options require special handling, because they behave differently
   // with gcc -E, when the output file is not specified.
-  if (args[i] == "-MD" || args[i] == "-MMD") {
+  if ((args[i] == "-MD" || args[i] == "-MMD")
+      && config.compiler_type() != CompilerType::cl) {
     args_info.generating_dependencies = true;
     args_info.seen_MD_MMD = true;
     state.dep_args.push_back(args[i]);
@@ -845,7 +856,8 @@ process_arg(const Context& ctx,
 
   // Same as above but options with concatenated argument beginning with a
   // slash.
-  if (args[i][0] == '-') {
+  if (args[i][0] == '-'
+      || (config.compiler_type() == CompilerType::cl && args[i][0] == '/')) {
     size_t slash_pos = args[i].find('/');
     if (slash_pos != std::string::npos) {
       std::string option = args[i].substr(0, slash_pos);
@@ -883,7 +895,8 @@ process_arg(const Context& ctx,
   }
 
   // Other options.
-  if (args[i][0] == '-') {
+  if (args[i][0] == '-'
+      || (config.compiler_type() == CompilerType::cl && args[i][0] == '/')) {
     if (compopt_affects_cpp_output(args[i])
         || compopt_prefix_affects_cpp_output(args[i])) {
       state.cpp_args.push_back(args[i]);
index 85cc59a88483ac588fef34e943a1f5ca31a5daae..df8e6206841e189f21b9765f943e8cff27d78442 100644 (file)
@@ -232,6 +232,8 @@ guess_compiler(string_view path)
     return CompilerType::nvcc;
   } else if (name == "pump" || name == "distcc-pump") {
     return CompilerType::pump;
+  } else if (name.find("cl") != nonstd::string_view::npos) {
+    return CompilerType::cl;
   } else {
     return CompilerType::other;
   }
@@ -850,8 +852,12 @@ to_cache(Context& ctx,
          const Args& depend_extra_args,
          Hash* depend_mode_hash)
 {
-  args.push_back("-o");
-  args.push_back(ctx.args_info.output_obj);
+  if (ctx.config.compiler_type() == CompilerType::cl) {
+    args.push_back(fmt::format("-Fo{}", ctx.args_info.output_obj));
+  } else {
+    args.push_back("-o");
+    args.push_back(ctx.args_info.output_obj);
+  }
 
   if (ctx.config.hard_link() && ctx.args_info.output_obj != "/dev/null") {
     // Workaround for Clang bug where it overwrites an existing object file
@@ -930,9 +936,28 @@ to_cache(Context& ctx,
     return nonstd::make_unexpected(Statistic::missing_cache_file);
   }
 
+  // MSVC compiler always print the input file name to stdout,
+  // plus parts of the warnings/error messages.
+  // So we have to fusion that into stderr...
+  // Transform \r\n into \n. This way ninja won't produce empty newlines
+  // for the /showIncludes argument.
+  if (ctx.config.compiler_type() == CompilerType::cl) {
+    const std::string merged_output =
+      Util::read_file(tmp_stdout_path) + Util::read_file(tmp_stderr_path);
+    const std::string merged_output_with_unix_line_endings =
+      util::replace_all(merged_output, "\r\n", "\n");
+    try {
+      Util::write_file(tmp_stderr_path, merged_output_with_unix_line_endings);
+    } catch (const core::Error& e) {
+      LOG("Failed writing to {}: {}", tmp_stderr_path, e.what());
+      return nonstd::make_unexpected(Statistic::internal_error);
+    }
+  }
+
   // distcc-pump outputs lines like this:
   // __________Using # distcc servers in pump mode
-  if (st.size() != 0 && ctx.config.compiler_type() != CompilerType::pump) {
+  if (st.size() != 0 && ctx.config.compiler_type() != CompilerType::pump
+      && ctx.config.compiler_type() != CompilerType::cl) {
     LOG_RAW("Compiler produced stdout");
     return nonstd::make_unexpected(Statistic::compiler_produced_stdout);
   }
index e2f0fe1e442220e750c9806e4a30716322d8c65f..84fe3ab11b86059229f136852b69a86296e7da05 100644 (file)
@@ -142,6 +142,20 @@ const CompOpt compopts[] = {
   {"-stdlib=", AFFECTS_CPP | TAKES_CONCAT_ARG},
   {"-trigraphs", AFFECTS_CPP},
   {"-u", TAKES_ARG | TAKES_CONCAT_ARG},
+  {"/AI", TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},               // msvc
+  {"/D", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG},               // msvc
+  {"/E", TOO_HARD},                                                 // msvc
+  {"/EP", TOO_HARD},                                                // msvc
+  {"/FI", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, // msvc
+  {"/FU", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, // msvc
+  {"/I", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH},  // msvc
+  {"/L", TAKES_ARG},                                                // msvc
+  {"/P", TOO_HARD},                                                 // msvc
+  {"/U", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG},               // msvc
+  {"/Yc", TAKES_ARG | TOO_HARD},                                    // msvc
+  {"/ZI", TOO_HARD},                                                // msvc
+  {"/Zi", TOO_HARD},                                                // msvc
+  {"/u", AFFECTS_CPP},                                              // msvc
 };
 
 static int
index 1aea024aecc6527d5fb99b348897985d9385c551..391b71bd86f13426779e3961f38347ffb3f71de6 100644 (file)
@@ -76,7 +76,7 @@ TEST_CASE("Args::from_string")
   CHECK(args[3] == "f");
 }
 
-TEST_CASE("Args::from_gcc_atfile")
+TEST_CASE("Args::from_atfile")
 {
   TestContext test_context;
 
@@ -84,20 +84,20 @@ TEST_CASE("Args::from_gcc_atfile")
 
   SUBCASE("Nonexistent file")
   {
-    CHECK(Args::from_gcc_atfile("at_file") == nonstd::nullopt);
+    CHECK(Args::from_atfile("at_file") == nonstd::nullopt);
   }
 
   SUBCASE("Empty")
   {
     Util::write_file("at_file", "");
-    args = *Args::from_gcc_atfile("at_file");
+    args = *Args::from_atfile("at_file");
     CHECK(args.size() == 0);
   }
 
   SUBCASE("One argument without newline")
   {
     Util::write_file("at_file", "foo");
-    args = *Args::from_gcc_atfile("at_file");
+    args = *Args::from_atfile("at_file");
     CHECK(args.size() == 1);
     CHECK(args[0] == "foo");
   }
@@ -105,7 +105,7 @@ TEST_CASE("Args::from_gcc_atfile")
   SUBCASE("One argument with newline")
   {
     Util::write_file("at_file", "foo\n");
-    args = *Args::from_gcc_atfile("at_file");
+    args = *Args::from_atfile("at_file");
     CHECK(args.size() == 1);
     CHECK(args[0] == "foo");
   }
@@ -113,7 +113,7 @@ TEST_CASE("Args::from_gcc_atfile")
   SUBCASE("Multiple simple arguments")
   {
     Util::write_file("at_file", "x y z\n");
-    args = *Args::from_gcc_atfile("at_file");
+    args = *Args::from_atfile("at_file");
     CHECK(args.size() == 3);
     CHECK(args[0] == "x");
     CHECK(args[1] == "y");
@@ -126,7 +126,7 @@ TEST_CASE("Args::from_gcc_atfile")
       "at_file",
       "first\rsec\\\tond\tthi\\\\rd\nfourth  \tfif\\ th \"si'x\\\" th\""
       " 'seve\nth'\\");
-    args = *Args::from_gcc_atfile("at_file");
+    args = *Args::from_atfile("at_file");
     CHECK(args.size() == 7);
     CHECK(args[0] == "first");
     CHECK(args[1] == "sec\tond");