forked from qt-creator/qt-creator
CMake: Move more code into BuildDirManager
Continue to concentrate all the code reading random cmake files in BuildDirManager. Now the task is to clean up the code, make it less dependent on values it should not depend on (kits, etc.), make it handle changes better and finally add another implementation that uses the cmake server mode to extract the data. Change-Id: I533625e376b969b64287bc205bd2e4be7a605306 Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -36,12 +36,16 @@
|
|||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <coreplugin/progressmanager/progressmanager.h>
|
#include <coreplugin/progressmanager/progressmanager.h>
|
||||||
|
#include <cpptools/projectpartbuilder.h>
|
||||||
|
#include <projectexplorer/headerpath.h>
|
||||||
#include <projectexplorer/kit.h>
|
#include <projectexplorer/kit.h>
|
||||||
|
#include <projectexplorer/kitinformation.h>
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
#include <projectexplorer/projectnodes.h>
|
#include <projectexplorer/projectnodes.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
#include <projectexplorer/taskhub.h>
|
#include <projectexplorer/taskhub.h>
|
||||||
|
#include <projectexplorer/toolchain.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
@@ -247,6 +251,44 @@ void BuildDirManager::generateProjectTree(CMakeProjectNode *root)
|
|||||||
m_files.clear(); // Some of the FileNodes in files() were deleted!
|
m_files.clear(); // Some of the FileNodes in files() were deleted!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSet<Core::Id> BuildDirManager::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
|
||||||
|
{
|
||||||
|
QSet<Core::Id> languages;
|
||||||
|
ToolChain *tc = ToolChainKitInformation::toolChain(kit(), ToolChain::Language::Cxx);
|
||||||
|
const Utils::FileName sysroot = SysRootKitInformation::sysRoot(kit());
|
||||||
|
|
||||||
|
QHash<QString, QStringList> targetDataCache;
|
||||||
|
foreach (const CMakeBuildTarget &cbt, buildTargets()) {
|
||||||
|
if (cbt.targetType == UtilityType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// CMake shuffles the include paths that it reports via the CodeBlocks generator
|
||||||
|
// So remove the toolchain include paths, so that at least those end up in the correct
|
||||||
|
// place.
|
||||||
|
QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache);
|
||||||
|
QSet<QString> tcIncludes;
|
||||||
|
foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot))
|
||||||
|
tcIncludes.insert(hp.path());
|
||||||
|
QStringList includePaths;
|
||||||
|
foreach (const QString &i, cbt.includeFiles) {
|
||||||
|
if (!tcIncludes.contains(i))
|
||||||
|
includePaths.append(i);
|
||||||
|
}
|
||||||
|
includePaths += buildDirectory().toString();
|
||||||
|
ppBuilder.setIncludePaths(includePaths);
|
||||||
|
ppBuilder.setCFlags(cxxflags);
|
||||||
|
ppBuilder.setCxxFlags(cxxflags);
|
||||||
|
ppBuilder.setDefines(cbt.defines);
|
||||||
|
ppBuilder.setDisplayName(cbt.title);
|
||||||
|
|
||||||
|
const QSet<Core::Id> partLanguages
|
||||||
|
= QSet<Core::Id>::fromList(ppBuilder.createProjectPartsForFiles(cbt.files));
|
||||||
|
|
||||||
|
languages.unite(partLanguages);
|
||||||
|
}
|
||||||
|
return languages;
|
||||||
|
}
|
||||||
|
|
||||||
void BuildDirManager::parse()
|
void BuildDirManager::parse()
|
||||||
{
|
{
|
||||||
checkConfiguration();
|
checkConfiguration();
|
||||||
@@ -545,6 +587,102 @@ void BuildDirManager::processCMakeError()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList BuildDirManager::getCXXFlagsFor(const CMakeBuildTarget &buildTarget,
|
||||||
|
QHash<QString, QStringList> &cache)
|
||||||
|
{
|
||||||
|
// check cache:
|
||||||
|
auto it = cache.constFind(buildTarget.title);
|
||||||
|
if (it != cache.constEnd())
|
||||||
|
return *it;
|
||||||
|
|
||||||
|
if (extractCXXFlagsFromMake(buildTarget, cache))
|
||||||
|
return cache.value(buildTarget.title);
|
||||||
|
|
||||||
|
if (extractCXXFlagsFromNinja(buildTarget, cache))
|
||||||
|
return cache.value(buildTarget.title);
|
||||||
|
|
||||||
|
cache.insert(buildTarget.title, QStringList());
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildDirManager::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget,
|
||||||
|
QHash<QString, QStringList> &cache)
|
||||||
|
{
|
||||||
|
QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand);
|
||||||
|
int startIndex = makeCommand.indexOf('\"');
|
||||||
|
int endIndex = makeCommand.indexOf('\"', startIndex + 1);
|
||||||
|
if (startIndex != -1 && endIndex != -1) {
|
||||||
|
startIndex += 1;
|
||||||
|
QString makefile = makeCommand.mid(startIndex, endIndex - startIndex);
|
||||||
|
int slashIndex = makefile.lastIndexOf('/');
|
||||||
|
makefile.truncate(slashIndex);
|
||||||
|
makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make");
|
||||||
|
QFile file(makefile);
|
||||||
|
if (file.exists()) {
|
||||||
|
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
|
QTextStream stream(&file);
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
QString line = stream.readLine().trimmed();
|
||||||
|
if (line.startsWith("CXX_FLAGS =")) {
|
||||||
|
// Skip past =
|
||||||
|
cache.insert(buildTarget.title,
|
||||||
|
line.mid(11).trimmed().split(' ', QString::SkipEmptyParts));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildDirManager::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget,
|
||||||
|
QHash<QString, QStringList> &cache)
|
||||||
|
{
|
||||||
|
Q_UNUSED(buildTarget)
|
||||||
|
if (!cache.isEmpty()) // We fill the cache in one go!
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were
|
||||||
|
// found
|
||||||
|
// Get "all" target's working directory
|
||||||
|
QByteArray ninjaFile;
|
||||||
|
QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory);
|
||||||
|
buildNinjaFile += "/build.ninja";
|
||||||
|
QFile buildNinja(buildNinjaFile);
|
||||||
|
if (buildNinja.exists()) {
|
||||||
|
buildNinja.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
|
ninjaFile = buildNinja.readAll();
|
||||||
|
buildNinja.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ninjaFile.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QTextStream stream(ninjaFile);
|
||||||
|
bool cxxFound = false;
|
||||||
|
const QString targetSignature = "# Object build statements for ";
|
||||||
|
QString currentTarget;
|
||||||
|
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
// 1. Look for a block that refers to the current target
|
||||||
|
// 2. Look for a build rule which invokes CXX_COMPILER
|
||||||
|
// 3. Return the FLAGS definition
|
||||||
|
QString line = stream.readLine().trimmed();
|
||||||
|
if (line.startsWith('#')) {
|
||||||
|
if (line.startsWith(targetSignature)) {
|
||||||
|
int pos = line.lastIndexOf(' ');
|
||||||
|
currentTarget = line.mid(pos + 1);
|
||||||
|
}
|
||||||
|
} else if (!currentTarget.isEmpty() && line.startsWith("build")) {
|
||||||
|
cxxFound = line.indexOf("CXX_COMPILER") != -1;
|
||||||
|
} else if (cxxFound && line.startsWith("FLAGS =")) {
|
||||||
|
// Skip past =
|
||||||
|
cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !cache.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
void BuildDirManager::checkConfiguration()
|
void BuildDirManager::checkConfiguration()
|
||||||
{
|
{
|
||||||
if (m_tempDir) // always throw away changes in the tmpdir!
|
if (m_tempDir) // always throw away changes in the tmpdir!
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ QT_FORWARD_DECLARE_CLASS(QTemporaryDir);
|
|||||||
QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher);
|
QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher);
|
||||||
|
|
||||||
namespace Core { class IDocument; }
|
namespace Core { class IDocument; }
|
||||||
|
namespace CppTools { class ProjectPartBuilder; }
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
class FileNode;
|
class FileNode;
|
||||||
@@ -80,6 +81,7 @@ public:
|
|||||||
bool persistCMakeState();
|
bool persistCMakeState();
|
||||||
|
|
||||||
void generateProjectTree(CMakeProjectNode *root);
|
void generateProjectTree(CMakeProjectNode *root);
|
||||||
|
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
|
||||||
|
|
||||||
QList<CMakeBuildTarget> buildTargets() const;
|
QList<CMakeBuildTarget> buildTargets() const;
|
||||||
CMakeConfig parsedConfiguration() const;
|
CMakeConfig parsedConfiguration() const;
|
||||||
@@ -117,6 +119,10 @@ private:
|
|||||||
void processCMakeOutput();
|
void processCMakeOutput();
|
||||||
void processCMakeError();
|
void processCMakeError();
|
||||||
|
|
||||||
|
QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
|
||||||
|
bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
|
||||||
|
bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
|
||||||
|
|
||||||
bool m_hasData = false;
|
bool m_hasData = false;
|
||||||
|
|
||||||
CMakeBuildConfiguration *m_buildConfiguration = nullptr;
|
CMakeBuildConfiguration *m_buildConfiguration = nullptr;
|
||||||
|
|||||||
@@ -211,6 +211,11 @@ void CMakeBuildConfiguration::generateProjectTree(CMakeProjectNode *root) const
|
|||||||
return m_buildDirManager->generateProjectTree(root);
|
return m_buildDirManager->generateProjectTree(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSet<Core::Id> CMakeBuildConfiguration::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
|
||||||
|
{
|
||||||
|
return m_buildDirManager->updateCodeModel(ppBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath,
|
FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath,
|
||||||
const Kit *k,
|
const Kit *k,
|
||||||
const QString &bcName,
|
const QString &bcName,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <projectexplorer/buildconfiguration.h>
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
#include <projectexplorer/abi.h>
|
#include <projectexplorer/abi.h>
|
||||||
|
|
||||||
|
namespace CppTools { class ProjectPartBuilder; }
|
||||||
namespace ProjectExplorer { class ToolChain; }
|
namespace ProjectExplorer { class ToolChain; }
|
||||||
|
|
||||||
namespace CMakeProjectManager {
|
namespace CMakeProjectManager {
|
||||||
@@ -82,6 +83,7 @@ public:
|
|||||||
|
|
||||||
QList<CMakeBuildTarget> buildTargets() const;
|
QList<CMakeBuildTarget> buildTargets() const;
|
||||||
void generateProjectTree(CMakeProjectNode *root) const;
|
void generateProjectTree(CMakeProjectNode *root) const;
|
||||||
|
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
|
||||||
|
|
||||||
static Utils::FileName
|
static Utils::FileName
|
||||||
shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k,
|
shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k,
|
||||||
|
|||||||
@@ -94,102 +94,6 @@ CMakeProject::~CMakeProject()
|
|||||||
qDeleteAll(m_extraCompilers);
|
qDeleteAll(m_extraCompilers);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList CMakeProject::getCXXFlagsFor(const CMakeBuildTarget &buildTarget,
|
|
||||||
QHash<QString, QStringList> &cache)
|
|
||||||
{
|
|
||||||
// check cache:
|
|
||||||
auto it = cache.constFind(buildTarget.title);
|
|
||||||
if (it != cache.constEnd())
|
|
||||||
return *it;
|
|
||||||
|
|
||||||
if (extractCXXFlagsFromMake(buildTarget, cache))
|
|
||||||
return cache.value(buildTarget.title);
|
|
||||||
|
|
||||||
if (extractCXXFlagsFromNinja(buildTarget, cache))
|
|
||||||
return cache.value(buildTarget.title);
|
|
||||||
|
|
||||||
cache.insert(buildTarget.title, QStringList());
|
|
||||||
return QStringList();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CMakeProject::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget,
|
|
||||||
QHash<QString, QStringList> &cache)
|
|
||||||
{
|
|
||||||
QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand);
|
|
||||||
int startIndex = makeCommand.indexOf('\"');
|
|
||||||
int endIndex = makeCommand.indexOf('\"', startIndex + 1);
|
|
||||||
if (startIndex != -1 && endIndex != -1) {
|
|
||||||
startIndex += 1;
|
|
||||||
QString makefile = makeCommand.mid(startIndex, endIndex - startIndex);
|
|
||||||
int slashIndex = makefile.lastIndexOf('/');
|
|
||||||
makefile.truncate(slashIndex);
|
|
||||||
makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make");
|
|
||||||
QFile file(makefile);
|
|
||||||
if (file.exists()) {
|
|
||||||
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
|
||||||
QTextStream stream(&file);
|
|
||||||
while (!stream.atEnd()) {
|
|
||||||
QString line = stream.readLine().trimmed();
|
|
||||||
if (line.startsWith("CXX_FLAGS =")) {
|
|
||||||
// Skip past =
|
|
||||||
cache.insert(buildTarget.title,
|
|
||||||
line.mid(11).trimmed().split(' ', QString::SkipEmptyParts));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CMakeProject::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget,
|
|
||||||
QHash<QString, QStringList> &cache)
|
|
||||||
{
|
|
||||||
Q_UNUSED(buildTarget)
|
|
||||||
if (!cache.isEmpty()) // We fill the cache in one go!
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were
|
|
||||||
// found
|
|
||||||
// Get "all" target's working directory
|
|
||||||
QByteArray ninjaFile;
|
|
||||||
QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory);
|
|
||||||
buildNinjaFile += "/build.ninja";
|
|
||||||
QFile buildNinja(buildNinjaFile);
|
|
||||||
if (buildNinja.exists()) {
|
|
||||||
buildNinja.open(QIODevice::ReadOnly | QIODevice::Text);
|
|
||||||
ninjaFile = buildNinja.readAll();
|
|
||||||
buildNinja.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ninjaFile.isEmpty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
QTextStream stream(ninjaFile);
|
|
||||||
bool cxxFound = false;
|
|
||||||
const QString targetSignature = "# Object build statements for ";
|
|
||||||
QString currentTarget;
|
|
||||||
|
|
||||||
while (!stream.atEnd()) {
|
|
||||||
// 1. Look for a block that refers to the current target
|
|
||||||
// 2. Look for a build rule which invokes CXX_COMPILER
|
|
||||||
// 3. Return the FLAGS definition
|
|
||||||
QString line = stream.readLine().trimmed();
|
|
||||||
if (line.startsWith('#')) {
|
|
||||||
if (line.startsWith(targetSignature)) {
|
|
||||||
int pos = line.lastIndexOf(' ');
|
|
||||||
currentTarget = line.mid(pos + 1);
|
|
||||||
}
|
|
||||||
} else if (!currentTarget.isEmpty() && line.startsWith("build")) {
|
|
||||||
cxxFound = line.indexOf("CXX_COMPILER") != -1;
|
|
||||||
} else if (cxxFound && line.startsWith("FLAGS =")) {
|
|
||||||
// Skip past =
|
|
||||||
cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !cache.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMakeProject::updateProjectData()
|
void CMakeProject::updateProjectData()
|
||||||
{
|
{
|
||||||
auto cmakeBc = qobject_cast<CMakeBuildConfiguration *>(sender());
|
auto cmakeBc = qobject_cast<CMakeBuildConfiguration *>(sender());
|
||||||
@@ -225,39 +129,11 @@ void CMakeProject::updateProjectData()
|
|||||||
activeQtVersion = CppTools::ProjectPart::Qt5;
|
activeQtVersion = CppTools::ProjectPart::Qt5;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileName sysroot = SysRootKitInformation::sysRoot(k);
|
|
||||||
|
|
||||||
ppBuilder.setQtVersion(activeQtVersion);
|
ppBuilder.setQtVersion(activeQtVersion);
|
||||||
|
|
||||||
QHash<QString, QStringList> targetDataCache;
|
const QSet<Core::Id> languages = cmakeBc->updateCodeModel(ppBuilder);
|
||||||
foreach (const CMakeBuildTarget &cbt, buildTargets()) {
|
for (const auto &lid : languages)
|
||||||
if (cbt.targetType == UtilityType)
|
setProjectLanguage(lid, true);
|
||||||
continue;
|
|
||||||
|
|
||||||
// CMake shuffles the include paths that it reports via the CodeBlocks generator
|
|
||||||
// So remove the toolchain include paths, so that at least those end up in the correct
|
|
||||||
// place.
|
|
||||||
QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache);
|
|
||||||
QSet<QString> tcIncludes;
|
|
||||||
foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot)) {
|
|
||||||
tcIncludes.insert(hp.path());
|
|
||||||
}
|
|
||||||
QStringList includePaths;
|
|
||||||
foreach (const QString &i, cbt.includeFiles) {
|
|
||||||
if (!tcIncludes.contains(i))
|
|
||||||
includePaths.append(i);
|
|
||||||
}
|
|
||||||
includePaths += projectDirectory().toString();
|
|
||||||
ppBuilder.setIncludePaths(includePaths);
|
|
||||||
ppBuilder.setCFlags(cxxflags);
|
|
||||||
ppBuilder.setCxxFlags(cxxflags);
|
|
||||||
ppBuilder.setDefines(cbt.defines);
|
|
||||||
ppBuilder.setDisplayName(cbt.title);
|
|
||||||
|
|
||||||
const QList<Core::Id> languages = ppBuilder.createProjectPartsForFiles(cbt.files);
|
|
||||||
foreach (Core::Id language, languages)
|
|
||||||
setProjectLanguage(language, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_codeModelFuture.cancel();
|
m_codeModelFuture.cancel();
|
||||||
pinfo.finish();
|
pinfo.finish();
|
||||||
|
|||||||
@@ -120,9 +120,6 @@ private:
|
|||||||
QStringList filesGeneratedFrom(const QString &sourceFile) const final;
|
QStringList filesGeneratedFrom(const QString &sourceFile) const final;
|
||||||
void updateTargetRunConfigurations(ProjectExplorer::Target *t);
|
void updateTargetRunConfigurations(ProjectExplorer::Target *t);
|
||||||
void updateApplicationAndDeploymentTargets();
|
void updateApplicationAndDeploymentTargets();
|
||||||
QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
|
|
||||||
bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
|
|
||||||
bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
|
|
||||||
|
|
||||||
ProjectExplorer::Target *m_connectedTarget = nullptr;
|
ProjectExplorer::Target *m_connectedTarget = nullptr;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user