ProjectExplorer: Report project-specific kit errors/warnings

Report project-specific warnings about the kit used in Project Mode.
E.g. a python project should not complain about missing toolchains,
while a qmake project should.

Change-Id: I5ce6742683cdeffc7ff3f1a3e8f0b89aee9aa0b4
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Tobias Hunger
2018-04-18 13:39:05 +02:00
parent da18fc1f50
commit 460fdc02e0
17 changed files with 119 additions and 105 deletions

View File

@@ -363,14 +363,16 @@ bool CMakeProject::knowsAllBuildExecutables() const
return false;
}
bool CMakeProject::supportsKit(const Kit *k, QString *errorMessage) const
QList<Task> CMakeProject::projectIssues(const Kit *k) const
{
if (!CMakeKitInformation::cmakeTool(k)) {
if (errorMessage)
*errorMessage = tr("No cmake tool set.");
return false;
}
return true;
QList<Task> result = Project::projectIssues(k);
if (!CMakeKitInformation::cmakeTool(k))
result.append(createProjectTask(Task::TaskType::Error, tr("No cmake tool set.")));
if (ToolChainKitInformation::toolChains(k).isEmpty())
result.append(createProjectTask(Task::TaskType::Warning, tr("No compilers set in kit.")));
return result;
}
void CMakeProject::runCMake()

View File

@@ -67,7 +67,7 @@ public:
bool knowsAllBuildExecutables() const final;
bool supportsKit(const ProjectExplorer::Kit *k, QString *errorMessage = nullptr) const final;
QList<ProjectExplorer::Task> projectIssues(const ProjectExplorer::Kit *k) const final;
void runCMake();
void runCMakeAndScanProjectTree();

View File

@@ -154,20 +154,16 @@ void NimProject::updateProject()
emitParsingFinished(true);
}
bool NimProject::supportsKit(const Kit *k, QString *errorMessage) const
QList<Task> NimProject::projectIssues(const Kit *k) const
{
QList<Task> result = Project::projectIssues(k);
auto tc = dynamic_cast<NimToolChain*>(ToolChainKitInformation::toolChain(k, Constants::C_NIMLANGUAGE_ID));
if (!tc) {
if (errorMessage)
*errorMessage = tr("No Nim compiler set.");
return false;
}
if (!tc->compilerCommand().exists()) {
if (errorMessage)
*errorMessage = tr("Nim compiler does not exist.");
return false;
}
return true;
if (!tc)
result.append(createProjectTask(Task::TaskType::Error, tr("No Nim compiler set.")));
if (!tc->compilerCommand().exists())
result.append(createProjectTask(Task::TaskType::Error, tr("Nim compiler does not exist.")));
return result;
}
FileNameList NimProject::nimFiles() const

View File

@@ -41,7 +41,7 @@ class NimProject : public ProjectExplorer::Project
public:
explicit NimProject(const Utils::FileName &fileName);
bool supportsKit(const ProjectExplorer::Kit *k, QString *errorMessage) const final;
QList<ProjectExplorer::Task> projectIssues(const ProjectExplorer::Kit *k) const final;
Utils::FileNameList nimFiles() const;
QVariantMap toMap() const final;

View File

@@ -407,7 +407,7 @@ bool IBuildConfigurationFactory::canHandle(const Target *target) const
if (m_supportedProjectType.isValid() && m_supportedProjectType != target->project()->id())
return false;
if (!target->project()->supportsKit(target->kit()))
if (containsType(target->project()->projectIssues(target->kit()), Task::TaskType::Error))
return false;
if (!supportsTargetDeviceType(DeviceTypeKitInformation::deviceTypeId(target->kit())))

View File

