CMake: Fix projects with complex CMakeLists.txt layouts

This fixes soft assserts in e.g. Musescore.

Change-Id: I860c8c184161e53918fb901d66b89cffb0c09e23
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Tobias Hunger
2017-03-13 18:13:47 +01:00
parent b3238d5736
commit b8417e0f0c
4 changed files with 29 additions and 79 deletions

View File

@@ -218,7 +218,7 @@ QList<CMakeBuildTarget> CMakeBuildConfiguration::buildTargets() const
CMakeListsNode * CMakeListsNode *
CMakeBuildConfiguration::generateProjectTree(const QList<const FileNode*> &allFiles) const CMakeBuildConfiguration::generateProjectTree(const QList<const FileNode*> &allFiles) const
{ {
auto root = new CMakeListsNode(target()->project()->projectFilePath()); auto root = new CMakeListsNode(target()->project()->projectDirectory());
if (!m_buildDirManager || m_buildDirManager->isParsing()) if (!m_buildDirManager || m_buildDirManager->isParsing())
return nullptr; return nullptr;

View File

@@ -531,87 +531,31 @@ void ServerModeReader::addCMakeLists(CMakeListsNode *root, const QList<FileNode
{ {
const QDir baseDir = QDir(m_parameters.sourceDirectory.toString()); const QDir baseDir = QDir(m_parameters.sourceDirectory.toString());
QHash<QString, FileNode *> nodeHash; root->addNestedNodes(cmakeLists, Utils::FileName(),
for (FileNode *cm : cmakeLists) { [&cmakeLists](const Utils::FileName &fp) -> ProjectExplorer::FolderNode * {
const QString relPath = baseDir.relativeFilePath(cm->filePath().parentDir().toString()); if (Utils::contains(cmakeLists, [&fp](const FileNode *fn) { return fn->filePath().parentDir() == fp; }))
QTC_CHECK(!nodeHash.contains(relPath)); return new CMakeListsNode(fp);
nodeHash[(relPath == ".") ? QString() : relPath ] = cm;
}
QStringList tmp = nodeHash.keys();
Utils::sort(tmp, [](const QString &a, const QString &b) { return a.count() < b.count(); });
const QStringList keys = tmp;
QHash<QString, CMakeListsNode *> knownNodes;
knownNodes[QString()] = root;
for (const QString &k : keys) {
FileNode *fn = nodeHash[k];
CMakeListsNode *parentNode = nullptr;
QString prefix = k;
forever {
if (knownNodes.contains(prefix)) {
parentNode = knownNodes.value(prefix);
break;
}
const int pos = prefix.lastIndexOf('/');
prefix = (pos < 0) ? QString() : prefix.left(prefix.lastIndexOf('/'));
}
// Find or create CMakeListsNode:
CMakeListsNode *cmln = nullptr;
if (parentNode->filePath() == fn->filePath())
cmln = parentNode; // Top level!
else else
cmln = static_cast<CMakeListsNode *>(parentNode->projectNode(fn->filePath())); return new FolderNode(fp);
if (!cmln) { });
cmln = new CMakeListsNode(fn->filePath());
parentNode->addNode(cmln);
}
// Find or create CMakeLists.txt filenode below CMakeListsNode:
FileNode *cmFn = cmln->fileNode(fn->filePath());
if (!cmFn) {
cmFn = fn;
cmln->addNode(cmFn);
}
// Update displayName of CMakeListsNode:
const QString dn = prefix.isEmpty() ? k : k.mid(prefix.count() + 1);
if (!dn.isEmpty())
cmln->setDisplayName(dn); // Set partial path as display name
knownNodes.insert(k, cmln);
}
} }
static CMakeListsNode *findCMakeNode(CMakeListsNode *root, const Utils::FileName &dir) static CMakeListsNode *findCMakeNode(CMakeListsNode *root, const Utils::FileName &dir)
{ {
const Utils::FileName stepDir = dir; const Utils::FileName stepDir = dir;
const Utils::FileName topDir = root->filePath().parentDir(); const Utils::FileName topDir = root->filePath();
QStringList relative = stepDir.relativeChildPath(topDir).toString().split('/', QString::SkipEmptyParts); QStringList relative = stepDir.relativeChildPath(topDir).toString().split('/', QString::SkipEmptyParts);
CMakeListsNode *result = root; FolderNode *result = root;
QString relativePathElement;
while (!relative.isEmpty()) { while (!relative.isEmpty()) {
const QString nextDirPath = result->filePath().parentDir().toString(); Utils::FileName nextFullPath = result->filePath();
Utils::FileName nextFullPath; nextFullPath.appendPath(relative.takeFirst());
// Some directory may not contain CMakeLists.txt file, skip it: result = findOrDefault(result->folderNodes(), Utils::equal(&FolderNode::filePath, nextFullPath));
do { if (!result)
relativePathElement += '/' + relative.takeFirst(); return nullptr;
nextFullPath = Utils::FileName::fromString(nextDirPath + relativePathElement + "/CMakeLists.txt");
} while (!nextFullPath.exists() && !relative.isEmpty());
result = static_cast<CMakeListsNode *>(result->projectNode(nextFullPath));
// Intermediate directory can contain CMakeLists.txt file
// that is not a part of the root node, skip it:
if (!result && !relative.isEmpty()) {
result = root;
} else {
relativePathElement.clear();
} }
QTC_ASSERT(result, return nullptr); return dynamic_cast<CMakeListsNode *>(result);
}
return result;
} }
static CMakeProjectNode *findOrCreateProjectNode(CMakeListsNode *root, const Utils::FileName &dir, static CMakeProjectNode *findOrCreateProjectNode(CMakeListsNode *root, const Utils::FileName &dir,

View File

@@ -60,7 +60,8 @@ static FolderNode *folderNode(const FolderNode *folder, const Utils::FileName &d
static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder, static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder,
const Utils::FileName &directory, const Utils::FileName &directory,
const Utils::FileName &overrideBaseDir) const Utils::FileName &overrideBaseDir,
const FolderNode::FolderNodeFactory &factory)
{ {
Utils::FileName path = overrideBaseDir.isEmpty() ? folder->filePath() : overrideBaseDir; Utils::FileName path = overrideBaseDir.isEmpty() ? folder->filePath() : overrideBaseDir;
@@ -90,7 +91,7 @@ static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder,
FolderNode *next = folderNode(parent, path); FolderNode *next = folderNode(parent, path);
if (!next) { if (!next) {
// No FolderNode yet, so create it // No FolderNode yet, so create it
auto tmp = new ProjectExplorer::FolderNode(path); auto tmp = factory(path);
tmp->setDisplayName(part); tmp->setDisplayName(part);
parent->addNode(tmp); parent->addNode(tmp);
next = tmp; next = tmp;
@@ -460,20 +461,22 @@ QList<FolderNode*> FolderNode::folderNodes() const
return result; return result;
} }
void FolderNode::addNestedNode(FileNode *fileNode, const Utils::FileName &overrideBaseDir) void FolderNode::addNestedNode(FileNode *fileNode, const Utils::FileName &overrideBaseDir,
const FolderNodeFactory &factory)
{ {
// Get relative path to rootNode // Get relative path to rootNode
QString parentDir = fileNode->filePath().toFileInfo().absolutePath(); QString parentDir = fileNode->filePath().toFileInfo().absolutePath();
FolderNode *folder = recursiveFindOrCreateFolderNode(this, Utils::FileName::fromString(parentDir), FolderNode *folder = recursiveFindOrCreateFolderNode(this, Utils::FileName::fromString(parentDir),
overrideBaseDir); overrideBaseDir, factory);
folder->addNode(fileNode); folder->addNode(fileNode);
} }
void FolderNode::addNestedNodes(QList<FileNode *> &files, const Utils::FileName &overrideBaseDir) void FolderNode::addNestedNodes(const QList<FileNode *> &files, const Utils::FileName &overrideBaseDir,
const FolderNodeFactory &factory)
{ {
for (FileNode *fn : files) for (FileNode *fn : files)
addNestedNode(fn, overrideBaseDir); addNestedNode(fn, overrideBaseDir, factory);
} }
// "Compress" a tree of foldernodes such that foldernodes with exactly one foldernode as a child // "Compress" a tree of foldernodes such that foldernodes with exactly one foldernode as a child

View File

@@ -200,8 +200,11 @@ public:
QList<FileNode *> fileNodes() const; QList<FileNode *> fileNodes() const;
FileNode *fileNode(const Utils::FileName &file) const; FileNode *fileNode(const Utils::FileName &file) const;
QList<FolderNode *> folderNodes() const; QList<FolderNode *> folderNodes() const;
void addNestedNodes(QList<FileNode *> &files, const Utils::FileName &overrideBaseDir = Utils::FileName()); using FolderNodeFactory = std::function<FolderNode *(const Utils::FileName &)>;
void addNestedNode(FileNode *fileNode, const Utils::FileName &overrideBaseDir = Utils::FileName()); void addNestedNodes(const QList<FileNode *> &files, const Utils::FileName &overrideBaseDir = Utils::FileName(),
const FolderNodeFactory &factory = [](const Utils::FileName &fn) { return new FolderNode(fn); });
void addNestedNode(FileNode *fileNode, const Utils::FileName &overrideBaseDir = Utils::FileName(),
const FolderNodeFactory &factory = [](const Utils::FileName &fn) { return new FolderNode(fn); });
void compress(); void compress();
bool isAncesterOf(Node *n); bool isAncesterOf(Node *n);