forked from qt-creator/qt-creator
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:
@@ -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;
|
||||||
|
|
||||||
|
@@ -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,
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user