diff --git a/src/Config.cpp b/src/Config.cpp index 68dc3f5752..575bcad9f0 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -431,6 +431,7 @@ compiler_type_to_string(CompilerType compiler_type) CASE(nvcc); CASE(other); CASE(pump); + CASE(cl); } #undef CASE diff --git a/src/Config.hpp b/src/Config.hpp index eb574dc8c4..968ee3ac05 100644 --- a/src/Config.hpp +++ b/src/Config.hpp @@ -30,7 +30,7 @@ #include #include -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); diff --git a/src/argprocessing.cpp b/src/argprocessing.cpp index 612eadd7e5..791331888c 100644 --- a/src/argprocessing.cpp +++ b/src/argprocessing.cpp @@ -230,7 +230,7 @@ process_arg(Context& ctx, } // Special case for -E. - if (args[i] == "-E") { + if (args[i] == "-E" || args[i] == "/E") { return Statistic::called_for_preprocessing; } @@ -364,11 +364,17 @@ process_arg(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) { @@ -477,7 +483,8 @@ process_arg(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]); @@ -790,7 +797,8 @@ process_arg(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); @@ -828,7 +836,8 @@ process_arg(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]); diff --git a/src/ccache.cpp b/src/ccache.cpp index 10724c95df..c84e92fbb4 100644 --- a/src/ccache.cpp +++ b/src/ccache.cpp @@ -74,6 +74,7 @@ #include #include #include +#include #ifndef MYNAME # define MYNAME "ccache" @@ -342,6 +343,9 @@ 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 + || name.find("clang-cl") != nonstd::string_view::npos) { + return CompilerType::cl; } else { return CompilerType::other; } @@ -958,8 +962,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 @@ -1034,9 +1042,62 @@ to_cache(Context& ctx, throw Failure(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) { + std::string tmp_stderr2 = fmt::format("{}.2", tmp_stderr_path); + Util::rename(tmp_stderr_path, tmp_stderr2); + + 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_path, std::ios_base::binary); + if (!result_stream.is_open()) { + LOG("Failed opening {}: {}", tmp_stderr_path, strerror(errno)); + throw Failure(Statistic::no_input_file); + } + + std::ostreambuf_iterator to(result_stream); + for (auto& file : {tmp_stdout_path, 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()) { + LOG("Failed opening {}: {}", file, strerror(errno)); + throw Failure(Statistic::no_input_file); + } + + 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()) { + LOG("Failed at writing data into {}: {}", tmp_stderr_path, strerror(errno)); + throw Failure(Statistic::bad_output_file); + } + + Util::unlink_tmp(tmp_stderr2); + } + // 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"); throw Failure(Statistic::compiler_produced_stdout); } diff --git a/src/compopt.cpp b/src/compopt.cpp index 1931d5f442..ef7dae3e72 100644 --- a/src/compopt.cpp +++ b/src/compopt.cpp @@ -134,6 +134,17 @@ 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 + {"/u", AFFECTS_CPP}, // msvc }; static int