@@ -184,7 +184,7 @@ bool DeployConfigurationFactory::canHandle(Target *target) const
return false;
}
if (!target->project()->supportsKit(target->kit()))
if (containsType(target->project()->projectIssues(target->kit()), Task::TaskType::Error))
return false;
if (!m_supportedTargetDeviceTypes.isEmpty()) {

View File

@@ -112,7 +112,7 @@ public:
int m_nestedBlockingLevel = 0;
bool m_autodetected = false;
bool m_sdkProvided = false;
bool m_isValid = true;
bool m_hasError = false;
bool m_hasWarning = false;
bool m_hasValidityInfo = false;
bool m_mustNotify = false;
@@ -209,7 +209,7 @@ Kit *Kit::clone(bool keepName) const
k->d->m_autodetected = false;
k->d->m_data = d->m_data;
// Do not clone m_fileSystemFriendlyName, needs to be unique
k->d->m_isValid = d->m_isValid;
k->d->m_hasError = d->m_hasError;
k->d->m_cachedIcon = d->m_cachedIcon;
k->d->m_iconPath = d->m_iconPath;
k->d->m_sticky = d->m_sticky;
@@ -240,7 +240,7 @@ bool Kit::isValid() const
if (!d->m_hasValidityInfo)
validate();
return d->m_isValid;
return !d->m_hasError;
}
bool Kit::hasWarning() const
@@ -255,18 +255,13 @@ QList<Task> Kit::validate() const
{
QList<Task> result;
QList<KitInformation *> infoList = KitManager::kitInformation();
d->m_isValid = true;
d->m_hasWarning = false;
foreach (KitInformation *i, infoList) {
for (KitInformation *i : infoList) {
QList<Task> tmp = i->validate(this);
foreach (const Task &t, tmp) {
if (t.type == Task::Error)
d->m_isValid = false;
if (t.type == Task::Warning)
d->m_hasWarning = true;
}
result.append(tmp);
}
d->m_hasError = containsType(result, Task::TaskType::Error);
d->m_hasWarning = containsType(result, Task::TaskType::Warning);
Utils::sort(result);
d->m_hasValidityInfo = true;
return result;

View File

@@ -198,7 +198,9 @@ Project::Project(const QString &mimeType, const Utils::FileName &fileName,
// Only set up containernode after d is set so that it will find the project directory!
d->m_containerNode = std::make_unique<ContainerNode>(this);
setRequiredKitPredicate([this](const Kit *k) { return supportsKit(k); });
setRequiredKitPredicate([this](const Kit *k) {
return !containsType(projectIssues(k), Task::TaskType::Error);
});
}
Project::~Project()
@@ -317,11 +319,12 @@ Target *Project::target(Kit *k) const
return Utils::findOrDefault(d->m_targets, Utils::equal(&Target::kit, k));
}
bool Project::supportsKit(const Kit *k, QString *errorMessage) const
QList<Task> Project::projectIssues(const Kit *k) const
{
Q_UNUSED(k);
Q_UNUSED(errorMessage);
return true;
QList<Task> result;
if (!k->isValid())
result.append(createProjectTask(Task::TaskType::Error, tr("Kit is not valid.")));
return {};
}
Target *Project::createTarget(Kit *k)
@@ -762,6 +765,11 @@ void Project::projectLoaded()
{
}
Task Project::createProjectTask(Task::TaskType type, const QString &description)
{
return Task(type, description, Utils::FileName(), -1, Core::Id());
}
Core::Context Project::projectContext() const
{
return Core::Context(d->m_id);

View File

@@ -122,7 +122,7 @@ public:
Target *activeTarget() const;
Target *target(Core::Id id) const;
Target *target(Kit *k) const;
virtual bool supportsKit(const Kit *k, QString *errorMessage = nullptr) const;
virtual QList<Task> projectIssues(const Kit *k) const;
Target *createTarget(Kit *k);
static bool copySteps(Target *sourceTarget, Target *newTarget);
@@ -238,6 +238,9 @@ protected:
void setProjectLanguage(Core::Id id, bool enabled);
virtual void projectLoaded(); // Called when the project is fully loaded.
static ProjectExplorer::Task createProjectTask(ProjectExplorer::Task::TaskType type,
const QString &description);
private:
// The predicate used to select kits available in TargetSetupPage.
void setRequiredKitPredicate(const Kit::Predicate &predicate);

View File

@@ -540,7 +540,7 @@ bool RunConfigurationFactory::canHandle(Target *target) const
const Project *project = target->project();
Kit *kit = target->kit();
if (!project->supportsKit(kit))
if (containsType(target->project()->projectIssues(kit), Task::TaskType::Error))
return false;
if (!m_supportedProjectTypes.isEmpty())

View File

@@ -40,6 +40,7 @@
#include "session.h"
#include "target.h"
#include "targetsetuppage.h"
#include "task.h"
#include <app/app_version.h>
@@ -282,9 +283,12 @@ class TargetItem : public TypedTreeItem<TreeItem, TargetGroupItem>
public:
enum { DefaultPage = 0 }; // Build page.
TargetItem(Project *project, Id kitId)
: m_project(project), m_kitId(kitId)
TargetItem(Project *project, Id kitId, const QList<Task> &issues)
: m_project(project), m_kitId(kitId), m_kitIssues(issues)
{
m_kitWarningForProject = containsType(m_kitIssues, Task::TaskType::Warning);
m_kitErrorsForProject = containsType(m_kitIssues, Task::TaskType::Error);
updateSubItems();
}
@@ -298,7 +302,8 @@ public:
Qt::ItemFlags flags(int column) const override
{
Q_UNUSED(column)
return Qt::ItemFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
return m_kitErrorsForProject ? Qt::ItemFlags(0)
: Qt::ItemFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}
QVariant data(int column, int role) const override
@@ -313,11 +318,11 @@ public:
case Qt::DecorationRole: {
const Kit *k = KitManager::kit(m_kitId);
QTC_ASSERT(k, return QVariant());
if (m_kitErrorsForProject)
return kitIconWithOverlay(*k, IconOverlay::Error);
if (!isEnabled())
return kitIconWithOverlay(*k, IconOverlay::Add);
if (!k->isValid())
return kitIconWithOverlay(*k, IconOverlay::Error);
if (k->hasWarning())
if (m_kitWarningForProject)
return kitIconWithOverlay(*k, IconOverlay::Warning);
return k->icon();
}
@@ -341,9 +346,12 @@ public:
Kit *k = KitManager::kit(m_kitId);
QTC_ASSERT(k, return QVariant());
QString toolTip;
if (!isEnabled())
if (m_kitErrorsForProject)
toolTip = "<h3>" + tr("Kit is unsuited for Project") + "</h3>";
else if (!isEnabled())
toolTip = "<h3>" + tr("Click to activate:") + "</h3>";
toolTip += k->toHtml();
if (!m_kitIssues.isEmpty())
toolTip += toHtml(m_kitIssues);
return toolTip;
}
@@ -466,12 +474,15 @@ public:
}
}
bool isEnabled() const { return target() != 0; }
bool isEnabled() const { return target() != nullptr; }
public:
QPointer<Project> m_project; // Not owned.
Id m_kitId;
int m_currentChild = DefaultPage; // Use run page by default.
int m_currentChild = DefaultPage;
bool m_kitErrorsForProject = false;
bool m_kitWarningForProject = false;
QList<Task> m_kitIssues;
private:
enum class IconOverlay {
@@ -526,10 +537,9 @@ public:
BuildOrRunItem(Project *project, Id kitId, SubIndex subIndex)
: m_project(project), m_kitId(kitId), m_subIndex(subIndex)
{
}
{ }
~BuildOrRunItem()
~BuildOrRunItem() override
{
delete m_panel;
}
@@ -769,7 +779,7 @@ TargetItem *TargetGroupItem::targetItem(Target *target) const
Id needle = target->id(); // Unconfigured project have no active target.
return findFirstLevelChild([needle](TargetItem *item) { return item->m_kitId == needle; });
}
return 0;
return nullptr;
}
void TargetGroupItemPrivate::handleRemovedKit(Kit *kit)
@@ -786,8 +796,7 @@ void TargetGroupItemPrivate::handleUpdatedKit(Kit *kit)
void TargetGroupItemPrivate::handleAddedKit(Kit *kit)
{
if (m_project->supportsKit(kit))
q->appendChild(new TargetItem(m_project, kit->id()));
q->appendChild(new TargetItem(m_project, kit->id(), m_project->projectIssues(kit)));
}
void TargetItem::updateSubItems()
@@ -805,11 +814,9 @@ void TargetGroupItemPrivate::rebuildContents()
{
q->removeChildren();
const QList<Kit *> kits = KitManager::sortKits(KitManager::kits([this](const Kit *kit) {
return m_project->supportsKit(const_cast<Kit *>(kit));
}));
const QList<Kit *> kits = KitManager::sortKits(KitManager::kits());
for (Kit *kit : kits)
q->appendChild(new TargetItem(m_project, kit->id()));
q->appendChild(new TargetItem(m_project, kit->id(), m_project->projectIssues(kit)));
if (q->parent())
q->parent()->setData(0, QVariant::fromValue(static_cast<TreeItem *>(q)),

View File

@@ -25,13 +25,15 @@
#include "task.h"
#include "projectexplorerconstants.h"
#include <app/app_version.h>
#include <texteditor/textmark.h>
#include <utils/algorithm.h>
#include <utils/utilsicons.h>
#include <utils/qtcassert.h>
#include "projectexplorerconstants.h"
#include <QTextStream>
namespace ProjectExplorer
@@ -174,4 +176,9 @@ QString toHtml(const QList<Task> &issues)
return result;
}
bool containsType(const QList<Task> &issues, Task::TaskType type)
{
return Utils::contains(issues, [type](const Task &t) { return t.type == type; });
}
} // namespace ProjectExplorer

