From e89dc0ab0a48725e416ad61ce58a1880660dc514 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Tue, 13 Aug 2019 17:31:33 +0200 Subject: [PATCH] logic: only use precompiled headers for compilation database commands that specify the "-include-pch" flag (issue #719) --- src/lib_cxx/project/SourceGroupCxxCdb.cpp | 19 +++++--- src/lib_cxx/project/utilitySourceGroupCxx.cpp | 45 +++++++++++++++++++ src/lib_cxx/project/utilitySourceGroupCxx.h | 9 ++++ .../path/QtProjectWizardContentPathCxxPch.cpp | 43 ++++++++++++++++++ .../path/QtProjectWizardContentPathCxxPch.h | 1 + testing/project_setup/cxx_cdb/checklist.txt | 6 +++ testing/project_setup/cxx_cdb_pch/1_setup.sh | 2 +- .../project_setup/cxx_cdb_pch/checklist.txt | 5 ++- 8 files changed, 121 insertions(+), 9 deletions(-) diff --git a/src/lib_cxx/project/SourceGroupCxxCdb.cpp b/src/lib_cxx/project/SourceGroupCxxCdb.cpp index e154bb9b..5332e7b9 100644 --- a/src/lib_cxx/project/SourceGroupCxxCdb.cpp +++ b/src/lib_cxx/project/SourceGroupCxxCdb.cpp @@ -101,7 +101,8 @@ std::shared_ptr SourceGroupCxxCdb::getIndexerCommandProv std::vector compilerFlags = getBaseCompilerFlags(); utility::append(compilerFlags, m_settings->getCompilerFlags()); - utility::append(compilerFlags, utility::getIncludePchFlags(m_settings.get())); + + const std::vector includePchFlags = utility::getIncludePchFlags(m_settings.get()); const std::set indexedHeaderPaths = utility::toSet(m_settings->getIndexedHeaderPathsExpandedAndAbsolute()); const std::set excludeFilters = utility::toSet(m_settings->getExcludeFiltersExpandedAndAbsolute()); @@ -122,13 +123,17 @@ std::shared_ptr SourceGroupCxxCdb::getIndexerCommandProv if (filesToIndex.find(sourcePath) != filesToIndex.end() && sourceFilePaths.find(sourcePath) != sourceFilePaths.end()) { - std::vector mergedCompilerFlags; - mergedCompilerFlags.reserve(compilerFlags.size() + command.CommandLine.size()); - for (const std::string& arg : command.CommandLine) + std::vector cdbFlags = utility::convert( + command.CommandLine, + [](const std::string& s) { return utility::decodeFromUtf8(s); } + ); + + utility::removeIncludePchFlag(cdbFlags); + + if (command.CommandLine.size() != cdbFlags.size()) { - mergedCompilerFlags.emplace_back(utility::decodeFromUtf8(arg)); + utility::append(cdbFlags, includePchFlags); } - mergedCompilerFlags.insert(mergedCompilerFlags.end(), compilerFlags.begin(), compilerFlags.end()); provider->addCommand(std::make_shared( sourcePath, @@ -136,7 +141,7 @@ std::shared_ptr SourceGroupCxxCdb::getIndexerCommandProv excludeFilters, std::set(), FilePath(utility::decodeFromUtf8(command.Directory)), - mergedCompilerFlags + utility::concat(cdbFlags, compilerFlags) )); } } diff --git a/src/lib_cxx/project/utilitySourceGroupCxx.cpp b/src/lib_cxx/project/utilitySourceGroupCxx.cpp index ef218682..d600000e 100644 --- a/src/lib_cxx/project/utilitySourceGroupCxx.cpp +++ b/src/lib_cxx/project/utilitySourceGroupCxx.cpp @@ -1,5 +1,7 @@ #include "utilitySourceGroupCxx.h" +#include + #include "CanonicalFilePathCache.h" #include "CxxCompilationDatabaseSingle.h" #include "CxxDiagnosticConsumer.h" @@ -48,6 +50,7 @@ namespace utility const FilePath pchOutputFilePath = pchDependenciesDirectoryPath.getConcatenated(pchInputFilePath.fileName()).replaceExtension(L"pch"); + utility::removeIncludePchFlag(compilerFlags); compilerFlags.push_back(pchInputFilePath.wstr()); compilerFlags.push_back(L"-emit-pch"); compilerFlags.push_back(L"-o"); @@ -102,6 +105,48 @@ namespace utility ); } + bool containsIncludePchFlags(std::shared_ptr cdb) + { + for (const clang::tooling::CompileCommand& command : cdb->getAllCompileCommands()) + { + if (command.CommandLine.size() != getWithRemoveIncludePchFlag(utility::convert( + command.CommandLine, + [](const std::string& s) {return utility::decodeFromUtf8(s); } + )).size()) + { + return true; + } + } + return false; + } + + std::vector getWithRemoveIncludePchFlag(const std::vector& args) + { + std::vector ret = args; + removeIncludePchFlag(ret); + return ret; + } + + void removeIncludePchFlag(std::vector& args) + { + const std::wstring includePchPrefix = L"-include-pch"; + for (size_t i = 0; i < args.size(); i++) + { + const std::wstring arg = utility::trim(args[i]); + if (utility::isPrefix(includePchPrefix, arg)) + { + if (i + 1 < args.size() && + !utility::isPrefix(L"-", utility::trim(args[i + 1])) && + arg == includePchPrefix) + { + args.erase(args.begin() + i + 1); + } + args.erase(args.begin() + i); + i--; + } + } + } + std::vector getIncludePchFlags(const SourceGroupSettingsCxx* settings) { const SourceGroupSettingsWithCxxPchOptions* pchSettings = diff --git a/src/lib_cxx/project/utilitySourceGroupCxx.h b/src/lib_cxx/project/utilitySourceGroupCxx.h index 834c7f08..82d2a190 100644 --- a/src/lib_cxx/project/utilitySourceGroupCxx.h +++ b/src/lib_cxx/project/utilitySourceGroupCxx.h @@ -5,6 +5,12 @@ #include #include +namespace clang { + namespace tooling { + class JSONCompilationDatabase; + } +} + class DialogView; class SourceGroupSettingsCxx; class StorageProvider; @@ -15,6 +21,9 @@ namespace utility std::shared_ptr createBuildPchTask( const SourceGroupSettingsCxx* settings, std::vector compilerFlags, std::shared_ptr storageProvider, std::shared_ptr dialogView); + bool containsIncludePchFlags(std::shared_ptr cdb); + std::vector getWithRemoveIncludePchFlag(const std::vector& args); + void removeIncludePchFlag(std::vector& args); std::vector getIncludePchFlags(const SourceGroupSettingsCxx* settings); } diff --git a/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.cpp b/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.cpp index af726ed8..2debbcac 100644 --- a/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.cpp +++ b/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.cpp @@ -1,8 +1,13 @@ #include "QtProjectWizardContentPathCxxPch.h" +#include + +#include "IndexerCommandCxx.h" +#include "SourceGroupSettingsCxxCdb.h" #include "SourceGroupSettingsWithCxxPchOptions.h" #include "utility.h" #include "utilityFile.h" +#include "utilitySourceGroupCxx.h" QtProjectWizardContentPathCxxPch::QtProjectWizardContentPathCxxPch( std::shared_ptr settings, @@ -40,6 +45,44 @@ void QtProjectWizardContentPathCxxPch::save() m_settingsCxxPch->setPchInputFilePathFilePath(FilePath(m_picker->getText().toStdWString())); } +bool QtProjectWizardContentPathCxxPch::check() +{ + if (std::shared_ptr cdbSettings = std::dynamic_pointer_cast(m_settings)) + { + const FilePath cdbPath = cdbSettings->getCompilationDatabasePathExpandedAndAbsolute(); + std::shared_ptr cdb = IndexerCommandCxx::loadCDB(cdbPath); + if (!cdb) + { + QMessageBox msgBox; + msgBox.setText("Unable to open and read the provided compilation database file."); + msgBox.exec(); + return false; + } + + if (utility::containsIncludePchFlags(cdb)) + { + if (m_settingsCxxPch->getPchInputFilePath().empty()) + { + QMessageBox msgBox; + msgBox.setText("The provided compilation database file uses precompiled headers. Please specify a Precompiled Header input file."); + msgBox.exec(); + return false; + } + } + else + { + if (!m_settingsCxxPch->getPchInputFilePath().empty()) + { + QMessageBox msgBox; + msgBox.setText("The provided compilation database file does not use precompiled headers. Please do not specify a Precompiled Header input file."); + msgBox.exec(); + return false; + } + } + } + return true; +} + std::shared_ptr QtProjectWizardContentPathCxxPch::getSourceGroupSettings() { return m_settings; diff --git a/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.h b/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.h index 91fc5f12..17568e0e 100644 --- a/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.h +++ b/src/lib_gui/qt/project_wizard/content/path/QtProjectWizardContentPathCxxPch.h @@ -22,6 +22,7 @@ public: void load() override; void save() override; + bool check() override; private: std::shared_ptr getSourceGroupSettings() override; diff --git a/testing/project_setup/cxx_cdb/checklist.txt b/testing/project_setup/cxx_cdb/checklist.txt index b09bb972..433fe2b5 100644 --- a/testing/project_setup/cxx_cdb/checklist.txt +++ b/testing/project_setup/cxx_cdb/checklist.txt @@ -13,6 +13,12 @@ * Validate "Header Files & Directories to Index" contains "src" entry * Add "**/Foo.h" to "Excluded Files & Directories" * Click "Next" +* Pick any file at "Precompiled Header File" +* Click "Next" +* Validate dialog appears that asks NOT to specify PCH input file +* Click "OK" +* Clear "Precompiled Header File" +* Click "Next" * Click "Create" * Validate "All files" is the only option selectable * Click "Start" diff --git a/testing/project_setup/cxx_cdb_pch/1_setup.sh b/testing/project_setup/cxx_cdb_pch/1_setup.sh index 61fd4519..f78d427c 100755 --- a/testing/project_setup/cxx_cdb_pch/1_setup.sh +++ b/testing/project_setup/cxx_cdb_pch/1_setup.sh @@ -21,7 +21,7 @@ cp -a $SRC_PATH/. $WORKING_COPY_PATH echo "[" >> $CDB_PATH echo " {" >> $CDB_PATH echo " \"directory\": \"${WORKING_COPY_PATH}\"," >> $CDB_PATH -echo " \"command\": \"clang-tool -fms-extensions -fms-compatibility -fms-compatibility-version=19 -isystem \\\"${WORKING_COPY_SRC_PATH}/include\\\" -DCDB_FLAG -D _DEBUG -D _MT -D _DLL -D WIN32 -D _WINDOWS -D BUILD_TYPE=\\\"\\\" -D QT_WIDGETS_LIB -D QT_GUI_LIB -D QT_CORE_LIB -D QT_NETWORK_LIB -D QT_WINEXTRAS_LIB -D QT_SVG_LIB -D CMAKE_INTDIR=\\\"Debug\\\" -D _MBCS -std=c++14 \\\"${WORKING_COPY_SRC_PATH}/main.cpp\\\"\"," >> $CDB_PATH +echo " \"command\": \"clang-tool -fms-extensions -fms-compatibility -fms-compatibility-version=19 -isystem \\\"${WORKING_COPY_SRC_PATH}/include\\\" -DCDB_FLAG -D _DEBUG -D _MT -D _DLL -D WIN32 -D _WINDOWS -D BUILD_TYPE=\\\"\\\" -D QT_WIDGETS_LIB -D QT_GUI_LIB -D QT_CORE_LIB -D QT_NETWORK_LIB -D QT_WINEXTRAS_LIB -D QT_SVG_LIB -D CMAKE_INTDIR=\\\"Debug\\\" -D _MBCS -std=c++14 -include-pch some/file/path \\\"${WORKING_COPY_SRC_PATH}/main.cpp\\\"\"," >> $CDB_PATH echo " \"file\": \"${WORKING_COPY_SRC_PATH}/main.cpp\"" >> $CDB_PATH echo " }" >> $CDB_PATH echo "]" >> $CDB_PATH diff --git a/testing/project_setup/cxx_cdb_pch/checklist.txt b/testing/project_setup/cxx_cdb_pch/checklist.txt index 2262b9e5..e1bca5e6 100644 --- a/testing/project_setup/cxx_cdb_pch/checklist.txt +++ b/testing/project_setup/cxx_cdb_pch/checklist.txt @@ -14,8 +14,11 @@ * Add "**/Foo.h" to "Excluded Files & Directories" * Click "Next" * Click "Next" -* Add "-DCOMPILER_FLAG" to "Additional Compiler Flags" +* Validate dialog appears that asks to specify PCH input file +* Click "OK" * Add "./src/pch.h" to "Precompiled Header File" +* Click "Next" +* Add "-DCOMPILER_FLAG" to "Additional Compiler Flags" * Add "-DPCH_FLAG" to "Precompiled Header Flags" * Click "Create" * Validate "All files" is the only option selectable