CMakePM: Implement "FOLDER" property for targets

See the CMake documentation https://cmake.org/cmake/help/latest/
prop_tgt/FOLDER.html

This allows placement of targets in the project view in specific folders
like e.g. "tests"

Qt Creator (ab)uses the FOLDER property for the "qtc_runnable" feature,
see https://doc.qt.io/qtcreator/creator-run-settings.html
which would not allow to have both.

The "qtc_runnable" should be fixed by having a property "QTC_RUNNABLE"
for a target and have Qt Creator parse the code.

Fixes: QTCREATORBUG-28873
Change-Id: I73433de78b9a86f631ee9d7903db535b69b734f6
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Cristian Adam
2024-07-17 20:02:26 +02:00
parent b4b2dd12fe
commit a8cbd17733
4 changed files with 44 additions and 30 deletions

View File

@@ -33,6 +33,8 @@ namespace CMakeProjectManager::Internal {
static Q_LOGGING_CATEGORY(cmakeLogger, "qtc.cmake.fileApiExtractor", QtWarningMsg); static Q_LOGGING_CATEGORY(cmakeLogger, "qtc.cmake.fileApiExtractor", QtWarningMsg);
static const char QTC_RUNNABLE[] = "qtc_runnable";
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// Helpers: // Helpers:
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@@ -275,7 +277,8 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t,
|| f.fragment.contains("Qt6Gui")); || f.fragment.contains("Qt6Gui"));
}); });
ct.qtcRunnable = t.folderTargetProperty == "qtc_runnable"; // FIXME: remove the usage of "qtc_runnable" by parsing the CMake code instead
ct.qtcRunnable = t.folderTargetProperty == QTC_RUNNABLE;
// Extract library directories for executables: // Extract library directories for executables:
for (const FragmentInfo &f : t.link.value().fragments) { for (const FragmentInfo &f : t.link.value().fragments) {
@@ -761,7 +764,8 @@ static void addGeneratedFilesNode(ProjectNode *targetRoot, const FilePath &topLe
addCMakeVFolder(targetRoot, buildDir, 10, Tr::tr("<Generated Files>"), std::move(nodes)); addCMakeVFolder(targetRoot, buildDir, 10, Tr::tr("<Generated Files>"), std::move(nodes));
} }
static void addTargets(const QFuture<void> &cancelFuture, static void addTargets(FolderNode *root,
const QFuture<void> &cancelFuture,
const QHash<FilePath, ProjectNode *> &cmakeListsNodes, const QHash<FilePath, ProjectNode *> &cmakeListsNodes,
const Configuration &config, const Configuration &config,
const std::vector<TargetDetails> &targetDetails, const std::vector<TargetDetails> &targetDetails,
@@ -772,23 +776,55 @@ static void addTargets(const QFuture<void> &cancelFuture,
for (const TargetDetails &t : targetDetails) for (const TargetDetails &t : targetDetails)
targetDetailsHash.insert(t.id, &t); targetDetailsHash.insert(t.id, &t);
const TargetDetails defaultTargetDetails; const TargetDetails defaultTargetDetails;
auto getTargetDetails = [&targetDetailsHash, &defaultTargetDetails](const QString &id) auto getTargetDetails = [&targetDetailsHash,
-> const TargetDetails & { &defaultTargetDetails](const QString &id) -> const TargetDetails & {
auto it = targetDetailsHash.constFind(id); auto it = targetDetailsHash.constFind(id);
if (it != targetDetailsHash.constEnd()) if (it != targetDetailsHash.constEnd())
return *it.value(); return *it.value();
return defaultTargetDetails; return defaultTargetDetails;
}; };
auto createTargetNode = [](auto &cmakeListsNodes,
const Utils::FilePath &dir,
const QString &displayName) -> CMakeTargetNode * {
auto *cmln = cmakeListsNodes.value(dir);
QTC_ASSERT(cmln, return nullptr);
QString targetId = displayName;
CMakeTargetNode *tn = static_cast<CMakeTargetNode *>(
cmln->findNode([&targetId](const Node *n) { return n->buildKey() == targetId; }));
if (!tn) {
auto newNode = std::make_unique<CMakeTargetNode>(dir, displayName);
tn = newNode.get();
cmln->addNode(std::move(newNode));
}
tn->setDisplayName(displayName);
return tn;
};
QHash<FilePath, FolderNode *> folderNodes;
for (const FileApiDetails::Target &t : config.targets) { for (const FileApiDetails::Target &t : config.targets) {
if (cancelFuture.isCanceled()) if (cancelFuture.isCanceled())
return; return;
const TargetDetails &td = getTargetDetails(t.id); const TargetDetails &td = getTargetDetails(t.id);
CMakeTargetNode *tNode{nullptr};
const FilePath dir = directorySourceDir(config, sourceDir, t.directory); const FilePath dir = directorySourceDir(config, sourceDir, t.directory);
CMakeTargetNode *tNode = createTargetNode(cmakeListsNodes, dir, t.name); // FIXME: remove the usage of "qtc_runnable" by parsing the CMake code instead
if (!td.folderTargetProperty.isEmpty() && td.folderTargetProperty != QTC_RUNNABLE) {
const FilePath folderDir = FilePath::fromString(td.folderTargetProperty);
if (!folderNodes.contains(folderDir))
folderNodes.insert(
folderDir, createSourceGroupNode(td.folderTargetProperty, folderDir, root));
tNode = createTargetNode(folderNodes, folderDir, t.name);
} else {
tNode = createTargetNode(cmakeListsNodes, dir, t.name);
}
QTC_ASSERT(tNode, continue); QTC_ASSERT(tNode, continue);
tNode->setTargetInformation(td.artifacts, td.type); tNode->setTargetInformation(td.artifacts, td.type);
@@ -821,7 +857,8 @@ static std::unique_ptr<CMakeProjectNode> generateRootProjectNode(const QFuture<v
if (cancelFuture.isCanceled()) if (cancelFuture.isCanceled())
return {}; return {};
addTargets(cancelFuture, addTargets(result.get(),
cancelFuture,
cmakeListsNodes, cmakeListsNodes,
data.codemodel, data.codemodel,
data.targetDetails, data.targetDetails,

View File

@@ -173,26 +173,6 @@ void createProjectNode(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNo
pn->setDisplayName(displayName); pn->setDisplayName(displayName);
} }
CMakeTargetNode *createTargetNode(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes,
const Utils::FilePath &dir,
const QString &displayName)
{
ProjectNode *cmln = cmakeListsNodes.value(dir);
QTC_ASSERT(cmln, return nullptr);
QString targetId = displayName;
CMakeTargetNode *tn = static_cast<CMakeTargetNode *>(
cmln->findNode([&targetId](const Node *n) { return n->buildKey() == targetId; }));
if (!tn) {
auto newNode = std::make_unique<CMakeTargetNode>(dir, displayName);
tn = newNode.get();
cmln->addNode(std::move(newNode));
}
tn->setDisplayName(displayName);
return tn;
}
template<typename Result> template<typename Result>
static std::unique_ptr<Result> cloneFolderNode(FolderNode *node) static std::unique_ptr<Result> cloneFolderNode(FolderNode *node)
{ {

View File

@@ -43,10 +43,6 @@ QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> addCMakeLists(
void createProjectNode(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes, void createProjectNode(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
const Utils::FilePath &dir, const Utils::FilePath &dir,
const QString &displayName); const QString &displayName);
CMakeTargetNode *createTargetNode(
const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes,
const Utils::FilePath &dir,
const QString &displayName);
void addFileSystemNodes(ProjectExplorer::ProjectNode *root, void addFileSystemNodes(ProjectExplorer::ProjectNode *root,
const std::shared_ptr<ProjectExplorer::FolderNode> &folderNode); const std::shared_ptr<ProjectExplorer::FolderNode> &folderNode);

View File

@@ -35,3 +35,4 @@ file(GLOB SOURCE_FILES CONFIGURE_DEPENDS *.cpp *.h *.ui)
add_executable(hello-widgets-glob ${SOURCE_FILES}) add_executable(hello-widgets-glob ${SOURCE_FILES})
target_link_libraries(hello-widgets-glob PRIVATE Qt6::Widgets) target_link_libraries(hello-widgets-glob PRIVATE Qt6::Widgets)
set_target_properties(hello-widgets-glob PROPERTIES FOLDER "Glob")