diff --git a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp index c74f3a2f516..d4ca71687bc 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp @@ -58,12 +58,19 @@ void JsonKitsPage::initializePage() const Id platform = Id::fromString(wiz->stringValue(QLatin1String("Platform"))); const QSet preferred = evaluate(m_preferredFeatures, wiz->value(QLatin1String("PreferredFeatures")), wiz); - const QSet required - = evaluate(m_requiredFeatures, wiz->value(QLatin1String("RequiredFeatures")), wiz); + const QSet required = evaluate(m_requiredFeatures, + wiz->value(QLatin1String("RequiredFeatures")), + wiz); - setRequiredKitPredicate([required](const Kit *k) { return k->hasFeatures(required); }); - setPreferredKitPredicate([platform, preferred](const Kit *k) { - return k->supportedPlatforms().contains(platform) && k->hasFeatures(preferred); + setTasksGenerator([required, preferred, platform](const Kit *k) -> Tasks { + if (!k->hasFeatures(required)) + return {CompileTask(Task::Error, tr("At least one required feature is not present."))}; + if (!k->supportedPlatforms().contains(platform)) + return {CompileTask(Task::Unknown, tr("Platform is not supported."))}; + if (!k->hasFeatures(preferred)) + return { + CompileTask(Task::Unknown, tr("At least one preferred feature is not present."))}; + return {}; }); setProjectPath(wiz->expander()->expand(Utils::FilePath::fromString(unexpandedProjectPath()))); diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index 9532bce6cba..75f52ec1c89 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -567,13 +567,16 @@ IOutputParser *Kit::createOutputParser() const return first; } -QString Kit::toHtml(const Tasks &additional) const +QString Kit::toHtml(const Tasks &additional, const QString &extraText) const { QString result; QTextStream str(&result); str << ""; str << "

" << displayName() << "

"; + if (!extraText.isEmpty()) + str << "

" << extraText << "

"; + if (!isValid() || hasWarning() || !additional.isEmpty()) str << "

" << ProjectExplorer::toHtml(additional + validate()) << "

"; diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index 52aed192ffa..fe76b750dc2 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -118,7 +118,7 @@ public: void addToEnvironment(Utils::Environment &env) const; IOutputParser *createOutputParser() const; - QString toHtml(const Tasks &additional = Tasks()) const; + QString toHtml(const Tasks &additional = Tasks(), const QString &extraText = QString()) const; Kit *clone(bool keepName = false) const; void copyFrom(const Kit *k); @@ -173,6 +173,8 @@ private: Kit *m_kit; }; +using TasksGenerator = std::function; + } // namespace ProjectExplorer Q_DECLARE_METATYPE(ProjectExplorer::Kit *) diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index c2f20a6ccd1..c9b6ef0463b 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -201,9 +201,6 @@ public: QString m_displayName; - Kit::Predicate m_requiredKitPredicate; - Kit::Predicate m_preferredKitPredicate; - Utils::MacroExpander m_macroExpander; Utils::FilePath m_rootProjectDirectory; mutable QVector m_sortedNodeList; @@ -229,10 +226,6 @@ Project::Project(const QString &mimeType, // Only set up containernode after d is set so that it will find the project directory! d->m_containerNode = std::make_unique(this); - - setRequiredKitPredicate([this](const Kit *k) { - return !containsType(projectIssues(k), Task::TaskType::Error); - }); } Project::~Project() @@ -974,31 +967,11 @@ ProjectImporter *Project::projectImporter() const return nullptr; } -Kit::Predicate Project::requiredKitPredicate() const -{ - return d->m_requiredKitPredicate; -} - -void Project::setRequiredKitPredicate(const Kit::Predicate &predicate) -{ - d->m_requiredKitPredicate = predicate; -} - void Project::setCanBuildProducts() { d->m_canBuildProducts = true; } -Kit::Predicate Project::preferredKitPredicate() const -{ - return d->m_preferredKitPredicate; -} - -void Project::setPreferredKitPredicate(const Kit::Predicate &predicate) -{ - d->m_preferredKitPredicate = predicate; -} - void Project::setExtraData(const QString &key, const QVariant &data) { d->m_extraData.insert(key, data); diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index bc2bee3b457..5fb9c3ad8e7 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -144,9 +144,6 @@ public: virtual ProjectImporter *projectImporter() const; - Kit::Predicate requiredKitPredicate() const; - Kit::Predicate preferredKitPredicate() const; - // The build system is able to report all executables that can be built, independent // of configuration. bool knowsAllBuildExecutables() const; @@ -208,12 +205,6 @@ protected: void createTargetFromMap(const QVariantMap &map, int index); virtual bool setupTarget(Target *t); - // Used to pre-check kits in the TargetSetupPage. RequiredKitPredicate - // is used to select kits available in the TargetSetupPage - void setPreferredKitPredicate(const Kit::Predicate &predicate); - // The predicate used to select kits available in TargetSetupPage. - void setRequiredKitPredicate(const Kit::Predicate &predicate); - void setCanBuildProducts(); void setId(Core::Id id); diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index e84f0280896..9d3c1ee9d3a 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -159,8 +159,8 @@ void TargetSetupPageWrapper::addTargetSetupPage() m_targetSetupPage = new TargetSetupPage(this); m_targetSetupPage->setUseScrollArea(false); m_targetSetupPage->setProjectPath(m_project->projectFilePath()); - m_targetSetupPage->setRequiredKitPredicate(m_project->requiredKitPredicate()); - m_targetSetupPage->setPreferredKitPredicate(m_project->preferredKitPredicate()); + m_targetSetupPage->setTasksGenerator( + [this](const Kit *k) { return m_project->projectIssues(k); }); m_targetSetupPage->setProjectImporter(m_project->projectImporter()); m_targetSetupPage->initializePage(); m_targetSetupPage->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); @@ -322,14 +322,14 @@ public: case Qt::ToolTipRole: { Kit *k = KitManager::kit(m_kitId); QTC_ASSERT(k, return QVariant()); - QString toolTip; - if (m_kitErrorsForProject) - toolTip = "

" + tr("Kit is unsuited for project") + "

"; - else if (!isEnabled()) - toolTip = "

" + tr("Click to activate:") + "

" + k->toHtml(); - if (!m_kitIssues.isEmpty()) - toolTip += toHtml(m_kitIssues); - return toolTip; + const QString extraText = [this]() { + if (m_kitErrorsForProject) + return QString("

" + tr("Kit is unsuited for project") + "

"); + if (!isEnabled()) + return QString("

" + tr("Click to activate") + "

"); + return QString(); + }(); + return k->toHtml(m_kitIssues, extraText); } case PanelWidgetRole: diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index e7ece0a9b4b..e750f06adf0 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -26,14 +26,15 @@ #include "targetsetuppage.h" #include "buildconfiguration.h" #include "buildinfo.h" +#include "importwidget.h" #include "kit.h" #include "kitmanager.h" -#include "importwidget.h" #include "project.h" #include "projectexplorerconstants.h" #include "session.h" #include "target.h" #include "targetsetupwidget.h" +#include "task.h" #include @@ -159,13 +160,27 @@ public: } // namespace Internal +static TasksGenerator defaultTasksGenerator(const TasksGenerator &childGenerator) +{ + return [childGenerator](const Kit *k) -> Tasks { + if (!k->isValid()) + return { + CompileTask(Task::Error, + QCoreApplication::translate("ProjectExplorer", "Kit is not valid."))}; + if (childGenerator) + return childGenerator(k); + return {}; + }; +} + using namespace Internal; -TargetSetupPage::TargetSetupPage(QWidget *parent) : - WizardPage(parent), - m_ui(new TargetSetupPageUi), - m_importWidget(new ImportWidget(this)), - m_spacer(new QSpacerItem(0,0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding)) +TargetSetupPage::TargetSetupPage(QWidget *parent) + : WizardPage(parent) + , m_tasksGenerator(defaultTasksGenerator({})) + , m_ui(new TargetSetupPageUi) + , m_importWidget(new ImportWidget(this)) + , m_spacer(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding)) { m_importWidget->setVisible(false); @@ -217,9 +232,9 @@ void TargetSetupPage::initializePage() } } -void TargetSetupPage::setRequiredKitPredicate(const Kit::Predicate &predicate) +void TargetSetupPage::setTasksGenerator(const TasksGenerator &tasksGenerator) { - m_requiredPredicate = predicate; + m_tasksGenerator = defaultTasksGenerator(tasksGenerator); } QList TargetSetupPage::selectedKits() const @@ -232,11 +247,6 @@ QList TargetSetupPage::selectedKits() const return result; } -void TargetSetupPage::setPreferredKitPredicate(const Kit::Predicate &predicate) -{ - m_preferredPredicate = predicate; -} - TargetSetupPage::~TargetSetupPage() { disconnect(); @@ -396,8 +406,10 @@ void TargetSetupPage::selectAtLeastOneEnabledKit() TargetSetupWidget *toCheckWidget = nullptr; const Kit *defaultKit = KitManager::defaultKit(); + auto isPreferred = [this](const TargetSetupWidget *w) { - return w->isEnabled() && (!m_preferredPredicate || m_preferredPredicate(w->kit())); + const Tasks tasks = m_tasksGenerator(w->kit()); + return w->isEnabled() && tasks.isEmpty(); }; // Use default kit if that is preferred: @@ -620,12 +632,12 @@ void TargetSetupPage::removeAdditionalWidgets(QLayout *layout) void TargetSetupPage::updateWidget(TargetSetupWidget *widget) { QTC_ASSERT(widget, return ); - widget->update(m_requiredPredicate); + widget->update(m_tasksGenerator); } bool TargetSetupPage::isUsable(const Kit *kit) const { - return kit->isValid() && (!m_requiredPredicate || m_requiredPredicate(kit)); + return !containsType(m_tasksGenerator(kit), Task::Error); } bool TargetSetupPage::setupProject(Project *project) diff --git a/src/plugins/projectexplorer/targetsetuppage.h b/src/plugins/projectexplorer/targetsetuppage.h index 095e5d9e2be..82c24040348 100644 --- a/src/plugins/projectexplorer/targetsetuppage.h +++ b/src/plugins/projectexplorer/targetsetuppage.h @@ -30,6 +30,7 @@ #include "kitinformation.h" #include "kitmanager.h" #include "projectimporter.h" +#include "task.h" #include @@ -66,8 +67,7 @@ public: void initializePage() override; // Call these before initializePage! - void setRequiredKitPredicate(const Kit::Predicate &predicate); - void setPreferredKitPredicate(const Kit::Predicate &predicate); + void setTasksGenerator(const TasksGenerator &tasksGenerator); void setProjectPath(const Utils::FilePath &dir); void setProjectImporter(ProjectImporter *importer); bool importLineEditHasFocus() const; @@ -125,8 +125,7 @@ private: Internal::TargetSetupWidget *widget(const Core::Id kitId, Internal::TargetSetupWidget *fallback = nullptr) const; - Kit::Predicate m_requiredPredicate; - Kit::Predicate m_preferredPredicate; + TasksGenerator m_tasksGenerator; QPointer m_importer; QLayout *m_baseLayout = nullptr; Utils::FilePath m_projectPath; diff --git a/src/plugins/projectexplorer/targetsetupwidget.cpp b/src/plugins/projectexplorer/targetsetupwidget.cpp index 31b3f5d6af3..281723dedd2 100644 --- a/src/plugins/projectexplorer/targetsetupwidget.cpp +++ b/src/plugins/projectexplorer/targetsetupwidget.cpp @@ -224,24 +224,25 @@ void TargetSetupWidget::expandWidget() m_detailsWidget->setState(Utils::DetailsWidget::Expanded); } -void TargetSetupWidget::update(const Kit::Predicate &predicate) +void TargetSetupWidget::update(const TasksGenerator &generator) { - m_detailsWidget->setSummaryText(kit()->displayName()); + const Tasks tasks = generator(kit()); - // Kits that we deem invalid get a warning icon, but users can still select them, - // e.g. in case we misdetected an ABI mismatch. - // Kits that don't fulfill the project predicate are not selectable, because we cannot + m_detailsWidget->setSummaryText(kit()->displayName()); + m_detailsWidget->setIcon(kit()->isValid() ? kit()->icon() : Icons::CRITICAL.icon()); + + const Task errorTask = Utils::findOrDefault(tasks, Utils::equal(&Task::type, Task::Error)); + + // Kits that where the taskGenarator reports an error are not selectable, because we cannot // guarantee that we can handle the project sensibly (e.g. qmake project without Qt). - if (predicate && !predicate(kit())) { + if (!errorTask.isNull()) { toggleEnabled(false); + m_detailsWidget->setToolTip(kit()->toHtml(tasks, "")); m_infoStore.clear(); - m_detailsWidget->setToolTip(tr("You cannot use this kit, because it does not fulfill " - "the project's prerequisites.")); return; } + toggleEnabled(true); - m_detailsWidget->setIcon(kit()->isValid() ? kit()->icon() : Icons::CRITICAL.icon()); - m_detailsWidget->setToolTip(m_kit->toHtml()); updateDefaultBuildDirectories(); } diff --git a/src/plugins/projectexplorer/targetsetupwidget.h b/src/plugins/projectexplorer/targetsetupwidget.h index 6534baf7009..02d44706b9d 100644 --- a/src/plugins/projectexplorer/targetsetupwidget.h +++ b/src/plugins/projectexplorer/targetsetupwidget.h @@ -70,7 +70,7 @@ public: const QList selectedBuildInfoList() const; void setProjectPath(const Utils::FilePath &projectPath); void expandWidget(); - void update(const Kit::Predicate &predicate); + void update(const TasksGenerator &generator); signals: void selectedToggled() const; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 91987e2574f..b7bb4693558 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -117,16 +117,15 @@ private: /*! \class QmakeProject - QmakeProject manages information about an individual Qt 4 (.pro) project file. + QmakeProject manages information about an individual qmake project file (.pro). */ -static bool matchesKit(const Project *p, const Kit *kit) +static QtSupport::BaseQtVersion *projectIsPartOfQt(const Project *p) { FilePath filePath = p->projectFilePath(); - QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit); - return QtSupport::QtVersionManager::version([&filePath, version](const QtSupport::BaseQtVersion *v) { - return v->isValid() && v->isSubProject(filePath) && v == version; + return QtSupport::QtVersionManager::version([&filePath](const QtSupport::BaseQtVersion *v) { + return v->isValid() && v->isSubProject(filePath); }); } @@ -138,8 +137,6 @@ QmakeProject::QmakeProject(const FilePath &fileName) : setDisplayName(fileName.toFileInfo().completeBaseName()); setCanBuildProducts(); setHasMakeInstallEquivalent(true); - - setPreferredKitPredicate([this](const Kit *kit) -> bool { return matchesKit(this, kit); }); } QmakeProject::~QmakeProject() @@ -603,12 +600,21 @@ void QmakeBuildSystem::buildFinished(bool success) Tasks QmakeProject::projectIssues(const Kit *k) const { Tasks result = Project::projectIssues(k); - if (!QtSupport::QtKitAspect::qtVersion(k)) + const QtSupport::BaseQtVersion *const qtFromKit = QtSupport::QtKitAspect::qtVersion(k); + if (!qtFromKit) result.append(createProjectTask(Task::TaskType::Error, tr("No Qt version set in kit."))); - else if (!QtSupport::QtKitAspect::qtVersion(k)->isValid()) + else if (!qtFromKit->isValid()) result.append(createProjectTask(Task::TaskType::Error, tr("Qt version is invalid."))); if (!ToolChainKitAspect::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID)) result.append(createProjectTask(Task::TaskType::Error, tr("No C++ compiler set in kit."))); + + const QtSupport::BaseQtVersion *const qtThatContainsProject = projectIsPartOfQt(this); + if (qtThatContainsProject && qtThatContainsProject != qtFromKit) { + result.append(CompileTask(Task::Warning, + tr("Project is part of Qt sources that do not match " + "the Qt defined in the Kit"))); + } + return result; } diff --git a/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp b/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp index c6d90f617f3..38074cb0c61 100644 --- a/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp +++ b/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -178,14 +179,23 @@ BaseQmakeProjectWizardDialog::~BaseQmakeProjectWizardDialog() int BaseQmakeProjectWizardDialog::addTargetSetupPage(int id) { m_targetSetupPage = new ProjectExplorer::TargetSetupPage; - const Core::Id platform = selectedPlatform(); - QSet features = {QtSupport::Constants::FEATURE_DESKTOP}; - if (!platform.isValid()) - m_targetSetupPage->setPreferredKitPredicate(QtKitAspect::qtVersionPredicate(features)); - else - m_targetSetupPage->setPreferredKitPredicate(QtKitAspect::platformPredicate(platform)); - m_targetSetupPage->setRequiredKitPredicate(QtKitAspect::qtVersionPredicate(requiredFeatures())); + m_targetSetupPage->setTasksGenerator([this](const Kit *k) -> Tasks { + if (!QtKitAspect::qtVersionPredicate(requiredFeatures())(k)) + return { + ProjectExplorer::CompileTask(Task::Error, tr("Required Qt features not present."))}; + + const Core::Id platform = selectedPlatform(); + if (platform.isValid() && !QtKitAspect::platformPredicate(platform)(k)) + return {ProjectExplorer::CompileTask( + ProjectExplorer::Task::Warning, + tr("Qt version does not target the expected platform."))}; + QSet features = {QtSupport::Constants::FEATURE_DESKTOP}; + if (!QtKitAspect::qtVersionPredicate(features)(k)) + return {ProjectExplorer::CompileTask(ProjectExplorer::Task::Unknown, + tr("Qt version does not provide all features."))}; + return {}; + }); resize(900, 450); if (id >= 0)