+fo
copyable
creat
files'
}
optional<Args>
-Args::from_gcc_atfile(const std::string& filename)
+Args::from_atfile(const std::string& filename, bool ignore_backslash)
{
std::string argtext;
try {
while (true) {
switch (*pos) {
case '\\':
+ if (ignore_backslash) {
+ break;
+ }
pos++;
if (*pos == '\0') {
continue;
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;
CASE(nvcc);
CASE(other);
CASE(pump);
+ CASE(cl);
}
#undef CASE
#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);
}
// Special case for -E.
- if (args[i] == "-E") {
+ if (args[i] == "-E" || args[i] == "/E") {
return Statistic::called_for_preprocessing;
}
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;
// 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;
// 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;
}
}
// 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) {
// 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]);
// 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);
}
// 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]);
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;
}
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
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);
}
{"-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
CHECK(args[3] == "f");
}
-TEST_CASE("Args::from_gcc_atfile")
+TEST_CASE("Args::from_atfile")
{
TestContext test_context;
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");
}
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");
}
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");
"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");