2019-07-22 14:39:01 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2019 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://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 "pythonproject.h"
|
|
|
|
|
|
|
|
|
|
#include "pythonconstants.h"
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
#include <projectexplorer/buildsystem.h>
|
2019-07-22 14:39:01 +02:00
|
|
|
#include <projectexplorer/buildtargetinfo.h>
|
|
|
|
|
#include <projectexplorer/kitmanager.h>
|
|
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
|
|
|
#include <projectexplorer/projectnodes.h>
|
|
|
|
|
#include <projectexplorer/target.h>
|
|
|
|
|
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
#include <QProcessEnvironment>
|
|
|
|
|
#include <QRegularExpression>
|
2019-10-25 09:55:32 +02:00
|
|
|
#include <QTimer>
|
2019-07-22 14:39:01 +02:00
|
|
|
|
|
|
|
|
#include <coreplugin/documentmanager.h>
|
|
|
|
|
#include <coreplugin/icontext.h>
|
|
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
#include <coreplugin/messagemanager.h>
|
|
|
|
|
|
|
|
|
|
#include <utils/fileutils.h>
|
|
|
|
|
|
|
|
|
|
using namespace Core;
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
|
|
|
|
|
namespace Python {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
class PythonBuildSystem : public BuildSystem
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2019-10-25 09:55:32 +02:00
|
|
|
explicit PythonBuildSystem(Target *target);
|
2019-10-22 14:55:51 +02:00
|
|
|
|
|
|
|
|
bool supportsAction(Node *context, ProjectAction action, const Node *node) const override;
|
|
|
|
|
bool addFiles(Node *, const QStringList &filePaths, QStringList *) override;
|
|
|
|
|
RemovedFilesFromProject removeFiles(Node *, const QStringList &filePaths, QStringList *) override;
|
|
|
|
|
bool deleteFiles(Node *, const QStringList &) override;
|
|
|
|
|
bool renameFile(Node *, const QString &filePath, const QString &newFilePath) override;
|
|
|
|
|
|
|
|
|
|
bool saveRawFileList(const QStringList &rawFileList);
|
|
|
|
|
bool saveRawList(const QStringList &rawList, const QString &fileName);
|
|
|
|
|
void parse();
|
|
|
|
|
QStringList processEntries(const QStringList &paths,
|
|
|
|
|
QHash<QString, QString> *map = nullptr) const;
|
2019-07-22 14:39:01 +02:00
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool writePyProjectFile(const QString &fileName, QString &content,
|
|
|
|
|
const QStringList &rawList, QString *errorMessage);
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
void triggerParsing() final;
|
2019-07-22 14:39:01 +02:00
|
|
|
|
|
|
|
|
private:
|
2019-10-22 14:55:51 +02:00
|
|
|
QStringList m_rawFileList;
|
|
|
|
|
QStringList m_files;
|
|
|
|
|
QHash<QString, QString> m_rawListEntries;
|
2019-07-22 14:39:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Provides displayName relative to project node
|
|
|
|
|
*/
|
|
|
|
|
class PythonFileNode : public FileNode
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PythonFileNode(const FilePath &filePath, const QString &nodeDisplayName,
|
|
|
|
|
FileType fileType = FileType::Source)
|
|
|
|
|
: FileNode(filePath, fileType)
|
|
|
|
|
, m_displayName(nodeDisplayName)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
QString displayName() const override { return m_displayName; }
|
|
|
|
|
private:
|
|
|
|
|
QString m_displayName;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static QStringList readLines(const FilePath &projectFile)
|
|
|
|
|
{
|
|
|
|
|
const QString projectFileName = projectFile.fileName();
|
|
|
|
|
QSet<QString> visited = { projectFileName };
|
|
|
|
|
QStringList lines = { projectFileName };
|
|
|
|
|
|
|
|
|
|
QFile file(projectFile.toString());
|
|
|
|
|
if (file.open(QFile::ReadOnly)) {
|
|
|
|
|
QTextStream stream(&file);
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
const QString line = stream.readLine();
|
|
|
|
|
if (line.isNull())
|
|
|
|
|
break;
|
|
|
|
|
if (visited.contains(line))
|
|
|
|
|
continue;
|
|
|
|
|
lines.append(line);
|
|
|
|
|
visited.insert(line);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QStringList readLinesJson(const FilePath &projectFile, QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
const QString projectFileName = projectFile.fileName();
|
|
|
|
|
QStringList lines = { projectFileName };
|
|
|
|
|
|
|
|
|
|
QFile file(projectFile.toString());
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
|
|
|
*errorMessage = PythonProject::tr("Unable to open \"%1\" for reading: %2")
|
|
|
|
|
.arg(projectFile.toUserOutput(), file.errorString());
|
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QByteArray content = file.readAll();
|
|
|
|
|
|
|
|
|
|
// This assumes the project file is formed with only one field called
|
|
|
|
|
// 'files' that has a list associated of the files to include in the project.
|
|
|
|
|
if (content.isEmpty()) {
|
|
|
|
|
*errorMessage = PythonProject::tr("Unable to read \"%1\": The file is empty.")
|
|
|
|
|
.arg(projectFile.toUserOutput());
|
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QJsonParseError error;
|
|
|
|
|
const QJsonDocument doc = QJsonDocument::fromJson(content, &error);
|
|
|
|
|
if (doc.isNull()) {
|
|
|
|
|
const int line = content.left(error.offset).count('\n') + 1;
|
|
|
|
|
*errorMessage = PythonProject::tr("Unable to parse \"%1\":%2: %3")
|
|
|
|
|
.arg(projectFile.toUserOutput()).arg(line)
|
|
|
|
|
.arg(error.errorString());
|
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QJsonObject obj = doc.object();
|
|
|
|
|
if (obj.contains("files")) {
|
|
|
|
|
const QJsonValue files = obj.value("files");
|
|
|
|
|
const QJsonArray files_array = files.toArray();
|
|
|
|
|
QSet<QString> visited;
|
|
|
|
|
for (const auto &file : files_array)
|
|
|
|
|
visited.insert(file.toString());
|
|
|
|
|
|
|
|
|
|
lines.append(Utils::toList(visited));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lines;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
class PythonProjectNode : public ProjectNode
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PythonProjectNode(const Utils::FilePath &path)
|
|
|
|
|
: ProjectNode(path)
|
|
|
|
|
{
|
|
|
|
|
setDisplayName(path.toFileInfo().completeBaseName());
|
|
|
|
|
setAddFileFilter("*.py");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-22 14:39:01 +02:00
|
|
|
PythonProject::PythonProject(const FilePath &fileName)
|
|
|
|
|
: Project(Constants::C_PY_MIMETYPE, fileName)
|
|
|
|
|
{
|
|
|
|
|
setId(PythonProjectId);
|
|
|
|
|
setProjectLanguages(Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
|
|
|
|
|
setDisplayName(fileName.toFileInfo().completeBaseName());
|
|
|
|
|
|
|
|
|
|
setNeedsBuildConfigurations(false);
|
2019-10-25 09:55:32 +02:00
|
|
|
setBuildSystemCreator([](Target *t) { return new PythonBuildSystem(t); });
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2019-12-06 13:04:17 +02:00
|
|
|
static FileType getFileType(const FilePath &f)
|
2019-11-27 15:37:59 +01:00
|
|
|
{
|
2019-12-06 13:04:17 +02:00
|
|
|
if (f.endsWith(".py"))
|
|
|
|
|
return FileType::Source;
|
|
|
|
|
if (f.endsWith(".pyproject") || f.endsWith(".pyqtc"))
|
2019-11-27 15:37:59 +01:00
|
|
|
return FileType::Project;
|
2019-12-06 13:04:17 +02:00
|
|
|
if (f.endsWith(".qrc"))
|
2019-11-27 15:37:59 +01:00
|
|
|
return FileType::Resource;
|
2019-12-06 13:04:17 +02:00
|
|
|
if (f.endsWith(".ui"))
|
2019-11-27 15:37:59 +01:00
|
|
|
return FileType::Form;
|
2019-12-06 13:04:17 +02:00
|
|
|
if (f.endsWith(".qml") || f.endsWith(".js"))
|
2019-11-27 15:37:59 +01:00
|
|
|
return FileType::QML;
|
2019-12-06 13:04:17 +02:00
|
|
|
return Node::fileTypeForFileName(f);
|
2019-11-27 15:37:59 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
void PythonBuildSystem::triggerParsing()
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
ParseGuard guard = guardParsingRun();
|
2019-10-22 14:55:51 +02:00
|
|
|
parse();
|
2019-07-22 14:39:01 +02:00
|
|
|
|
|
|
|
|
const QDir baseDir(projectDirectory().toString());
|
|
|
|
|
QList<BuildTargetInfo> appTargets;
|
2019-10-22 14:55:51 +02:00
|
|
|
|
|
|
|
|
auto newRoot = std::make_unique<PythonProjectNode>(projectDirectory());
|
2019-07-22 14:39:01 +02:00
|
|
|
for (const QString &f : qAsConst(m_files)) {
|
|
|
|
|
const QString displayName = baseDir.relativeFilePath(f);
|
2019-11-14 15:43:05 +01:00
|
|
|
const FilePath filePath = FilePath::fromString(f);
|
2019-12-06 13:04:17 +02:00
|
|
|
const FileType fileType = getFileType(filePath);
|
2019-11-14 15:43:05 +01:00
|
|
|
|
|
|
|
|
newRoot->addNestedNode(std::make_unique<PythonFileNode>(filePath, displayName, fileType));
|
2019-07-22 14:39:01 +02:00
|
|
|
if (fileType == FileType::Source) {
|
|
|
|
|
BuildTargetInfo bti;
|
|
|
|
|
bti.buildKey = f;
|
2019-11-14 15:43:05 +01:00
|
|
|
bti.targetFilePath = filePath;
|
2019-07-22 14:39:01 +02:00
|
|
|
bti.projectFilePath = projectFilePath();
|
|
|
|
|
appTargets.append(bti);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-20 15:21:54 +01:00
|
|
|
setRootProjectNode(std::move(newRoot));
|
2019-07-22 14:39:01 +02:00
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
setApplicationTargets(appTargets);
|
2019-07-22 14:39:01 +02:00
|
|
|
|
|
|
|
|
guard.markAsSuccess();
|
2019-11-15 15:44:45 +01:00
|
|
|
|
|
|
|
|
emitBuildSystemUpdated();
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool PythonBuildSystem::saveRawFileList(const QStringList &rawFileList)
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
const bool result = saveRawList(rawFileList, projectFilePath().toString());
|
|
|
|
|
// refresh(PythonProject::Files);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool PythonBuildSystem::saveRawList(const QStringList &rawList, const QString &fileName)
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
FileChangeBlocker changeGuarg(fileName);
|
|
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
|
|
// New project file
|
|
|
|
|
if (fileName.endsWith(".pyproject")) {
|
|
|
|
|
FileSaver saver(fileName, QIODevice::ReadOnly | QIODevice::Text);
|
|
|
|
|
if (!saver.hasError()) {
|
|
|
|
|
QString content = QTextStream(saver.file()).readAll();
|
|
|
|
|
if (saver.finalize(ICore::mainWindow())) {
|
|
|
|
|
QString errorMessage;
|
|
|
|
|
result = writePyProjectFile(fileName, content, rawList, &errorMessage);
|
|
|
|
|
if (!errorMessage.isEmpty())
|
|
|
|
|
MessageManager::write(errorMessage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else { // Old project file
|
|
|
|
|
FileSaver saver(fileName, QIODevice::WriteOnly | QIODevice::Text);
|
|
|
|
|
if (!saver.hasError()) {
|
|
|
|
|
QTextStream stream(saver.file());
|
|
|
|
|
for (const QString &filePath : rawList)
|
|
|
|
|
stream << filePath << '\n';
|
|
|
|
|
saver.setResult(&stream);
|
|
|
|
|
result = saver.finalize(ICore::mainWindow());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool PythonBuildSystem::writePyProjectFile(const QString &fileName, QString &content,
|
2019-07-22 14:39:01 +02:00
|
|
|
const QStringList &rawList, QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
QFile file(fileName);
|
|
|
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
|
|
|
*errorMessage = PythonProject::tr("Unable to open \"%1\" for reading: %2")
|
|
|
|
|
.arg(fileName, file.errorString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build list of files with the current rawList for the JSON file
|
|
|
|
|
QString files("[");
|
|
|
|
|
for (const QString &f : rawList)
|
|
|
|
|
if (!f.endsWith(".pyproject"))
|
|
|
|
|
files += QString("\"%1\",").arg(f);
|
|
|
|
|
files = files.left(files.lastIndexOf(',')); // Removing leading comma
|
|
|
|
|
files += ']';
|
|
|
|
|
|
|
|
|
|
// Removing everything inside square parenthesis
|
|
|
|
|
// to replace it with the new list of files for the JSON file.
|
|
|
|
|
QRegularExpression pattern(R"(\[.*\])");
|
|
|
|
|
content.replace(pattern, files);
|
|
|
|
|
file.write(content.toUtf8());
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool PythonBuildSystem::addFiles(Node *, const QStringList &filePaths, QStringList *)
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
QStringList newList = m_rawFileList;
|
|
|
|
|
|
|
|
|
|
const QDir baseDir(projectDirectory().toString());
|
|
|
|
|
for (const QString &filePath : filePaths)
|
|
|
|
|
newList.append(baseDir.relativeFilePath(filePath));
|
|
|
|
|
|
|
|
|
|
return saveRawFileList(newList);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
RemovedFilesFromProject PythonBuildSystem::removeFiles(Node *, const QStringList &filePaths, QStringList *)
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
QStringList newList = m_rawFileList;
|
|
|
|
|
|
|
|
|
|
for (const QString &filePath : filePaths) {
|
|
|
|
|
const QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
|
|
|
|
|
if (i != m_rawListEntries.end())
|
|
|
|
|
newList.removeOne(i.value());
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool res = saveRawFileList(newList);
|
|
|
|
|
|
|
|
|
|
return res ? RemovedFilesFromProject::Ok : RemovedFilesFromProject::Error;
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool PythonBuildSystem::deleteFiles(Node *, const QStringList &)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PythonBuildSystem::renameFile(Node *, const QString &filePath, const QString &newFilePath)
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
QStringList newList = m_rawFileList;
|
|
|
|
|
|
|
|
|
|
const QHash<QString, QString>::iterator i = m_rawListEntries.find(filePath);
|
|
|
|
|
if (i != m_rawListEntries.end()) {
|
|
|
|
|
const int index = newList.indexOf(i.value());
|
|
|
|
|
if (index != -1) {
|
|
|
|
|
const QDir baseDir(projectDirectory().toString());
|
|
|
|
|
newList.replace(index, baseDir.relativeFilePath(newFilePath));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return saveRawFileList(newList);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
void PythonBuildSystem::parse()
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
m_rawListEntries.clear();
|
|
|
|
|
const FilePath filePath = projectFilePath();
|
|
|
|
|
// The PySide project file is JSON based
|
|
|
|
|
if (filePath.endsWith(".pyproject")) {
|
|
|
|
|
QString errorMessage;
|
|
|
|
|
m_rawFileList = readLinesJson(filePath, &errorMessage);
|
|
|
|
|
if (!errorMessage.isEmpty())
|
|
|
|
|
MessageManager::write(errorMessage);
|
|
|
|
|
}
|
|
|
|
|
// To keep compatibility with PyQt we keep the compatibility with plain
|
|
|
|
|
// text files as project files.
|
|
|
|
|
else if (filePath.endsWith(".pyqtc"))
|
|
|
|
|
m_rawFileList = readLines(filePath);
|
|
|
|
|
|
|
|
|
|
m_files = processEntries(m_rawFileList, &m_rawListEntries);
|
|
|
|
|
}
|
2019-10-22 14:55:51 +02:00
|
|
|
|
2019-07-22 14:39:01 +02:00
|
|
|
/**
|
|
|
|
|
* Expands environment variables in the given \a string when they are written
|
|
|
|
|
* like $$(VARIABLE).
|
|
|
|
|
*/
|
|
|
|
|
static void expandEnvironmentVariables(const QProcessEnvironment &env, QString &string)
|
|
|
|
|
{
|
|
|
|
|
static QRegExp candidate(QLatin1String("\\$\\$\\((.+)\\)"));
|
|
|
|
|
|
|
|
|
|
int index = candidate.indexIn(string);
|
|
|
|
|
while (index != -1) {
|
|
|
|
|
const QString value = env.value(candidate.cap(1));
|
|
|
|
|
|
|
|
|
|
string.replace(index, candidate.matchedLength(), value);
|
|
|
|
|
index += value.length();
|
|
|
|
|
|
|
|
|
|
index = candidate.indexIn(string, index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Expands environment variables and converts the path from relative to the
|
|
|
|
|
* project to an absolute path.
|
|
|
|
|
*
|
|
|
|
|
* The \a map variable is an optional argument that will map the returned
|
|
|
|
|
* absolute paths back to their original \a paths.
|
|
|
|
|
*/
|
2019-10-22 14:55:51 +02:00
|
|
|
QStringList PythonBuildSystem::processEntries(const QStringList &paths,
|
2019-07-22 14:39:01 +02:00
|
|
|
QHash<QString, QString> *map) const
|
|
|
|
|
{
|
|
|
|
|
const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
|
|
|
|
const QDir projectDir(projectDirectory().toString());
|
|
|
|
|
|
|
|
|
|
QFileInfo fileInfo;
|
|
|
|
|
QStringList absolutePaths;
|
|
|
|
|
for (const QString &path : paths) {
|
|
|
|
|
QString trimmedPath = path.trimmed();
|
|
|
|
|
if (trimmedPath.isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
expandEnvironmentVariables(env, trimmedPath);
|
|
|
|
|
|
|
|
|
|
trimmedPath = FilePath::fromUserInput(trimmedPath).toString();
|
|
|
|
|
|
|
|
|
|
fileInfo.setFile(projectDir, trimmedPath);
|
|
|
|
|
if (fileInfo.exists()) {
|
|
|
|
|
const QString absPath = fileInfo.absoluteFilePath();
|
|
|
|
|
absolutePaths.append(absPath);
|
|
|
|
|
if (map)
|
|
|
|
|
map->insert(absPath, trimmedPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
absolutePaths.removeDuplicates();
|
|
|
|
|
return absolutePaths;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Project::RestoreResult PythonProject::fromMap(const QVariantMap &map, QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
Project::RestoreResult res = Project::fromMap(map, errorMessage);
|
|
|
|
|
if (res == RestoreResult::Ok) {
|
|
|
|
|
if (!activeTarget())
|
|
|
|
|
addTargetForDefaultKit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
PythonBuildSystem::PythonBuildSystem(Target *target)
|
|
|
|
|
: BuildSystem(target)
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
connect(target->project(), &Project::projectFileIsDirty, this, [this]() { triggerParsing(); });
|
|
|
|
|
QTimer::singleShot(0, this, &PythonBuildSystem::triggerParsing);
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-22 14:55:51 +02:00
|
|
|
bool PythonBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
|
2019-07-22 14:39:01 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2019-10-22 14:55:51 +02:00
|
|
|
return BuildSystem::supportsAction(context, action, node);
|
2019-07-22 14:39:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Python
|