View File

@@ -99,6 +99,7 @@ uint PROJECTEXPLORER_EXPORT qHash(const Task &task);
bool PROJECTEXPLORER_EXPORT operator<(const Task &a, const Task &b);
QString PROJECTEXPLORER_EXPORT toHtml(const QList<Task> &issues);
bool PROJECTEXPLORER_EXPORT containsType(const QList<Task> &issues, Task::TaskType);
} //namespace ProjectExplorer

View File

@@ -50,6 +50,7 @@
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
@@ -596,12 +597,14 @@ void QmakeProject::buildFinished(bool success)
m_invalidateQmakeVfsContents = true;
}
bool QmakeProject::supportsKit(const Kit *k, QString *errorMessage) const
QList<Task> QmakeProject::projectIssues(const Kit *k) const
{
QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(k);
if (!version && errorMessage)
*errorMessage = tr("No Qt version set in kit.");
return version;
QList<Task> result = Project::projectIssues(k);
if (!QtSupport::QtKitInformation::qtVersion(k))
result.append(createProjectTask(Task::TaskType::Error, tr("No Qt version set in kit.")));
if (!ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID))
result.append(createProjectTask(Task::TaskType::Error, tr("No C++ compiler set in kit.")));
return result;
}
// Find the folder that contains a file with a certain name (recurse down)

