forked from qt-creator/qt-creator
ProjectExplorer: Move BuildSystem owership to BuildConfiguration
... or Target. This patch moves build system from conceptually "one per project" to "one per target (i.e. per project-and-kit)" or "per BuildConfigurations" for targets where the builds differ significantly. Building requires usually items from the kit (Qt version, compiler, ...) so a target-agnostic build is practically almost always wrong. Moving the build system to the target also has the potential to solve issues caused by switching targets while parsing, that used Project::activeTarget() regularly, with potentially different results before and after the switch. This patch might create performance/size regressions when several targets are set up per project as the build system implementation's internal data are duplicated in this case. The idea is to fix that by sharing per-project pieces again in the project implementation once these problems occur. Change-Id: I87f640ce418b93175b5029124eaa55f3b8721dca Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmlproject.h"
|
||||
|
||||
#include "fileformat/qmlprojectfileformat.h"
|
||||
#include "fileformat/qmlprojectitem.h"
|
||||
#include "qmlprojectrunconfiguration.h"
|
||||
@@ -31,6 +32,9 @@
|
||||
#include "qmlprojectmanagerconstants.h"
|
||||
#include "qmlprojectnodes.h"
|
||||
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/editormanager/documentmodel.h>
|
||||
#include <coreplugin/editormanager/ieditor.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
@@ -46,66 +50,84 @@
|
||||
|
||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||
|
||||
#include <texteditor/textdocument.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextCodec>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace QmlProjectManager::Internal;
|
||||
|
||||
namespace QmlProjectManager {
|
||||
|
||||
QmlProject::QmlProject(const Utils::FilePath &fileName)
|
||||
: Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName)
|
||||
{
|
||||
const QString normalized
|
||||
= Utils::FileUtils::normalizePathName(fileName.toFileInfo().canonicalFilePath());
|
||||
m_canonicalProjectDir = Utils::FilePath::fromString(normalized).parentDir();
|
||||
|
||||
setId(QmlProjectManager::Constants::QML_PROJECT_ID);
|
||||
setProjectLanguages(Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID));
|
||||
setDisplayName(fileName.toFileInfo().completeBaseName());
|
||||
|
||||
setNeedsBuildConfigurations(false);
|
||||
setBuildSystemCreator([](Project *p) { return new Internal::QmlBuildSystem(p); });
|
||||
|
||||
connect(this, &QmlProject::projectFileIsDirty, this, &QmlProject::refreshProjectFile);
|
||||
setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); });
|
||||
}
|
||||
|
||||
QmlProject::~QmlProject()
|
||||
QmlBuildSystem::QmlBuildSystem(Target *target)
|
||||
: BuildSystem(target)
|
||||
{
|
||||
const QString normalized
|
||||
= Utils::FileUtils::normalizePathName(target->project()
|
||||
->projectFilePath().toFileInfo().canonicalFilePath());
|
||||
m_canonicalProjectDir = Utils::FilePath::fromString(normalized).parentDir();
|
||||
|
||||
connect(target->project(), &Project::projectFileIsDirty,
|
||||
this, &QmlBuildSystem::refreshProjectFile);
|
||||
|
||||
// refresh first - project information is used e.g. to decide the default RC's
|
||||
refresh(Everything);
|
||||
|
||||
// FIXME: Check. Probably bogus after the BuildSystem move.
|
||||
// // addedTarget calls updateEnabled on the runconfigurations
|
||||
// // which needs to happen after refresh
|
||||
// foreach (Target *t, targets())
|
||||
// addedTarget(t);
|
||||
|
||||
connect(target->project(), &Project::activeTargetChanged,
|
||||
this, &QmlBuildSystem::onActiveTargetChanged);
|
||||
updateDeploymentData();
|
||||
}
|
||||
|
||||
QmlBuildSystem::~QmlBuildSystem()
|
||||
{
|
||||
delete m_projectItem.data();
|
||||
}
|
||||
|
||||
void QmlProject::addedTarget(Target *target)
|
||||
void QmlBuildSystem::triggerParsing()
|
||||
{
|
||||
updateDeploymentData(target);
|
||||
refresh(Everything);
|
||||
}
|
||||
|
||||
void QmlProject::onActiveTargetChanged(Target *target)
|
||||
{
|
||||
if (m_activeTarget)
|
||||
disconnect(m_activeTarget, &Target::kitChanged, this, &QmlProject::onKitChanged);
|
||||
m_activeTarget = target;
|
||||
if (m_activeTarget)
|
||||
connect(target, &Target::kitChanged, this, &QmlProject::onKitChanged);
|
||||
|
||||
// make sure e.g. the default qml imports are adapted
|
||||
refresh(Configuration);
|
||||
}
|
||||
|
||||
void QmlProject::onKitChanged()
|
||||
void QmlBuildSystem::onActiveTargetChanged(Target *)
|
||||
{
|
||||
// make sure e.g. the default qml imports are adapted
|
||||
refresh(Configuration);
|
||||
}
|
||||
|
||||
Utils::FilePath QmlProject::canonicalProjectDir() const
|
||||
void QmlBuildSystem::onKitChanged()
|
||||
{
|
||||
// make sure e.g. the default qml imports are adapted
|
||||
refresh(Configuration);
|
||||
}
|
||||
|
||||
Utils::FilePath QmlBuildSystem::canonicalProjectDir() const
|
||||
{
|
||||
return m_canonicalProjectDir;
|
||||
}
|
||||
|
||||
void QmlProject::parseProject(RefreshOptions options)
|
||||
void QmlBuildSystem::parseProject(RefreshOptions options)
|
||||
{
|
||||
if (options & Files) {
|
||||
if (options & ProjectFile)
|
||||
@@ -115,7 +137,7 @@ void QmlProject::parseProject(RefreshOptions options)
|
||||
m_projectItem = QmlProjectFileFormat::parseProjectFile(projectFilePath(), &errorMessage);
|
||||
if (m_projectItem) {
|
||||
connect(m_projectItem.data(), &QmlProjectItem::qmlFilesChanged,
|
||||
this, &QmlProject::refreshFiles);
|
||||
this, &QmlBuildSystem::refreshFiles);
|
||||
|
||||
} else {
|
||||
MessageManager::write(tr("Error while loading project file %1.")
|
||||
@@ -153,7 +175,7 @@ void QmlProject::parseProject(RefreshOptions options)
|
||||
}
|
||||
}
|
||||
|
||||
void QmlProject::refresh(RefreshOptions options)
|
||||
void QmlBuildSystem::refresh(RefreshOptions options)
|
||||
{
|
||||
ParseGuard guard = guardParsingRun();
|
||||
parseProject(options);
|
||||
@@ -166,32 +188,32 @@ void QmlProject::refresh(RefreshOptions options)
|
||||
return;
|
||||
|
||||
QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
|
||||
modelManager->defaultProjectInfoForProject(this);
|
||||
modelManager->defaultProjectInfoForProject(project());
|
||||
foreach (const QString &searchPath, makeAbsolute(canonicalProjectDir(), customImportPaths()))
|
||||
projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(searchPath),
|
||||
QmlJS::Dialect::Qml);
|
||||
|
||||
modelManager->updateProjectInfo(projectInfo, this);
|
||||
modelManager->updateProjectInfo(projectInfo, project());
|
||||
|
||||
guard.markAsSuccess();
|
||||
}
|
||||
|
||||
QString QmlProject::mainFile() const
|
||||
QString QmlBuildSystem::mainFile() const
|
||||
{
|
||||
if (m_projectItem)
|
||||
return m_projectItem.data()->mainFile();
|
||||
return QString();
|
||||
}
|
||||
|
||||
void QmlProject::setMainFile(const QString &mainFilePath)
|
||||
void QmlBuildSystem::setMainFile(const QString &mainFilePath)
|
||||
{
|
||||
if (m_projectItem)
|
||||
m_projectItem.data()->setMainFile(mainFilePath);
|
||||
}
|
||||
|
||||
Utils::FilePath QmlProject::targetDirectory(const Target *target) const
|
||||
Utils::FilePath QmlBuildSystem::targetDirectory() const
|
||||
{
|
||||
if (DeviceTypeKitAspect::deviceTypeId(target->kit())
|
||||
if (DeviceTypeKitAspect::deviceTypeId(target()->kit())
|
||||
== ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
|
||||
return canonicalProjectDir();
|
||||
|
||||
@@ -199,53 +221,42 @@ Utils::FilePath QmlProject::targetDirectory(const Target *target) const
|
||||
: Utils::FilePath();
|
||||
}
|
||||
|
||||
Utils::FilePath QmlProject::targetFile(const Utils::FilePath &sourceFile,
|
||||
const Target *target) const
|
||||
Utils::FilePath QmlBuildSystem::targetFile(const Utils::FilePath &sourceFile) const
|
||||
{
|
||||
const QDir sourceDir(m_projectItem ? m_projectItem->sourceDirectory()
|
||||
: canonicalProjectDir().toString());
|
||||
const QDir targetDir(targetDirectory(target).toString());
|
||||
const QDir targetDir(targetDirectory().toString());
|
||||
const QString relative = sourceDir.relativeFilePath(sourceFile.toString());
|
||||
return Utils::FilePath::fromString(QDir::cleanPath(targetDir.absoluteFilePath(relative)));
|
||||
}
|
||||
|
||||
Utils::EnvironmentItems QmlProject::environment() const
|
||||
Utils::EnvironmentItems QmlBuildSystem::environment() const
|
||||
{
|
||||
if (m_projectItem)
|
||||
return m_projectItem.data()->environment();
|
||||
return {};
|
||||
}
|
||||
|
||||
QStringList QmlProject::customImportPaths() const
|
||||
QStringList QmlBuildSystem::customImportPaths() const
|
||||
{
|
||||
if (m_projectItem)
|
||||
return m_projectItem.data()->importPaths();
|
||||
return {};
|
||||
}
|
||||
|
||||
QStringList QmlProject::customFileSelectors() const
|
||||
QStringList QmlBuildSystem::customFileSelectors() const
|
||||
{
|
||||
if (m_projectItem)
|
||||
return m_projectItem.data()->fileSelectors();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool QmlProject::addFiles(const QStringList &filePaths)
|
||||
void QmlBuildSystem::refreshProjectFile()
|
||||
{
|
||||
QStringList toAdd;
|
||||
foreach (const QString &filePath, filePaths) {
|
||||
if (!m_projectItem.data()->matchesFile(filePath))
|
||||
toAdd << filePaths;
|
||||
}
|
||||
return toAdd.isEmpty();
|
||||
refresh(QmlBuildSystem::ProjectFile | Files);
|
||||
}
|
||||
|
||||
void QmlProject::refreshProjectFile()
|
||||
{
|
||||
refresh(QmlProject::ProjectFile | Files);
|
||||
}
|
||||
|
||||
QStringList QmlProject::makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths)
|
||||
QStringList QmlBuildSystem::makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths)
|
||||
{
|
||||
if (path.isEmpty())
|
||||
return relativePaths;
|
||||
@@ -256,14 +267,7 @@ QStringList QmlProject::makeAbsolute(const Utils::FilePath &path, const QStringL
|
||||
});
|
||||
}
|
||||
|
||||
QVariant QmlProject::additionalData(Id id, const Target *) const
|
||||
{
|
||||
if (id == Constants::customFileSelectorsData)
|
||||
return customFileSelectors();
|
||||
return {};
|
||||
}
|
||||
|
||||
void QmlProject::refreshFiles(const QSet<QString> &/*added*/, const QSet<QString> &removed)
|
||||
void QmlBuildSystem::refreshFiles(const QSet<QString> &/*added*/, const QSet<QString> &removed)
|
||||
{
|
||||
refresh(Files);
|
||||
if (!removed.isEmpty()) {
|
||||
@@ -273,11 +277,9 @@ void QmlProject::refreshFiles(const QSet<QString> &/*added*/, const QSet<QString
|
||||
refreshTargetDirectory();
|
||||
}
|
||||
|
||||
void QmlProject::refreshTargetDirectory()
|
||||
void QmlBuildSystem::refreshTargetDirectory()
|
||||
{
|
||||
const QList<Target *> targetList = targets();
|
||||
for (Target *target : targetList)
|
||||
updateDeploymentData(target);
|
||||
updateDeploymentData();
|
||||
}
|
||||
|
||||
Tasks QmlProject::projectIssues(const Kit *k) const
|
||||
@@ -324,9 +326,6 @@ Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *erro
|
||||
if (result != RestoreResult::Ok)
|
||||
return result;
|
||||
|
||||
// refresh first - project information is used e.g. to decide the default RC's
|
||||
refresh(Everything);
|
||||
|
||||
if (!activeTarget()) {
|
||||
// find a kit that matches prerequisites (prefer default one)
|
||||
const QList<Kit*> kits = Utils::filtered(KitManager::kits(), [this](const Kit *k) {
|
||||
@@ -341,18 +340,6 @@ Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *erro
|
||||
}
|
||||
}
|
||||
|
||||
// addedTarget calls updateEnabled on the runconfigurations
|
||||
// which needs to happen after refresh
|
||||
foreach (Target *t, targets())
|
||||
addedTarget(t);
|
||||
|
||||
connect(this, &ProjectExplorer::Project::addedTarget, this, &QmlProject::addedTarget);
|
||||
|
||||
connect(this, &ProjectExplorer::Project::activeTargetChanged,
|
||||
this, &QmlProject::onActiveTargetChanged);
|
||||
|
||||
onActiveTargetChanged(activeTarget());
|
||||
|
||||
return RestoreResult::Ok;
|
||||
}
|
||||
|
||||
@@ -361,12 +348,12 @@ ProjectExplorer::DeploymentKnowledge QmlProject::deploymentKnowledge() const
|
||||
return DeploymentKnowledge::Perfect;
|
||||
}
|
||||
|
||||
void QmlProject::generateProjectTree()
|
||||
void QmlBuildSystem::generateProjectTree()
|
||||
{
|
||||
if (!m_projectItem)
|
||||
return;
|
||||
|
||||
auto newRoot = std::make_unique<Internal::QmlProjectNode>(this);
|
||||
auto newRoot = std::make_unique<QmlProjectNode>(project());
|
||||
|
||||
for (const QString &f : m_projectItem.data()->files()) {
|
||||
const Utils::FilePath fileName = Utils::FilePath::fromString(f);
|
||||
@@ -376,16 +363,16 @@ void QmlProject::generateProjectTree()
|
||||
}
|
||||
newRoot->addNestedNode(std::make_unique<FileNode>(projectFilePath(), FileType::Project));
|
||||
|
||||
setRootProjectNode(std::move(newRoot));
|
||||
project()->setRootProjectNode(std::move(newRoot));
|
||||
refreshTargetDirectory();
|
||||
}
|
||||
|
||||
void QmlProject::updateDeploymentData(ProjectExplorer::Target *target)
|
||||
void QmlBuildSystem::updateDeploymentData()
|
||||
{
|
||||
if (!m_projectItem)
|
||||
return;
|
||||
|
||||
if (DeviceTypeKitAspect::deviceTypeId(target->kit())
|
||||
if (DeviceTypeKitAspect::deviceTypeId(target()->kit())
|
||||
== ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
|
||||
return;
|
||||
}
|
||||
@@ -394,10 +381,109 @@ void QmlProject::updateDeploymentData(ProjectExplorer::Target *target)
|
||||
for (const QString &file : m_projectItem->files()) {
|
||||
deploymentData.addFile(
|
||||
file,
|
||||
targetFile(Utils::FilePath::fromString(file), target).parentDir().toString());
|
||||
targetFile(Utils::FilePath::fromString(file)).parentDir().toString());
|
||||
}
|
||||
|
||||
target->setDeploymentData(deploymentData);
|
||||
setDeploymentData(deploymentData);
|
||||
}
|
||||
|
||||
QVariant QmlBuildSystem::additionalData(Id id) const
|
||||
{
|
||||
if (id == Constants::customFileSelectorsData)
|
||||
return customFileSelectors();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
|
||||
{
|
||||
if (dynamic_cast<QmlProjectNode *>(context)) {
|
||||
if (action == AddNewFile || action == EraseFile)
|
||||
return true;
|
||||
QTC_ASSERT(node, return false);
|
||||
|
||||
if (action == Rename && node->asFileNode()) {
|
||||
const FileNode *fileNode = node->asFileNode();
|
||||
QTC_ASSERT(fileNode, return false);
|
||||
return fileNode->fileType() != FileType::Project;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return BuildSystem::supportsAction(context, action, node);
|
||||
}
|
||||
|
||||
QmlProject *QmlBuildSystem::qmlProject() const
|
||||
{
|
||||
return static_cast<QmlProject *>(BuildSystem::project());
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::addFiles(Node *context, const QStringList &filePaths, QStringList *)
|
||||
{
|
||||
if (!dynamic_cast<QmlProjectNode *>(context))
|
||||
return false;
|
||||
|
||||
QStringList toAdd;
|
||||
foreach (const QString &filePath, filePaths) {
|
||||
if (!m_projectItem.data()->matchesFile(filePath))
|
||||
toAdd << filePaths;
|
||||
}
|
||||
return toAdd.isEmpty();
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::deleteFiles(Node *context, const QStringList &filePaths)
|
||||
{
|
||||
if (dynamic_cast<QmlProjectNode *>(context))
|
||||
return true;
|
||||
|
||||
return BuildSystem::deleteFiles(context, filePaths);
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::renameFile(Node * context, const QString &filePath, const QString &newFilePath)
|
||||
{
|
||||
if (dynamic_cast<QmlProjectNode *>(context)) {
|
||||
if (filePath.endsWith(mainFile())) {
|
||||
setMainFile(newFilePath);
|
||||
|
||||
// make sure to change it also in the qmlproject file
|
||||
const QString qmlProjectFilePath = project()->projectFilePath().toString();
|
||||
Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath);
|
||||
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath);
|
||||
TextEditor::TextDocument *document = nullptr;
|
||||
if (!editors.isEmpty()) {
|
||||
document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document());
|
||||
if (document && document->isModified())
|
||||
if (!Core::DocumentManager::saveDocument(document))
|
||||
return false;
|
||||
}
|
||||
|
||||
QString fileContent;
|
||||
QString error;
|
||||
Utils::TextFileFormat textFileFormat;
|
||||
const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
|
||||
if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error)
|
||||
!= Utils::TextFileFormat::ReadSuccess) {
|
||||
qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error;
|
||||
}
|
||||
|
||||
// find the mainFile and do the file name with brackets in a capture group and mask the . with \.
|
||||
QString originalFileName = QFileInfo(filePath).fileName();
|
||||
originalFileName.replace(".", "\\.");
|
||||
const QRegularExpression expression(QString("mainFile:\\s*\"(%1)\"").arg(originalFileName));
|
||||
const QRegularExpressionMatch match = expression.match(fileContent);
|
||||
|
||||
fileContent.replace(match.capturedStart(1), match.capturedLength(1), QFileInfo(newFilePath).fileName());
|
||||
|
||||
if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error))
|
||||
qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error;
|
||||
|
||||
refresh(Everything);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return BuildSystem::renameFile(context, filePath, newFilePath);
|
||||
}
|
||||
|
||||
} // namespace QmlProjectManager
|
||||
|
||||
@@ -35,19 +35,18 @@
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace ProjectExplorer { class RunConfiguration; }
|
||||
|
||||
namespace QmlProjectManager {
|
||||
|
||||
class QmlProject;
|
||||
class QmlProjectItem;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class QmlBuildSystem : public ProjectExplorer::BuildSystem
|
||||
{
|
||||
public:
|
||||
explicit QmlBuildSystem(ProjectExplorer::Project *project) : BuildSystem(project) {}
|
||||
explicit QmlBuildSystem(ProjectExplorer::Target *target);
|
||||
~QmlBuildSystem();
|
||||
|
||||
void triggerParsing() final;
|
||||
|
||||
bool supportsAction(ProjectExplorer::Node *context,
|
||||
ProjectExplorer::ProjectAction action,
|
||||
@@ -59,20 +58,9 @@ public:
|
||||
bool renameFile(ProjectExplorer::Node *context,
|
||||
const QString &filePath, const QString &newFilePath) override;
|
||||
|
||||
QmlProject *project() const;
|
||||
};
|
||||
QmlProject *qmlProject() const;
|
||||
|
||||
} // Internal
|
||||
|
||||
class QMLPROJECTMANAGER_EXPORT QmlProject : public ProjectExplorer::Project
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QmlProject(const Utils::FilePath &filename);
|
||||
~QmlProject() override;
|
||||
|
||||
ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final;
|
||||
QVariant additionalData(Core::Id id) const override;
|
||||
|
||||
enum RefreshOption {
|
||||
ProjectFile = 0x01,
|
||||
@@ -87,9 +75,8 @@ public:
|
||||
Utils::FilePath canonicalProjectDir() const;
|
||||
QString mainFile() const;
|
||||
void setMainFile(const QString &mainFilePath);
|
||||
Utils::FilePath targetDirectory(const ProjectExplorer::Target *target) const;
|
||||
Utils::FilePath targetFile(const Utils::FilePath &sourceFile,
|
||||
const ProjectExplorer::Target *target) const;
|
||||
Utils::FilePath targetDirectory() const;
|
||||
Utils::FilePath targetFile(const Utils::FilePath &sourceFile) const;
|
||||
|
||||
Utils::EnvironmentItems environment() const;
|
||||
QStringList customImportPaths() const;
|
||||
@@ -101,7 +88,28 @@ public:
|
||||
|
||||
static QStringList makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths);
|
||||
|
||||
QVariant additionalData(Core::Id id, const ProjectExplorer::Target *target) const override;
|
||||
void generateProjectTree();
|
||||
void updateDeploymentData();
|
||||
void refreshFiles(const QSet<QString> &added, const QSet<QString> &removed);
|
||||
void refreshTargetDirectory();
|
||||
void onActiveTargetChanged(ProjectExplorer::Target *target);
|
||||
void onKitChanged();
|
||||
|
||||
// plain format
|
||||
void parseProject(RefreshOptions options);
|
||||
|
||||
QPointer<QmlProjectItem> m_projectItem;
|
||||
Utils::FilePath m_canonicalProjectDir;
|
||||
};
|
||||
|
||||
class QMLPROJECTMANAGER_EXPORT QmlProject : public ProjectExplorer::Project
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QmlProject(const Utils::FilePath &filename);
|
||||
|
||||
ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final;
|
||||
|
||||
protected:
|
||||
RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override;
|
||||
@@ -109,23 +117,8 @@ protected:
|
||||
private:
|
||||
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
|
||||
|
||||
void generateProjectTree();
|
||||
void updateDeploymentData(ProjectExplorer::Target *target);
|
||||
void refreshFiles(const QSet<QString> &added, const QSet<QString> &removed);
|
||||
void refreshTargetDirectory();
|
||||
void addedTarget(ProjectExplorer::Target *target);
|
||||
void onActiveTargetChanged(ProjectExplorer::Target *target);
|
||||
void onKitChanged();
|
||||
|
||||
// plain format
|
||||
void parseProject(RefreshOptions options);
|
||||
|
||||
ProjectExplorer::Target *m_activeTarget = nullptr;
|
||||
|
||||
QPointer<QmlProjectItem> m_projectItem;
|
||||
Utils::FilePath m_canonicalProjectDir;
|
||||
};
|
||||
|
||||
} // namespace QmlProjectManager
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QmlProjectManager::QmlProject::RefreshOptions)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QmlProjectManager::QmlBuildSystem::RefreshOptions)
|
||||
|
||||
@@ -24,28 +24,19 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qmlprojectnodes.h"
|
||||
#include "qmlproject.h"
|
||||
|
||||
#include <coreplugin/idocument.h>
|
||||
#include <coreplugin/fileiconprovider.h>
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/editormanager/documentmodel.h>
|
||||
#include <coreplugin/editormanager/ieditor.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/textfileformat.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QTextCodec>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace QmlProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
QmlProjectNode::QmlProjectNode(QmlProject *project) : ProjectNode(project->projectDirectory()),
|
||||
m_project(project)
|
||||
QmlProjectNode::QmlProjectNode(Project *project)
|
||||
: ProjectNode(project->projectDirectory())
|
||||
{
|
||||
setDisplayName(project->projectFilePath().toFileInfo().completeBaseName());
|
||||
|
||||
@@ -53,91 +44,5 @@ QmlProjectNode::QmlProjectNode(QmlProject *project) : ProjectNode(project->proje
|
||||
setIcon(qmlProjectIcon);
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
|
||||
{
|
||||
if (dynamic_cast<QmlProjectNode *>(context)) {
|
||||
if (action == AddNewFile || action == EraseFile)
|
||||
return true;
|
||||
QTC_ASSERT(node, return false);
|
||||
|
||||
if (action == Rename && node->asFileNode()) {
|
||||
const FileNode *fileNode = node->asFileNode();
|
||||
QTC_ASSERT(fileNode, return false);
|
||||
return fileNode->fileType() != FileType::Project;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return BuildSystem::supportsAction(context, action, node);
|
||||
}
|
||||
|
||||
QmlProject *QmlBuildSystem::project() const
|
||||
{
|
||||
return static_cast<QmlProject *>(BuildSystem::project());
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::addFiles(Node *context, const QStringList &filePaths, QStringList *notAdded)
|
||||
{
|
||||
if (dynamic_cast<QmlProjectNode *>(context))
|
||||
return project()->addFiles(filePaths);
|
||||
|
||||
return BuildSystem::addFiles(context, filePaths, notAdded);
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::deleteFiles(Node *context, const QStringList &filePaths)
|
||||
{
|
||||
if (dynamic_cast<QmlProjectNode *>(context))
|
||||
return true;
|
||||
|
||||
return BuildSystem::deleteFiles(context, filePaths);
|
||||
}
|
||||
|
||||
bool QmlBuildSystem::renameFile(Node * context, const QString &filePath, const QString &newFilePath)
|
||||
{
|
||||
if (dynamic_cast<QmlProjectNode *>(context)) {
|
||||
if (filePath.endsWith(project()->mainFile())) {
|
||||
project()->setMainFile(newFilePath);
|
||||
|
||||
// make sure to change it also in the qmlproject file
|
||||
const QString qmlProjectFilePath = project()->projectFilePath().toString();
|
||||
Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath);
|
||||
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath);
|
||||
TextEditor::TextDocument *document = nullptr;
|
||||
if (!editors.isEmpty()) {
|
||||
document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document());
|
||||
if (document && document->isModified())
|
||||
if (!Core::DocumentManager::saveDocument(document))
|
||||
return false;
|
||||
}
|
||||
|
||||
QString fileContent;
|
||||
QString error;
|
||||
Utils::TextFileFormat textFileFormat;
|
||||
const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
|
||||
if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error)
|
||||
!= Utils::TextFileFormat::ReadSuccess) {
|
||||
qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error;
|
||||
}
|
||||
|
||||
// find the mainFile and do the file name with brackets in a capture group and mask the . with \.
|
||||
QString originalFileName = QFileInfo(filePath).fileName();
|
||||
originalFileName.replace(".", "\\.");
|
||||
const QRegularExpression expression(QString("mainFile:\\s*\"(%1)\"").arg(originalFileName));
|
||||
const QRegularExpressionMatch match = expression.match(fileContent);
|
||||
|
||||
fileContent.replace(match.capturedStart(1), match.capturedLength(1), QFileInfo(newFilePath).fileName());
|
||||
|
||||
if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error))
|
||||
qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error;
|
||||
project()->refresh(QmlProject::Everything);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return BuildSystem::renameFile(context, filePath, newFilePath);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QmlProjectManager
|
||||
|
||||
@@ -28,18 +28,12 @@
|
||||
#include <projectexplorer/projectnodes.h>
|
||||
|
||||
namespace QmlProjectManager {
|
||||
|
||||
class QmlProject;
|
||||
|
||||
namespace Internal {
|
||||
|
||||
class QmlProjectNode : public ProjectExplorer::ProjectNode
|
||||
{
|
||||
public:
|
||||
QmlProjectNode(QmlProject *project);
|
||||
|
||||
private:
|
||||
QmlProject *m_project;
|
||||
explicit QmlProjectNode(ProjectExplorer::Project *project);
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -73,7 +73,7 @@ static bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
|
||||
class MainQmlFileAspect : public ProjectConfigurationAspect
|
||||
{
|
||||
public:
|
||||
explicit MainQmlFileAspect(QmlProject *project);
|
||||
explicit MainQmlFileAspect(Target *target);
|
||||
~MainQmlFileAspect() override { delete m_fileListCombo; }
|
||||
|
||||
enum MainScriptSource {
|
||||
@@ -97,7 +97,12 @@ public:
|
||||
bool isQmlFilePresent();
|
||||
|
||||
public:
|
||||
QmlProject *m_project;
|
||||
QmlBuildSystem *qmlBuildSystem() const
|
||||
{
|
||||
return static_cast<QmlBuildSystem *>(m_target->buildSystem());
|
||||
}
|
||||
|
||||
Target *m_target = nullptr;
|
||||
QPointer<QComboBox> m_fileListCombo;
|
||||
QStandardItemModel m_fileListModel;
|
||||
QString m_scriptFile;
|
||||
@@ -107,8 +112,8 @@ public:
|
||||
QString m_mainScriptFilename;
|
||||
};
|
||||
|
||||
MainQmlFileAspect::MainQmlFileAspect(QmlProject *project)
|
||||
: m_project(project)
|
||||
MainQmlFileAspect::MainQmlFileAspect(Target *target)
|
||||
: m_target(target)
|
||||
, m_scriptFile(M_CURRENT_FILE)
|
||||
{
|
||||
connect(EditorManager::instance(), &EditorManager::currentEditorChanged,
|
||||
@@ -153,7 +158,7 @@ void MainQmlFileAspect::fromMap(const QVariantMap &map)
|
||||
|
||||
void MainQmlFileAspect::updateFileComboBox()
|
||||
{
|
||||
QDir projectDir(m_project->projectDirectory().toString());
|
||||
QDir projectDir(m_target->project()->projectDirectory().toString());
|
||||
|
||||
if (mainScriptSource() == FileInProjectFile) {
|
||||
const QString mainScriptInFilePath = projectDir.relativeFilePath(mainScript());
|
||||
@@ -170,7 +175,7 @@ void MainQmlFileAspect::updateFileComboBox()
|
||||
m_fileListModel.appendRow(new QStandardItem(QLatin1String(CURRENT_FILE)));
|
||||
QModelIndex currentIndex;
|
||||
|
||||
QStringList sortedFiles = Utils::transform(m_project->files(Project::SourceFiles),
|
||||
QStringList sortedFiles = Utils::transform(m_target->project()->files(Project::SourceFiles),
|
||||
&Utils::FilePath::toString);
|
||||
|
||||
// make paths relative to project directory
|
||||
@@ -207,7 +212,7 @@ void MainQmlFileAspect::updateFileComboBox()
|
||||
|
||||
MainQmlFileAspect::MainScriptSource MainQmlFileAspect::mainScriptSource() const
|
||||
{
|
||||
if (!m_project->mainFile().isEmpty())
|
||||
if (!qmlBuildSystem()->mainFile().isEmpty())
|
||||
return FileInProjectFile;
|
||||
if (!m_mainScriptFilename.isEmpty())
|
||||
return FileInSettings;
|
||||
@@ -234,7 +239,7 @@ void MainQmlFileAspect::setScriptSource(MainScriptSource source, const QString &
|
||||
m_mainScriptFilename.clear();
|
||||
} else { // FileInSettings
|
||||
m_scriptFile = settingsPath;
|
||||
m_mainScriptFilename = m_project->projectDirectory().toString() + '/' + m_scriptFile;
|
||||
m_mainScriptFilename = m_target->project()->projectDirectory().toString() + '/' + m_scriptFile;
|
||||
}
|
||||
|
||||
emit changed();
|
||||
@@ -246,12 +251,12 @@ void MainQmlFileAspect::setScriptSource(MainScriptSource source, const QString &
|
||||
*/
|
||||
QString MainQmlFileAspect::mainScript() const
|
||||
{
|
||||
if (!m_project->mainFile().isEmpty()) {
|
||||
const QString pathInProject = m_project->mainFile();
|
||||
if (!qmlBuildSystem()->mainFile().isEmpty()) {
|
||||
const QString pathInProject = qmlBuildSystem()->mainFile();
|
||||
if (QFileInfo(pathInProject).isAbsolute())
|
||||
return pathInProject;
|
||||
else
|
||||
return QDir(m_project->canonicalProjectDir().toString()).absoluteFilePath(pathInProject);
|
||||
return QDir(qmlBuildSystem()->canonicalProjectDir().toString()).absoluteFilePath(pathInProject);
|
||||
}
|
||||
|
||||
if (!m_mainScriptFilename.isEmpty())
|
||||
@@ -279,9 +284,9 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id)
|
||||
{
|
||||
auto envAspect = addAspect<EnvironmentAspect>();
|
||||
|
||||
auto envModifier = [target](Environment env) {
|
||||
if (auto project = qobject_cast<const QmlProject *>(target->project()))
|
||||
env.modify(project->environment());
|
||||
auto envModifier = [this](Environment env) {
|
||||
if (auto bs = dynamic_cast<const QmlBuildSystem *>(activeBuildSystem()))
|
||||
env.modify(bs->environment());
|
||||
return env;
|
||||
};
|
||||
|
||||
@@ -311,9 +316,7 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id)
|
||||
CommandLine::Raw);
|
||||
});
|
||||
|
||||
auto qmlProject = qobject_cast<QmlProject *>(target->project());
|
||||
QTC_ASSERT(qmlProject, return);
|
||||
m_mainQmlFileAspect = addAspect<MainQmlFileAspect>(qmlProject);
|
||||
m_mainQmlFileAspect = addAspect<MainQmlFileAspect>(target);
|
||||
connect(m_mainQmlFileAspect, &MainQmlFileAspect::changed,
|
||||
this, &QmlProjectRunConfiguration::updateEnabledState);
|
||||
|
||||
@@ -329,7 +332,8 @@ Runnable QmlProjectRunConfiguration::runnable() const
|
||||
Runnable r;
|
||||
r.setCommandLine(commandLine());
|
||||
r.environment = aspect<EnvironmentAspect>()->environment();
|
||||
r.workingDirectory = static_cast<QmlProject *>(project())->targetDirectory(target()).toString();
|
||||
const QmlBuildSystem *bs = static_cast<QmlBuildSystem *>(activeBuildSystem());
|
||||
r.workingDirectory = bs->targetDirectory().toString();
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -377,38 +381,35 @@ QString QmlProjectRunConfiguration::commandLineArguments() const
|
||||
{
|
||||
// arguments in .user file
|
||||
QString args = aspect<ArgumentsAspect>()->arguments(macroExpander());
|
||||
const Target *currentTarget = target();
|
||||
const IDevice::ConstPtr device = DeviceKitAspect::device(currentTarget->kit());
|
||||
const IDevice::ConstPtr device = DeviceKitAspect::device(target()->kit());
|
||||
const Utils::OsType osType = device ? device->osType() : Utils::HostOsInfo::hostOs();
|
||||
|
||||
// arguments from .qmlproject file
|
||||
const QmlProject *project = static_cast<QmlProject *>(currentTarget->project());
|
||||
const QmlBuildSystem *bs = static_cast<QmlBuildSystem *>(target()->buildSystem());
|
||||
foreach (const QString &importPath,
|
||||
QmlProject::makeAbsolute(project->targetDirectory(currentTarget), project->customImportPaths())) {
|
||||
QmlBuildSystem::makeAbsolute(bs->targetDirectory(), bs->customImportPaths())) {
|
||||
Utils::QtcProcess::addArg(&args, QLatin1String("-I"), osType);
|
||||
Utils::QtcProcess::addArg(&args, importPath, osType);
|
||||
}
|
||||
|
||||
for (const QString &fileSelector : project->customFileSelectors()) {
|
||||
for (const QString &fileSelector : bs->customFileSelectors()) {
|
||||
Utils::QtcProcess::addArg(&args, QLatin1String("-S"), osType);
|
||||
Utils::QtcProcess::addArg(&args, fileSelector, osType);
|
||||
}
|
||||
|
||||
const QString main = project->targetFile(Utils::FilePath::fromString(mainScript()),
|
||||
currentTarget).toString();
|
||||
const QString main = bs->targetFile(FilePath::fromString(mainScript())).toString();
|
||||
if (!main.isEmpty())
|
||||
Utils::QtcProcess::addArg(&args, main, osType);
|
||||
return args;
|
||||
}
|
||||
|
||||
void QmlProjectRunConfiguration::updateEnabledState()
|
||||
bool QmlProjectRunConfiguration::isEnabled() const
|
||||
{
|
||||
bool enabled = false;
|
||||
if (m_mainQmlFileAspect->isQmlFilePresent() && !commandLine().executable().isEmpty()) {
|
||||
Project *p = target()->project();
|
||||
enabled = !p->isParsing() && p->hasParsingData();
|
||||
BuildSystem *bs = activeBuildSystem();
|
||||
return !bs->isParsing() && bs->hasParsingData();
|
||||
}
|
||||
setEnabled(enabled);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MainQmlFileAspect::isQmlFilePresent()
|
||||
@@ -430,7 +431,7 @@ bool MainQmlFileAspect::isQmlFilePresent()
|
||||
|| mainScriptMimeType.matchesName(QLatin1String(QmlJSTools::Constants::QMLPROJECT_MIMETYPE))) {
|
||||
// find a qml file with lowercase filename. This is slow, but only done
|
||||
// in initialization/other border cases.
|
||||
const auto files = m_project->files(Project::SourceFiles);
|
||||
const auto files = m_target->project()->files(Project::SourceFiles);
|
||||
for (const Utils::FilePath &filename : files) {
|
||||
const QFileInfo fi = filename.toFileInfo();
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
private:
|
||||
ProjectExplorer::Runnable runnable() const final;
|
||||
QString disabledReason() const final;
|
||||
void updateEnabledState() final;
|
||||
bool isEnabled() const final;
|
||||
|
||||
QString mainScript() const;
|
||||
QString theExecutable() const;
|
||||
|
||||
Reference in New Issue
Block a user