2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2010-12-17 16:01:08 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2008-12-02 16:19:05 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "project.h"
|
2008-12-02 16:19:05 +01:00
|
|
|
|
2012-04-24 15:49:09 +02:00
|
|
|
#include "buildconfiguration.h"
|
2019-08-27 15:56:25 +02:00
|
|
|
#include "buildinfo.h"
|
2019-08-09 11:22:49 +02:00
|
|
|
#include "buildsystem.h"
|
2015-11-16 16:52:36 +01:00
|
|
|
#include "deployconfiguration.h"
|
2010-02-08 15:50:06 +01:00
|
|
|
#include "editorconfiguration.h"
|
2015-11-16 16:52:36 +01:00
|
|
|
#include "kit.h"
|
2020-03-10 15:52:15 +01:00
|
|
|
#include "kitinformation.h"
|
2019-04-12 14:49:59 +02:00
|
|
|
#include "makestep.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "projectexplorer.h"
|
2020-01-15 16:47:54 +01:00
|
|
|
#include "projectmacroexpander.h"
|
2016-01-08 12:49:00 +01:00
|
|
|
#include "projectnodes.h"
|
2019-08-27 15:56:25 +02:00
|
|
|
#include "runconfiguration.h"
|
|
|
|
|
#include "runcontrol.h"
|
2015-07-28 18:29:52 +02:00
|
|
|
#include "session.h"
|
2019-08-27 15:56:25 +02:00
|
|
|
#include "target.h"
|
2020-03-10 15:52:15 +01:00
|
|
|
#include "taskhub.h"
|
2017-11-15 14:14:38 +01:00
|
|
|
#include "userfileaccessor.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-02-14 16:43:51 +01:00
|
|
|
#include <coreplugin/idocument.h>
|
2017-03-29 11:11:49 +02:00
|
|
|
#include <coreplugin/documentmanager.h>
|
2011-04-12 12:17:19 +02:00
|
|
|
#include <coreplugin/icontext.h>
|
2014-02-25 18:10:42 +01:00
|
|
|
#include <coreplugin/icore.h>
|
2017-03-17 12:26:00 +01:00
|
|
|
#include <coreplugin/iversioncontrol.h>
|
|
|
|
|
#include <coreplugin/vcsmanager.h>
|
2021-05-07 09:19:39 +02:00
|
|
|
#include <coreplugin/editormanager/documentmodel.h>
|
2017-03-17 12:26:00 +01:00
|
|
|
|
2011-10-24 13:10:38 +00:00
|
|
|
#include <projectexplorer/buildmanager.h>
|
2012-09-03 18:31:44 +02:00
|
|
|
#include <projectexplorer/kitmanager.h>
|
2017-03-10 10:20:53 +01:00
|
|
|
#include <projectexplorer/projecttree.h>
|
2014-11-15 20:16:53 +01:00
|
|
|
|
2019-08-15 12:44:13 +02:00
|
|
|
#include <utils/algorithm.h>
|
2019-07-05 16:58:07 +02:00
|
|
|
#include <utils/environment.h>
|
2014-11-15 20:16:53 +01:00
|
|
|
#include <utils/macroexpander.h>
|
2019-07-05 16:58:07 +02:00
|
|
|
#include <utils/pointeralgorithm.h>
|
2014-11-15 20:16:53 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2020-03-10 15:52:15 +01:00
|
|
|
#include <utils/stringutils.h>
|
2014-11-15 20:16:53 +01:00
|
|
|
|
2019-03-14 15:34:14 +01:00
|
|
|
#include <QFileDialog>
|
|
|
|
|
|
2014-11-15 20:16:53 +01:00
|
|
|
#include <limits>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-04-14 12:58:14 +02:00
|
|
|
/*!
|
|
|
|
|
\class ProjectExplorer::Project
|
|
|
|
|
|
2013-06-05 14:29:24 +02:00
|
|
|
\brief The Project class implements a project node in the project explorer.
|
2011-04-14 12:58:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
\fn void ProjectExplorer::Project::environmentChanged()
|
|
|
|
|
|
2013-09-10 17:16:10 +02:00
|
|
|
A convenience signal emitted if activeBuildConfiguration emits
|
|
|
|
|
environmentChanged or if the active build configuration changes
|
|
|
|
|
(including due to the active target changing).
|
2011-04-14 12:58:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
\fn void ProjectExplorer::Project::buildConfigurationEnabledChanged()
|
|
|
|
|
|
2013-09-10 17:16:10 +02:00
|
|
|
A convenience signal emitted if activeBuildConfiguration emits
|
|
|
|
|
isEnabledChanged() or if the active build configuration changes
|
|
|
|
|
(including due to the active target changing).
|
2011-04-14 12:58:14 +02:00
|
|
|
*/
|
|
|
|
|
|
2010-01-14 19:08:43 +01:00
|
|
|
namespace {
|
2012-06-28 17:17:43 +02:00
|
|
|
const char ACTIVE_TARGET_KEY[] = "ProjectExplorer.Project.ActiveTarget";
|
|
|
|
|
const char TARGET_KEY_PREFIX[] = "ProjectExplorer.Project.Target.";
|
|
|
|
|
const char TARGET_COUNT_KEY[] = "ProjectExplorer.Project.TargetCount";
|
|
|
|
|
const char EDITOR_SETTINGS_KEY[] = "ProjectExplorer.Project.EditorSettings";
|
|
|
|
|
const char PLUGIN_SETTINGS_KEY[] = "ProjectExplorer.Project.PluginSettings";
|
2019-12-10 17:09:49 +01:00
|
|
|
|
|
|
|
|
const char PROJECT_ENV_KEY[] = "ProjectExplorer.Project.Environment";
|
2010-01-14 19:08:43 +01:00
|
|
|
} // namespace
|
|
|
|
|
|
2010-09-21 09:17:37 +02:00
|
|
|
namespace ProjectExplorer {
|
2017-03-17 12:26:00 +01:00
|
|
|
|
2017-12-06 10:58:59 +01:00
|
|
|
static bool isListedFileNode(const Node *node)
|
|
|
|
|
{
|
2018-04-11 14:35:03 +02:00
|
|
|
return node->asContainerNode() || node->listInProject();
|
2017-12-06 10:58:59 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-04 12:58:10 +01:00
|
|
|
static bool nodeLessThan(const Node *n1, const Node *n2)
|
|
|
|
|
{
|
|
|
|
|
return n1->filePath() < n2->filePath();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-06 10:58:59 +01:00
|
|
|
const Project::NodeMatcher Project::AllFiles = [](const Node *node) {
|
|
|
|
|
return isListedFileNode(node);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Project::NodeMatcher Project::SourceFiles = [](const Node *node) {
|
|
|
|
|
return isListedFileNode(node) && !node->isGenerated();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Project::NodeMatcher Project::GeneratedFiles = [](const Node *node) {
|
|
|
|
|
return isListedFileNode(node) && node->isGenerated();
|
|
|
|
|
};
|
|
|
|
|
|
2017-03-29 11:11:49 +02:00
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
// ProjectDocument:
|
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
|
2019-08-15 12:44:13 +02:00
|
|
|
class ProjectDocument : public Core::IDocument
|
2017-03-29 11:11:49 +02:00
|
|
|
{
|
2019-08-15 12:44:13 +02:00
|
|
|
public:
|
|
|
|
|
ProjectDocument(const QString &mimeType, const Utils::FilePath &fileName, Project *project);
|
|
|
|
|
|
|
|
|
|
Core::IDocument::ReloadBehavior reloadBehavior(Core::IDocument::ChangeTrigger state,
|
|
|
|
|
Core::IDocument::ChangeType type) const final;
|
|
|
|
|
bool reload(QString *errorString,
|
|
|
|
|
Core::IDocument::ReloadFlag flag,
|
|
|
|
|
Core::IDocument::ChangeType type) final;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Project *m_project;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ProjectDocument::ProjectDocument(const QString &mimeType,
|
|
|
|
|
const Utils::FilePath &fileName,
|
|
|
|
|
Project *project)
|
|
|
|
|
: m_project(project)
|
|
|
|
|
{
|
|
|
|
|
QTC_CHECK(project);
|
|
|
|
|
|
2017-03-29 11:11:49 +02:00
|
|
|
setFilePath(fileName);
|
|
|
|
|
setMimeType(mimeType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Core::IDocument::ReloadBehavior
|
|
|
|
|
ProjectDocument::reloadBehavior(Core::IDocument::ChangeTrigger state,
|
|
|
|
|
Core::IDocument::ChangeType type) const
|
|
|
|
|
{
|
2019-07-23 10:58:00 +02:00
|
|
|
Q_UNUSED(state)
|
|
|
|
|
Q_UNUSED(type)
|
2017-03-29 11:11:49 +02:00
|
|
|
return BehaviorSilent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ProjectDocument::reload(QString *errorString, Core::IDocument::ReloadFlag flag,
|
|
|
|
|
Core::IDocument::ChangeType type)
|
|
|
|
|
{
|
2019-07-23 10:58:00 +02:00
|
|
|
Q_UNUSED(errorString)
|
|
|
|
|
Q_UNUSED(flag)
|
|
|
|
|
Q_UNUSED(type)
|
2017-03-29 11:11:49 +02:00
|
|
|
|
2019-08-15 12:44:13 +02:00
|
|
|
emit m_project->projectFileIsDirty(filePath());
|
2017-03-29 11:11:49 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-14 19:08:43 +01:00
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
|
// Project
|
|
|
|
|
// -------------------------------------------------------------------------
|
2011-04-12 12:17:19 +02:00
|
|
|
class ProjectPrivate
|
|
|
|
|
{
|
2010-09-21 09:17:37 +02:00
|
|
|
public:
|
2012-08-17 13:18:31 +02:00
|
|
|
~ProjectPrivate();
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id m_id;
|
2018-12-12 16:10:34 +01:00
|
|
|
bool m_needsInitialExpansion = false;
|
2019-06-03 17:36:02 +02:00
|
|
|
bool m_canBuildProducts = false;
|
2019-08-13 12:11:07 +02:00
|
|
|
bool m_hasMakeInstallEquivalent = false;
|
|
|
|
|
bool m_needsBuildConfigurations = true;
|
2019-10-25 09:55:32 +02:00
|
|
|
bool m_needsDeployConfigurations = true;
|
2021-05-05 11:18:51 +02:00
|
|
|
bool m_shuttingDown = false;
|
2019-10-25 09:55:32 +02:00
|
|
|
|
|
|
|
|
std::function<BuildSystem *(Target *)> m_buildSystemCreator;
|
|
|
|
|
|
2017-06-14 17:33:05 +02:00
|
|
|
std::unique_ptr<Core::IDocument> m_document;
|
2019-08-15 12:42:26 +02:00
|
|
|
std::vector<std::unique_ptr<Core::IDocument>> m_extraProjectDocuments;
|
2018-03-14 15:21:51 +01:00
|
|
|
std::unique_ptr<ProjectNode> m_rootProjectNode;
|
2017-06-14 17:33:05 +02:00
|
|
|
std::unique_ptr<ContainerNode> m_containerNode;
|
2018-05-24 12:57:00 +02:00
|
|
|
std::vector<std::unique_ptr<Target>> m_targets;
|
2016-04-13 15:52:14 +02:00
|
|
|
Target *m_activeTarget = nullptr;
|
2014-11-06 15:49:52 +01:00
|
|
|
EditorConfiguration m_editorConfiguration;
|
2012-11-21 16:18:53 +01:00
|
|
|
Core::Context m_projectLanguages;
|
2011-08-01 13:21:01 +00:00
|
|
|
QVariantMap m_pluginSettings;
|
2017-06-14 17:33:05 +02:00
|
|
|
std::unique_ptr<Internal::UserFileAccessor> m_accessor;
|
2014-07-23 09:09:20 +02:00
|
|
|
|
2017-04-07 14:35:49 +02:00
|
|
|
QString m_displayName;
|
|
|
|
|
|
2014-11-15 20:16:53 +01:00
|
|
|
Utils::MacroExpander m_macroExpander;
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath m_rootProjectDirectory;
|
2017-12-04 12:58:10 +01:00
|
|
|
mutable QVector<const Node *> m_sortedNodeList;
|
2020-01-13 17:38:33 +01:00
|
|
|
|
|
|
|
|
QVariantMap m_extraData;
|
2010-09-21 09:17:37 +02:00
|
|
|
};
|
|
|
|
|
|
2012-08-17 13:18:31 +02:00
|
|
|
ProjectPrivate::~ProjectPrivate()
|
2016-01-08 11:09:37 +01:00
|
|
|
{
|
2018-03-14 15:21:51 +01:00
|
|
|
// Make sure our root node is null when deleting the actual node
|
|
|
|
|
std::unique_ptr<ProjectNode> oldNode = std::move(m_rootProjectNode);
|
2016-01-08 11:09:37 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2019-08-15 12:42:26 +02:00
|
|
|
Project::Project(const QString &mimeType,
|
2019-08-15 12:44:13 +02:00
|
|
|
const Utils::FilePath &fileName)
|
2019-08-15 12:42:26 +02:00
|
|
|
: d(new ProjectPrivate)
|
2014-11-15 20:16:53 +01:00
|
|
|
{
|
2019-08-15 12:44:13 +02:00
|
|
|
d->m_document = std::make_unique<ProjectDocument>(mimeType, fileName, this);
|
2021-06-10 12:38:46 +02:00
|
|
|
Core::DocumentManager::addDocument(d->m_document.get());
|
2019-08-15 12:42:26 +02:00
|
|
|
|
2014-11-15 20:16:53 +01:00
|
|
|
d->m_macroExpander.setDisplayName(tr("Project"));
|
|
|
|
|
d->m_macroExpander.registerVariable("Project:Name", tr("Project Name"),
|
|
|
|
|
[this] { return displayName(); });
|
2017-03-29 11:29:48 +02:00
|
|
|
|
|
|
|
|
// Only set up containernode after d is set so that it will find the project directory!
|
2017-06-14 17:33:05 +02:00
|
|
|
d->m_containerNode = std::make_unique<ContainerNode>(this);
|
2014-11-15 20:16:53 +01:00
|
|
|
}
|
2010-09-21 09:17:37 +02:00
|
|
|
|
2009-01-16 16:30:22 +01:00
|
|
|
Project::~Project()
|
|
|
|
|
{
|
2011-09-16 15:00:41 +02:00
|
|
|
delete d;
|
2009-01-16 16:30:22 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-07 14:35:49 +02:00
|
|
|
QString Project::displayName() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_displayName;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id Project::id() const
|
2013-09-27 16:30:20 +02:00
|
|
|
{
|
|
|
|
|
QTC_CHECK(d->m_id.isValid());
|
|
|
|
|
return d->m_id;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 11:18:51 +02:00
|
|
|
void Project::markAsShuttingDown()
|
|
|
|
|
{
|
|
|
|
|
d->m_shuttingDown = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Project::isShuttingDown() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_shuttingDown;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 12:38:05 +01:00
|
|
|
QString Project::mimeType() const
|
|
|
|
|
{
|
2019-08-16 09:57:59 +02:00
|
|
|
return d->m_document->mimeType();
|
2018-01-10 12:38:05 +01:00
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:36:02 +02:00
|
|
|
bool Project::canBuildProducts() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_canBuildProducts;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
BuildSystem *Project::createBuildSystem(Target *target) const
|
2019-08-09 11:22:49 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
return d->m_buildSystemCreator ? d->m_buildSystemCreator(target) : nullptr;
|
2019-08-09 11:22:49 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath Project::projectFilePath() const
|
2013-07-23 12:30:17 +02:00
|
|
|
{
|
2019-08-16 09:57:59 +02:00
|
|
|
QTC_ASSERT(d->m_document, return Utils::FilePath());
|
|
|
|
|
return d->m_document->filePath();
|
2013-07-23 12:30:17 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-24 12:57:00 +02:00
|
|
|
void Project::addTarget(std::unique_ptr<Target> &&t)
|
2010-02-08 15:50:06 +01:00
|
|
|
{
|
2018-05-24 12:57:00 +02:00
|
|
|
auto pointer = t.get();
|
|
|
|
|
QTC_ASSERT(t && !Utils::contains(d->m_targets, pointer), return);
|
2012-09-03 18:31:44 +02:00
|
|
|
QTC_ASSERT(!target(t->kit()), return);
|
2010-02-08 15:50:06 +01:00
|
|
|
Q_ASSERT(t->project() == this);
|
2010-01-15 10:54:29 +01:00
|
|
|
|
2009-09-24 16:02:02 +02:00
|
|
|
// add it
|
2018-05-24 12:57:00 +02:00
|
|
|
d->m_targets.emplace_back(std::move(t));
|
|
|
|
|
emit addedTarget(pointer);
|
2010-02-08 15:50:06 +01:00
|
|
|
|
|
|
|
|
// check activeTarget:
|
2016-04-13 15:52:14 +02:00
|
|
|
if (!activeTarget())
|
2018-05-30 16:54:41 +02:00
|
|
|
SessionManager::setActiveTarget(this, pointer, SetActive::Cascade);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2019-08-01 14:58:00 +02:00
|
|
|
Target *Project::addTargetForDefaultKit()
|
|
|
|
|
{
|
|
|
|
|
return addTargetForKit(KitManager::defaultKit());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Target *Project::addTargetForKit(Kit *kit)
|
|
|
|
|
{
|
|
|
|
|
if (!kit || target(kit))
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
auto t = std::make_unique<Target>(this, kit, Target::_constructor_tag{});
|
|
|
|
|
Target *pointer = t.get();
|
|
|
|
|
|
|
|
|
|
if (!setupTarget(pointer))
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
addTarget(std::move(t));
|
|
|
|
|
|
|
|
|
|
return pointer;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-24 13:10:38 +00:00
|
|
|
bool Project::removeTarget(Target *target)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2018-05-24 12:57:00 +02:00
|
|
|
QTC_ASSERT(target && Utils::contains(d->m_targets, target), return false);
|
2011-10-24 13:10:38 +00:00
|
|
|
|
2013-09-05 14:36:20 +02:00
|
|
|
if (BuildManager::isBuilding(target))
|
2011-10-24 13:10:38 +00:00
|
|
|
return false;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2021-05-05 11:18:51 +02:00
|
|
|
target->markAsShuttingDown();
|
2011-12-06 15:13:50 +01:00
|
|
|
emit aboutToRemoveTarget(target);
|
2018-05-24 12:57:00 +02:00
|
|
|
auto keep = Utils::take(d->m_targets, target);
|
2018-05-29 15:43:42 +02:00
|
|
|
if (target == d->m_activeTarget) {
|
|
|
|
|
Target *newActiveTarget = (d->m_targets.size() == 0 ? nullptr : d->m_targets.at(0).get());
|
|
|
|
|
SessionManager::setActiveTarget(this, newActiveTarget, SetActive::Cascade);
|
|
|
|
|
}
|
2011-12-06 15:13:50 +01:00
|
|
|
emit removedTarget(target);
|
|
|
|
|
|
2011-11-03 14:13:23 +01:00
|
|
|
return true;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-03 14:57:21 +01:00
|
|
|
const QList<Target *> Project::targets() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2018-05-24 12:57:00 +02:00
|
|
|
return Utils::toRawPointer<QList>(d->m_targets);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-02-08 15:50:06 +01:00
|
|
|
Target *Project::activeTarget() const
|
2010-01-19 16:33:44 +01:00
|
|
|
{
|
2010-09-21 09:17:37 +02:00
|
|
|
return d->m_activeTarget;
|
2010-01-19 16:33:44 +01:00
|
|
|
}
|
|
|
|
|
|
2010-02-08 15:50:06 +01:00
|
|
|
void Project::setActiveTarget(Target *target)
|
2010-01-19 16:33:44 +01:00
|
|
|
{
|
2018-05-29 15:43:42 +02:00
|
|
|
if (d->m_activeTarget == target)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Allow to set nullptr just before the last target is removed or when no target exists.
|
|
|
|
|
if ((!target && d->m_targets.size() == 0) ||
|
|
|
|
|
(target && Utils::contains(d->m_targets, target))) {
|
2010-09-21 09:17:37 +02:00
|
|
|
d->m_activeTarget = target;
|
|
|
|
|
emit activeTargetChanged(d->m_activeTarget);
|
2020-02-28 15:54:54 +01:00
|
|
|
ProjectExplorerPlugin::updateActions();
|
2010-02-08 15:50:06 +01:00
|
|
|
}
|
2010-01-19 16:33:44 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-12 16:10:34 +01:00
|
|
|
bool Project::needsInitialExpansion() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_needsInitialExpansion;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Project::setNeedsInitialExpansion(bool needsExpansion)
|
|
|
|
|
{
|
|
|
|
|
d->m_needsInitialExpansion = needsExpansion;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-10 10:39:56 +02:00
|
|
|
void Project::setExtraProjectFiles(const QSet<Utils::FilePath> &projectDocumentPaths,
|
2020-09-24 11:36:10 +02:00
|
|
|
const DocGenerator &docGenerator,
|
|
|
|
|
const DocUpdater &docUpdater)
|
2019-08-15 12:42:26 +02:00
|
|
|
{
|
2020-03-02 18:34:12 +01:00
|
|
|
QSet<Utils::FilePath> uniqueNewFiles = projectDocumentPaths;
|
2019-08-15 12:42:26 +02:00
|
|
|
uniqueNewFiles.remove(projectFilePath()); // Make sure to never add the main project file!
|
|
|
|
|
|
|
|
|
|
QSet<Utils::FilePath> existingWatches = Utils::transform<QSet>(d->m_extraProjectDocuments,
|
|
|
|
|
&Core::IDocument::filePath);
|
|
|
|
|
|
2020-03-02 15:13:25 +01:00
|
|
|
const QSet<Utils::FilePath> toAdd = uniqueNewFiles - existingWatches;
|
|
|
|
|
const QSet<Utils::FilePath> toRemove = existingWatches - uniqueNewFiles;
|
2019-08-15 12:42:26 +02:00
|
|
|
|
|
|
|
|
Utils::erase(d->m_extraProjectDocuments, [&toRemove](const std::unique_ptr<Core::IDocument> &d) {
|
|
|
|
|
return toRemove.contains(d->filePath());
|
|
|
|
|
});
|
2020-09-24 11:36:10 +02:00
|
|
|
if (docUpdater) {
|
|
|
|
|
for (const auto &doc : qAsConst(d->m_extraProjectDocuments))
|
|
|
|
|
docUpdater(doc.get());
|
|
|
|
|
}
|
2021-06-10 12:38:46 +02:00
|
|
|
QList<Core::IDocument *> toRegister;
|
2019-08-15 12:42:26 +02:00
|
|
|
for (const Utils::FilePath &p : toAdd) {
|
2020-09-10 10:39:56 +02:00
|
|
|
if (docGenerator) {
|
|
|
|
|
std::unique_ptr<Core::IDocument> doc = docGenerator(p);
|
|
|
|
|
QTC_ASSERT(doc, continue);
|
|
|
|
|
d->m_extraProjectDocuments.push_back(std::move(doc));
|
|
|
|
|
} else {
|
2021-06-10 12:38:46 +02:00
|
|
|
auto document = std::make_unique<ProjectDocument>(d->m_document->mimeType(), p, this);
|
|
|
|
|
toRegister.append(document.get());
|
|
|
|
|
d->m_extraProjectDocuments.emplace_back(std::move(document));
|
2020-09-10 10:39:56 +02:00
|
|
|
}
|
2019-08-15 12:42:26 +02:00
|
|
|
}
|
2021-06-10 12:38:46 +02:00
|
|
|
Core::DocumentManager::addDocuments(toRegister);
|
2019-08-15 12:42:26 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-12 11:00:14 +02:00
|
|
|
void Project::updateExtraProjectFiles(const QSet<Utils::FilePath> &projectDocumentPaths,
|
|
|
|
|
const DocUpdater &docUpdater)
|
|
|
|
|
{
|
|
|
|
|
for (const Utils::FilePath &fp : projectDocumentPaths) {
|
|
|
|
|
for (const auto &doc : d->m_extraProjectDocuments) {
|
|
|
|
|
if (doc->filePath() == fp) {
|
|
|
|
|
docUpdater(doc.get());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Project::updateExtraProjectFiles(const DocUpdater &docUpdater)
|
|
|
|
|
{
|
|
|
|
|
for (const auto &doc : qAsConst(d->m_extraProjectDocuments))
|
|
|
|
|
docUpdater(doc.get());
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
Target *Project::target(Utils::Id id) const
|
2009-05-06 16:33:43 +02:00
|
|
|
{
|
2014-07-10 12:57:06 +02:00
|
|
|
return Utils::findOrDefault(d->m_targets, Utils::equal(&Target::id, id));
|
2009-05-06 16:33:43 +02:00
|
|
|
}
|
|
|
|
|
|
2012-09-03 18:31:44 +02:00
|
|
|
Target *Project::target(Kit *k) const
|
2012-04-24 15:49:09 +02:00
|
|
|
{
|
2014-07-10 12:57:06 +02:00
|
|
|
return Utils::findOrDefault(d->m_targets, Utils::equal(&Target::kit, k));
|
2012-04-24 15:49:09 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-27 16:09:44 +02:00
|
|
|
Tasks Project::projectIssues(const Kit *k) const
|
2012-04-24 15:49:09 +02:00
|
|
|
{
|
2019-05-27 16:09:44 +02:00
|
|
|
Tasks result;
|
2018-04-18 13:39:05 +02:00
|
|
|
if (!k->isValid())
|
|
|
|
|
result.append(createProjectTask(Task::TaskType::Error, tr("Kit is not valid.")));
|
|
|
|
|
return {};
|
2012-04-24 15:49:09 +02:00
|
|
|
}
|
|
|
|
|
|
2016-07-22 15:53:01 +02:00
|
|
|
bool Project::copySteps(Target *sourceTarget, Target *newTarget)
|
2015-11-16 16:52:36 +01:00
|
|
|
{
|
2017-01-19 10:22:38 +01:00
|
|
|
QTC_ASSERT(newTarget, return false);
|
2016-07-22 15:53:01 +02:00
|
|
|
bool fatalError = false;
|
2015-11-16 16:52:36 +01:00
|
|
|
QStringList buildconfigurationError;
|
|
|
|
|
QStringList deployconfigurationError;
|
|
|
|
|
QStringList runconfigurationError;
|
|
|
|
|
|
2020-01-15 16:47:54 +01:00
|
|
|
const Project * const project = newTarget->project();
|
2020-02-28 16:58:37 +01:00
|
|
|
for (BuildConfiguration *sourceBc : sourceTarget->buildConfigurations()) {
|
2020-01-15 16:47:54 +01:00
|
|
|
ProjectMacroExpander expander(project->projectFilePath(), project->displayName(),
|
|
|
|
|
newTarget->kit(), sourceBc->displayName(),
|
|
|
|
|
sourceBc->buildType());
|
2019-01-29 08:55:52 +01:00
|
|
|
BuildConfiguration *newBc = BuildConfigurationFactory::clone(newTarget, sourceBc);
|
2015-11-16 16:52:36 +01:00
|
|
|
if (!newBc) {
|
|
|
|
|
buildconfigurationError << sourceBc->displayName();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
newBc->setDisplayName(sourceBc->displayName());
|
2020-01-15 16:47:54 +01:00
|
|
|
newBc->setBuildDirectory(project->projectDirectory()
|
|
|
|
|
.resolvePath(expander.expand(ProjectExplorerPlugin::buildDirectoryTemplate())));
|
2015-11-16 16:52:36 +01:00
|
|
|
newTarget->addBuildConfiguration(newBc);
|
|
|
|
|
if (sourceTarget->activeBuildConfiguration() == sourceBc)
|
|
|
|
|
SessionManager::setActiveBuildConfiguration(newTarget, newBc, SetActive::NoCascade);
|
|
|
|
|
}
|
|
|
|
|
if (!newTarget->activeBuildConfiguration()) {
|
|
|
|
|
QList<BuildConfiguration *> bcs = newTarget->buildConfigurations();
|
|
|
|
|
if (!bcs.isEmpty())
|
|
|
|
|
SessionManager::setActiveBuildConfiguration(newTarget, bcs.first(), SetActive::NoCascade);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 16:58:37 +01:00
|
|
|
for (DeployConfiguration *sourceDc : sourceTarget->deployConfigurations()) {
|
2019-01-23 09:18:42 +01:00
|
|
|
DeployConfiguration *newDc = DeployConfigurationFactory::clone(newTarget, sourceDc);
|
2015-11-16 16:52:36 +01:00
|
|
|
if (!newDc) {
|
|
|
|
|
deployconfigurationError << sourceDc->displayName();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
newDc->setDisplayName(sourceDc->displayName());
|
|
|
|
|
newTarget->addDeployConfiguration(newDc);
|
|
|
|
|
if (sourceTarget->activeDeployConfiguration() == sourceDc)
|
|
|
|
|
SessionManager::setActiveDeployConfiguration(newTarget, newDc, SetActive::NoCascade);
|
|
|
|
|
}
|
|
|
|
|
if (!newTarget->activeBuildConfiguration()) {
|
|
|
|
|
QList<DeployConfiguration *> dcs = newTarget->deployConfigurations();
|
|
|
|
|
if (!dcs.isEmpty())
|
|
|
|
|
SessionManager::setActiveDeployConfiguration(newTarget, dcs.first(), SetActive::NoCascade);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 16:58:37 +01:00
|
|
|
for (RunConfiguration *sourceRc : sourceTarget->runConfigurations()) {
|
2018-03-06 17:14:43 +01:00
|
|
|
RunConfiguration *newRc = RunConfigurationFactory::clone(newTarget, sourceRc);
|
2015-11-16 16:52:36 +01:00
|
|
|
if (!newRc) {
|
|
|
|
|
runconfigurationError << sourceRc->displayName();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
newRc->setDisplayName(sourceRc->displayName());
|
|
|
|
|
newTarget->addRunConfiguration(newRc);
|
|
|
|
|
if (sourceTarget->activeRunConfiguration() == sourceRc)
|
|
|
|
|
newTarget->setActiveRunConfiguration(newRc);
|
|
|
|
|
}
|
|
|
|
|
if (!newTarget->activeRunConfiguration()) {
|
|
|
|
|
QList<RunConfiguration *> rcs = newTarget->runConfigurations();
|
|
|
|
|
if (!rcs.isEmpty())
|
|
|
|
|
newTarget->setActiveRunConfiguration(rcs.first());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (buildconfigurationError.count() == sourceTarget->buildConfigurations().count())
|
|
|
|
|
fatalError = true;
|
|
|
|
|
|
|
|
|
|
if (deployconfigurationError.count() == sourceTarget->deployConfigurations().count())
|
|
|
|
|
fatalError = true;
|
|
|
|
|
|
|
|
|
|
if (runconfigurationError.count() == sourceTarget->runConfigurations().count())
|
|
|
|
|
fatalError = true;
|
|
|
|
|
|
|
|
|
|
if (fatalError) {
|
|
|
|
|
// That could be a more granular error message
|
2020-06-02 09:10:40 +02:00
|
|
|
QMessageBox::critical(Core::ICore::dialogParent(),
|
2015-11-16 16:52:36 +01:00
|
|
|
tr("Incompatible Kit"),
|
|
|
|
|
tr("Kit %1 is incompatible with kit %2.")
|
2020-06-02 09:10:40 +02:00
|
|
|
.arg(sourceTarget->kit()->displayName())
|
|
|
|
|
.arg(newTarget->kit()->displayName()));
|
2015-11-16 16:52:36 +01:00
|
|
|
} else if (!buildconfigurationError.isEmpty()
|
|
|
|
|
|| !deployconfigurationError.isEmpty()
|
|
|
|
|
|| ! runconfigurationError.isEmpty()) {
|
|
|
|
|
|
|
|
|
|
QString error;
|
|
|
|
|
if (!buildconfigurationError.isEmpty())
|
|
|
|
|
error += tr("Build configurations:") + QLatin1Char('\n')
|
|
|
|
|
+ buildconfigurationError.join(QLatin1Char('\n'));
|
|
|
|
|
|
|
|
|
|
if (!deployconfigurationError.isEmpty()) {
|
|
|
|
|
if (!error.isEmpty())
|
|
|
|
|
error.append(QLatin1Char('\n'));
|
|
|
|
|
error += tr("Deploy configurations:") + QLatin1Char('\n')
|
|
|
|
|
+ deployconfigurationError.join(QLatin1Char('\n'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!runconfigurationError.isEmpty()) {
|
|
|
|
|
if (!error.isEmpty())
|
|
|
|
|
error.append(QLatin1Char('\n'));
|
|
|
|
|
error += tr("Run configurations:") + QLatin1Char('\n')
|
|
|
|
|
+ runconfigurationError.join(QLatin1Char('\n'));
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-02 09:10:40 +02:00
|
|
|
QMessageBox msgBox(Core::ICore::dialogParent());
|
2015-11-16 16:52:36 +01:00
|
|
|
msgBox.setIcon(QMessageBox::Warning);
|
|
|
|
|
msgBox.setWindowTitle(tr("Partially Incompatible Kit"));
|
|
|
|
|
msgBox.setText(tr("Some configurations could not be copied."));
|
|
|
|
|
msgBox.setDetailedText(error);
|
|
|
|
|
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
2016-07-22 15:53:01 +02:00
|
|
|
fatalError = msgBox.exec() != QDialog::Accepted;
|
2015-11-16 16:52:36 +01:00
|
|
|
}
|
|
|
|
|
|
2016-07-22 15:53:01 +02:00
|
|
|
return !fatalError;
|
2015-11-16 16:52:36 +01:00
|
|
|
}
|
|
|
|
|
|
2012-10-02 17:46:19 +02:00
|
|
|
bool Project::setupTarget(Target *t)
|
|
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
if (d->m_needsBuildConfigurations)
|
2018-04-19 13:30:15 +02:00
|
|
|
t->updateDefaultBuildConfigurations();
|
2019-10-25 09:55:32 +02:00
|
|
|
if (d->m_needsDeployConfigurations)
|
|
|
|
|
t->updateDefaultDeployConfigurations();
|
2012-10-02 17:46:19 +02:00
|
|
|
t->updateDefaultRunConfigurations();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-07 14:35:49 +02:00
|
|
|
void Project::setDisplayName(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
if (name == d->m_displayName)
|
|
|
|
|
return;
|
|
|
|
|
d->m_displayName = name;
|
|
|
|
|
emit displayNameChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
void Project::setId(Utils::Id id)
|
2013-09-27 16:30:20 +02:00
|
|
|
{
|
2018-01-10 12:37:06 +01:00
|
|
|
QTC_ASSERT(!d->m_id.isValid(), return); // Id may not change ever!
|
2013-09-27 16:30:20 +02:00
|
|
|
d->m_id = id;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-26 14:41:46 +02:00
|
|
|
void Project::setRootProjectNode(std::unique_ptr<ProjectNode> &&root)
|
2016-01-08 12:49:00 +01:00
|
|
|
{
|
2018-05-03 13:15:54 +02:00
|
|
|
QTC_ASSERT(d->m_rootProjectNode.get() != root.get() || !root, return);
|
2017-03-03 14:14:09 +01:00
|
|
|
|
2018-04-26 11:00:59 +02:00
|
|
|
if (root && root->isEmpty()) {
|
2017-03-24 11:06:26 +01:00
|
|
|
// Something went wrong with parsing: At least the project file needs to be
|
|
|
|
|
// shown so that the user can fix the breakage.
|
|
|
|
|
// Do not leak root and use default project tree in this case.
|
2018-04-26 14:41:46 +02:00
|
|
|
root.reset();
|
2017-03-24 11:06:26 +01:00
|
|
|
}
|
|
|
|
|
|
2018-01-19 11:55:28 +01:00
|
|
|
if (root) {
|
2018-04-26 14:41:46 +02:00
|
|
|
ProjectTree::applyTreeManager(root.get());
|
2018-01-19 11:55:28 +01:00
|
|
|
root->setParentFolderNode(d->m_containerNode.get());
|
|
|
|
|
}
|
2017-03-10 10:20:53 +01:00
|
|
|
|
2018-03-14 15:21:51 +01:00
|
|
|
std::unique_ptr<ProjectNode> oldNode = std::move(d->m_rootProjectNode);
|
2018-01-19 11:55:28 +01:00
|
|
|
|
2018-04-26 14:41:46 +02:00
|
|
|
d->m_rootProjectNode = std::move(root);
|
|
|
|
|
if (oldNode || d->m_rootProjectNode)
|
2018-01-19 11:55:28 +01:00
|
|
|
handleSubTreeChanged(d->m_containerNode.get());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Project::handleSubTreeChanged(FolderNode *node)
|
|
|
|
|
{
|
|
|
|
|
QVector<const Node *> nodeList;
|
|
|
|
|
if (d->m_rootProjectNode) {
|
|
|
|
|
d->m_rootProjectNode->forEachGenericNode([&nodeList](const Node *n) {
|
2017-12-04 12:58:10 +01:00
|
|
|
nodeList.append(n);
|
|
|
|
|
});
|
|
|
|
|
Utils::sort(nodeList, &nodeLessThan);
|
2017-04-04 14:36:03 +02:00
|
|
|
}
|
2018-01-19 11:55:28 +01:00
|
|
|
d->m_sortedNodeList = nodeList;
|
2017-03-17 12:26:00 +01:00
|
|
|
|
2018-01-19 11:55:28 +01:00
|
|
|
ProjectTree::emitSubtreeChanged(node);
|
|
|
|
|
emit fileListChanged();
|
2016-01-08 12:49:00 +01:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void Project::saveSettings()
|
|
|
|
|
{
|
2011-08-01 13:21:01 +00:00
|
|
|
emit aboutToSaveSettings();
|
2012-08-17 13:18:31 +02:00
|
|
|
if (!d->m_accessor)
|
2017-06-14 17:33:05 +02:00
|
|
|
d->m_accessor = std::make_unique<Internal::UserFileAccessor>(this);
|
2014-06-18 13:02:41 +02:00
|
|
|
if (!targets().isEmpty())
|
2020-06-02 09:10:40 +02:00
|
|
|
d->m_accessor->saveSettings(toMap(), Core::ICore::dialogParent());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-05-18 16:57:29 +02:00
|
|
|
Project::RestoreResult Project::restoreSettings(QString *errorMessage)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2012-08-17 13:18:31 +02:00
|
|
|
if (!d->m_accessor)
|
2017-06-14 17:33:05 +02:00
|
|
|
d->m_accessor = std::make_unique<Internal::UserFileAccessor>(this);
|
2020-06-02 09:10:40 +02:00
|
|
|
QVariantMap map(d->m_accessor->restoreSettings(Core::ICore::dialogParent()));
|
2015-05-18 16:57:29 +02:00
|
|
|
RestoreResult result = fromMap(map, errorMessage);
|
|
|
|
|
if (result == RestoreResult::Ok)
|
2011-08-01 13:21:01 +00:00
|
|
|
emit settingsLoaded();
|
2019-08-06 11:00:43 +02:00
|
|
|
|
2015-05-18 16:57:29 +02:00
|
|
|
return result;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-04 12:58:10 +01:00
|
|
|
/*!
|
|
|
|
|
* Returns a sorted list of all files matching the predicate \a filter.
|
|
|
|
|
*/
|
2019-12-17 14:07:53 +01:00
|
|
|
Utils::FilePaths Project::files(const Project::NodeMatcher &filter) const
|
2017-03-27 12:12:38 +02:00
|
|
|
{
|
2019-08-22 11:05:42 +02:00
|
|
|
QTC_ASSERT(filter, return {});
|
|
|
|
|
|
2019-12-17 14:07:53 +01:00
|
|
|
Utils::FilePaths result;
|
2018-01-10 12:36:00 +01:00
|
|
|
if (d->m_sortedNodeList.empty() && filter(containerNode()))
|
|
|
|
|
result.append(projectFilePath());
|
2017-03-27 12:12:38 +02:00
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath lastAdded;
|
2018-04-08 23:40:00 +03:00
|
|
|
for (const Node *n : qAsConst(d->m_sortedNodeList)) {
|
2019-08-22 11:05:42 +02:00
|
|
|
if (!filter(n))
|
2017-12-04 12:58:10 +01:00
|
|
|
continue;
|
2017-12-06 10:58:59 +01:00
|
|
|
|
2017-12-04 12:58:10 +01:00
|
|
|
// Remove duplicates:
|
2019-05-28 13:49:26 +02:00
|
|
|
const Utils::FilePath path = n->filePath();
|
2017-12-04 12:58:10 +01:00
|
|
|
if (path == lastAdded)
|
|
|
|
|
continue; // skip duplicates
|
|
|
|
|
lastAdded = path;
|
2017-12-06 10:58:59 +01:00
|
|
|
|
|
|
|
|
result.append(path);
|
2019-08-09 12:21:59 +02:00
|
|
|
}
|
2017-03-27 12:12:38 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-14 12:58:14 +02:00
|
|
|
/*!
|
2013-09-10 17:16:10 +02:00
|
|
|
Serializes all data into a QVariantMap.
|
2011-04-14 12:58:14 +02:00
|
|
|
|
|
|
|
|
This map is then saved in the .user file of the project.
|
|
|
|
|
Just put all your data into the map.
|
|
|
|
|
|
2013-10-07 13:34:40 +02:00
|
|
|
\note Do not forget to call your base class' toMap function.
|
2011-04-14 12:58:14 +02:00
|
|
|
\note Do not forget to call setActiveBuildConfiguration when
|
2013-09-10 17:16:10 +02:00
|
|
|
creating new build configurations.
|
2011-04-14 12:58:14 +02:00
|
|
|
*/
|
|
|
|
|
|
2010-01-19 16:33:44 +01:00
|
|
|
QVariantMap Project::toMap() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-02-08 15:50:06 +01:00
|
|
|
const QList<Target *> ts = targets();
|
2009-11-24 15:36:31 +01:00
|
|
|
|
2010-01-19 16:33:44 +01:00
|
|
|
QVariantMap map;
|
2010-09-21 09:17:37 +02:00
|
|
|
map.insert(QLatin1String(ACTIVE_TARGET_KEY), ts.indexOf(d->m_activeTarget));
|
2010-02-08 15:50:06 +01:00
|
|
|
map.insert(QLatin1String(TARGET_COUNT_KEY), ts.size());
|
|
|
|
|
for (int i = 0; i < ts.size(); ++i)
|
|
|
|
|
map.insert(QString::fromLatin1(TARGET_KEY_PREFIX) + QString::number(i), ts.at(i)->toMap());
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-11-06 15:49:52 +01:00
|
|
|
map.insert(QLatin1String(EDITOR_SETTINGS_KEY), d->m_editorConfiguration.toMap());
|
2020-11-09 15:44:44 +02:00
|
|
|
if (!d->m_pluginSettings.isEmpty())
|
|
|
|
|
map.insert(QLatin1String(PLUGIN_SETTINGS_KEY), d->m_pluginSettings);
|
2010-01-19 16:33:44 +01:00
|
|
|
|
|
|
|
|
return map;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-24 11:38:11 +01:00
|
|
|
/*!
|
|
|
|
|
Returns the directory that contains the project.
|
|
|
|
|
|
|
|
|
|
This includes the absolute path.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath Project::projectDirectory() const
|
2010-03-25 13:19:27 +01:00
|
|
|
{
|
2014-05-02 12:53:36 +02:00
|
|
|
return projectDirectory(projectFilePath());
|
2010-06-08 14:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
2016-02-24 11:38:11 +01:00
|
|
|
/*!
|
|
|
|
|
Returns the directory that contains the file \a top.
|
|
|
|
|
|
|
|
|
|
This includes the absolute path.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath Project::projectDirectory(const Utils::FilePath &top)
|
2010-06-08 14:19:05 +02:00
|
|
|
{
|
2012-09-03 18:31:44 +02:00
|
|
|
if (top.isEmpty())
|
2019-05-28 13:49:26 +02:00
|
|
|
return Utils::FilePath();
|
|
|
|
|
return Utils::FilePath::fromString(top.toFileInfo().absoluteDir().path());
|
2010-03-25 13:19:27 +01:00
|
|
|
}
|
|
|
|
|
|
2019-03-14 15:34:14 +01:00
|
|
|
void Project::changeRootProjectDirectory()
|
|
|
|
|
{
|
2021-07-26 17:41:28 +02:00
|
|
|
Utils::FilePath rootPath = Utils::FileUtils::getExistingDirectory(
|
|
|
|
|
tr("Select the Root Directory"),
|
|
|
|
|
rootProjectDirectory(),
|
|
|
|
|
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
2019-03-14 15:34:14 +01:00
|
|
|
if (rootPath != d->m_rootProjectDirectory) {
|
|
|
|
|
d->m_rootProjectDirectory = rootPath;
|
|
|
|
|
setNamedSettings(Constants::PROJECT_ROOT_PATH_KEY, d->m_rootProjectDirectory.toString());
|
|
|
|
|
emit rootProjectDirectoryChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-14 13:07:04 +02:00
|
|
|
/*!
|
2019-06-25 08:48:29 +02:00
|
|
|
Returns the common root directory that contains all files which belong to a project.
|
2018-08-14 13:07:04 +02:00
|
|
|
*/
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath Project::rootProjectDirectory() const
|
2018-08-14 13:07:04 +02:00
|
|
|
{
|
2019-03-14 15:34:14 +01:00
|
|
|
if (!d->m_rootProjectDirectory.isEmpty())
|
|
|
|
|
return d->m_rootProjectDirectory;
|
|
|
|
|
|
|
|
|
|
return projectDirectory();
|
2018-08-14 13:07:04 +02:00
|
|
|
}
|
|
|
|
|
|
2016-01-08 12:49:00 +01:00
|
|
|
ProjectNode *Project::rootProjectNode() const
|
|
|
|
|
{
|
2018-03-14 15:21:51 +01:00
|
|
|
return d->m_rootProjectNode.get();
|
2016-01-08 12:49:00 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-27 17:51:19 +02:00
|
|
|
ContainerNode *Project::containerNode() const
|
2017-03-17 12:26:00 +01:00
|
|
|
{
|
2017-06-14 17:33:05 +02:00
|
|
|
return d->m_containerNode.get();
|
2017-03-17 12:26:00 +01:00
|
|
|
}
|
|
|
|
|
|
2015-05-18 16:57:29 +02:00
|
|
|
Project::RestoreResult Project::fromMap(const QVariantMap &map, QString *errorMessage)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2019-07-23 10:58:00 +02:00
|
|
|
Q_UNUSED(errorMessage)
|
2010-01-19 16:33:44 +01:00
|
|
|
if (map.contains(QLatin1String(EDITOR_SETTINGS_KEY))) {
|
|
|
|
|
QVariantMap values(map.value(QLatin1String(EDITOR_SETTINGS_KEY)).toMap());
|
2014-11-06 15:49:52 +01:00
|
|
|
d->m_editorConfiguration.fromMap(values);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-08-01 13:21:01 +00:00
|
|
|
if (map.contains(QLatin1String(PLUGIN_SETTINGS_KEY)))
|
|
|
|
|
d->m_pluginSettings = map.value(QLatin1String(PLUGIN_SETTINGS_KEY)).toMap();
|
|
|
|
|
|
2010-01-19 16:33:44 +01:00
|
|
|
bool ok;
|
2010-02-08 15:50:06 +01:00
|
|
|
int maxI(map.value(QLatin1String(TARGET_COUNT_KEY), 0).toInt(&ok));
|
2010-01-19 16:33:44 +01:00
|
|
|
if (!ok || maxI < 0)
|
|
|
|
|
maxI = 0;
|
2010-02-08 15:50:06 +01:00
|
|
|
int active(map.value(QLatin1String(ACTIVE_TARGET_KEY), 0).toInt(&ok));
|
2012-08-07 14:01:52 +02:00
|
|
|
if (!ok || active < 0 || active >= maxI)
|
2010-02-08 15:50:06 +01:00
|
|
|
active = 0;
|
2010-01-19 16:33:44 +01:00
|
|
|
|
2017-09-26 18:34:38 +02:00
|
|
|
if (active >= 0 && active < maxI)
|
|
|
|
|
createTargetFromMap(map, active); // sets activeTarget since it is the first target created!
|
2010-12-06 16:15:41 +01:00
|
|
|
|
2017-09-26 18:34:38 +02:00
|
|
|
for (int i = 0; i < maxI; ++i) {
|
|
|
|
|
if (i == active) // already covered!
|
2012-04-24 15:49:09 +02:00
|
|
|
continue;
|
2010-12-10 19:02:19 +01:00
|
|
|
|
2017-09-26 18:34:38 +02:00
|
|
|
createTargetFromMap(map, i);
|
2009-11-30 13:58:06 +01:00
|
|
|
}
|
2012-04-24 15:49:09 +02:00
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
d->m_rootProjectDirectory = Utils::FilePath::fromString(
|
2019-03-14 15:34:14 +01:00
|
|
|
namedSettings(Constants::PROJECT_ROOT_PATH_KEY).toString());
|
|
|
|
|
|
2015-05-18 16:57:29 +02:00
|
|
|
return RestoreResult::Ok;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2017-09-26 18:34:38 +02:00
|
|
|
void Project::createTargetFromMap(const QVariantMap &map, int index)
|
|
|
|
|
{
|
|
|
|
|
const QString key = QString::fromLatin1(TARGET_KEY_PREFIX) + QString::number(index);
|
|
|
|
|
if (!map.contains(key))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-08-01 15:44:21 +02:00
|
|
|
const QVariantMap targetMap = map.value(key).toMap();
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id id = idFromMap(targetMap);
|
2019-08-01 15:44:21 +02:00
|
|
|
if (target(id)) {
|
|
|
|
|
qWarning("Warning: Duplicated target id found, not restoring second target with id '%s'. Continuing.",
|
|
|
|
|
qPrintable(id.toString()));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Kit *k = KitManager::kit(id);
|
|
|
|
|
if (!k) {
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id deviceTypeId = Utils::Id::fromSetting(targetMap.value(Target::deviceTypeKey()));
|
2020-03-10 15:52:15 +01:00
|
|
|
if (!deviceTypeId.isValid())
|
|
|
|
|
deviceTypeId = Constants::DESKTOP_DEVICE_TYPE;
|
|
|
|
|
const QString formerKitName = targetMap.value(Target::displayNameKey()).toString();
|
|
|
|
|
k = KitManager::registerKit([deviceTypeId, &formerKitName](Kit *kit) {
|
|
|
|
|
const QString tempKitName = Utils::makeUniquelyNumbered(
|
|
|
|
|
tr("Replacement for \"%1\"").arg(formerKitName),
|
|
|
|
|
Utils::transform(KitManager::kits(), &Kit::unexpandedDisplayName));
|
|
|
|
|
kit->setUnexpandedDisplayName(tempKitName);
|
|
|
|
|
DeviceTypeKitAspect::setDeviceTypeId(kit, deviceTypeId);
|
2020-09-14 16:32:50 +02:00
|
|
|
kit->makeReplacementKit();
|
2020-03-10 15:52:15 +01:00
|
|
|
kit->setup();
|
|
|
|
|
}, id);
|
2020-08-01 21:39:58 +02:00
|
|
|
QTC_ASSERT(k, return);
|
2020-03-10 15:52:15 +01:00
|
|
|
TaskHub::addTask(BuildSystemTask(Task::Warning, tr("Project \"%1\" was configured for "
|
|
|
|
|
"kit \"%2\" with id %3, which does not exist anymore. The new kit \"%4\" was "
|
|
|
|
|
"created in its place, in an attempt not to lose custom project settings.")
|
|
|
|
|
.arg(displayName(), formerKitName, id.toString(), k->displayName())));
|
2019-08-01 15:44:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto t = std::make_unique<Target>(this, k, Target::_constructor_tag{});
|
|
|
|
|
if (!t->fromMap(targetMap))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (t->runConfigurations().isEmpty() && t->buildConfigurations().isEmpty())
|
2017-09-26 18:34:38 +02:00
|
|
|
return;
|
|
|
|
|
|
2018-05-24 12:57:00 +02:00
|
|
|
addTarget(std::move(t));
|
2017-09-26 18:34:38 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
EditorConfiguration *Project::editorConfiguration() const
|
|
|
|
|
{
|
2014-11-06 15:49:52 +01:00
|
|
|
return &d->m_editorConfiguration;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
bool Project::isKnownFile(const Utils::FilePath &filename) const
|
2017-12-04 12:58:10 +01:00
|
|
|
{
|
2018-01-10 12:36:00 +01:00
|
|
|
if (d->m_sortedNodeList.empty())
|
|
|
|
|
return filename == projectFilePath();
|
2019-02-25 12:08:58 +01:00
|
|
|
const FileNode element(filename, FileType::Unknown);
|
2019-01-21 08:57:46 +02:00
|
|
|
return std::binary_search(std::begin(d->m_sortedNodeList), std::end(d->m_sortedNodeList),
|
|
|
|
|
&element, nodeLessThan);
|
2017-12-04 12:58:10 +01:00
|
|
|
}
|
|
|
|
|
|
2020-09-10 10:39:56 +02:00
|
|
|
const Node *Project::nodeForFilePath(const Utils::FilePath &filePath,
|
2021-07-13 16:58:02 +02:00
|
|
|
const Project::NodeMatcher &extraMatcher) const
|
2020-09-10 10:39:56 +02:00
|
|
|
{
|
|
|
|
|
const FileNode dummy(filePath, FileType::Unknown);
|
|
|
|
|
const auto range = std::equal_range(d->m_sortedNodeList.cbegin(), d->m_sortedNodeList.cend(),
|
|
|
|
|
&dummy, &nodeLessThan);
|
|
|
|
|
for (auto it = range.first; it != range.second; ++it) {
|
|
|
|
|
if ((*it)->filePath() == filePath && (!extraMatcher || extraMatcher(*it)))
|
|
|
|
|
return *it;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-21 16:18:53 +01:00
|
|
|
void Project::setProjectLanguages(Core::Context language)
|
2011-04-12 12:17:19 +02:00
|
|
|
{
|
2013-04-09 15:30:20 +02:00
|
|
|
if (d->m_projectLanguages == language)
|
|
|
|
|
return;
|
2012-11-21 16:18:53 +01:00
|
|
|
d->m_projectLanguages = language;
|
2013-04-09 15:30:20 +02:00
|
|
|
emit projectLanguagesUpdated();
|
2011-04-12 12:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
void Project::addProjectLanguage(Utils::Id id)
|
2013-04-10 14:45:47 +02:00
|
|
|
{
|
|
|
|
|
Core::Context lang = projectLanguages();
|
|
|
|
|
int pos = lang.indexOf(id);
|
|
|
|
|
if (pos < 0)
|
|
|
|
|
lang.add(id);
|
|
|
|
|
setProjectLanguages(lang);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
void Project::removeProjectLanguage(Utils::Id id)
|
2013-04-10 14:45:47 +02:00
|
|
|
{
|
|
|
|
|
Core::Context lang = projectLanguages();
|
|
|
|
|
int pos = lang.indexOf(id);
|
|
|
|
|
if (pos >= 0)
|
|
|
|
|
lang.removeAt(pos);
|
|
|
|
|
setProjectLanguages(lang);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
void Project::setProjectLanguage(Utils::Id id, bool enabled)
|
2013-04-10 14:45:47 +02:00
|
|
|
{
|
|
|
|
|
if (enabled)
|
|
|
|
|
addProjectLanguage(id);
|
|
|
|
|
else
|
|
|
|
|
removeProjectLanguage(id);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-13 12:11:07 +02:00
|
|
|
void Project::setHasMakeInstallEquivalent(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
d->m_hasMakeInstallEquivalent = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Project::setNeedsBuildConfigurations(bool value)
|
|
|
|
|
{
|
|
|
|
|
d->m_needsBuildConfigurations = value;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
void Project::setNeedsDeployConfigurations(bool value)
|
2018-04-18 13:39:05 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
d->m_needsDeployConfigurations = value;
|
2018-04-18 13:39:05 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
Task Project::createProjectTask(Task::TaskType type, const QString &description)
|
2019-07-05 16:58:07 +02:00
|
|
|
{
|
2020-06-26 13:59:38 +02:00
|
|
|
return Task(type, description, Utils::FilePath(), -1, Utils::Id());
|
2019-07-05 16:58:07 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
void Project::setBuildSystemCreator(const std::function<BuildSystem *(Target *)> &creator)
|
2019-08-09 11:22:49 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
d->m_buildSystemCreator = creator;
|
2019-08-09 11:22:49 +02:00
|
|
|
}
|
|
|
|
|
|
2011-04-12 12:17:19 +02:00
|
|
|
Core::Context Project::projectContext() const
|
|
|
|
|
{
|
2017-12-20 11:13:26 +01:00
|
|
|
return Core::Context(d->m_id);
|
2011-04-12 12:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
2012-11-21 16:18:53 +01:00
|
|
|
Core::Context Project::projectLanguages() const
|
2011-04-12 12:17:19 +02:00
|
|
|
{
|
2012-11-21 16:18:53 +01:00
|
|
|
return d->m_projectLanguages;
|
2011-04-12 12:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
2011-08-01 13:21:01 +00:00
|
|
|
QVariant Project::namedSettings(const QString &name) const
|
|
|
|
|
{
|
|
|
|
|
return d->m_pluginSettings.value(name);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-24 16:23:13 +02:00
|
|
|
void Project::setNamedSettings(const QString &name, const QVariant &value)
|
2011-08-01 13:21:01 +00:00
|
|
|
{
|
2012-04-24 15:49:09 +02:00
|
|
|
if (value.isNull())
|
|
|
|
|
d->m_pluginSettings.remove(name);
|
|
|
|
|
else
|
|
|
|
|
d->m_pluginSettings.insert(name, value);
|
2011-08-01 13:21:01 +00:00
|
|
|
}
|
|
|
|
|
|
2019-12-10 17:09:49 +01:00
|
|
|
void Project::setAdditionalEnvironment(const Utils::EnvironmentItems &envItems)
|
|
|
|
|
{
|
|
|
|
|
setNamedSettings(PROJECT_ENV_KEY, Utils::NameValueItem::toStringList(envItems));
|
|
|
|
|
emit environmentChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Utils::EnvironmentItems Project::additionalEnvironment() const
|
|
|
|
|
{
|
|
|
|
|
return Utils::NameValueItem::fromStringList(namedSettings(PROJECT_ENV_KEY).toStringList());
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-28 10:15:04 +00:00
|
|
|
bool Project::needsConfiguration() const
|
|
|
|
|
{
|
2018-05-24 12:57:00 +02:00
|
|
|
return d->m_targets.size() == 0;
|
2011-10-28 10:15:04 +00:00
|
|
|
}
|
|
|
|
|
|
2018-04-19 13:30:15 +02:00
|
|
|
bool Project::needsBuildConfigurations() const
|
|
|
|
|
{
|
2019-08-13 12:11:07 +02:00
|
|
|
return d->m_needsBuildConfigurations;
|
2018-04-19 13:30:15 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-16 13:11:10 +02:00
|
|
|
void Project::configureAsExampleProject(Kit * /*kit*/)
|
2012-03-06 13:14:42 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-13 12:11:07 +02:00
|
|
|
bool Project::hasMakeInstallEquivalent() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_hasMakeInstallEquivalent;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-12 14:49:59 +02:00
|
|
|
MakeInstallCommand Project::makeInstallCommand(const Target *target, const QString &installRoot)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(hasMakeInstallEquivalent(), return MakeInstallCommand());
|
|
|
|
|
MakeInstallCommand cmd;
|
|
|
|
|
if (const BuildConfiguration * const bc = target->activeBuildConfiguration()) {
|
2019-12-05 16:19:42 +01:00
|
|
|
if (const auto makeStep = bc->buildSteps()->firstOfType<MakeStep>())
|
2019-10-07 12:08:48 +03:00
|
|
|
cmd.command = makeStep->makeExecutable();
|
2019-04-12 14:49:59 +02:00
|
|
|
}
|
|
|
|
|
cmd.arguments << "install" << ("INSTALL_ROOT=" + QDir::toNativeSeparators(installRoot));
|
|
|
|
|
return cmd;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-29 16:51:17 +01:00
|
|
|
void Project::setup(const QList<BuildInfo> &infoList)
|
2013-07-22 15:53:57 +02:00
|
|
|
{
|
2018-05-24 12:57:00 +02:00
|
|
|
std::vector<std::unique_ptr<Target>> toRegister;
|
2019-01-29 16:51:17 +01:00
|
|
|
for (const BuildInfo &info : infoList) {
|
|
|
|
|
Kit *k = KitManager::kit(info.kitId);
|
2013-07-22 15:53:57 +02:00
|
|
|
if (!k)
|
|
|
|
|
continue;
|
|
|
|
|
Target *t = target(k);
|
2018-05-24 12:57:00 +02:00
|
|
|
if (!t)
|
2014-07-10 12:57:06 +02:00
|
|
|
t = Utils::findOrDefault(toRegister, Utils::equal(&Target::kit, k));
|
2013-07-22 15:53:57 +02:00
|
|
|
if (!t) {
|
2018-05-24 12:57:00 +02:00
|
|
|
auto newTarget = std::make_unique<Target>(this, k, Target::_constructor_tag{});
|
|
|
|
|
t = newTarget.get();
|
|
|
|
|
toRegister.emplace_back(std::move(newTarget));
|
2013-07-22 15:53:57 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-09 18:42:28 +01:00
|
|
|
if (!info.factory)
|
2018-03-08 15:36:55 +01:00
|
|
|
continue;
|
|
|
|
|
|
2020-01-09 18:42:28 +01:00
|
|
|
if (BuildConfiguration *bc = info.factory->create(t, info))
|
2019-01-29 16:51:17 +01:00
|
|
|
t->addBuildConfiguration(bc);
|
2013-07-22 15:53:57 +02:00
|
|
|
}
|
2018-05-24 12:57:00 +02:00
|
|
|
for (std::unique_ptr<Target> &t : toRegister) {
|
2013-08-13 10:52:57 +02:00
|
|
|
t->updateDefaultDeployConfigurations();
|
|
|
|
|
t->updateDefaultRunConfigurations();
|
2018-05-24 12:57:00 +02:00
|
|
|
addTarget(std::move(t));
|
2013-08-13 10:52:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-15 20:16:53 +01:00
|
|
|
Utils::MacroExpander *Project::macroExpander() const
|
|
|
|
|
{
|
|
|
|
|
return &d->m_macroExpander;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-05 10:29:48 +01:00
|
|
|
ProjectNode *Project::findNodeForBuildKey(const QString &buildKey) const
|
2018-11-28 18:45:18 +01:00
|
|
|
{
|
2018-12-18 14:01:36 +01:00
|
|
|
if (!d->m_rootProjectNode)
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
return d->m_rootProjectNode->findProjectNode([buildKey](const ProjectNode *node) {
|
|
|
|
|
return node->buildKey() == buildKey;
|
|
|
|
|
});
|
2018-11-28 18:45:18 +01:00
|
|
|
}
|
|
|
|
|
|
2016-10-05 08:31:16 +02:00
|
|
|
ProjectImporter *Project::projectImporter() const
|
2013-08-13 10:52:57 +02:00
|
|
|
{
|
2016-04-13 15:52:14 +02:00
|
|
|
return nullptr;
|
2013-07-22 15:53:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-06-03 17:36:02 +02:00
|
|
|
void Project::setCanBuildProducts()
|
|
|
|
|
{
|
|
|
|
|
d->m_canBuildProducts = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-13 17:38:33 +01:00
|
|
|
void Project::setExtraData(const QString &key, const QVariant &data)
|
|
|
|
|
{
|
|
|
|
|
d->m_extraData.insert(key, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant Project::extraData(const QString &key) const
|
|
|
|
|
{
|
|
|
|
|
return d->m_extraData.value(key);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 19:38:36 +02:00
|
|
|
QStringList Project::availableQmlPreviewTranslations(QString *errorMessage)
|
|
|
|
|
{
|
|
|
|
|
const auto projectDirectory = rootProjectDirectory().toFileInfo().absoluteFilePath();
|
|
|
|
|
const QDir languageDirectory(projectDirectory + "/i18n");
|
|
|
|
|
const auto qmFiles = languageDirectory.entryList({"qml_*.qm"});
|
|
|
|
|
if (qmFiles.isEmpty() && errorMessage)
|
2020-11-06 17:11:24 +01:00
|
|
|
errorMessage->append(tr("Could not find any qml_*.qm file at \"%1\"").arg(languageDirectory.absolutePath()));
|
2020-06-22 19:38:36 +02:00
|
|
|
return Utils::transform(qmFiles, [](const QString &qmFile) {
|
|
|
|
|
const int localeStartPosition = qmFile.lastIndexOf("_") + 1;
|
|
|
|
|
const int localeEndPosition = qmFile.size() - QString(".qm").size();
|
|
|
|
|
const QString locale = qmFile.left(localeEndPosition).mid(localeStartPosition);
|
|
|
|
|
return locale;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 09:19:39 +02:00
|
|
|
QList<Core::IDocument *> Project::modifiedDocuments() const
|
|
|
|
|
{
|
|
|
|
|
QList<Core::IDocument *> modifiedProjectDocuments;
|
|
|
|
|
|
|
|
|
|
for (Core::IDocument *doc : Core::DocumentModel::openedDocuments()) {
|
|
|
|
|
if (doc->isModified() && isKnownFile(doc->filePath()))
|
|
|
|
|
modifiedProjectDocuments.append(doc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return modifiedProjectDocuments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Project::isModified() const
|
|
|
|
|
{
|
|
|
|
|
return !modifiedDocuments().isEmpty();
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 12:38:41 +01:00
|
|
|
#if defined(WITH_TESTS)
|
|
|
|
|
|
|
|
|
|
} // namespace ProjectExplorer
|
|
|
|
|
|
2021-05-05 11:18:51 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2019-01-23 15:12:21 +01:00
|
|
|
#include <utils/hostosinfo.h>
|
2021-05-05 11:18:51 +02:00
|
|
|
#include <utils/temporarydirectory.h>
|
2019-01-23 15:12:21 +01:00
|
|
|
|
2021-05-05 11:18:51 +02:00
|
|
|
#include <QEventLoop>
|
2018-01-10 12:38:41 +01:00
|
|
|
#include <QSignalSpy>
|
2021-05-05 11:18:51 +02:00
|
|
|
#include <QTest>
|
|
|
|
|
#include <QTimer>
|
2018-01-10 12:38:41 +01:00
|
|
|
|
|
|
|
|
namespace ProjectExplorer {
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
static Utils::FilePath constructTestPath(const char *basePath)
|
2019-01-23 15:12:21 +01:00
|
|
|
{
|
2019-05-28 13:49:26 +02:00
|
|
|
Utils::FilePath drive;
|
2019-01-23 15:12:21 +01:00
|
|
|
if (Utils::HostOsInfo::isWindowsHost())
|
2019-05-28 13:49:26 +02:00
|
|
|
drive = Utils::FilePath::fromString("C:");
|
2019-01-23 15:12:21 +01:00
|
|
|
return drive + QLatin1String(basePath);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-28 13:49:26 +02:00
|
|
|
const Utils::FilePath TEST_PROJECT_PATH = constructTestPath("/tmp/foobar/baz.project");
|
|
|
|
|
const Utils::FilePath TEST_PROJECT_NONEXISTING_FILE = constructTestPath("/tmp/foobar/nothing.cpp");
|
|
|
|
|
const Utils::FilePath TEST_PROJECT_CPP_FILE = constructTestPath("/tmp/foobar/main.cpp");
|
|
|
|
|
const Utils::FilePath TEST_PROJECT_GENERATED_FILE = constructTestPath("/tmp/foobar/generated.foo");
|
2018-01-10 12:38:41 +01:00
|
|
|
const QString TEST_PROJECT_MIMETYPE = "application/vnd.test.qmakeprofile";
|
|
|
|
|
const QString TEST_PROJECT_DISPLAYNAME = "testProjectFoo";
|
|
|
|
|
const char TEST_PROJECT_ID[] = "Test.Project.Id";
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
class TestBuildSystem : public BuildSystem
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using BuildSystem::BuildSystem;
|
|
|
|
|
|
|
|
|
|
void triggerParsing() {}
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-10 12:38:41 +01:00
|
|
|
class TestProject : public Project
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
TestProject() : Project(TEST_PROJECT_MIMETYPE, TEST_PROJECT_PATH)
|
|
|
|
|
{
|
|
|
|
|
setId(TEST_PROJECT_ID);
|
|
|
|
|
setDisplayName(TEST_PROJECT_DISPLAYNAME);
|
2019-10-25 09:55:32 +02:00
|
|
|
setBuildSystemCreator([](Target *t) { return new TestBuildSystem(t); });
|
|
|
|
|
setNeedsBuildConfigurations(false);
|
|
|
|
|
setNeedsDeployConfigurations(false);
|
|
|
|
|
|
|
|
|
|
target = addTargetForKit(&testKit);
|
2018-01-10 12:38:41 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-09 13:30:13 +01:00
|
|
|
bool needsConfiguration() const final { return false; }
|
2019-10-25 09:55:32 +02:00
|
|
|
|
|
|
|
|
Kit testKit;
|
|
|
|
|
Target *target = nullptr;
|
2018-01-10 12:38:41 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void ProjectExplorerPlugin::testProject_setup()
|
|
|
|
|
{
|
|
|
|
|
TestProject project;
|
|
|
|
|
|
|
|
|
|
QCOMPARE(project.displayName(), TEST_PROJECT_DISPLAYNAME);
|
|
|
|
|
|
|
|
|
|
QVERIFY(!project.rootProjectNode());
|
|
|
|
|
QVERIFY(project.containerNode());
|
|
|
|
|
|
|
|
|
|
QVERIFY(project.macroExpander());
|
|
|
|
|
|
|
|
|
|
QCOMPARE(project.mimeType(), TEST_PROJECT_MIMETYPE);
|
|
|
|
|
QCOMPARE(project.projectFilePath(), TEST_PROJECT_PATH);
|
|
|
|
|
QCOMPARE(project.projectDirectory(), TEST_PROJECT_PATH.parentDir());
|
|
|
|
|
|
|
|
|
|
QCOMPARE(project.isKnownFile(TEST_PROJECT_PATH), true);
|
|
|
|
|
QCOMPARE(project.isKnownFile(TEST_PROJECT_NONEXISTING_FILE), false);
|
|
|
|
|
QCOMPARE(project.isKnownFile(TEST_PROJECT_CPP_FILE), false);
|
|
|
|
|
|
|
|
|
|
QCOMPARE(project.files(Project::AllFiles), {TEST_PROJECT_PATH});
|
|
|
|
|
QCOMPARE(project.files(Project::GeneratedFiles), {});
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
QCOMPARE(project.id(), Utils::Id(TEST_PROJECT_ID));
|
2018-01-10 12:38:41 +01:00
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
QVERIFY(!project.target->buildSystem()->isParsing());
|
|
|
|
|
QVERIFY(!project.target->buildSystem()->hasParsingData());
|
2018-01-10 12:38:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProjectExplorerPlugin::testProject_changeDisplayName()
|
|
|
|
|
{
|
|
|
|
|
TestProject project;
|
|
|
|
|
|
|
|
|
|
QSignalSpy spy(&project, &Project::displayNameChanged);
|
|
|
|
|
|
|
|
|
|
const QString newName = "other name";
|
|
|
|
|
project.setDisplayName(newName);
|
|
|
|
|
QCOMPARE(spy.count(), 1);
|
|
|
|
|
QVariantList args = spy.takeFirst();
|
|
|
|
|
QCOMPARE(args, {});
|
|
|
|
|
|
|
|
|
|
project.setDisplayName(newName);
|
|
|
|
|
QCOMPARE(spy.count(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProjectExplorerPlugin::testProject_parsingSuccess()
|
|
|
|
|
{
|
|
|
|
|
TestProject project;
|
|
|
|
|
|
2020-02-12 14:09:35 +01:00
|
|
|
QSignalSpy startSpy(project.target->buildSystem(), &BuildSystem::parsingStarted);
|
|
|
|
|
QSignalSpy stopSpy(project.target->buildSystem(), &BuildSystem::parsingFinished);
|
2018-01-10 12:38:41 +01:00
|
|
|
|
2019-08-06 14:46:37 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
BuildSystem::ParseGuard guard = project.target->buildSystem()->guardParsingRun();
|
2019-08-06 14:46:37 +02:00
|
|
|
QCOMPARE(startSpy.count(), 1);
|
|
|
|
|
QCOMPARE(stopSpy.count(), 0);
|
2018-01-10 12:38:41 +01:00
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
QVERIFY(project.target->buildSystem()->isParsing());
|
|
|
|
|
QVERIFY(!project.target->buildSystem()->hasParsingData());
|
2019-08-06 14:46:37 +02:00
|
|
|
|
|
|
|
|
guard.markAsSuccess();
|
|
|
|
|
}
|
2018-01-10 12:38:41 +01:00
|
|
|
|
|
|
|
|
QCOMPARE(startSpy.count(), 1);
|
|
|
|
|
QCOMPARE(stopSpy.count(), 1);
|
|
|
|
|
QCOMPARE(stopSpy.at(0), {QVariant(true)});
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
QVERIFY(!project.target->buildSystem()->isParsing());
|
|
|
|
|
QVERIFY(project.target->buildSystem()->hasParsingData());
|
2018-01-10 12:38:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProjectExplorerPlugin::testProject_parsingFail()
|
|
|
|
|
{
|
|
|
|
|
TestProject project;
|
|
|
|
|
|
2020-02-12 14:09:35 +01:00
|
|
|
QSignalSpy startSpy(project.target->buildSystem(), &BuildSystem::parsingStarted);
|
|
|
|
|
QSignalSpy stopSpy(project.target->buildSystem(), &BuildSystem::parsingFinished);
|
2018-01-10 12:38:41 +01:00
|
|
|
|
2019-08-06 14:46:37 +02:00
|
|
|
{
|
2019-10-25 09:55:32 +02:00
|
|
|
BuildSystem::ParseGuard guard = project.target->buildSystem()->guardParsingRun();
|
2019-08-06 14:46:37 +02:00
|
|
|
QCOMPARE(startSpy.count(), 1);
|
|
|
|
|
QCOMPARE(stopSpy.count(), 0);
|
2018-01-10 12:38:41 +01:00
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
QVERIFY(project.target->buildSystem()->isParsing());
|
|
|
|
|
QVERIFY(!project.target->buildSystem()->hasParsingData());
|
2019-08-06 14:46:37 +02:00
|
|
|
}
|
2018-01-10 12:38:41 +01:00
|
|
|
|
|
|
|
|
QCOMPARE(startSpy.count(), 1);
|
|
|
|
|
QCOMPARE(stopSpy.count(), 1);
|
|
|
|
|
QCOMPARE(stopSpy.at(0), {QVariant(false)});
|
|
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
QVERIFY(!project.target->buildSystem()->isParsing());
|
|
|
|
|
QVERIFY(!project.target->buildSystem()->hasParsingData());
|
2018-01-10 12:38:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<ProjectNode> createFileTree(Project *project)
|
|
|
|
|
{
|
2019-02-25 18:35:34 +01:00
|
|
|
std::unique_ptr<ProjectNode> root = std::make_unique<ProjectNode>(project->projectDirectory());
|
2018-04-26 16:39:15 +02:00
|
|
|
std::vector<std::unique_ptr<FileNode>> nodes;
|
2019-02-25 12:08:58 +01:00
|
|
|
nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_PATH, FileType::Project));
|
|
|
|
|
nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_CPP_FILE, FileType::Source));
|
|
|
|
|
nodes.emplace_back(std::make_unique<FileNode>(TEST_PROJECT_GENERATED_FILE, FileType::Source));
|
|
|
|
|
nodes.back()->setIsGenerated(true);
|
2018-04-26 16:39:15 +02:00
|
|
|
root->addNestedNodes(std::move(nodes));
|
2018-01-10 12:38:41 +01:00
|
|
|
|
|
|
|
|
return root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ProjectExplorerPlugin::testProject_projectTree()
|
|
|
|
|
{
|
|
|
|
|
TestProject project;
|
|
|
|
|
QSignalSpy fileSpy(&project, &Project::fileListChanged);
|
|
|
|
|
|
|
|
|
|
project.setRootProjectNode(nullptr);
|
|
|
|
|
QCOMPARE(fileSpy.count(), 0);
|
2018-01-11 15:23:14 +02:00
|
|
|
QVERIFY(!project.rootProjectNode());
|
2018-01-10 12:38:41 +01:00
|
|
|
|
2019-02-25 18:35:34 +01:00
|
|
|
project.setRootProjectNode(std::make_unique<ProjectNode>(project.projectDirectory()));
|
2018-01-10 12:38:41 +01:00
|
|
|
QCOMPARE(fileSpy.count(), 0);
|
2018-01-11 15:23:14 +02:00
|
|
|
QVERIFY(!project.rootProjectNode());
|
2018-01-10 12:38:41 +01:00
|
|
|
|
|
|
|
|
std::unique_ptr<ProjectNode> root = createFileTree(&project);
|
|
|
|
|
ProjectNode *rootNode = root.get();
|
2018-04-26 14:41:46 +02:00
|
|
|
project.setRootProjectNode(std::move(root));
|
2018-01-10 12:38:41 +01:00
|
|
|
QCOMPARE(fileSpy.count(), 1);
|
|
|
|
|
QCOMPARE(project.rootProjectNode(), rootNode);
|
|
|
|
|
|
|
|
|
|
// Test known files:
|
|
|
|
|
QCOMPARE(project.isKnownFile(TEST_PROJECT_PATH), true);
|
|
|
|
|
QCOMPARE(project.isKnownFile(TEST_PROJECT_NONEXISTING_FILE), false);
|
|
|
|
|
QCOMPARE(project.isKnownFile(TEST_PROJECT_CPP_FILE), true);
|
|
|
|
|
QCOMPARE(project.isKnownFile(TEST_PROJECT_GENERATED_FILE), true);
|
|
|
|
|
|
2019-12-17 14:07:53 +01:00
|
|
|
Utils::FilePaths allFiles = project.files(Project::AllFiles);
|
2018-01-10 12:38:41 +01:00
|
|
|
QCOMPARE(allFiles.count(), 3);
|
|
|
|
|
QVERIFY(allFiles.contains(TEST_PROJECT_PATH));
|
|
|
|
|
QVERIFY(allFiles.contains(TEST_PROJECT_CPP_FILE));
|
|
|
|
|
QVERIFY(allFiles.contains(TEST_PROJECT_GENERATED_FILE));
|
|
|
|
|
|
|
|
|
|
QCOMPARE(project.files(Project::GeneratedFiles), {TEST_PROJECT_GENERATED_FILE});
|
2019-12-17 14:07:53 +01:00
|
|
|
Utils::FilePaths sourceFiles = project.files(Project::SourceFiles);
|
2018-01-10 12:38:41 +01:00
|
|
|
QCOMPARE(sourceFiles.count(), 2);
|
|
|
|
|
QVERIFY(sourceFiles.contains(TEST_PROJECT_PATH));
|
|
|
|
|
QVERIFY(sourceFiles.contains(TEST_PROJECT_CPP_FILE));
|
|
|
|
|
|
|
|
|
|
project.setRootProjectNode(nullptr);
|
|
|
|
|
QCOMPARE(fileSpy.count(), 2);
|
2018-01-11 15:23:14 +02:00
|
|
|
QVERIFY(!project.rootProjectNode());
|
2018-01-10 12:38:41 +01:00
|
|
|
}
|
|
|
|
|
|
2021-05-05 11:18:51 +02:00
|
|
|
void ProjectExplorerPlugin::testProject_multipleBuildConfigs()
|
|
|
|
|
{
|
|
|
|
|
// Find suitable kit.
|
|
|
|
|
Kit * const kit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
|
|
|
|
|
return k->isValid();
|
|
|
|
|
});
|
|
|
|
|
if (!kit)
|
|
|
|
|
QSKIP("The test requires at least one valid kit.");
|
|
|
|
|
|
|
|
|
|
// Copy project from qrc file and set it up.
|
|
|
|
|
using namespace Utils;
|
|
|
|
|
QTemporaryDir * const tempDir = TemporaryDirectory::masterTemporaryDirectory();
|
|
|
|
|
QVERIFY(tempDir->isValid());
|
|
|
|
|
QString error;
|
|
|
|
|
const FilePath projectDir = FilePath::fromString(tempDir->path() + "/generic-project");
|
|
|
|
|
FileUtils::copyRecursively(FilePath::fromString(":/projectexplorer/testdata/generic-project"),
|
|
|
|
|
projectDir, &error);
|
|
|
|
|
QVERIFY2(error.isEmpty(), qPrintable(error));
|
|
|
|
|
const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files | QDir::Dirs);
|
|
|
|
|
for (const QFileInfo &f : files)
|
|
|
|
|
QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser);
|
2021-07-16 17:31:41 +02:00
|
|
|
const auto theProject = openProject(projectDir.pathAppended("generic-project.creator"));
|
2021-05-05 11:18:51 +02:00
|
|
|
QVERIFY2(theProject, qPrintable(theProject.errorMessage()));
|
|
|
|
|
theProject.project()->configureAsExampleProject(kit);
|
|
|
|
|
QCOMPARE(theProject.project()->targets().size(), 1);
|
|
|
|
|
Target * const target = theProject.project()->activeTarget();
|
|
|
|
|
QVERIFY(target);
|
|
|
|
|
QCOMPARE(target->buildConfigurations().size(), 6);
|
|
|
|
|
SessionManager::setActiveBuildConfiguration(target, target->buildConfigurations().at(1),
|
|
|
|
|
SetActive::Cascade);
|
|
|
|
|
BuildSystem * const bs = theProject.project()->activeTarget()->buildSystem();
|
|
|
|
|
QVERIFY(bs);
|
|
|
|
|
QCOMPARE(bs, target->activeBuildConfiguration()->buildSystem());
|
|
|
|
|
if (bs->isWaitingForParse() || bs->isParsing()) {
|
|
|
|
|
QEventLoop loop;
|
|
|
|
|
QTimer t;
|
|
|
|
|
t.setSingleShot(true);
|
|
|
|
|
connect(&t, &QTimer::timeout, &loop, &QEventLoop::quit);
|
|
|
|
|
connect(bs, &BuildSystem::parsingFinished, &loop, &QEventLoop::quit);
|
|
|
|
|
t.start(10000);
|
|
|
|
|
QVERIFY(loop.exec());
|
|
|
|
|
QVERIFY(t.isActive());
|
|
|
|
|
}
|
|
|
|
|
QVERIFY(!bs->isWaitingForParse() && !bs->isParsing());
|
|
|
|
|
|
|
|
|
|
QCOMPARE(SessionManager::startupProject(), theProject.project());
|
|
|
|
|
QCOMPARE(ProjectTree::currentProject(), theProject.project());
|
2021-05-25 06:52:00 +02:00
|
|
|
QVERIFY(Core::EditorManager::openEditor(projectDir.pathAppended("main.cpp")));
|
2021-05-05 11:18:51 +02:00
|
|
|
QVERIFY(ProjectTree::currentNode());
|
|
|
|
|
ProjectTree::instance()->expandAll();
|
|
|
|
|
SessionManager::closeAllProjects(); // QTCREATORBUG-25655
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 12:38:41 +01:00
|
|
|
#endif // WITH_TESTS
|
|
|
|
|
|
2010-09-21 09:17:37 +02:00
|
|
|
} // namespace ProjectExplorer
|