forked from qt-creator/qt-creator
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 888ea6bbbb
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>
This commit is contained in:
@@ -219,6 +219,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
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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::addIncludeFile(const QString &file)
|
||||
{
|
||||
if (QFile::exists(file)) {
|
||||
add({isClStyle() ? QLatin1String(includeFileOptionCl)
|
||||
: QLatin1String(includeFileOptionGcc),
|
||||
QDir::toNativeSeparators(file)});
|
||||
}
|
||||
}
|
||||
|
||||
void CompilerOptionsBuilder::addIncludedFiles(const QStringList &files)
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
const QString includeOptionString
|
||||
= QLatin1String(isClStyle() ? includeFileOptionCl : includeFileOptionGcc);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
return;
|
||||
|
||||
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)});
|
||||
}
|
||||
for (const QString &pchFile : m_projectPart.precompiledHeaders) {
|
||||
addIncludeFile(pchFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user