bool
detect_pch(const std::string& option,
const std::string& arg,
- std::string& included_pch_file,
+ ArgsInfo& args_info,
bool is_cc1_option,
ArgumentProcessingState& state)
{
+ auto& included_pch_file = args_info.included_pch_file;
+
// Try to be smart about detecting precompiled headers.
// If the option is an option for Clang (is_cc1_option), don't accept
// anything just because it has a corresponding precompiled header,
std::string pch_file;
if (option == "-Yc") {
state.found_Yc = true;
+ args_info.generating_pch = true;
if (!state.found_Fp_file.empty()) {
included_pch_file = state.found_Fp_file;
return true;
}
}
} else if (option == "-Fp") {
+ args_info.orig_included_pch_file = arg;
std::string file = arg;
if (!fs::path(file).has_extension()) {
file += ".pch";
// index further behind.
const size_t next = args[i + 1] == "-Xclang" && i + 2 < args.size() ? 2 : 1;
- if (!detect_pch(
- arg, args[i + next], args_info.included_pch_file, next == 2, state)) {
+ if (!detect_pch(arg, args[i + next], args_info, next == 2, state)) {
return Statistic::bad_compiler_arguments;
}
const size_t path_pos = util::starts_with(arg, "-include") ? 8 : 3;
if (!detect_pch(arg.substr(0, path_pos),
arg.substr(path_pos),
- args_info.included_pch_file,
+ args_info,
false,
state)) {
return Statistic::bad_compiler_arguments;
}
-
- if (state.found_Yc) {
- args_info.generating_pch = true;
- }
// Fall through to the next section, so intentionally not returning here.
}
return config.is_compiler_group_msvc() ? ".obj" : ".o";
}
+const char*
+get_default_pch_file_extension(const Config& config)
+{
+ return config.is_compiler_group_msvc() ? ".pch" : ".gch";
+}
+
} // namespace
ProcessArgsResult
args_info.output_obj =
pstr(core::make_relative_path(ctx, args_info.output_obj)).str();
+ // Determine a filepath for precompiled header.
+ if (ctx.config.is_compiler_group_msvc() && args_info.generating_pch) {
+ bool included_pch_file_by_source = args_info.included_pch_file.empty();
+
+ if (!included_pch_file_by_source
+ && (*args_info.orig_included_pch_file.rbegin() == '\\'
+ || DirEntry(args_info.orig_included_pch_file).is_directory())) {
+ LOG("Unsupported folder path value for -Fp: {}",
+ args_info.included_pch_file);
+ return Statistic::could_not_use_precompiled_header;
+ }
+
+ if (included_pch_file_by_source && !args_info.input_file.empty()) {
+ args_info.included_pch_file =
+ pstr(fs::path(args_info.input_file)
+ .filename()
+ .replace_extension(get_default_pch_file_extension(ctx.config)))
+ .str();
+ LOG(
+ "Setting PCH filepath from the base source file (during generating): "
+ "{}",
+ args_info.included_pch_file);
+ }
+ }
+
// Determine output dependency file.
// On argument processing error, return now since we have determined
|| is_precompiled_header(args_info.output_obj);
if (args_info.output_is_precompiled_header && output_obj_by_source) {
- args_info.orig_output_obj = args_info.orig_input_file + ".gch";
+ args_info.orig_output_obj =
+ args_info.orig_input_file + get_default_pch_file_extension(config);
args_info.output_obj =
pstr(core::make_relative_path(ctx, args_info.orig_output_obj)).str();
}
}
}
+TEST_CASE("MSVC PCH options with empty -Yc and without -Fp")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.config.set_compiler_type(CompilerType::msvc);
+ util::write_file("foo.cpp", "");
+ util::write_file("pch.h", "");
+ util::write_file("pch.cpp", "");
+
+ SUBCASE("Create PCH")
+ {
+ ctx.orig_args = Args::from_string("cl.exe /Yc /Fopch.cpp.obj /c pch.cpp");
+ const ProcessArgsResult result = process_args(ctx);
+ REQUIRE(!result.error);
+ CHECK(ctx.args_info.generating_pch);
+ 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");
+ }
+
+ util::write_file("pch.pch", "");
+ ctx.config.update_from_map({{"sloppiness", "pch_defines,time_macros"}});
+
+ SUBCASE("Consume PCH")
+ {
+ ctx.orig_args = Args::from_string(
+ "cl.exe /Yupch.h /Fppch.pch /FIpch.h /Fofoo.cpp.obj /c foo.cpp");
+ const ProcessArgsResult result = process_args(ctx);
+ REQUIRE(!result.error);
+ CHECK(!ctx.args_info.generating_pch);
+ CHECK(ctx.args_info.included_pch_file == "pch.pch");
+ CHECK(ctx.args_info.output_obj == "foo.cpp.obj");
+ 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");
+ }
+}
+
+TEST_CASE("MSVC PCH options with empty -Yc and without -Fp and -Fo")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.config.set_compiler_type(CompilerType::msvc);
+ util::write_file("foo.cpp", "");
+ util::write_file("pch.h", "");
+ util::write_file("pch.cpp", "");
+
+ SUBCASE("Create PCH")
+ {
+ ctx.orig_args = Args::from_string("cl.exe /Yc /c pch.cpp");
+ const ProcessArgsResult result = process_args(ctx);
+ REQUIRE(!result.error);
+ CHECK(ctx.args_info.generating_pch);
+ 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");
+ }
+
+ util::write_file("pch.pch", "");
+ ctx.config.update_from_map({{"sloppiness", "pch_defines,time_macros"}});
+
+ SUBCASE("Consume PCH")
+ {
+ ctx.orig_args = Args::from_string(
+ "cl.exe /Yupch.h /Fppch.pch /FIpch.h /Fofoo.cpp.obj /c foo.cpp");
+ const ProcessArgsResult result = process_args(ctx);
+ REQUIRE(!result.error);
+ CHECK(!ctx.args_info.generating_pch);
+ CHECK(ctx.args_info.included_pch_file == "pch.pch");
+ CHECK(ctx.args_info.output_obj == "foo.cpp.obj");
+ 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");
+ }
+}
+
+TEST_CASE("MSVC PCH unsupported options")
+{
+ TestContext test_context;
+ Context ctx;
+ ctx.config.set_compiler_type(CompilerType::msvc);
+ util::write_file("pch.h", "");
+ util::write_file("pch.cpp", "");
+
+ SUBCASE("/Fp with absolute folder path")
+ {
+ ctx.orig_args =
+ Args::from_string("cl.exe /Yc /FpE:\\foo\\bar\\ /c pch.cpp");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(result.error == Statistic::could_not_use_precompiled_header);
+ CHECK(ctx.args_info.generating_pch);
+ CHECK(ctx.args_info.orig_included_pch_file == "E:\\foo\\bar\\");
+ CHECK(ctx.args_info.output_obj == "pch.obj");
+ }
+
+ SUBCASE("/Fp with relative folder path")
+ {
+ ctx.orig_args = Args::from_string("cl.exe /Yc /Fpfolder\\ /c pch.cpp");
+ const ProcessArgsResult result = process_args(ctx);
+ CHECK(result.error == Statistic::could_not_use_precompiled_header);
+ CHECK(ctx.args_info.generating_pch);
+ CHECK(ctx.args_info.orig_included_pch_file == "folder\\");
+ CHECK(ctx.args_info.output_obj == "pch.obj");
+ }
+}
+
TEST_CASE("MSVC debug information format options")
{
TestContext test_context;