Files
qt-creator/src/plugins/nim/project/nimblebuildsystem.cpp

247 lines
8.2 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com>
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "nimblebuildsystem.h"
#include "nimbleproject.h"
#include "nimproject.h"
#include <projectexplorer/target.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QProcess>
#include <QStandardPaths>
using namespace ProjectExplorer;
using namespace Utils;
namespace Nim {
const char C_NIMBLEPROJECT_TASKS[] = "Nim.NimbleProject.Tasks";
const char C_NIMBLEPROJECT_METADATA[] = "Nim.NimbleProject.Metadata";
static std::vector<NimbleTask> parseTasks(const QString &nimblePath, const QString &workingDirectory)
{
QProcess process;
process.setWorkingDirectory(workingDirectory);
process.start(QStandardPaths::findExecutable(nimblePath), {"tasks"});
process.waitForFinished();
std::vector<NimbleTask> result;
QList<QByteArray> lines = process.readAllStandardOutput().split('\n');
lines = Utils::transform(lines, [](const QByteArray &line){ return line.trimmed(); });
Utils::erase(lines, [](const QByteArray &line) { return line.isEmpty(); });
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 QString &nimblePath, const QString &workingDirectory)
{
QProcess process;
process.setWorkingDirectory(workingDirectory);
process.start(QStandardPaths::findExecutable(nimblePath), {"dump"});
process.waitForFinished();
NimbleMetadata result = {};
QList<QByteArray> lines = process.readAllStandardOutput().split('\n');
lines = Utils::transform(lines, [](const QByteArray &line){ return line.trimmed(); });
Utils::erase(lines, [](const QByteArray &line) { return line.isEmpty(); });
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().toString())
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().toString())
requestDelayedParse();
});
requestDelayedParse();
}
void NimbleBuildSystem::triggerParsing()
{
m_guard = guardParsingRun();
m_projectScanner.startScan();
}
void NimbleBuildSystem::updateProject()
{
const FilePath projectDir = projectDirectory();
m_metadata = parseMetadata(QStandardPaths::findExecutable("nimble"), projectDir.toString());
const FilePath binDir = projectDir.pathAppended(m_metadata.binDir);
const FilePath srcDir = projectDir.pathAppended("src");
QList<BuildTargetInfo> targets = Utils::transform(m_metadata.bin, [&](const QString &bin){
BuildTargetInfo info = {};
info.displayName = bin;
info.targetFilePath = binDir.pathAppended(HostOsInfo::withExecutableSuffix(bin));
info.projectFilePath = srcDir.pathAppended(bin).stringAppended(".nim");
info.workingDirectory = binDir;
info.buildKey = bin;
return info;
});
setApplicationTargets(std::move(targets));
std::vector<NimbleTask> tasks = parseTasks(QStandardPaths::findExecutable("nimble"), projectDir.toString());
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;
}
NimbleMetadata NimbleBuildSystem::metadata() const
{
return m_metadata;
}
void NimbleBuildSystem::saveSettings()
{
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()
{
QStringList list = project()->namedSettings(C_NIMBLEPROJECT_TASKS).toStringList();
m_tasks.clear();
if (list.size() % 2 != 0)
return;
std::vector<NimbleTask> result;
for (int i = 0; i < list.size(); i += 2)
result.push_back({list[i], list[i + 1]});
requestParse();
}
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 QStringList &filePaths, QStringList *)
{
return m_projectScanner.addFiles(filePaths);
}
RemovedFilesFromProject NimbleBuildSystem::removeFiles(Node *,
const QStringList &filePaths,
QStringList *)
{
return m_projectScanner.removeFiles(filePaths);
}
bool NimbleBuildSystem::deleteFiles(Node *, const QStringList &)
{
return true;
}
bool NimbleBuildSystem::renameFile(Node *, const QString &filePath, const QString &newFilePath)
{
return m_projectScanner.renameFile(filePath, newFilePath);
}
} // Nim