View File

@@ -60,7 +60,7 @@ public:
QmakeProFile *rootProFile() const;
bool supportsKit(const ProjectExplorer::Kit *k, QString *errorMesage) const final;
QList<ProjectExplorer::Task> projectIssues(const ProjectExplorer::Kit *k) const final;
QmakeProFileNode *rootProjectNode() const final;

View File

@@ -275,52 +275,42 @@ void QmlProject::refreshTargetDirectory()
updateDeploymentData(target);
}
bool QmlProject::supportsKit(const Kit *k, QString *errorMessage) const
QList<Task> QmlProject::projectIssues(const Kit *k) const
{
if (!k->isValid()) {
if (errorMessage)
*errorMessage = tr("Kit is not valid.");
return false;
}
QList<Task> result = Project::projectIssues(k);
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(k);
if (!version)
result.append(createProjectTask(Task::TaskType::Error, tr("No Qt version set in kit.")));
IDevice::ConstPtr dev = DeviceKitInformation::device(k);
if (dev.isNull()) {
if (errorMessage)
*errorMessage = tr("Kit has no device.");
return false;
}
if (dev.isNull())
result.append(createProjectTask(Task::TaskType::Error, tr("Kit has no device.")));
QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(k);
if (!version) {
if (errorMessage)
*errorMessage = tr("No Qt version set in kit.");
return false;
}
if (version->qtVersion() < QtSupport::QtVersionNumber(5, 0, 0)) {
if (errorMessage)
*errorMessage = tr("Qt version is too old.");
return false;
}
if (version && version->qtVersion() < QtSupport::QtVersionNumber(5, 0, 0))
result.append(createProjectTask(Task::TaskType::Error, tr("Qt version is too old.")));
if (dev.isNull() || !version)
return result; // No need to check deeper than this
if (dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
if (version->type() == QtSupport::Constants::DESKTOPQT) {
if (static_cast<QtSupport::DesktopQtVersion *>(version)->qmlsceneCommand().isEmpty()) {
if (errorMessage)
*errorMessage = tr("Qt version has no qmlscene command.");
return false;
if (static_cast<const QtSupport::DesktopQtVersion *>(version)->qmlsceneCommand().isEmpty()) {
result.append(createProjectTask(Task::TaskType::Error,
tr("Qt version has no qmlscene command.")));
}
} else {
// Non-desktop Qt on a desktop device? We don't support that.
if (errorMessage)
*errorMessage = tr("Non-desktop Qt is used with a Desktop device.");
return false;
result.append(createProjectTask(Task::TaskType::Error,
tr("Non-desktop Qt is used with a Desktop device.")));
}
}
} else {
// If not a desktop device, don't check the Qt version for qmlscene.
// The device is responsible for providing it and we assume qmlscene can be found
// in $PATH if it's not explicitly given.
return true;
}
return result;
}
Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage)
@@ -334,7 +324,9 @@ Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *erro
if (!activeTarget()) {
// find a kit that matches prerequisites (prefer default one)
const QList<Kit*> kits = KitManager::kits([this](const Kit *k) { return supportsKit(k, nullptr); });
const QList<Kit*> kits = KitManager::kits([this](const Kit *k) {
return !containsType(projectIssues(k), Task::TaskType::Error);
});
if (!kits.isEmpty()) {
Kit *kit = kits.contains(KitManager::defaultKit()) ? KitManager::defaultKit() : kits.first();

View File

@@ -48,7 +48,7 @@ public:
explicit QmlProject(const Utils::FileName &filename);
~QmlProject() override;
bool supportsKit(const ProjectExplorer::Kit *k, QString *errorMessage = nullptr) const final;
QList<ProjectExplorer::Task> projectIssues(const ProjectExplorer::Kit *k) const final;
bool validProjectFile() const;