Commit 9e67dfbe authored by Tobias Hunger's avatar Tobias Hunger

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's avatarTim Jenssen <tim.jenssen@qt.io>
parent 2817a1b6
......@@ -36,12 +36,16 @@
#include <coreplugin/messagemanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cpptools/projectpartbuilder.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.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!
}
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()
{
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()
{
if (m_tempDir) // always throw away changes in the tmpdir!
......
......@@ -45,6 +45,7 @@ QT_FORWARD_DECLARE_CLASS(QTemporaryDir);
QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher);
namespace Core { class IDocument; }
namespace CppTools { class ProjectPartBuilder; }
namespace ProjectExplorer {
class FileNode;
......@@ -80,6 +81,7 @@ public:
bool persistCMakeState();
void generateProjectTree(CMakeProjectNode *root);
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
QList<CMakeBuildTarget> buildTargets() const;
CMakeConfig parsedConfiguration() const;
......@@ -117,6 +119,10 @@ private:
void processCMakeOutput();
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;
CMakeBuildConfiguration *m_buildConfiguration = nullptr;
......
......@@ -211,6 +211,11 @@ void CMakeBuildConfiguration::generateProjectTree(CMakeProjectNode *root) const
return m_buildDirManager->generateProjectTree(root);
}
QSet<Core::Id> CMakeBuildConfiguration::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
{
return m_buildDirManager->updateCodeModel(ppBuilder);
}
FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath,
const Kit *k,
const QString &bcName,
......
......@@ -32,6 +32,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/abi.h>
namespace CppTools { class ProjectPartBuilder; }
namespace ProjectExplorer { class ToolChain; }
namespace CMakeProjectManager {
......@@ -82,6 +83,7 @@ public:
QList<CMakeBuildTarget> buildTargets() const;
void generateProjectTree(CMakeProjectNode *root) const;
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
static Utils::FileName
shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k,
......
......@@ -94,102 +94,6 @@ CMakeProject::~CMakeProject()
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()
{
auto cmakeBc = qobject_cast<CMakeBuildConfiguration *>(sender());
......@@ -225,39 +129,11 @@ void CMakeProject::updateProjectData()
activeQtVersion = CppTools::ProjectPart::Qt5;
}
const FileName sysroot = SysRootKitInformation::sysRoot(k);
ppBuilder.setQtVersion(activeQtVersion);
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 += 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);
}
const QSet<Core::Id> languages = cmakeBc->updateCodeModel(ppBuilder);
for (const auto &lid : languages)
setProjectLanguage(lid, true);
m_codeModelFuture.cancel();
pinfo.finish();
......
......@@ -120,9 +120,6 @@ private:
QStringList filesGeneratedFrom(const QString &sourceFile) const final;
void updateTargetRunConfigurations(ProjectExplorer::Target *t);
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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment