From a55df20499fe9329269efccc8552f26029ae4a0e Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Fri, 10 Jan 2020 17:05:27 +0100 Subject: Multi-Ninja: Add precompile headers support Fixes: #19789 --- Source/cmGeneratorTarget.cxx | 11 +- Source/cmGlobalGenerator.h | 2 + Source/cmGlobalVisualStudioGenerator.h | 2 + Source/cmLocalGenerator.cxx | 265 +++++++++++---------- .../PrecompileHeaders/DisabledPch-check.cmake | 3 + .../PrecompileHeaders/PchDebugGenex-check.cmake | 17 ++ .../RunCMake/PrecompileHeaders/PchDebugGenex.cmake | 9 + .../PrecompileHeaders/PchInterface-check.cmake | 4 + .../PrecompileHeaders/PchMultilanguage-check.cmake | 4 + .../PchPrologueEpilogue-check.cmake | 3 + .../RunCMake/PrecompileHeaders/RunCMakeTest.cmake | 1 + .../VS10Project/VsPrecompileHeaders-check.cmake | 2 +- .../XcodePrecompileHeaders-check.cmake | 2 +- 13 files changed, 192 insertions(+), 133 deletions(-) create mode 100644 Tests/RunCMake/PrecompileHeaders/PchDebugGenex-check.cmake create mode 100644 Tests/RunCMake/PrecompileHeaders/PchDebugGenex.cmake diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index b3fb132..6aadcf3 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -3396,8 +3396,15 @@ std::string cmGeneratorTarget::GetPchHeader(const std::string& config, { "OBJCXX", ".objcxx.hxx" } }; - filename = cmStrCat(filename, "CMakeFiles/", generatorTarget->GetName(), - ".dir/cmake_pch", languageToExtension.at(language)); + filename = + cmStrCat(filename, "CMakeFiles/", generatorTarget->GetName(), ".dir"); + + if (this->GetGlobalGenerator()->IsMultiConfig()) { + filename = cmStrCat(filename, "/", config); + } + + filename = + cmStrCat(filename, "/cmake_pch", languageToExtension.at(language)); const std::string filename_tmp = cmStrCat(filename, ".tmp"); if (!pchReuseFrom) { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index b427992..da58214 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -415,6 +415,8 @@ public: virtual bool IsXcode() const { return false; } + virtual bool IsVisualStudio() const { return false; } + /** Return true if we know the exact location of object files. If false, store the reason in the given string. This is meaningful only after EnableLanguage has been called. */ diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 4f2007f..29a5c2c 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -150,6 +150,8 @@ public: bool Open(const std::string& bindir, const std::string& projectName, bool dryRun) override; + bool IsVisualStudio() const override { return true; } + protected: cmGlobalVisualStudioGenerator(cmake* cm, std::string const& platformInGeneratorName); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 18f82dd..ee35cfe 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -2413,163 +2413,170 @@ void cmLocalGenerator::AppendFlagEscape(std::string& flags, void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) { - // FIXME: Handle all configurations in multi-config generators. - std::string config; - if (!this->GetGlobalGenerator()->IsMultiConfig()) { - config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); - } - const std::string buildType = cmSystemTools::UpperCase(config); - - // FIXME: Refactor collection of sources to not evaluate object libraries. - std::vector sources; - target->GetSourceFiles(sources, buildType); - - for (const std::string& lang : { "C", "CXX", "OBJC", "OBJCXX" }) { - auto langSources = - std::count_if(sources.begin(), sources.end(), [lang](cmSourceFile* sf) { - return lang == sf->GetLanguage() && - !sf->GetProperty("SKIP_PRECOMPILE_HEADERS"); - }); - if (langSources == 0) { - continue; - } + std::vector configsList; + std::string configDefault = this->Makefile->GetConfigurations(configsList); + if (configsList.empty()) { + configsList.push_back(configDefault); + } + + for (std::string const& config : configsList) { + const std::string buildType = cmSystemTools::UpperCase(config); + + // FIXME: Refactor collection of sources to not evaluate object libraries. + std::vector sources; + target->GetSourceFiles(sources, buildType); + + for (const std::string& lang : { "C", "CXX", "OBJC", "OBJCXX" }) { + auto langSources = std::count_if( + sources.begin(), sources.end(), [lang](cmSourceFile* sf) { + return lang == sf->GetLanguage() && + !sf->GetProperty("SKIP_PRECOMPILE_HEADERS"); + }); + if (langSources == 0) { + continue; + } - const std::string pchSource = target->GetPchSource(config, lang); - const std::string pchHeader = target->GetPchHeader(config, lang); + const std::string pchSource = target->GetPchSource(config, lang); + const std::string pchHeader = target->GetPchHeader(config, lang); - if (pchSource.empty() || pchHeader.empty()) { - continue; - } + if (pchSource.empty() || pchHeader.empty()) { + continue; + } - const std::string pchExtension = - this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION"); + const std::string pchExtension = + this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION"); - if (pchExtension.empty()) { - continue; - } + if (pchExtension.empty()) { + continue; + } - const char* pchReuseFrom = - target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM"); + const char* pchReuseFrom = + target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM"); - auto pch_sf = this->Makefile->GetOrCreateSource( - pchSource, false, cmSourceFileLocationKind::Known); + auto pch_sf = this->Makefile->GetOrCreateSource( + pchSource, false, cmSourceFileLocationKind::Known); - if (!this->GetGlobalGenerator()->IsXcode()) { - if (!pchReuseFrom) { - target->AddSource(pchSource, true); - } + if (!this->GetGlobalGenerator()->IsXcode()) { + if (!pchReuseFrom) { + target->AddSource(pchSource, true); + } - const std::string pchFile = target->GetPchFile(config, lang); + const std::string pchFile = target->GetPchFile(config, lang); - // Exclude the pch files from linking - if (this->Makefile->IsOn("CMAKE_LINK_PCH")) { - if (!pchReuseFrom) { - pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str()); - } else { - auto reuseTarget = - this->GlobalGenerator->FindGeneratorTarget(pchReuseFrom); + // Exclude the pch files from linking + if (this->Makefile->IsOn("CMAKE_LINK_PCH")) { + if (!pchReuseFrom) { + pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str()); + } else { + auto reuseTarget = + this->GlobalGenerator->FindGeneratorTarget(pchReuseFrom); - if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) { + if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) { - const std::string pdb_prefix = - this->GetGlobalGenerator()->IsMultiConfig() - ? cmStrCat(this->GlobalGenerator->GetCMakeCFGIntDir(), "/") - : ""; + const std::string pdb_prefix = + this->GetGlobalGenerator()->IsMultiConfig() + ? cmStrCat(this->GlobalGenerator->GetCMakeCFGIntDir(), "/") + : ""; - const std::string target_compile_pdb_dir = cmStrCat( - target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/", - target->GetName(), ".dir/"); + const std::string target_compile_pdb_dir = cmStrCat( + target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/", + target->GetName(), ".dir/"); - const std::string copy_script = - cmStrCat(target_compile_pdb_dir, "copy_idb_pdb.cmake"); - cmGeneratedFileStream file(copy_script); + const std::string copy_script = + cmStrCat(target_compile_pdb_dir, "copy_idb_pdb.cmake"); + cmGeneratedFileStream file(copy_script); - file << "# CMake generated file\n"; - for (auto extension : { ".pdb", ".idb" }) { - const std::string from_file = cmStrCat( - reuseTarget->GetLocalGenerator()->GetCurrentBinaryDirectory(), - "/", pchReuseFrom, ".dir/${PDB_PREFIX}", pchReuseFrom, - extension); + file << "# CMake generated file\n"; + for (auto extension : { ".pdb", ".idb" }) { + const std::string from_file = + cmStrCat(reuseTarget->GetLocalGenerator() + ->GetCurrentBinaryDirectory(), + "/", pchReuseFrom, ".dir/${PDB_PREFIX}", + pchReuseFrom, extension); - const std::string to_dir = cmStrCat( - target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/", - target->GetName(), ".dir/${PDB_PREFIX}"); + const std::string to_dir = cmStrCat( + target->GetLocalGenerator()->GetCurrentBinaryDirectory(), + "/", target->GetName(), ".dir/${PDB_PREFIX}"); - const std::string to_file = - cmStrCat(to_dir, pchReuseFrom, extension); + const std::string to_file = + cmStrCat(to_dir, pchReuseFrom, extension); - std::string dest_file = to_file; + std::string dest_file = to_file; - const std::string prefix = target->GetSafeProperty("PREFIX"); - if (!prefix.empty()) { - dest_file = cmStrCat(to_dir, prefix, pchReuseFrom, extension); - } + const std::string prefix = target->GetSafeProperty("PREFIX"); + if (!prefix.empty()) { + dest_file = + cmStrCat(to_dir, prefix, pchReuseFrom, extension); + } - file << "if (EXISTS \"" << from_file << "\" AND \"" << from_file - << "\" IS_NEWER_THAN \"" << dest_file << "\")\n"; - file << " file(COPY \"" << from_file << "\"" - << " DESTINATION \"" << to_dir << "\")\n"; - if (!prefix.empty()) { - file << " file(REMOVE \"" << dest_file << "\")\n"; - file << " file(RENAME \"" << to_file << "\" \"" << dest_file + file << "if (EXISTS \"" << from_file << "\" AND \"" + << from_file << "\" IS_NEWER_THAN \"" << dest_file << "\")\n"; + file << " file(COPY \"" << from_file << "\"" + << " DESTINATION \"" << to_dir << "\")\n"; + if (!prefix.empty()) { + file << " file(REMOVE \"" << dest_file << "\")\n"; + file << " file(RENAME \"" << to_file << "\" \"" << dest_file + << "\")\n"; + } + file << "endif()\n"; } - file << "endif()\n"; - } - cmCustomCommandLines commandLines = cmMakeSingleCommandLine( - { cmSystemTools::GetCMakeCommand(), - cmStrCat("-DPDB_PREFIX=", pdb_prefix), "-P", copy_script }); - - const std::string no_main_dependency; - const std::vector no_deps; - const char* no_message = ""; - const char* no_current_dir = nullptr; - std::vector no_byproducts; - - std::vector outputs; - outputs.push_back(cmStrCat(target_compile_pdb_dir, pdb_prefix, - pchReuseFrom, ".pdb")); - - if (this->GetGlobalGenerator()->IsMultiConfig()) { - this->AddCustomCommandToTarget( - target->GetName(), outputs, no_deps, commandLines, - cmCustomCommandType::PRE_BUILD, no_message, no_current_dir); - } else { - cmImplicitDependsList no_implicit_depends; - cmSourceFile* copy_rule = this->AddCustomCommandToOutput( - outputs, no_byproducts, no_deps, no_main_dependency, - no_implicit_depends, commandLines, no_message, no_current_dir); - - if (copy_rule) { - target->AddSource(copy_rule->ResolveFullPath()); + cmCustomCommandLines commandLines = cmMakeSingleCommandLine( + { cmSystemTools::GetCMakeCommand(), + cmStrCat("-DPDB_PREFIX=", pdb_prefix), "-P", copy_script }); + + const std::string no_main_dependency; + const std::vector no_deps; + const char* no_message = ""; + const char* no_current_dir = nullptr; + std::vector no_byproducts; + + std::vector outputs; + outputs.push_back(cmStrCat(target_compile_pdb_dir, pdb_prefix, + pchReuseFrom, ".pdb")); + + if (this->GetGlobalGenerator()->IsVisualStudio()) { + this->AddCustomCommandToTarget( + target->GetName(), outputs, no_deps, commandLines, + cmCustomCommandType::PRE_BUILD, no_message, no_current_dir); + } else { + cmImplicitDependsList no_implicit_depends; + cmSourceFile* copy_rule = this->AddCustomCommandToOutput( + outputs, no_byproducts, no_deps, no_main_dependency, + no_implicit_depends, commandLines, no_message, + no_current_dir); + + if (copy_rule) { + target->AddSource(copy_rule->ResolveFullPath()); + } } - } - target->Target->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY", - target_compile_pdb_dir); - } + target->Target->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY", + target_compile_pdb_dir); + } - std::string pchSourceObj = - reuseTarget->GetPchFileObject(config, lang); + std::string pchSourceObj = + reuseTarget->GetPchFileObject(config, lang); - // Link to the pch object file - target->Target->AppendProperty( - "LINK_FLAGS", - cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL)), - true); + // Link to the pch object file + target->Target->AppendProperty( + "LINK_FLAGS", + cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL)), + true); + } + } else { + pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str()); } - } else { - pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str()); - } - // Add pchHeader to source files, which will - // be grouped as "Precompile Header File" - auto pchHeader_sf = this->Makefile->GetOrCreateSource( - pchHeader, false, cmSourceFileLocationKind::Known); - std::string err; - pchHeader_sf->ResolveFullPath(&err); - target->AddSource(pchHeader); + // Add pchHeader to source files, which will + // be grouped as "Precompile Header File" + auto pchHeader_sf = this->Makefile->GetOrCreateSource( + pchHeader, false, cmSourceFileLocationKind::Known); + std::string err; + pchHeader_sf->ResolveFullPath(&err); + target->AddSource(pchHeader); + } } } } diff --git a/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake b/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake index 494bcf7..cc719be 100644 --- a/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake +++ b/Tests/RunCMake/PrecompileHeaders/DisabledPch-check.cmake @@ -1,4 +1,7 @@ set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h") +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/Debug/cmake_pch.h") +endif() set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h") if (NOT EXISTS ${foo_pch_header}) diff --git a/Tests/RunCMake/PrecompileHeaders/PchDebugGenex-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchDebugGenex-check.cmake new file mode 100644 index 0000000..cb94ee5 --- /dev/null +++ b/Tests/RunCMake/PrecompileHeaders/PchDebugGenex-check.cmake @@ -0,0 +1,17 @@ +if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) + return() +endif() + +set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/Debug/cmake_pch.h") + +if (NOT EXISTS ${foo_pch_header}) + set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist") + return() +endif() + +file(STRINGS ${foo_pch_header} foo_pch_header_strings) + +if (NOT foo_pch_header_strings MATCHES ";#include \"[^\"]*PrecompileHeaders/include/foo.h\";#include (;|$)") + set(RunCMake_TEST_FAILED "Generated foo pch header\n ${foo_pch_header}\nhas bad content:\n ${foo_pch_header_strings}") + return() +endif() diff --git a/Tests/RunCMake/PrecompileHeaders/PchDebugGenex.cmake b/Tests/RunCMake/PrecompileHeaders/PchDebugGenex.cmake new file mode 100644 index 0000000..854689f --- /dev/null +++ b/Tests/RunCMake/PrecompileHeaders/PchDebugGenex.cmake @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.15) +project(PchDebugGenex C) + +add_library(foo foo.c) +target_include_directories(foo PUBLIC include) +target_precompile_headers(foo PUBLIC + "$<$:${CMAKE_CURRENT_SOURCE_DIR}/include/foo.h>" + +) diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake index 4e62b81..28c6ba4 100644 --- a/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake +++ b/Tests/RunCMake/PrecompileHeaders/PchInterface-check.cmake @@ -1,5 +1,9 @@ set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/cmake_pch.h") set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h") +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(foo_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo.dir/Debug/cmake_pch.h") + set(foobar_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/Debug/cmake_pch.h") +endif() if (NOT EXISTS ${foo_pch_header}) set(RunCMake_TEST_FAILED "Generated foo pch header ${foo_pch_header} does not exist") diff --git a/Tests/RunCMake/PrecompileHeaders/PchMultilanguage-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchMultilanguage-check.cmake index cc01ecb..1696037 100644 --- a/Tests/RunCMake/PrecompileHeaders/PchMultilanguage-check.cmake +++ b/Tests/RunCMake/PrecompileHeaders/PchMultilanguage-check.cmake @@ -1,5 +1,9 @@ set(foobar_pch_h_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.h") set(foobar_pch_hxx_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/cmake_pch.hxx") +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(foobar_pch_h_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/Debug/cmake_pch.h") + set(foobar_pch_hxx_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foobar.dir/Debug/cmake_pch.hxx") +endif() if (NOT EXISTS ${foobar_pch_h_header}) set(RunCMake_TEST_FAILED "Generated foobar C pch header ${foobar_pch_h_header} does not exist") diff --git a/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake index 9018664..f1504a7 100644 --- a/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake +++ b/Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue-check.cmake @@ -1,4 +1,7 @@ set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/cmake_pch.hxx") +if (RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(main_pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/main.dir/Debug/cmake_pch.hxx") +endif() file(STRINGS ${main_pch_header} main_pch_header_strings) string(REGEX MATCH "#pragma warning\\(push, 0\\).*#include.*pch.h.*#pragma warning\\(pop\\)" matched_code ${main_pch_header_strings}) diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake index 8d2f4f9..f587c7d 100644 --- a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake +++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake @@ -13,6 +13,7 @@ function(run_test name) endfunction() run_cmake(DisabledPch) +run_cmake(PchDebugGenex) run_test(PchInterface) run_cmake(PchPrologueEpilogue) run_test(SkipPrecompileHeaders) diff --git a/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake b/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake index 91cea0e..9c214f1 100644 --- a/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake +++ b/Tests/RunCMake/VS10Project/VsPrecompileHeaders-check.cmake @@ -1,4 +1,4 @@ -set(pch_header "CMakeFiles/tgt.dir/cmake_pch.hxx") +set(pch_header "CMakeFiles/tgt.dir/Debug/cmake_pch.hxx") set(pch_source [=[CMakeFiles\\tgt.dir\\cmake_pch.cxx]=]) if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${pch_header}") diff --git a/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake index aa3eafc..4e85db6 100644 --- a/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake +++ b/Tests/RunCMake/XcodeProject/XcodePrecompileHeaders-check.cmake @@ -1,4 +1,4 @@ -set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/cmake_pch.hxx") +set(pch_header "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/tgt.dir/Debug/cmake_pch.hxx") if(NOT EXISTS "${pch_header}") set(RunCMake_TEST_FAILED "Generated PCH header ${pch_header} does not exist.") -- cgit v0.12