diff options
author | Christian Kandeler <[email protected]> | 2025-01-24 14:48:59 +0100 |
---|---|---|
committer | Christian Kandeler <[email protected]> | 2025-01-24 16:07:48 +0000 |
commit | 6c56d0deb92225636fa4a34352bf771577dfc536 (patch) | |
tree | 229cf56ac410402d6279fff5c0afed08a193320e /src/plugins/nim | |
parent | 72acdb5a98a8f04d032e128955702a7663b4fa50 (diff) |
Nim: Merge Project/BuildSystem/BuildConfiguration classes into one TU
Change-Id: Iffbbc67893a20e603a81b7b3f6250d8302101d6b
Reviewed-by: hjk <[email protected]>
Diffstat (limited to 'src/plugins/nim')
21 files changed, 760 insertions, 909 deletions
diff --git a/src/plugins/nim/CMakeLists.txt b/src/plugins/nim/CMakeLists.txt index 4bb9fa28bb2..d1a4cff1591 100644 --- a/src/plugins/nim/CMakeLists.txt +++ b/src/plugins/nim/CMakeLists.txt @@ -14,10 +14,6 @@ add_qtc_plugin(Nim project/nimbleproject.h project/nimbleproject.cpp project/nimblerunconfiguration.h project/nimblerunconfiguration.cpp project/nimbletaskstep.h project/nimbletaskstep.cpp - project/nimblebuildsystem.h project/nimblebuildsystem.cpp - project/nimblebuildconfiguration.h project/nimblebuildconfiguration.cpp - project/nimbuildsystem.cpp project/nimbuildsystem.h - project/nimbuildconfiguration.cpp project/nimbuildconfiguration.h project/nimcompilerbuildstep.cpp project/nimcompilerbuildstep.h project/nimcompilercleanstep.cpp project/nimcompilercleanstep.h project/nimoutputtaskparser.cpp project/nimoutputtaskparser.h diff --git a/src/plugins/nim/nim.qbs b/src/plugins/nim/nim.qbs index 2b8fbcecff5..8639b3be725 100644 --- a/src/plugins/nim/nim.qbs +++ b/src/plugins/nim/nim.qbs @@ -38,8 +38,6 @@ QtcPlugin { name: "Project" prefix: "project/" files: [ - "nimbuildsystem.cpp", "nimbuildsystem.h", - "nimbuildconfiguration.h", "nimbuildconfiguration.cpp", "nimcompilerbuildstep.h", "nimcompilerbuildstep.cpp", "nimcompilercleanstep.h", "nimcompilercleanstep.cpp", "nimoutputtaskparser.h", "nimoutputtaskparser.cpp", @@ -50,8 +48,6 @@ QtcPlugin { "nimbleproject.h", "nimbleproject.cpp", "nimblerunconfiguration.h", "nimblerunconfiguration.cpp", "nimbletaskstep.h", "nimbletaskstep.cpp", - "nimblebuildsystem.h", "nimblebuildsystem.cpp", - "nimblebuildconfiguration.h", "nimblebuildconfiguration.cpp", ] } diff --git a/src/plugins/nim/nimplugin.cpp b/src/plugins/nim/nimplugin.cpp index 6315b5e92cc..ccf78860bf3 100644 --- a/src/plugins/nim/nimplugin.cpp +++ b/src/plugins/nim/nimplugin.cpp @@ -4,12 +4,10 @@ #include "nimconstants.h" #include "nimtr.h" #include "editor/nimeditorfactory.h" -#include "project/nimblebuildconfiguration.h" #include "project/nimblebuildstep.h" #include "project/nimbleproject.h" #include "project/nimblerunconfiguration.h" #include "project/nimbletaskstep.h" -#include "project/nimbuildconfiguration.h" #include "project/nimcompilerbuildstep.h" #include "project/nimcompilercleanstep.h" #include "project/nimoutputtaskparser.h" @@ -44,8 +42,6 @@ class NimPluginPrivate { public: NimEditorFactory editorFactory; - NimBuildConfigurationFactory buildConfigFactory; - NimbleBuildConfigurationFactory nimbleBuildConfigFactory; NimRunConfigurationFactory nimRunConfigFactory; NimbleRunConfigurationFactory nimbleRunConfigFactory; NimbleTestConfigurationFactory nimbleTestConfigFactory; diff --git a/src/plugins/nim/project/nimblebuildconfiguration.cpp b/src/plugins/nim/project/nimblebuildconfiguration.cpp deleted file mode 100644 index a0efe78fa7b..00000000000 --- a/src/plugins/nim/project/nimblebuildconfiguration.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "nimblebuildconfiguration.h" - -#include "nimconstants.h" -#include "nimtr.h" - -#include <projectexplorer/buildinfo.h> -#include <projectexplorer/buildstep.h> -#include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kit.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Nim { - -NimbleBuildConfiguration::NimbleBuildConfiguration(Target *target, Id id) - : BuildConfiguration(target, id) -{ - setConfigWidgetDisplayName(Tr::tr("General")); - setConfigWidgetHasFrame(true); - setBuildDirectorySettingsKey("Nim.NimbleBuildConfiguration.BuildDirectory"); - appendInitialBuildStep(Constants::C_NIMBLEBUILDSTEP_ID); - - setInitializer([this](const BuildInfo &info) { - setBuildType(info.buildType); - setBuildDirectory(project()->projectDirectory()); - }); -} - -BuildConfiguration::BuildType NimbleBuildConfiguration::buildType() const -{ - return m_buildType; -} - -void NimbleBuildConfiguration::fromMap(const Store &map) -{ - m_buildType = static_cast<BuildType>(map[Constants::C_NIMBLEBUILDCONFIGURATION_BUILDTYPE].toInt()); - BuildConfiguration::fromMap(map); -} - -void NimbleBuildConfiguration::toMap(Store &map) const -{ - BuildConfiguration::toMap(map); - map[Constants::C_NIMBLEBUILDCONFIGURATION_BUILDTYPE] = buildType(); -} - -void NimbleBuildConfiguration::setBuildType(BuildConfiguration::BuildType buildType) -{ - if (buildType == m_buildType) - return; - m_buildType = buildType; - emit buildTypeChanged(); -} - -NimbleBuildConfigurationFactory::NimbleBuildConfigurationFactory() -{ - registerBuildConfiguration<NimbleBuildConfiguration>(Constants::C_NIMBLEBUILDCONFIGURATION_ID); - setSupportedProjectType(Constants::C_NIMBLEPROJECT_ID); - setSupportedProjectMimeTypeName(Constants::C_NIMBLE_MIMETYPE); - - setBuildGenerator([](const Kit *, const FilePath &projectPath, bool forSetup) { - const auto oneBuild = [&](BuildConfiguration::BuildType buildType, const QString &typeName) { - BuildInfo info; - info.buildType = buildType; - info.typeName = typeName; - if (forSetup) { - info.displayName = info.typeName; - info.buildDirectory = projectPath.parentDir(); - } - return info; - }; - return QList<BuildInfo>{ - oneBuild(BuildConfiguration::Debug, Tr::tr("Debug")), - oneBuild(BuildConfiguration::Release, Tr::tr("Release")) - }; - }); -} - -} // Nim diff --git a/src/plugins/nim/project/nimblebuildconfiguration.h b/src/plugins/nim/project/nimblebuildconfiguration.h deleted file mode 100644 index 4b1e9593097..00000000000 --- a/src/plugins/nim/project/nimblebuildconfiguration.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/target.h> - -namespace Nim { - -class NimbleBuildConfiguration : public ProjectExplorer::BuildConfiguration -{ - Q_OBJECT - - friend class ProjectExplorer::BuildConfigurationFactory; - - NimbleBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); - - BuildType buildType() const override; - - void fromMap(const Utils::Store &map) override; - void toMap(Utils::Store &map) const override; - -private: - void setBuildType(BuildType buildType); - - BuildType m_buildType = ProjectExplorer::BuildConfiguration::Unknown; -}; - -class NimbleBuildConfigurationFactory final : public ProjectExplorer::BuildConfigurationFactory -{ -public: - NimbleBuildConfigurationFactory(); -}; - -} // Nim diff --git a/src/plugins/nim/project/nimblebuildstep.cpp b/src/plugins/nim/project/nimblebuildstep.cpp index efda9aa5baf..f867a337985 100644 --- a/src/plugins/nim/project/nimblebuildstep.cpp +++ b/src/plugins/nim/project/nimblebuildstep.cpp @@ -4,12 +4,13 @@ #include "nimblebuildstep.h" #include "nimconstants.h" -#include "nimbuildsystem.h" #include "nimoutputtaskparser.h" +#include "nimproject.h" #include "nimtr.h" #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/processparameters.h> +#include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfigurationaspects.h> diff --git a/src/plugins/nim/project/nimblebuildsystem.cpp b/src/plugins/nim/project/nimblebuildsystem.cpp deleted file mode 100644 index f7b6fb4a7d0..00000000000 --- a/src/plugins/nim/project/nimblebuildsystem.cpp +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "nimblebuildsystem.h" - -#include "nimbuildsystem.h" -#include "../nimconstants.h" - -#include <projectexplorer/target.h> -#include <projectexplorer/taskhub.h> - -#include <utils/algorithm.h> -#include <utils/qtcprocess.h> -#include <utils/qtcassert.h> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Nim { - -const char C_NIMBLEPROJECT_TASKS[] = "Nim.NimbleProject.Tasks"; - -static QList<QByteArray> linesFromProcessOutput(Process *process) -{ - QList<QByteArray> lines = process->readAllRawStandardOutput().split('\n'); - lines = Utils::transform(lines, [](const QByteArray &line){ return line.trimmed(); }); - Utils::erase(lines, [](const QByteArray &line) { return line.isEmpty(); }); - return lines; -} - -static std::vector<NimbleTask> parseTasks(const FilePath &nimblePath, const FilePath &workingDirectory) -{ - Process process; - process.setCommand({nimblePath, {"tasks"}}); - process.setWorkingDirectory(workingDirectory); - process.start(); - process.waitForFinished(); - - std::vector<NimbleTask> result; - - if (process.exitCode() != 0) { - TaskHub::addTask(ProjectExplorer::BuildSystemTask(Task::Error, process.cleanedStdOut())); - return result; - } - - const QList<QByteArray> &lines = linesFromProcessOutput(&process); - - for (const QByteArray &line : lines) { - QList<QByteArray> tokens = line.trimmed().split(' '); - QTC_ASSERT(!tokens.empty(), continue); - QString taskName = QString::fromUtf8(tokens.takeFirst()); - QString taskDesc = QString::fromUtf8(tokens.join(' ')); - result.push_back({std::move(taskName), std::move(taskDesc)}); - } - - return result; -} - -static NimbleMetadata parseMetadata(const FilePath &nimblePath, const FilePath &workingDirectory) -{ - Process process; - process.setCommand({nimblePath, {"dump"}}); - process.setWorkingDirectory(workingDirectory); - process.start(); - process.waitForFinished(); - - NimbleMetadata result = {}; - - if (process.exitCode() != 0) { - TaskHub::addTask(ProjectExplorer::BuildSystemTask(Task::Error, process.cleanedStdOut())); - return result; - } - const QList<QByteArray> &lines = linesFromProcessOutput(&process); - - for (const QByteArray &line : lines) { - QList<QByteArray> tokens = line.trimmed().split(':'); - QTC_ASSERT(tokens.size() == 2, continue); - QString name = QString::fromUtf8(tokens.takeFirst()).trimmed(); - QString value = QString::fromUtf8(tokens.takeFirst()).trimmed(); - QTC_ASSERT(value.size() >= 2, continue); - QTC_ASSERT(value.front() == QChar('"'), continue); - QTC_ASSERT(value.back() == QChar('"'), continue); - value.remove(0, 1); - value.remove(value.size() - 1, 1); - - if (name == "binDir") - result.binDir = value; - else if (name == "srcDir") - result.srcDir = value; - else if (name == "bin") { - QStringList bin = value.split(','); - bin = Utils::transform(bin, [](const QString &x){ return x.trimmed(); }); - Utils::erase(bin, [](const QString &x) { return x.isEmpty(); }); - result.bin = std::move(bin); - } - } - - return result; -} - -NimbleBuildSystem::NimbleBuildSystem(Target *target) - : BuildSystem(target), m_projectScanner(target->project()) -{ - m_projectScanner.watchProjectFilePath(); - - connect(&m_projectScanner, &NimProjectScanner::fileChanged, this, [this](const QString &path) { - if (path == projectFilePath().toUrlishString()) - requestDelayedParse(); - }); - - connect(&m_projectScanner, &NimProjectScanner::requestReparse, - this, &NimbleBuildSystem::requestDelayedParse); - - connect(&m_projectScanner, &NimProjectScanner::finished, this, &NimbleBuildSystem::updateProject); - - connect(&m_projectScanner, &NimProjectScanner::directoryChanged, this, [this] (const QString &directory){ - // Workaround for nimble creating temporary files in project root directory - // when querying the list of tasks. - // See https://github.com/nim-lang/nimble/issues/720 - if (directory != projectDirectory().toUrlishString()) - requestDelayedParse(); - }); - - connect(target->project(), &ProjectExplorer::Project::settingsLoaded, - this, &NimbleBuildSystem::loadSettings); - connect(target->project(), &ProjectExplorer::Project::aboutToSaveSettings, - this, &NimbleBuildSystem::saveSettings); - requestDelayedParse(); -} - -void NimbleBuildSystem::triggerParsing() -{ - // Only allow one parsing run at the same time: - auto guard = guardParsingRun(); - if (!guard.guardsProject()) - return; - m_guard = std::move(guard); - - m_projectScanner.startScan(); -} - -void NimbleBuildSystem::updateProject() -{ - const FilePath projectDir = projectDirectory(); - const FilePath nimble = Nim::nimblePathFromKit(kit()); - - const NimbleMetadata metadata = parseMetadata(nimble, projectDir); - const FilePath binDir = projectDir.pathAppended(metadata.binDir); - const FilePath srcDir = projectDir.pathAppended("src"); - - QList<BuildTargetInfo> targets = Utils::transform(metadata.bin, [&](const QString &bin){ - BuildTargetInfo info = {}; - info.displayName = bin; - info.targetFilePath = binDir.pathAppended(bin); - info.projectFilePath = projectFilePath(); - info.workingDirectory = binDir; - info.buildKey = bin; - return info; - }); - - setApplicationTargets(std::move(targets)); - - std::vector<NimbleTask> tasks = parseTasks(nimble, projectDir); - if (tasks != m_tasks) { - m_tasks = std::move(tasks); - emit tasksChanged(); - } - - // Complete scan - m_guard.markAsSuccess(); - m_guard = {}; - - emitBuildSystemUpdated(); -} - -std::vector<NimbleTask> NimbleBuildSystem::tasks() const -{ - return m_tasks; -} - -void NimbleBuildSystem::saveSettings() -{ - // only handles nimble specific settings - NimProjectScanner handles general settings - QStringList result; - for (const NimbleTask &task : m_tasks) { - result.push_back(task.name); - result.push_back(task.description); - } - - project()->setNamedSettings(C_NIMBLEPROJECT_TASKS, result); -} - -void NimbleBuildSystem::loadSettings() -{ - // only handles nimble specific settings - NimProjectScanner handles general settings - QStringList list = project()->namedSettings(C_NIMBLEPROJECT_TASKS).toStringList(); - - m_tasks.clear(); - if (list.size() % 2 != 0) - return; - - for (int i = 0; i < list.size(); i += 2) - m_tasks.push_back({list[i], list[i + 1]}); -} - -bool NimbleBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const -{ - if (node->asFileNode()) { - return action == ProjectAction::Rename - || action == ProjectAction::RemoveFile; - } - if (node->isFolderNodeType() || node->isProjectNodeType()) { - return action == ProjectAction::AddNewFile - || action == ProjectAction::RemoveFile - || action == ProjectAction::AddExistingFile; - } - return BuildSystem::supportsAction(context, action, node); -} - -bool NimbleBuildSystem::addFiles(Node *, const FilePaths &filePaths, FilePaths *) -{ - return m_projectScanner.addFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); -} - -RemovedFilesFromProject NimbleBuildSystem::removeFiles(Node *, - const FilePaths &filePaths, - FilePaths *) -{ - return m_projectScanner.removeFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); -} - -bool NimbleBuildSystem::deleteFiles(Node *, const FilePaths &) -{ - return true; -} - -bool NimbleBuildSystem::renameFiles(Node *, const FilePairs &filesToRename, FilePaths *notRenamed) -{ - bool success = true; - for (const auto &[oldFilePath, newFilePath] : filesToRename) { - if (!m_projectScanner.renameFile(oldFilePath.toUrlishString(), newFilePath.toUrlishString())) { - success = false; - if (notRenamed) - *notRenamed << oldFilePath; - } - } - return success; -} - -} // Nim diff --git a/src/plugins/nim/project/nimblebuildsystem.h b/src/plugins/nim/project/nimblebuildsystem.h deleted file mode 100644 index 78e130ea746..00000000000 --- a/src/plugins/nim/project/nimblebuildsystem.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "nimbuildsystem.h" - -namespace Nim { - -struct NimbleTask -{ - QString name; - QString description; - - bool operator==(const NimbleTask &o) const { - return name == o.name && description == o.description; - } -}; - -struct NimbleMetadata -{ - QStringList bin; - QString binDir; - QString srcDir; - - bool operator==(const NimbleMetadata &o) const { - return bin == o.bin && binDir == o.binDir && srcDir == o.srcDir; - } - bool operator!=(const NimbleMetadata &o) const { - return !operator==(o); - } -}; - -class NimbleBuildSystem final : public ProjectExplorer::BuildSystem -{ - Q_OBJECT - -public: - NimbleBuildSystem(ProjectExplorer::Target *target); - - std::vector<NimbleTask> tasks() const; - -signals: - void tasksChanged(); - -private: - void loadSettings(); - void saveSettings(); - - void updateProject(); - - bool supportsAction(ProjectExplorer::Node *, - ProjectExplorer::ProjectAction action, - const ProjectExplorer::Node *node) const override; - bool addFiles(ProjectExplorer::Node *node, - const Utils::FilePaths &filePaths, Utils::FilePaths *) override; - ProjectExplorer::RemovedFilesFromProject removeFiles(ProjectExplorer::Node *node, - const Utils::FilePaths &filePaths, - Utils::FilePaths *) override; - bool deleteFiles(ProjectExplorer::Node *, const Utils::FilePaths &) override; - bool renameFiles( - ProjectExplorer::Node *, - const Utils::FilePairs &filesToRename, - Utils::FilePaths *notRenamed) override; - QString name() const final { return QLatin1String("mimble"); } - void triggerParsing() final; - - std::vector<NimbleTask> m_tasks; - - NimProjectScanner m_projectScanner; - ParseGuard m_guard; -}; - -} // Nim diff --git a/src/plugins/nim/project/nimbleproject.cpp b/src/plugins/nim/project/nimbleproject.cpp index 982ca06514f..70bde36c72c 100644 --- a/src/plugins/nim/project/nimbleproject.cpp +++ b/src/plugins/nim/project/nimbleproject.cpp @@ -3,13 +3,19 @@ #include "nimbleproject.h" #include "nimconstants.h" -#include "nimblebuildsystem.h" +#include "../nimtr.h" #include <coreplugin/icontext.h> +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/buildinfo.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/target.h> +#include <projectexplorer/taskhub.h> +#include <utils/algorithm.h> +#include <utils/qtcprocess.h> #include <utils/qtcassert.h> using namespace ProjectExplorer; @@ -17,6 +23,324 @@ using namespace Utils; namespace Nim { +struct NimbleMetadata +{ + QStringList bin; + QString binDir; + QString srcDir; + + bool operator==(const NimbleMetadata &o) const { + return bin == o.bin && binDir == o.binDir && srcDir == o.srcDir; + } + bool operator!=(const NimbleMetadata &o) const { + return !operator==(o); + } +}; + +const char C_NIMBLEPROJECT_TASKS[] = "Nim.NimbleProject.Tasks"; + +static QList<QByteArray> linesFromProcessOutput(Process *process) +{ + QList<QByteArray> lines = process->readAllRawStandardOutput().split('\n'); + lines = Utils::transform(lines, [](const QByteArray &line){ return line.trimmed(); }); + Utils::erase(lines, [](const QByteArray &line) { return line.isEmpty(); }); + return lines; +} + +static std::vector<NimbleTask> parseTasks(const FilePath &nimblePath, const FilePath &workingDirectory) +{ + Process process; + process.setCommand({nimblePath, {"tasks"}}); + process.setWorkingDirectory(workingDirectory); + process.start(); + process.waitForFinished(); + + std::vector<NimbleTask> result; + + if (process.exitCode() != 0) { + TaskHub::addTask(ProjectExplorer::BuildSystemTask(Task::Error, process.cleanedStdOut())); + return result; + } + + const QList<QByteArray> &lines = linesFromProcessOutput(&process); + + for (const QByteArray &line : lines) { + QList<QByteArray> tokens = line.trimmed().split(' '); + QTC_ASSERT(!tokens.empty(), continue); + QString taskName = QString::fromUtf8(tokens.takeFirst()); + QString taskDesc = QString::fromUtf8(tokens.join(' ')); + result.push_back({std::move(taskName), std::move(taskDesc)}); + } + + return result; +} + +static NimbleMetadata parseMetadata(const FilePath &nimblePath, const FilePath &workingDirectory) +{ + Process process; + process.setCommand({nimblePath, {"dump"}}); + process.setWorkingDirectory(workingDirectory); + process.start(); + process.waitForFinished(); + + NimbleMetadata result = {}; + + if (process.exitCode() != 0) { + TaskHub::addTask(ProjectExplorer::BuildSystemTask(Task::Error, process.cleanedStdOut())); + return result; + } + const QList<QByteArray> &lines = linesFromProcessOutput(&process); + + for (const QByteArray &line : lines) { + QList<QByteArray> tokens = line.trimmed().split(':'); + QTC_ASSERT(tokens.size() == 2, continue); + QString name = QString::fromUtf8(tokens.takeFirst()).trimmed(); + QString value = QString::fromUtf8(tokens.takeFirst()).trimmed(); + QTC_ASSERT(value.size() >= 2, continue); + QTC_ASSERT(value.front() == QChar('"'), continue); + QTC_ASSERT(value.back() == QChar('"'), continue); + value.remove(0, 1); + value.remove(value.size() - 1, 1); + + if (name == "binDir") + result.binDir = value; + else if (name == "srcDir") + result.srcDir = value; + else if (name == "bin") { + QStringList bin = value.split(','); + bin = Utils::transform(bin, [](const QString &x){ return x.trimmed(); }); + Utils::erase(bin, [](const QString &x) { return x.isEmpty(); }); + result.bin = std::move(bin); + } + } + + return result; +} + +NimbleBuildSystem::NimbleBuildSystem(Target *target) + : BuildSystem(target), m_projectScanner(target->project()) +{ + m_projectScanner.watchProjectFilePath(); + + connect(&m_projectScanner, &NimProjectScanner::fileChanged, this, [this](const QString &path) { + if (path == projectFilePath().toUrlishString()) + requestDelayedParse(); + }); + + connect(&m_projectScanner, &NimProjectScanner::requestReparse, + this, &NimbleBuildSystem::requestDelayedParse); + + connect(&m_projectScanner, &NimProjectScanner::finished, this, &NimbleBuildSystem::updateProject); + + connect(&m_projectScanner, &NimProjectScanner::directoryChanged, this, [this] (const QString &directory){ + // Workaround for nimble creating temporary files in project root directory + // when querying the list of tasks. + // See https://github.com/nim-lang/nimble/issues/720 + if (directory != projectDirectory().toUrlishString()) + requestDelayedParse(); + }); + + connect(target->project(), &ProjectExplorer::Project::settingsLoaded, + this, &NimbleBuildSystem::loadSettings); + connect(target->project(), &ProjectExplorer::Project::aboutToSaveSettings, + this, &NimbleBuildSystem::saveSettings); + requestDelayedParse(); +} + +void NimbleBuildSystem::triggerParsing() +{ + // Only allow one parsing run at the same time: + auto guard = guardParsingRun(); + if (!guard.guardsProject()) + return; + m_guard = std::move(guard); + + m_projectScanner.startScan(); +} + +void NimbleBuildSystem::updateProject() +{ + const FilePath projectDir = projectDirectory(); + const FilePath nimble = Nim::nimblePathFromKit(kit()); + + const NimbleMetadata metadata = parseMetadata(nimble, projectDir); + const FilePath binDir = projectDir.pathAppended(metadata.binDir); + const FilePath srcDir = projectDir.pathAppended("src"); + + QList<BuildTargetInfo> targets = Utils::transform(metadata.bin, [&](const QString &bin){ + BuildTargetInfo info = {}; + info.displayName = bin; + info.targetFilePath = binDir.pathAppended(bin); + info.projectFilePath = projectFilePath(); + info.workingDirectory = binDir; + info.buildKey = bin; + return info; + }); + + setApplicationTargets(std::move(targets)); + + std::vector<NimbleTask> tasks = parseTasks(nimble, projectDir); + if (tasks != m_tasks) { + m_tasks = std::move(tasks); + emit tasksChanged(); + } + + // Complete scan + m_guard.markAsSuccess(); + m_guard = {}; + + emitBuildSystemUpdated(); +} + +std::vector<NimbleTask> NimbleBuildSystem::tasks() const +{ + return m_tasks; +} + +void NimbleBuildSystem::saveSettings() +{ + // only handles nimble specific settings - NimProjectScanner handles general settings + QStringList result; + for (const NimbleTask &task : m_tasks) { + result.push_back(task.name); + result.push_back(task.description); + } + + project()->setNamedSettings(C_NIMBLEPROJECT_TASKS, result); +} + +void NimbleBuildSystem::loadSettings() +{ + // only handles nimble specific settings - NimProjectScanner handles general settings + QStringList list = project()->namedSettings(C_NIMBLEPROJECT_TASKS).toStringList(); + + m_tasks.clear(); + if (list.size() % 2 != 0) + return; + + for (int i = 0; i < list.size(); i += 2) + m_tasks.push_back({list[i], list[i + 1]}); +} + +bool NimbleBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const +{ + if (node->asFileNode()) { + return action == ProjectAction::Rename + || action == ProjectAction::RemoveFile; + } + if (node->isFolderNodeType() || node->isProjectNodeType()) { + return action == ProjectAction::AddNewFile + || action == ProjectAction::RemoveFile + || action == ProjectAction::AddExistingFile; + } + return BuildSystem::supportsAction(context, action, node); +} + +bool NimbleBuildSystem::addFiles(Node *, const FilePaths &filePaths, FilePaths *) +{ + return m_projectScanner.addFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); +} + +RemovedFilesFromProject NimbleBuildSystem::removeFiles(Node *, + const FilePaths &filePaths, + FilePaths *) +{ + return m_projectScanner.removeFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); +} + +bool NimbleBuildSystem::deleteFiles(Node *, const FilePaths &) +{ + return true; +} + +bool NimbleBuildSystem::renameFiles(Node *, const FilePairs &filesToRename, FilePaths *notRenamed) +{ + bool success = true; + for (const auto &[oldFilePath, newFilePath] : filesToRename) { + if (!m_projectScanner.renameFile(oldFilePath.toUrlishString(), newFilePath.toUrlishString())) { + success = false; + if (notRenamed) + *notRenamed << oldFilePath; + } + } + return success; +} + +class NimbleBuildConfiguration : public ProjectExplorer::BuildConfiguration +{ + Q_OBJECT + + friend class ProjectExplorer::BuildConfigurationFactory; + + NimbleBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id) + : BuildConfiguration(target, id) + { + setConfigWidgetDisplayName(Tr::tr("General")); + setConfigWidgetHasFrame(true); + setBuildDirectorySettingsKey("Nim.NimbleBuildConfiguration.BuildDirectory"); + appendInitialBuildStep(Constants::C_NIMBLEBUILDSTEP_ID); + + setInitializer([this](const BuildInfo &info) { + setBuildType(info.buildType); + setBuildDirectory(project()->projectDirectory()); + }); + } + + BuildType buildType() const override { return m_buildType; } + + void fromMap(const Utils::Store &map) override + { + m_buildType = static_cast<BuildType>( + map[Constants::C_NIMBLEBUILDCONFIGURATION_BUILDTYPE].toInt()); + BuildConfiguration::fromMap(map); + } + + void toMap(Utils::Store &map) const override + { + BuildConfiguration::toMap(map); + map[Constants::C_NIMBLEBUILDCONFIGURATION_BUILDTYPE] = buildType(); + } + +private: + void setBuildType(BuildType buildType) + { + if (buildType == m_buildType) + return; + m_buildType = buildType; + emit buildTypeChanged(); + } + + BuildType m_buildType = ProjectExplorer::BuildConfiguration::Unknown; +}; + +class NimbleBuildConfigurationFactory final : public ProjectExplorer::BuildConfigurationFactory +{ +public: + NimbleBuildConfigurationFactory() + { + registerBuildConfiguration<NimbleBuildConfiguration>(Constants::C_NIMBLEBUILDCONFIGURATION_ID); + setSupportedProjectType(Constants::C_NIMBLEPROJECT_ID); + setSupportedProjectMimeTypeName(Constants::C_NIMBLE_MIMETYPE); + + setBuildGenerator([](const Kit *, const FilePath &projectPath, bool forSetup) { + const auto oneBuild = [&](BuildConfiguration::BuildType buildType, const QString &typeName) { + BuildInfo info; + info.buildType = buildType; + info.typeName = typeName; + if (forSetup) { + info.displayName = info.typeName; + info.buildDirectory = projectPath.parentDir(); + } + return info; + }; + return QList<BuildInfo>{ + oneBuild(BuildConfiguration::Debug, Tr::tr("Debug")), + oneBuild(BuildConfiguration::Release, Tr::tr("Release")) + }; + }); + } +}; + NimbleProject::NimbleProject(const FilePath &fileName) : Project(Constants::C_NIMBLE_MIMETYPE, fileName) { @@ -54,7 +378,10 @@ void NimbleProject::setExcludedFiles(const QStringList &excludedFiles) void setupNimbleProject() { + static const NimbleBuildConfigurationFactory nimbleBuildConfigFactory; ProjectManager::registerProjectType<NimbleProject>(Constants::C_NIMBLE_MIMETYPE); } } // Nim + +#include <nimbleproject.moc> diff --git a/src/plugins/nim/project/nimbleproject.h b/src/plugins/nim/project/nimbleproject.h index 03d7fb3299a..beb9d82c654 100644 --- a/src/plugins/nim/project/nimbleproject.h +++ b/src/plugins/nim/project/nimbleproject.h @@ -3,10 +3,63 @@ #pragma once +#include "nimproject.h" + +#include <projectexplorer/buildsystem.h> #include <projectexplorer/project.h> namespace Nim { +struct NimbleTask +{ + QString name; + QString description; + + bool operator==(const NimbleTask &o) const { + return name == o.name && description == o.description; + } +}; + +class NimbleBuildSystem final : public ProjectExplorer::BuildSystem +{ + Q_OBJECT + +public: + NimbleBuildSystem(ProjectExplorer::Target *target); + + std::vector<NimbleTask> tasks() const; + +signals: + void tasksChanged(); + +private: + void loadSettings(); + void saveSettings(); + + void updateProject(); + + bool supportsAction(ProjectExplorer::Node *, + ProjectExplorer::ProjectAction action, + const ProjectExplorer::Node *node) const override; + bool addFiles(ProjectExplorer::Node *node, + const Utils::FilePaths &filePaths, Utils::FilePaths *) override; + ProjectExplorer::RemovedFilesFromProject removeFiles(ProjectExplorer::Node *node, + const Utils::FilePaths &filePaths, + Utils::FilePaths *) override; + bool deleteFiles(ProjectExplorer::Node *, const Utils::FilePaths &) override; + bool renameFiles( + ProjectExplorer::Node *, + const Utils::FilePairs &filesToRename, + Utils::FilePaths *notRenamed) override; + QString name() const final { return QLatin1String("mimble"); } + void triggerParsing() final; + + std::vector<NimbleTask> m_tasks; + + NimProjectScanner m_projectScanner; + ParseGuard m_guard; +}; + class NimbleProject final : public ProjectExplorer::Project { public: diff --git a/src/plugins/nim/project/nimblerunconfiguration.cpp b/src/plugins/nim/project/nimblerunconfiguration.cpp index 4e6884e8a0a..adecb710e9c 100644 --- a/src/plugins/nim/project/nimblerunconfiguration.cpp +++ b/src/plugins/nim/project/nimblerunconfiguration.cpp @@ -3,10 +3,11 @@ #include "nimblerunconfiguration.h" -#include "nimbuildsystem.h" #include "nimconstants.h" +#include "nimproject.h" #include "nimtr.h" +#include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> diff --git a/src/plugins/nim/project/nimbletaskstep.cpp b/src/plugins/nim/project/nimbletaskstep.cpp index 1885a9daa15..e2a34018f8f 100644 --- a/src/plugins/nim/project/nimbletaskstep.cpp +++ b/src/plugins/nim/project/nimbletaskstep.cpp @@ -3,8 +3,8 @@ #include "nimbletaskstep.h" +#include "nimbleproject.h" #include "nimconstants.h" -#include "nimblebuildsystem.h" #include "nimtr.h" #include <projectexplorer/abstractprocessstep.h> diff --git a/src/plugins/nim/project/nimbuildconfiguration.cpp b/src/plugins/nim/project/nimbuildconfiguration.cpp deleted file mode 100644 index 0db746ee7a9..00000000000 --- a/src/plugins/nim/project/nimbuildconfiguration.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "nimbuildconfiguration.h" -#include "nimcompilerbuildstep.h" - -#include "../nimtr.h" -#include "../nimconstants.h" - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/buildinfo.h> -#include <projectexplorer/buildsteplist.h> -#include <projectexplorer/buildstep.h> -#include <projectexplorer/kit.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/target.h> - -#include <utils/aspects.h> -#include <utils/qtcassert.h> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Nim { - -static FilePath defaultBuildDirectory(const Kit *k, - const FilePath &projectFilePath, - const QString &bc, - BuildConfiguration::BuildType buildType) -{ - return BuildConfiguration::buildDirectoryFromTemplate( - projectFilePath.parentDir(), projectFilePath, projectFilePath.baseName(), - k, bc, buildType, "nim"); -} - -NimBuildConfiguration::NimBuildConfiguration(Target *target, Utils::Id id) - : BuildConfiguration(target, id) -{ - setConfigWidgetDisplayName(Tr::tr("General")); - setConfigWidgetHasFrame(true); - setBuildDirectorySettingsKey("Nim.NimBuildConfiguration.BuildDirectory"); - - appendInitialBuildStep(Constants::C_NIMCOMPILERBUILDSTEP_ID); - appendInitialCleanStep(Constants::C_NIMCOMPILERCLEANSTEP_ID); - - setInitializer([this, target](const BuildInfo &info) { - // Create the build configuration and initialize it from build info - setBuildDirectory(defaultBuildDirectory(target->kit(), - project()->projectFilePath(), - displayName(), - buildType())); - - auto nimCompilerBuildStep = buildSteps()->firstOfType<NimCompilerBuildStep>(); - QTC_ASSERT(nimCompilerBuildStep, return); - nimCompilerBuildStep->setBuildType(info.buildType); - }); -} - - -FilePath NimBuildConfiguration::cacheDirectory() const -{ - return buildDirectory().pathAppended("nimcache"); -} - -FilePath NimBuildConfiguration::outFilePath() const -{ - auto nimCompilerBuildStep = buildSteps()->firstOfType<NimCompilerBuildStep>(); - QTC_ASSERT(nimCompilerBuildStep, return {}); - return nimCompilerBuildStep->outFilePath(); -} - -// NimBuildConfigurationFactory - -NimBuildConfigurationFactory::NimBuildConfigurationFactory() -{ - registerBuildConfiguration<NimBuildConfiguration>(Constants::C_NIMBUILDCONFIGURATION_ID); - setSupportedProjectType(Constants::C_NIMPROJECT_ID); - setSupportedProjectMimeTypeName(Constants::C_NIM_PROJECT_MIMETYPE); - - setBuildGenerator([](const Kit *k, const FilePath &projectPath, bool forSetup) { - const auto oneBuild = [&](BuildConfiguration::BuildType buildType, const QString &typeName) { - BuildInfo info; - info.buildType = buildType; - info.typeName = typeName; - if (forSetup) { - info.displayName = info.typeName; - info.buildDirectory = defaultBuildDirectory(k, projectPath, info.typeName, buildType); - } - return info; - }; - return QList<BuildInfo>{ - oneBuild(BuildConfiguration::Debug, Tr::tr("Debug")), - oneBuild(BuildConfiguration::Release, Tr::tr("Release")) - }; - }); -} - -} // namespace Nim - diff --git a/src/plugins/nim/project/nimbuildconfiguration.h b/src/plugins/nim/project/nimbuildconfiguration.h deleted file mode 100644 index b4b3fb0f57c..00000000000 --- a/src/plugins/nim/project/nimbuildconfiguration.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/target.h> - -namespace Nim { - -class NimBuildConfiguration : public ProjectExplorer::BuildConfiguration -{ - Q_OBJECT - - friend class ProjectExplorer::BuildConfigurationFactory; - NimBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); - -public: - Utils::FilePath cacheDirectory() const; - Utils::FilePath outFilePath() const; -}; - - -class NimBuildConfigurationFactory final : public ProjectExplorer::BuildConfigurationFactory -{ -public: - NimBuildConfigurationFactory(); -}; - -} // Nim diff --git a/src/plugins/nim/project/nimbuildsystem.cpp b/src/plugins/nim/project/nimbuildsystem.cpp deleted file mode 100644 index 91862171a2a..00000000000 --- a/src/plugins/nim/project/nimbuildsystem.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "nimbuildsystem.h" - -#include "nimconstants.h" -#include "nimbleproject.h" - -#include <projectexplorer/target.h> -#include <projectexplorer/toolchain.h> -#include <projectexplorer/toolchainkitaspect.h> - -#include <utils/algorithm.h> -#include <utils/fileutils.h> -#include <utils/qtcassert.h> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Nim { - -const char SETTINGS_KEY[] = "Nim.BuildSystem"; -const char EXCLUDED_FILES_KEY[] = "ExcludedFiles"; - -NimProjectScanner::NimProjectScanner(Project *project) - : m_project(project) -{ - connect(&m_directoryWatcher, &FileSystemWatcher::directoryChanged, - this, &NimProjectScanner::directoryChanged); - connect(&m_directoryWatcher, &FileSystemWatcher::fileChanged, - this, &NimProjectScanner::fileChanged); - - connect(m_project, &Project::settingsLoaded, this, &NimProjectScanner::loadSettings); - connect(m_project, &Project::aboutToSaveSettings, this, &NimProjectScanner::saveSettings); - - connect(&m_scanner, &TreeScanner::finished, this, [this] { - // Collect scanned nodes - std::vector<std::unique_ptr<FileNode>> nodes; - TreeScanner::Result scanResult = m_scanner.release(); - for (FileNode *node : scanResult.takeAllFiles()) { - if (!node->path().endsWith(".nim") && !node->path().endsWith(".nimble")) - node->setEnabled(false); // Disable files that do not end in .nim - nodes.emplace_back(node); - } - - // Sync watched dirs - const QSet<FilePath> fsDirs = Utils::transform<QSet>(nodes, - [](const std::unique_ptr<FileNode> &fn) { return fn->directory(); }); - const QSet<FilePath> projectDirs = Utils::toSet(m_directoryWatcher.directoryPaths()); - m_directoryWatcher.addDirectories(Utils::toList(fsDirs - projectDirs), FileSystemWatcher::WatchAllChanges); - m_directoryWatcher.removeDirectories(Utils::toList(projectDirs - fsDirs)); - - // Sync project files - const QSet<FilePath> fsFiles = Utils::transform<QSet>(nodes, &FileNode::filePath); - const QSet<FilePath> projectFiles = Utils::toSet(m_project->files([](const Node *n) { return Project::AllFiles(n); })); - - if (fsFiles != projectFiles) { - auto projectNode = std::make_unique<ProjectNode>(m_project->projectDirectory()); - projectNode->setDisplayName(m_project->displayName()); - projectNode->addNestedNodes(std::move(nodes)); - m_project->setRootProjectNode(std::move(projectNode)); - } - - emit finished(); - }); -} - -void NimProjectScanner::loadSettings() -{ - QVariantMap settings = m_project->namedSettings(SETTINGS_KEY).toMap(); - if (settings.contains(EXCLUDED_FILES_KEY)) - setExcludedFiles(settings.value(EXCLUDED_FILES_KEY, excludedFiles()).toStringList()); - - emit requestReparse(); -} - -void NimProjectScanner::saveSettings() -{ - QVariantMap settings; - settings.insert(EXCLUDED_FILES_KEY, excludedFiles()); - m_project->setNamedSettings(SETTINGS_KEY, settings); -} - -void NimProjectScanner::startScan() -{ - m_scanner.setFilter( - [excludedFiles = excludedFiles()](const Utils::MimeType &, const FilePath &fp) { - const QString path = fp.toUrlishString(); - return excludedFiles.contains(path) || path.endsWith(".nimproject") - || path.contains(".nimproject.user") || path.contains(".nimble.user"); - }); - - m_scanner.asyncScanForFiles(m_project->projectDirectory()); -} - -void NimProjectScanner::watchProjectFilePath() -{ - m_directoryWatcher.addFile(m_project->projectFilePath(), FileSystemWatcher::WatchModifiedDate); -} - -void NimProjectScanner::setExcludedFiles(const QStringList &list) -{ - static_cast<NimbleProject *>(m_project)->setExcludedFiles(list); -} - -QStringList NimProjectScanner::excludedFiles() const -{ - return static_cast<NimbleProject *>(m_project)->excludedFiles(); -} - -bool NimProjectScanner::addFiles(const QStringList &filePaths) -{ - setExcludedFiles(Utils::filtered(excludedFiles(), [&](const QString & f) { - return !filePaths.contains(f); - })); - - emit requestReparse(); - - return true; -} - -RemovedFilesFromProject NimProjectScanner::removeFiles(const QStringList &filePaths) -{ - setExcludedFiles(Utils::filteredUnique(excludedFiles() + filePaths)); - - emit requestReparse(); - - return RemovedFilesFromProject::Ok; -} - -bool NimProjectScanner::renameFile(const QString &, const QString &to) -{ - QStringList files = excludedFiles(); - files.removeOne(to); - setExcludedFiles(files); - - emit requestReparse(); - - return true; -} - -// NimBuildSystem - -class NimBuildSystem final : public BuildSystem -{ -public: - explicit NimBuildSystem(Target *target); - - bool supportsAction(Node *, ProjectAction action, const Node *node) const final; - bool addFiles(Node *node, const FilePaths &filePaths, FilePaths *) final; - RemovedFilesFromProject removeFiles(Node *node, - const FilePaths &filePaths, - FilePaths *) final; - bool deleteFiles(Node *, const FilePaths &) final; - bool renameFiles( - Node *, - const Utils::FilePairs &filesToRename, - Utils::FilePaths *notRenamed) final; - QString name() const final { return QLatin1String("nim"); } - - void triggerParsing() final; - -protected: - ParseGuard m_guard; - NimProjectScanner m_projectScanner; -}; - -NimBuildSystem::NimBuildSystem(Target *target) - : BuildSystem(target), m_projectScanner(target->project()) -{ - connect(&m_projectScanner, &NimProjectScanner::finished, this, [this] { - m_guard.markAsSuccess(); - m_guard = {}; // Trigger destructor of previous object, emitting parsingFinished() - - emitBuildSystemUpdated(); - }); - - connect(&m_projectScanner, &NimProjectScanner::requestReparse, - this, &NimBuildSystem::requestDelayedParse); - - connect(&m_projectScanner, &NimProjectScanner::directoryChanged, this, [this] { - if (!isWaitingForParse()) - requestDelayedParse(); - }); - - requestDelayedParse(); -} - -void NimBuildSystem::triggerParsing() -{ - m_guard = guardParsingRun(); - m_projectScanner.startScan(); -} - -FilePath nimPathFromKit(Kit *kit) -{ - auto tc = ToolchainKitAspect::toolchain(kit, Constants::C_NIMLANGUAGE_ID); - QTC_ASSERT(tc, return {}); - const FilePath command = tc->compilerCommand(); - return command.isEmpty() ? FilePath() : command.absolutePath(); -} - -FilePath nimblePathFromKit(Kit *kit) -{ - // There's no extra setting for "nimble", derive it from the "nim" path. - const FilePath nimbleFromPath = FilePath("nimble").searchInPath(); - const FilePath nimPath = nimPathFromKit(kit); - const FilePath nimbleFromKit = nimPath.pathAppended("nimble").withExecutableSuffix(); - return nimbleFromKit.exists() ? nimbleFromKit.canonicalPath() : nimbleFromPath; -} - -bool NimBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const -{ - if (node->asFileNode()) { - return action == ProjectAction::Rename - || action == ProjectAction::RemoveFile; - } - if (node->isFolderNodeType() || node->isProjectNodeType()) { - return action == ProjectAction::AddNewFile - || action == ProjectAction::RemoveFile - || action == ProjectAction::AddExistingFile; - } - return BuildSystem::supportsAction(context, action, node); -} - -bool NimBuildSystem::addFiles(Node *, const FilePaths &filePaths, FilePaths *) -{ - return m_projectScanner.addFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); -} - -RemovedFilesFromProject NimBuildSystem::removeFiles(Node *, - const FilePaths &filePaths, - FilePaths *) -{ - return m_projectScanner.removeFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); -} - -bool NimBuildSystem::deleteFiles(Node *, const FilePaths &) -{ - return true; -} - -bool NimBuildSystem::renameFiles(Node *, const FilePairs &filesToRename, FilePaths *notRenamed) -{ - bool success = true; - for (const auto &[oldFilePath, newFilePath] : filesToRename) { - if (!m_projectScanner.renameFile(oldFilePath.toUrlishString(), newFilePath.toUrlishString())) { - success = false; - if (notRenamed) - *notRenamed << oldFilePath; - } - } - return success; -} - -BuildSystem *createNimBuildSystem(Target *target) -{ - return new NimBuildSystem(target); -} - -} // namespace Nim diff --git a/src/plugins/nim/project/nimbuildsystem.h b/src/plugins/nim/project/nimbuildsystem.h deleted file mode 100644 index 8eefbee50f2..00000000000 --- a/src/plugins/nim/project/nimbuildsystem.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) Filippo Cucchetto <[email protected]> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/buildsystem.h> -#include <projectexplorer/treescanner.h> - -#include <utils/filesystemwatcher.h> - -namespace ProjectExplorer { class Kit; } - -namespace Nim { - -Utils::FilePath nimPathFromKit(ProjectExplorer::Kit *kit); -Utils::FilePath nimblePathFromKit(ProjectExplorer::Kit *kit); - -class NimProjectScanner : public QObject -{ - Q_OBJECT - -public: - explicit NimProjectScanner(ProjectExplorer::Project *project); - - void startScan(); - void watchProjectFilePath(); - - void setExcludedFiles(const QStringList &list); - QStringList excludedFiles() const; - - bool addFiles(const QStringList &filePaths); - ProjectExplorer::RemovedFilesFromProject removeFiles(const QStringList &filePaths); - bool renameFile(const QString &from, const QString &to); - -signals: - void finished(); - void requestReparse(); - void directoryChanged(const QString &path); - void fileChanged(const QString &path); - -private: - void loadSettings(); - void saveSettings(); - - ProjectExplorer::Project *m_project = nullptr; - ProjectExplorer::TreeScanner m_scanner; - Utils::FileSystemWatcher m_directoryWatcher; -}; - -ProjectExplorer::BuildSystem *createNimBuildSystem(ProjectExplorer::Target *target); - -} // namespace Nim diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp index 9b724ecf058..ea2fb12774b 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.cpp +++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp @@ -3,10 +3,10 @@ #include "nimcompilerbuildstep.h" -#include "nimbuildconfiguration.h" #include "nimconstants.h" #include "nimoutputtaskparser.h" #include "nimtr.h" +#include "project/nimproject.h" #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/ioutputparser.h> diff --git a/src/plugins/nim/project/nimcompilercleanstep.cpp b/src/plugins/nim/project/nimcompilercleanstep.cpp index 5de8a0b96de..4cde95e93b4 100644 --- a/src/plugins/nim/project/nimcompilercleanstep.cpp +++ b/src/plugins/nim/project/nimcompilercleanstep.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "nimcompilercleanstep.h" -#include "nimbuildconfiguration.h" +#include "nimproject.h" #include "../nimconstants.h" #include "../nimtr.h" diff --git a/src/plugins/nim/project/nimproject.cpp b/src/plugins/nim/project/nimproject.cpp index 53e91b88a80..d3eb6b2a6a7 100644 --- a/src/plugins/nim/project/nimproject.cpp +++ b/src/plugins/nim/project/nimproject.cpp @@ -5,12 +5,15 @@ #include "../nimconstants.h" #include "../nimtr.h" -#include "nimbuildsystem.h" +#include "nimbleproject.h" +#include "nimcompilerbuildstep.h" #include <coreplugin/icontext.h> +#include <projectexplorer/buildinfo.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> #include <projectexplorer/toolchainkitaspect.h> @@ -19,6 +22,317 @@ using namespace Utils; namespace Nim { +const char SETTINGS_KEY[] = "Nim.BuildSystem"; +const char EXCLUDED_FILES_KEY[] = "ExcludedFiles"; + +NimProjectScanner::NimProjectScanner(Project *project) + : m_project(project) +{ + connect(&m_directoryWatcher, &FileSystemWatcher::directoryChanged, + this, &NimProjectScanner::directoryChanged); + connect(&m_directoryWatcher, &FileSystemWatcher::fileChanged, + this, &NimProjectScanner::fileChanged); + + connect(m_project, &Project::settingsLoaded, this, &NimProjectScanner::loadSettings); + connect(m_project, &Project::aboutToSaveSettings, this, &NimProjectScanner::saveSettings); + + connect(&m_scanner, &TreeScanner::finished, this, [this] { + // Collect scanned nodes + std::vector<std::unique_ptr<FileNode>> nodes; + TreeScanner::Result scanResult = m_scanner.release(); + for (FileNode *node : scanResult.takeAllFiles()) { + if (!node->path().endsWith(".nim") && !node->path().endsWith(".nimble")) + node->setEnabled(false); // Disable files that do not end in .nim + nodes.emplace_back(node); + } + + // Sync watched dirs + const QSet<FilePath> fsDirs = Utils::transform<QSet>(nodes, + [](const std::unique_ptr<FileNode> &fn) { return fn->directory(); }); + const QSet<FilePath> projectDirs = Utils::toSet(m_directoryWatcher.directoryPaths()); + m_directoryWatcher.addDirectories(Utils::toList(fsDirs - projectDirs), FileSystemWatcher::WatchAllChanges); + m_directoryWatcher.removeDirectories(Utils::toList(projectDirs - fsDirs)); + + // Sync project files + const QSet<FilePath> fsFiles = Utils::transform<QSet>(nodes, &FileNode::filePath); + const QSet<FilePath> projectFiles = Utils::toSet(m_project->files([](const Node *n) { return Project::AllFiles(n); })); + + if (fsFiles != projectFiles) { + auto projectNode = std::make_unique<ProjectNode>(m_project->projectDirectory()); + projectNode->setDisplayName(m_project->displayName()); + projectNode->addNestedNodes(std::move(nodes)); + m_project->setRootProjectNode(std::move(projectNode)); + } + + emit finished(); + }); +} + +void NimProjectScanner::loadSettings() +{ + QVariantMap settings = m_project->namedSettings(SETTINGS_KEY).toMap(); + if (settings.contains(EXCLUDED_FILES_KEY)) + setExcludedFiles(settings.value(EXCLUDED_FILES_KEY, excludedFiles()).toStringList()); + + emit requestReparse(); +} + +void NimProjectScanner::saveSettings() +{ + QVariantMap settings; + settings.insert(EXCLUDED_FILES_KEY, excludedFiles()); + m_project->setNamedSettings(SETTINGS_KEY, settings); +} + +void NimProjectScanner::startScan() +{ + m_scanner.setFilter( + [excludedFiles = excludedFiles()](const Utils::MimeType &, const FilePath &fp) { + const QString path = fp.toUrlishString(); + return excludedFiles.contains(path) || path.endsWith(".nimproject") + || path.contains(".nimproject.user") || path.contains(".nimble.user"); + }); + + m_scanner.asyncScanForFiles(m_project->projectDirectory()); +} + +void NimProjectScanner::watchProjectFilePath() +{ + m_directoryWatcher.addFile(m_project->projectFilePath(), FileSystemWatcher::WatchModifiedDate); +} + +void NimProjectScanner::setExcludedFiles(const QStringList &list) +{ + static_cast<NimbleProject *>(m_project)->setExcludedFiles(list); +} + +QStringList NimProjectScanner::excludedFiles() const +{ + return static_cast<NimbleProject *>(m_project)->excludedFiles(); +} + +bool NimProjectScanner::addFiles(const QStringList &filePaths) +{ + setExcludedFiles(Utils::filtered(excludedFiles(), [&](const QString & f) { + return !filePaths.contains(f); + })); + + emit requestReparse(); + + return true; +} + +RemovedFilesFromProject NimProjectScanner::removeFiles(const QStringList &filePaths) +{ + setExcludedFiles(Utils::filteredUnique(excludedFiles() + filePaths)); + + emit requestReparse(); + + return RemovedFilesFromProject::Ok; +} + +bool NimProjectScanner::renameFile(const QString &, const QString &to) +{ + QStringList files = excludedFiles(); + files.removeOne(to); + setExcludedFiles(files); + + emit requestReparse(); + + return true; +} + +// NimBuildSystem + +class NimBuildSystem final : public BuildSystem +{ +public: + explicit NimBuildSystem(Target *target); + + bool supportsAction(Node *, ProjectAction action, const Node *node) const final; + bool addFiles(Node *node, const FilePaths &filePaths, FilePaths *) final; + RemovedFilesFromProject removeFiles(Node *node, + const FilePaths &filePaths, + FilePaths *) final; + bool deleteFiles(Node *, const FilePaths &) final; + bool renameFiles( + Node *, + const Utils::FilePairs &filesToRename, + Utils::FilePaths *notRenamed) final; + QString name() const final { return QLatin1String("nim"); } + + void triggerParsing() final; + +protected: + ParseGuard m_guard; + NimProjectScanner m_projectScanner; +}; + +NimBuildSystem::NimBuildSystem(Target *target) + : BuildSystem(target), m_projectScanner(target->project()) +{ + connect(&m_projectScanner, &NimProjectScanner::finished, this, [this] { + m_guard.markAsSuccess(); + m_guard = {}; // Trigger destructor of previous object, emitting parsingFinished() + + emitBuildSystemUpdated(); + }); + + connect(&m_projectScanner, &NimProjectScanner::requestReparse, + this, &NimBuildSystem::requestDelayedParse); + + connect(&m_projectScanner, &NimProjectScanner::directoryChanged, this, [this] { + if (!isWaitingForParse()) + requestDelayedParse(); + }); + + requestDelayedParse(); +} + +void NimBuildSystem::triggerParsing() +{ + m_guard = guardParsingRun(); + m_projectScanner.startScan(); +} + +FilePath nimPathFromKit(Kit *kit) +{ + auto tc = ToolchainKitAspect::toolchain(kit, Constants::C_NIMLANGUAGE_ID); + QTC_ASSERT(tc, return {}); + const FilePath command = tc->compilerCommand(); + return command.isEmpty() ? FilePath() : command.absolutePath(); +} + +FilePath nimblePathFromKit(Kit *kit) +{ + // There's no extra setting for "nimble", derive it from the "nim" path. + const FilePath nimbleFromPath = FilePath("nimble").searchInPath(); + const FilePath nimPath = nimPathFromKit(kit); + const FilePath nimbleFromKit = nimPath.pathAppended("nimble").withExecutableSuffix(); + return nimbleFromKit.exists() ? nimbleFromKit.canonicalPath() : nimbleFromPath; +} + +bool NimBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const +{ + if (node->asFileNode()) { + return action == ProjectAction::Rename + || action == ProjectAction::RemoveFile; + } + if (node->isFolderNodeType() || node->isProjectNodeType()) { + return action == ProjectAction::AddNewFile + || action == ProjectAction::RemoveFile + || action == ProjectAction::AddExistingFile; + } + return BuildSystem::supportsAction(context, action, node); +} + +bool NimBuildSystem::addFiles(Node *, const FilePaths &filePaths, FilePaths *) +{ + return m_projectScanner.addFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); +} + +RemovedFilesFromProject NimBuildSystem::removeFiles(Node *, + const FilePaths &filePaths, + FilePaths *) +{ + return m_projectScanner.removeFiles(Utils::transform(filePaths, &FilePath::toUrlishString)); +} + +bool NimBuildSystem::deleteFiles(Node *, const FilePaths &) +{ + return true; +} + +bool NimBuildSystem::renameFiles(Node *, const FilePairs &filesToRename, FilePaths *notRenamed) +{ + bool success = true; + for (const auto &[oldFilePath, newFilePath] : filesToRename) { + if (!m_projectScanner.renameFile(oldFilePath.toUrlishString(), newFilePath.toUrlishString())) { + success = false; + if (notRenamed) + *notRenamed << oldFilePath; + } + } + return success; +} + +static FilePath defaultBuildDirectory(const Kit *k, + const FilePath &projectFilePath, + const QString &bc, + BuildConfiguration::BuildType buildType) +{ + return BuildConfiguration::buildDirectoryFromTemplate( + projectFilePath.parentDir(), projectFilePath, projectFilePath.baseName(), + k, bc, buildType, "nim"); +} + +NimBuildConfiguration::NimBuildConfiguration(Target *target, Utils::Id id) + : BuildConfiguration(target, id) +{ + setConfigWidgetDisplayName(Tr::tr("General")); + setConfigWidgetHasFrame(true); + setBuildDirectorySettingsKey("Nim.NimBuildConfiguration.BuildDirectory"); + + appendInitialBuildStep(Constants::C_NIMCOMPILERBUILDSTEP_ID); + appendInitialCleanStep(Constants::C_NIMCOMPILERCLEANSTEP_ID); + + setInitializer([this, target](const BuildInfo &info) { + // Create the build configuration and initialize it from build info + setBuildDirectory(defaultBuildDirectory(target->kit(), + project()->projectFilePath(), + displayName(), + buildType())); + + auto nimCompilerBuildStep = buildSteps()->firstOfType<NimCompilerBuildStep>(); + QTC_ASSERT(nimCompilerBuildStep, return); + nimCompilerBuildStep->setBuildType(info.buildType); + }); +} + + +FilePath NimBuildConfiguration::cacheDirectory() const +{ + return buildDirectory().pathAppended("nimcache"); +} + +FilePath NimBuildConfiguration::outFilePath() const +{ + auto nimCompilerBuildStep = buildSteps()->firstOfType<NimCompilerBuildStep>(); + QTC_ASSERT(nimCompilerBuildStep, return {}); + return nimCompilerBuildStep->outFilePath(); +} + +// NimBuildConfigurationFactory + +class NimBuildConfigurationFactory final : public ProjectExplorer::BuildConfigurationFactory +{ +public: + NimBuildConfigurationFactory() + { + registerBuildConfiguration<NimBuildConfiguration>(Constants::C_NIMBUILDCONFIGURATION_ID); + setSupportedProjectType(Constants::C_NIMPROJECT_ID); + setSupportedProjectMimeTypeName(Constants::C_NIM_PROJECT_MIMETYPE); + + setBuildGenerator([](const Kit *k, const FilePath &projectPath, bool forSetup) { + const auto oneBuild = [&](BuildConfiguration::BuildType buildType, const QString &typeName) { + BuildInfo info; + info.buildType = buildType; + info.typeName = typeName; + if (forSetup) { + info.displayName = info.typeName; + info.buildDirectory = defaultBuildDirectory(k, projectPath, info.typeName, buildType); + } + return info; + }; + return QList<BuildInfo>{ + oneBuild(BuildConfiguration::Debug, Tr::tr("Debug")), + oneBuild(BuildConfiguration::Release, Tr::tr("Release")) + }; + }); + } +}; + + class NimProject final : public Project { public: @@ -46,7 +360,7 @@ NimProject::NimProject(const FilePath &filePath) : Project(Constants::C_NIM_MIME // ensure debugging is enabled (Nim plugin translates nim code to C code) setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); - setBuildSystemCreator(&createNimBuildSystem); + setBuildSystemCreator<NimBuildSystem>(); } Tasks NimProject::projectIssues(const Kit *k) const @@ -90,6 +404,7 @@ void NimProject::setExcludedFiles(const QStringList &excludedFiles) void setupNimProject() { + static const NimBuildConfigurationFactory buildConfigFactory; ProjectManager::registerProjectType<NimProject>(Constants::C_NIM_PROJECT_MIMETYPE); } diff --git a/src/plugins/nim/project/nimproject.h b/src/plugins/nim/project/nimproject.h index 48e104f7ba3..ae7c746edd9 100644 --- a/src/plugins/nim/project/nimproject.h +++ b/src/plugins/nim/project/nimproject.h @@ -3,8 +3,61 @@ #pragma once +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/projectnodes.h> +#include <projectexplorer/treescanner.h> + +#include <utils/filesystemwatcher.h> + namespace Nim { +class NimBuildConfiguration : public ProjectExplorer::BuildConfiguration +{ + Q_OBJECT + + friend class ProjectExplorer::BuildConfigurationFactory; + NimBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); + +public: + Utils::FilePath cacheDirectory() const; + Utils::FilePath outFilePath() const; +}; + +Utils::FilePath nimPathFromKit(ProjectExplorer::Kit *kit); +Utils::FilePath nimblePathFromKit(ProjectExplorer::Kit *kit); + +class NimProjectScanner : public QObject +{ + Q_OBJECT + +public: + explicit NimProjectScanner(ProjectExplorer::Project *project); + + void startScan(); + void watchProjectFilePath(); + + void setExcludedFiles(const QStringList &list); + QStringList excludedFiles() const; + + bool addFiles(const QStringList &filePaths); + ProjectExplorer::RemovedFilesFromProject removeFiles(const QStringList &filePaths); + bool renameFile(const QString &from, const QString &to); + +signals: + void finished(); + void requestReparse(); + void directoryChanged(const QString &path); + void fileChanged(const QString &path); + +private: + void loadSettings(); + void saveSettings(); + + ProjectExplorer::Project *m_project = nullptr; + ProjectExplorer::TreeScanner m_scanner; + Utils::FileSystemWatcher m_directoryWatcher; +}; + void setupNimProject(); } // Nim diff --git a/src/plugins/nim/project/nimrunconfiguration.cpp b/src/plugins/nim/project/nimrunconfiguration.cpp index 30ed9c998df..c640234c7b4 100644 --- a/src/plugins/nim/project/nimrunconfiguration.cpp +++ b/src/plugins/nim/project/nimrunconfiguration.cpp @@ -1,14 +1,15 @@ // Copyright (C) Filippo Cucchetto <[email protected]> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "nimproject.h" #include "nimrunconfiguration.h" -#include "nimbuildconfiguration.h" #include "../nimconstants.h" #include "../nimtr.h" #include <projectexplorer/buildsystem.h> #include <projectexplorer/runconfigurationaspects.h> +#include <projectexplorer/target.h> #include <utils/qtcassert.h> |