diff options
author | Cristian Adam <cristian.adam@qt.io> | 2021-01-11 15:57:16 +0100 |
---|---|---|
committer | Cristian Adam <cristian.adam@qt.io> | 2021-01-14 08:08:40 +0000 |
commit | 98b92ed03ec49e1c0f614266fadf04b2bca408cd (patch) | |
tree | 1e4a8be094e5baf8a3971f0d9eb11076869bfd09 | |
parent | a64defa10bb9b1542aa87895e6a0ecd4fb3c6a77 (diff) | |
download | qt-creator-98b92ed03ec49e1c0f614266fadf04b2bca408cd.tar.gz |
CMakeProjectManager: Fix issues with precompiled headers
Clang code model can break if CMake project uses precompiled headers.
QtCreator will make a copy of the precompiled header, this way it
will not conflict with the build system one.
Ammends 888ea6bbbb0f4c6bb6b5616046e600b9520e4faf
Fixes: QTCREATORBUG-24945
Fixes: QTCREATORBUG-25213
Change-Id: I149fc416cd047683d095758a024de47c7baf681c
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
-rw-r--r-- | src/libs/utils/fileutils.cpp | 23 | ||||
-rw-r--r-- | src/libs/utils/fileutils.h | 2 | ||||
-rw-r--r-- | src/plugins/cmakeprojectmanager/fileapidataextractor.cpp | 48 | ||||
-rw-r--r-- | src/plugins/cpptools/compileroptionsbuilder.cpp | 52 | ||||
-rw-r--r-- | src/plugins/cpptools/compileroptionsbuilder.h | 3 |
5 files changed, 81 insertions, 47 deletions
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 23be348698..53b8957794 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -220,6 +220,29 @@ bool FileUtils::copyRecursively(const FilePath &srcFilePath, const FilePath &tgt } /*! + Copies a file specified by \a srcFilePath to \a tgtFilePath only if \a srcFilePath is different + (file size and last modification time). + + Returns whether the operation succeeded. +*/ + +bool FileUtils::copyIfDifferent(const FilePath &srcFilePath, const FilePath &tgtFilePath) +{ + if (QFile::exists(tgtFilePath.toString())) { + const QFileInfo srcFileInfo = srcFilePath.toFileInfo(); + const QFileInfo tgtFileInfo = tgtFilePath.toFileInfo(); + if (srcFileInfo.lastModified() == tgtFileInfo.lastModified() && + srcFileInfo.size() == tgtFileInfo.size()) { + return true; + } else { + QFile::remove(tgtFilePath.toString()); + } + } + + return QFile::copy(srcFilePath.toString(), tgtFilePath.toString()); +} + +/*! If this is a directory, the function will recursively check all files and return true if one of them is newer than \a timeStamp. If this is a single file, true will be returned if the file is newer than \a timeStamp. diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 5db3a0a1c2..9f1a0bef91 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -189,6 +189,8 @@ public: const FilePath &tgtFilePath, QString *error, T &©Helper); + static bool copyIfDifferent(const FilePath &srcFilePath, + const FilePath &tgtFilePath); static FilePath resolveSymlinks(const FilePath &path); static QString fileSystemFriendlyName(const QString &name); static int indexOfQmakeUnfriendly(const QString &name, int startpos = 0); diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index badf74c059..2ca34b68ad 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -348,10 +348,15 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, } QString ending; - if (ci.language == "C") + QString qtcPchFile; + if (ci.language == "C") { ending = "/cmake_pch.h"; - else if (ci.language == "CXX") + qtcPchFile = "qtc_cmake_pch.h"; + } + else if (ci.language == "CXX") { ending = "/cmake_pch.hxx"; + qtcPchFile = "qtc_cmake_pch.hxx"; + } ++counter; RawProjectPart rpp; @@ -362,13 +367,7 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, rpp.setMacros(transform<QVector>(ci.defines, &DefineInfo::define)); rpp.setHeaderPaths(transform<QVector>(ci.includes, &IncludeInfo::path)); - RawProjectPartFlags cProjectFlags; - cProjectFlags.commandLineFlags = splitFragments(ci.fragments); - rpp.setFlagsForC(cProjectFlags); - - RawProjectPartFlags cxxProjectFlags; - cxxProjectFlags.commandLineFlags = cProjectFlags.commandLineFlags; - rpp.setFlagsForCxx(cxxProjectFlags); + QStringList fragments = splitFragments(ci.fragments); FilePath precompiled_header = FilePath::fromString(findOrDefault(t.sources, [&ending](const SourceInfo &si) { @@ -383,9 +382,38 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, const FilePath parentDir = FilePath::fromString(sourceDir.absolutePath()); precompiled_header = parentDir.pathAppended(precompiled_header.toString()); } - rpp.setPreCompiledHeaders({precompiled_header.toString()}); + + // Remove the CMake PCH usage command line options in order to avoid the case + // when the build system would produce a .pch/.gch file that would be treated + // by the Clang code model as its own and fail. + auto remove = [&](const QStringList &args) { + auto foundPos = std::search(fragments.begin(), fragments.end(), + args.begin(), args.end()); + if (foundPos != fragments.end()) + fragments.erase(foundPos, std::next(foundPos, args.size())); + }; + + remove({"-Xclang", "-include-pch", "-Xclang", precompiled_header.toString() + ".gch"}); + remove({"-Xclang", "-include-pch", "-Xclang", precompiled_header.toString() + ".pch"}); + remove({"-Xclang", "-include", "-Xclang", precompiled_header.toString()}); + remove({"-include", precompiled_header.toString()}); + remove({"/FI", precompiled_header.toString()}); + + // Make a copy of the CMake PCH header and use it instead + FilePath qtc_precompiled_header = precompiled_header.parentDir().pathAppended(qtcPchFile); + FileUtils::copyIfDifferent(precompiled_header, qtc_precompiled_header); + + rpp.setPreCompiledHeaders({qtc_precompiled_header.toString()}); } + RawProjectPartFlags cProjectFlags; + cProjectFlags.commandLineFlags = fragments; + rpp.setFlagsForC(cProjectFlags); + + RawProjectPartFlags cxxProjectFlags; + cxxProjectFlags.commandLineFlags = cProjectFlags.commandLineFlags; + rpp.setFlagsForCxx(cxxProjectFlags); + const bool isExecutable = t.type == "EXECUTABLE"; rpp.setBuildTargetType(isExecutable ? ProjectExplorer::BuildTargetType::Executable : ProjectExplorer::BuildTargetType::Library); diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 02ce9a3616..37d64208b1 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -175,14 +175,6 @@ void CompilerOptionsBuilder::addSyntaxOnly() isClStyle() ? add("/Zs") : add("-fsyntax-only"); } -void CompilerOptionsBuilder::remove(const QStringList &args) -{ - auto foundPos = std::search(m_options.begin(), m_options.end(), - args.begin(), args.end()); - if (foundPos != m_options.end()) - m_options.erase(foundPos, std::next(foundPos, args.size())); -} - QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objcExt) { QStringList options; @@ -369,42 +361,32 @@ void CompilerOptionsBuilder::addHeaderPathOptions() } } -void CompilerOptionsBuilder::addIncludedFiles(const QStringList &files) +void CompilerOptionsBuilder::addIncludeFile(const QString &file) { - QStringList result; + if (QFile::exists(file)) { + add({isClStyle() ? QLatin1String(includeFileOptionCl) + : QLatin1String(includeFileOptionGcc), + QDir::toNativeSeparators(file)}); + } +} - const QString includeOptionString - = QLatin1String(isClStyle() ? includeFileOptionCl : includeFileOptionGcc); +void CompilerOptionsBuilder::addIncludedFiles(const QStringList &files) +{ for (const QString &file : files) { - if (QFile::exists(file)) { - result += includeOptionString; - result += QDir::toNativeSeparators(file); - } + if (m_projectPart.precompiledHeaders.contains(file)) + continue; + + addIncludeFile(file); } - m_options.append(result); } void CompilerOptionsBuilder::addPrecompiledHeaderOptions(UsePrecompiledHeaders usePrecompiledHeaders) { + if (usePrecompiledHeaders == UsePrecompiledHeaders::No) + return; + for (const QString &pchFile : m_projectPart.precompiledHeaders) { - // Bail if build system precompiled header artifacts exists. - // Clang cannot handle foreign PCH files. - if (QFile::exists(pchFile + ".gch") || QFile::exists(pchFile + ".pch")) - usePrecompiledHeaders = UsePrecompiledHeaders::No; - - if (usePrecompiledHeaders == UsePrecompiledHeaders::No) { - // CMake PCH will already have force included the header file in - // command line options, remove it if exists. - // In case of Clang compilers, also remove the pch-inclusion arguments. - remove({"-Xclang", "-include-pch", "-Xclang", pchFile + ".gch"}); - remove({"-Xclang", "-include-pch", "-Xclang", pchFile + ".pch"}); - remove({isClStyle() ? QLatin1String(includeFileOptionCl) - : QLatin1String(includeFileOptionGcc), pchFile}); - } else if (QFile::exists(pchFile)) { - add({isClStyle() ? QLatin1String(includeFileOptionCl) - : QLatin1String(includeFileOptionGcc), - QDir::toNativeSeparators(pchFile)}); - } + addIncludeFile(pchFile); } } diff --git a/src/plugins/cpptools/compileroptionsbuilder.h b/src/plugins/cpptools/compileroptionsbuilder.h index d28c081258..f81c515db0 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.h +++ b/src/plugins/cpptools/compileroptionsbuilder.h @@ -88,8 +88,6 @@ public: void add(const QStringList &args, bool gccOnlyOptions = false); virtual void addExtraOptions() {} - void remove(const QStringList &args); - static UseToolchainMacros useToolChainMacros(); void reset(); @@ -103,6 +101,7 @@ private: QStringList wrappedQtHeadersIncludePath() const; QStringList wrappedMingwHeadersIncludePath() const; QByteArray msvcVersion() const; + void addIncludeFile(const QString &file); private: const ProjectPart &m_projectPart; |