From 7470f8c532711c405cdacd6da5ae5d052d7f9dca Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Thu, 9 Jan 2020 23:11:04 +0100 Subject: [PATCH] Visual C/C++ compiler support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I picked only the compiler commits from: https://github.com/ccache/ccache/pull/162 The following commits I've adapted to the latest ccache C++ code: 375fe24: Add compiler_is_msvc() and MSVC specific option table. 7e01763: Add handling of /Fo option (replaces -o, but shall have no space) 0c5cd25: Manage /E, /c equivalence. -g is gcc only. -O or /O is msvc only. 4f61b59: MSVC send part of the error/warning messages to STDOUT, so concat wit… --- src/Util.cpp | 2 +- src/ccache.cpp | 103 +++++++++++++++++++++++++++++++++++++++++++----- src/ccache.hpp | 1 + src/compopt.cpp | 11 ++++++ 4 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/Util.cpp b/src/Util.cpp index f624f73fbb..d132e84e03 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -81,7 +81,7 @@ base_name(string_view path) size_t n = path.rfind('/'); #ifdef _WIN32 size_t n2 = path.rfind('\\'); - if (n2 != std::string::npos && n2 > n) { + if (n2 != std::string::npos && (n == std::string::npos || n2 > n)) { n = n2; } #endif diff --git a/src/ccache.cpp b/src/ccache.cpp index 7153917ebc..a07fae928f 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -568,14 +568,19 @@ guess_compiler(const char* path) { string_view name = Util::base_name(path); enum guessed_compiler result = GUESSED_UNKNOWN; - if (name == "clang") { + if (name == "clang" || name == "clang.exe" || + name == "clang++" || name == "clang++.exe") { result = GUESSED_CLANG; - } else if (name == "gcc" || name == "g++") { + } else if (name == "gcc" || name == "gcc.exe" || + name == "g++" || name == "g++.exe") { result = GUESSED_GCC; } else if (name == "nvcc") { result = GUESSED_NVCC; } else if (name == "pump" || name == "distcc-pump") { result = GUESSED_PUMP; + } else if (name == "cl" || name == "cl.exe" || + name == "clang-cl" || name == "clang-cl.exe") { + result = GUESSED_MSVC; } return result; } @@ -1289,8 +1294,14 @@ create_cachedir_tag(const std::string& dir) static void to_cache(struct args* args, struct hash* depend_mode_hash) { - args_add(args, "-o"); - args_add(args, output_obj); + if (guessed_compiler == GUESSED_MSVC) { + char *fo = format("-Fo%s", output_obj); + args_add(args, fo); + free(fo); + } else { + args_add(args, "-o"); + args_add(args, output_obj); + } if (g_config.hard_link()) { // Workaround for Clang bug where it overwrites an existing object file @@ -1375,9 +1386,72 @@ to_cache(struct args* args, struct hash* depend_mode_hash) failed(); } + // 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 (guessed_compiler == GUESSED_MSVC) { + char *tmp_stderr2 = format("%s.2", tmp_stderr); + if (x_rename(tmp_stderr, tmp_stderr2)) { + cc_log("Failed to rename %s to %s: %s", tmp_stderr, tmp_stderr2, + strerror(errno)); + stats_update(STATS_ERROR); + failed(); + } + + std::ofstream result_stream; + + std::vector output_buffer(READ_BUFFER_SIZE); + result_stream.rdbuf()->pubsetbuf(output_buffer.data(), output_buffer.size()); + + result_stream.open(tmp_stderr, std::ios_base::binary); + if (!result_stream.is_open()) { + cc_log("Failed opening %s: %s", tmp_stderr, strerror(errno)); + stats_update(STATS_ERROR); + failed(); + } + + std::ostreambuf_iterator to(result_stream); + for (char* file : {tmp_stdout, tmp_stderr2}) { + std::ifstream file_stream; + + std::vector read_buffer(READ_BUFFER_SIZE); + file_stream.rdbuf()->pubsetbuf(read_buffer.data(), read_buffer.size()); + + file_stream.open(file, std::ios_base::binary); + if (!file_stream.is_open()) { + cc_log("Failed opening %s: %s", file, strerror(errno)); + stats_update(STATS_ERROR); + failed(); + } + + std::istreambuf_iterator from(file_stream); + for (; from != std::istreambuf_iterator(); ++from, ++to) { + if (*from != '\r') { + *to = *from; + } else if (++from != std::istreambuf_iterator()) { + *to = (*from == '\n') ? '\n' : '\r'; + } + } + } + + result_stream.close(); + if (!result_stream.good()) { + cc_log("Failed at writing data into %s: %s", tmp_stderr, strerror(errno)); + stats_update(STATS_ERROR); + failed(); + } + + tmp_unlink(tmp_stderr2); + free(tmp_stderr2); + } + // distcc-pump outputs lines like this: // __________Using # distcc servers in pump mode - if (st.size() != 0 && guessed_compiler != GUESSED_PUMP) { + if (st.size() != 0 && + guessed_compiler != GUESSED_PUMP && + guessed_compiler != GUESSED_MSVC) { cc_log("Compiler produced stdout"); stats_update(STATS_STDOUT); tmp_unlink(tmp_stdout); @@ -2455,7 +2529,7 @@ cc_process_args(struct args* args, } // Special case for -E. - if (str_eq(argv[i], "-E")) { + if (str_eq(argv[i], "-E") || str_eq(argv[i], "/E")) { stats_update(STATS_PREPROCESSING); result = false; goto out; @@ -2618,7 +2692,7 @@ cc_process_args(struct args* args, } // We must have -c. - if (str_eq(argv[i], "-c")) { + if (str_eq(argv[i], "-c") || str_eq(argv[i], "/c")) { found_c_opt = true; continue; } @@ -2678,6 +2752,12 @@ cc_process_args(struct args* args, continue; } + // MSVC /Fo with no space. + if (str_startswith(argv[i], "/Fo") && guessed_compiler == GUESSED_MSVC) { + output_obj = make_relative_path(x_strdup(&argv[i][3])); + continue; + } + if (str_startswith(argv[i], "-fdebug-prefix-map=") || str_startswith(argv[i], "-ffile-prefix-map=")) { debug_prefix_maps = static_cast(x_realloc( @@ -2724,7 +2804,8 @@ cc_process_args(struct args* args, // These options require special handling, because they behave differently // with gcc -E, when the output file is not specified. - if (str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD")) { + if ((str_eq(argv[i], "-MD") || str_eq(argv[i], "-MMD")) && + guessed_compiler != GUESSED_MSVC) { generating_dependencies = true; args_add(dep_args, argv[i]); continue; @@ -3068,7 +3149,8 @@ cc_process_args(struct args* args, // Same as above but options with concatenated argument beginning with a // slash. - if (argv[i][0] == '-') { + if (argv[i][0] == '-' || + (guessed_compiler == GUESSED_MSVC && argv[i][0] == '/')) { char* slash_pos = strchr(argv[i], '/'); if (slash_pos) { char* option = x_strndup(argv[i], slash_pos - argv[i]); @@ -3112,7 +3194,8 @@ cc_process_args(struct args* args, } // Other options. - if (argv[i][0] == '-') { + if (argv[i][0] == '-' || + (guessed_compiler == GUESSED_MSVC && argv[i][0] == '/')) { if (compopt_affects_cpp(argv[i]) || compopt_prefix_affects_cpp(argv[i])) { args_add(cpp_args, argv[i]); } else { diff --git a/src/ccache.hpp b/src/ccache.hpp index 0c254703b2..ac1e41df8c 100644 --- a/src/ccache.hpp +++ b/src/ccache.hpp @@ -87,6 +87,7 @@ enum guessed_compiler { GUESSED_GCC, GUESSED_NVCC, GUESSED_PUMP, + GUESSED_MSVC, GUESSED_UNKNOWN }; diff --git a/src/compopt.cpp b/src/compopt.cpp index 61a8888cfc..2efbea26da 100644 --- a/src/compopt.cpp +++ b/src/compopt.cpp @@ -128,6 +128,17 @@ static const struct 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 + {"/u", AFFECTS_CPP}, // msvc }; static int